Discuss Scratch

sippingcider
Scratcher
500+ posts

Fast Broadcast and Wait - getting local clone variables

I am trying to make a rugby type of game. I have 1 sprite that makes clones of all the different players. Each player has different stats (strength and speed) and different shapes for their costumes. When two clones collide they need to check the strength of the clone they are colliding with so they can compare it to their own and determine which one pushes the other one back. In other words, each clone needs to be able to get the local variables of the clone they are colliding with, and since they have different costumes the only way to detect for collisions is using the
<touching [ v] ?> 
block.

The solution that I came up with (if you have a better one please share it) is to have a second sprite, with all the same costumes, that will quickly loop through each clone, changing to its costume and then going to its position. The clones update their positions and costumes in global lists, so the 2nd sprite will be able to go to their positions. I havn't made the code yet, but it will go something like this:

1st Sprite:
define Check for Collisions
set [i v] to [1]
repeat (length of [Clone Costumes v] :: list)
set [COSTUME v] to (item (i) of [Clone Costumes v] :: list)
set [GOTO_X v] to (item (i) of [Clone X v] :: list)
set [GOTO_Y v] to (item (i) of [Clone Y v] :: list)
broadcast [TEST_COLLISION v] and wait
if <touching [Sprite2 v] ?> then
doCollisionStuff
end
change [i v] by [1]

2nd Sprite:
when I receive [TEST_COLLISION v]
switch costume to (COSTUME)
go to x: (GOTO_X) y: (GOTO_Y)

So here's the issue: That broadcast and wait block will really slow this down. I want that Check for Collisions block to be a run without refresh. However, I need to know that sprite 2 is in the correct position and costume before running that if statement.

Is there someway of telling the 2nd sprite to move to the position and change costume , and then doing the check without using the broadcast and wait block?
_nix
Scratcher
1000+ posts

Fast Broadcast and Wait - getting local clone variables

Unfortunately, “broadcast and wait” always takes one tick to evaluate (assuming the receiver script takes 0 or 1 ticks to run) – rather thank thinking of the broadcast blocks as “do the code under this hat now”, it's more like “mark the code under this hat to run” (and in the case of “broadcast and wait”, “and wait for the code to have completed”).

Perhaps you could get away with simply using a loop and searching for the closest player? For example, you would do a script like this:

