Discuss Scratch

alexanderyou
New to Scratch
2 posts

Is it possible to create a "create clone of ( )" block but with the ability to transfer information with it?

As title says, I would like, for example, a clone enemy to create clone bullets. Similar to how when you do “create clone of myself” it makes a copy of the current sprite-only variables and you can use that to transfer information to the clone, I was wondering if there was a way to create a clone of another sprite and give it information from outside.
MegaApuTurkUltra
Scratcher
1000+ posts

Is it possible to create a "create clone of ( )" block but with the ability to transfer information with it?

yes

$(".box-head")[0].textContent = "committing AT crimes since $whenever"
_nix
Scratcher
1000+ posts

Is it possible to create a "create clone of ( )" block but with the ability to transfer information with it?

I'm not sure there's a good immediate way to send data to a newly-created clone from another sprite. A simple solution is to do something like this:

set [clone position x v] to [100] // global
set [clone position y v] to [100] // (also global)
create clone of [some other sprite v]

when I start as a clone // in the other sprite
go to x: (clone position x) y: (clone position y)

..but this only works if you only create one clone at a time. If you try this instead, it won't work:

run instantaneously // this custom block has "run without screen refresh" marked

define run instantaneously
set [clone position x v] to [0]
repeat (3)
change [clone position x v] by (40)
create clone of [some other sprite v]
end

All the clones will appear at X position 120, instead of 40, 80, 120. This is because the “when I start as a clone, go to x/y” script in the clone doesn't run until “run instantaneously” is done. (Note that if you weren't using a run-without-screen-refresh custom block, this would work, since “when I start as a clone” would run immediately after “create clone of some other sprite”.)

If you're trying to create many clones instantaneously by using a “run without screen refresh” block, you could use a queue of sorts:

define run instantaneously
delete (all v) of [clone x position queue v]
set [n v] to (0)
repeat (3)
change [n v] by (40)
add (n) to [clone x position queue v]
create clone of [some other sprite v]
end

when I start as a clone
set x to (item (last v) of [clone x position queue v]
delete (last v) of [clone x position queue v]

As a clone is spawned, it pops off the last item of the X-position queue (and goes to that position). So the next clone pops off the next value, etc.



Longer explanation, and why the above things work:

If you haven't already guessed, Scratch only actually runs one script at a time; it's not multi-threaded. Normally it gives an illusion of multi-threading by having particular blocks yield, letting the another script run. So, for example, the end of a “repeat” iteration causes a yield; you can see this visually by running both of these scripts at the same time:

when green flag clicked
repeat (10)
change x by (10)
end

when green flag clicked
repeat (10)
wait (0.2) seconds without yielding // "run without screen refresh" block - I'll get to that in a moment!
end

define wait (n) seconds without yielding
wait (n) secs

If Scratch didn't yield on each repeat iteration, the sprite would immediately move right 100 pixels, then wait (10 * 0.2 = 2) seconds. But it does, so it moves, then waits, then moves, then waits, etc.

You can force Scratch to not yield by creating a “run without screen refresh” custom block. These simply don't yield. This is handy when you want to do a lot of things really quickly; for example, I might want to draw a polygon with 120 sides. Normally, this would take 120 frames, or (120 / 30 = 4) seconds. By using a “run without screen refresh” custom block, it would only take one frame (actually zero; you could run the custom block multiple times in a single frame, since it wouldn't yield).

(NB: “Run without screen refresh” blocks do have a hard-coded limit – they will yield after 500 milliseconds. Try replacing “repeat 10, wait 0.2 seconds w/o yielding” with “wait 5 seconds w/o yielding”. But usually custom blocks don't take that long to run; it's only a problem if you have an extremely complex/slow operation.)

So here's an important thing - “create clone of” doesn't yield. Take a look at what happens if we do this:

when green flag clicked
set [clone position x v] to [-50]
create clone of [some other sprite v]
set [clone position x v] to [0]
create clone of [some other sprite v]
set [clone position x v] to [50]
create clone of [some other sprite v]

when I start as a clone // in the other sprite
set x to (clone position x)

All the clones appear at X position 50 because, by the time the “when I start as a clone” block runs – after the “when green flag clicked” script yields, which is only once all the blocks in it are complete – the “clone position x” variable is 50!

You can force Scratch to yield by using a block which yields. The typical example is “wait (0) secs”, since it causes a yield without any added time. You'd just put it after each “create clone of” block, like this:

when green flag clicked
set [clone position x v] to [-50]
create clone of [some other sprite v]
wait (0) secs
set [clone position x v] to [-50]
create clone of [some other sprite v]
wait (0) secs
set [clone position x v] to [-50]
create clone of [some other sprite v]

(We don't need to put a “wait (0) secs” block at the end of the script because a script ending already counts as a yield; “when a script is done, execute the next script.”)

This, however, makes the script take three frames to execute instead of one (or zero). Scratch re-renders the screen and waits a thirtieth of a second whenever it finishes executing all scripts, or rather, until each script yields once. So the execution here is “create clone of some other sprite, wait 0 secs; when the other sprite starts as a clone, set x position to the variable; render (and wait 1/30s); repeat.”

The solution, then, is to use a queue. I already showed that earlier, but here it is again:

when green flag clicked
delete (all v) of [clone x position queue v]
add [-50] to [clone x position queue v]
add [0] to [clone x position queue v]
add [50] to [clone x position queue v]
create (3) clones immediately // this is a "run without screen refresh" custom block, of course

define create (n) clones immediately
repeat (n)
create clone of [some other sprite v]
end

when I start as a clone // in the other sprite
set x to (item (last v) of [clone x position queue v])
delete (last v) of [clone x position queue v]

Scratch first runs the “when green flag clicked, (setup position queue list), create 3 clones” script all in one go; none of the blocks in that script cause a yield. The script ending does, though, so then Scratch runs the “when I start as a clone, go to and pop off the last item of the queue” script in each clone. After that, every script has yielded once, so Scratch renders the screen. So, the whole thing only takes one frame instead of three.



TL;DR, Scratch is not actually multi-threaded, and understanding the way it works helps you avoid bugs where blocks don't execute in the order you thought they would.

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

Is it possible to create a "create clone of ( )" block but with the ability to transfer information with it?

Create local variables. Each clone has its own instance of local variables, so changes you make after creating the clone will not effect the clone, unless you run it from the clone.
_nix
Scratcher
1000+ posts

Is it possible to create a "create clone of ( )" block but with the ability to transfer information with it?

JGames101 wrote:

Create local variables. Each clone has its own instance of local variables, so changes you make after creating the clone will not effect the clone, unless you run it from the clone.
This works, as the original poster already understands, but only for sprites (or clones) making clones of themselves. I'll quote the original post (emphasis mine):

alexanderyou wrote:

Similar to how when you do “create clone of myself” it makes a copy of the current sprite-only variables and you can use that to transfer information to the clone, I was wondering if there was a way to create a clone of another sprite and give it information from outside.

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

Is it possible to create a "create clone of ( )" block but with the ability to transfer information with it?

_nix wrote:

JGames101 wrote:

Create local variables. Each clone has its own instance of local variables, so changes you make after creating the clone will not effect the clone, unless you run it from the clone.
This works, as the original poster already understands, but only for sprites (or clones) making clones of themselves. I'll quote the original post (emphasis mine):

alexanderyou wrote:

Similar to how when you do “create clone of myself” it makes a copy of the current sprite-only variables and you can use that to transfer information to the clone, I was wondering if there was a way to create a clone of another sprite and give it information from outside.
Oh, sorry, I missed that part

Powered by DjangoBB