define find closest clone index
set [closest index v] to [1]
set [closest distance v] to [99999] // Rough hack, but works in any practical case :P
set [parse v] to [1]
repeat (# of clones)
if <not <(parse) = (my index)>> then // Don't count ourselves (to which our distance is zero)!
get distance to x: (item (parse) of [Clone X v]) y: (item (parse) of [Clone Y v])
if <(distance :: variables) < (closest distance)> then
set [closest index v] to (parse)
set [closest distance v] to (distance :: variables)
end
end
end

define get distance to x: (x) y: (y)
set [delta x v] to ((x) - (x position))
set [delta y v] to ((y) - (y position))
set [distance v] to ([sqrt v] of (((delta x) * (delta x)) + ((delta y) * (delta y)))

when I start as a clone
forever
if <touching [sprite v] ?> then
find closest clone index
set [other player's strength v] to (item (closest index) of [Clone Strengths v])
...
end
end

So, once we know we're certainly touching another clone, we use the custom block to search for the closest sprite. There's one corner case: If two players are both touching another clone, then only the closest one would be counted. (This could happen if they have different costumes or rotations.) If that's likely that is to happen, you might want to use some sort of a threshold; I won't go into the scripts in this post, but basically, once you know you're touching a sprite, instead of acting on just the closest one, you'd act on all of the ones that are within an arbitrary range of the clone (e.g. 30 pixels).

══ trans autistic lesbian enbydoggirls // 16 17 18 19 20, she/they
sparrows one word to the paragraph // <3 // ~(quasar) nebula
sippingcider
Scratcher
500+ posts

Fast Broadcast and Wait - getting local clone variables

_nix wrote:

Unfortunately, “broadcast and wait” always takes one tick to evaluate (assuming the receiver script takes 0 or 1 ticks to run) – rather thank thinking of the broadcast blocks as “do the code under this hat now”, it's more like “mark the code under this hat to run” (and in the case of “broadcast and wait”, “and wait for the code to have completed”).

Perhaps you could get away with simply using a loop and searching for the closest player? For example, you would do a script like this:

define find closest clone index
set [closest index v] to [1]
set [closest distance v] to [99999] // Rough hack, but works in any practical case :P
set [parse v] to [1]
repeat (# of clones)
if <not <(parse) = (my index)>> then // Don't count ourselves (to which our distance is zero)!
get distance to x: (item (parse) of [Clone X v]) y: (item (parse) of [Clone Y v])
if <(distance :: variables) < (closest distance)> then
set [closest index v] to (parse)
set [closest distance v] to (distance :: variables)
end
end
end

define get distance to x: (x) y: (y)
set [delta x v] to ((x) - (x position))
set [delta y v] to ((y) - (y position))
set [distance v] to ([sqrt v] of (((delta x) * (delta x)) + ((delta y) * (delta y)))

when I start as a clone
forever
if <touching [sprite v] ?> then
find closest clone index
set [other player's strength v] to (item (closest index) of [Clone Strengths v])
...
end
end

So, once we know we're certainly touching another clone, we use the custom block to search for the closest sprite. There's one corner case: If two players are both touching another clone, then only the closest one would be counted. (This could happen if they have different costumes or rotations.) If that's likely that is to happen, you might want to use some sort of a threshold; I won't go into the scripts in this post, but basically, once you know you're touching a sprite, instead of acting on just the closest one, you'd act on all of the ones that are within an arbitrary range of the clone (e.g. 30 pixels).

Thanks for taking all the time to write that post out! However, there will be many cases where multiple clones are touching each other, and each clone will need to get the strength of all the clones it is touching. To make matters worse, they will have different rotations, sizes, and shapes (for example, right now one of the players is the bear sprite, which is huge and wide shaped, while another player is scratch cat, which is tiny and long shaped.) So coming up with threshholds will be rather difficult, depending on the costume and rotation.

Last edited by sippingcider (Feb. 13, 2019 21:09:41)

sippingcider
Scratcher
500+ posts

Fast Broadcast and Wait - getting local clone variables

Ok, here is what @theLogFather suggested:

No, not using b-a-w. As I said, cleverly crafted with *multiple* broadcasts. –You have to have a broadcast that hides some subset of the clones, followed by another that does the touching test, followed by another that hides a different subset of clones, followed by another (different one) that does another touch test, etc. Note that I'd be assuming some relatively small upper bound on the no. simultaneously touching clones, and you'd use an initial bounding-box test to reduce it down to that.

Anyone think they can decipher it He is talking about a slightly different way that doesn't use a 2nd sprite, just the clones themselves.

I think he is saying something like this:

set [hideGroup v] to [group1]
broadcast [hideClones v]
broadcast [detectTouches v]
set [hideGroup v] to [group2]
broadcast [hideClones v]
broadcast [detectTouches v]

and in the receivers:

when I receive [Hidegroup v]
if <(myGroup) = (hideGroup)> then
hide
end

when I receive [detectTouches v]
if <touching [Sprite1 v]> then
add (hideGroup) to [myTouches v]
end

where myTouches list is a local list for the clones and myGroup is a variable assigned when the clones are spawned. Then, each clone, using the groups they have in their myTouches list, will be able to determine which of the clones they were touching.

Only thing is, I don't see how they would do it exactly, nor do I see how this would work without broadcast and wait…

Last edited by sippingcider (Feb. 13, 2019 23:18:58)

imfh
Scratcher
1000+ posts

Fast Broadcast and Wait - getting local clone variables

If the sprites aren't freely rotatable, you could calculate collision with a rectangular hitbox. You would make a list of the width and height for each costume. Then, with 1 sprite, you could use that information to loop through each clone and check if it is colliding with any other clone.

Scratch to Pygame converter: https://scratch.mit.edu/discuss/topic/600562/
sippingcider
Scratcher
500+ posts

Fast Broadcast and Wait - getting local clone variables

imfh wrote:

If the sprites aren't freely rotatable, you could calculate collision with a rectangular hitbox. You would make a list of the width and height for each costume. Then, with 1 sprite, you could use that information to loop through each clone and check if it is colliding with any other clone.

Thanks for the suggestion. However, the sprites are freely rotatable, and having precise collisions is pretty important for this game, otherwise it looks kinda funky.

I was originally going to make this with a 1 sprite for each player, that way the collision detection could be done easily, but I wanted to see if there was a way to do it with clones so that I wouldn't have to update all of the player sprite's code every-time I make a change to the player code. However, its not too much work to do that, so I think I will just do that.

Powered by DjangoBB