Avoid Clones


Sprites can be cloned. These clones behave similarly to the sprite they were cloned from, leading to common uses where many of the same sprite are needed. The idea is great however have limitations that are very easily encountered.

The main problem with clones is control. You can create them and delete them. You can also give them unique values using variables/lists set to "for this sprite only". This leads to clones often being used to store data. For example, you have a number of NPCs as clones, each storing their name, position, and health. But how do you make the clones do more? How do you prevent them from running all the scripts? How can they interact with each other? How do you keep track of which one is which?

These questions do have solutions but if the solutions require storing data in global variables and lists, suddenly you don't need clones. You can do everything in the original sprite and more, and avoid using clones entirely. It does require learning how to to enumerate over a list but clones also require learning. I argue that clones are a harder subject to learn given that their behaviour is unclear and debugging is awkward:

But, clones aren't useless. There are actually some things difficult to achieve without them. Here are some justifications for using clones:



Storing many objects

I mentioned above about enumerating over a list being easier to learn. Again using the NPC example, there are two common ways to store the data. One would be to have many lists in the form of a "spreadsheet", with each record being an NPC:

namexyhealth
Alfa1551280.5
Bravo-120-142100
Charlie228919.25
Delta-176-16750

Creating a new NPC could look like this:

      add [Echo] to [name v]
      add [0] to [x v]
      add [0] to [y v]
      add [100] to [health v]
    

Lists are indexed with numbers counting from 1. So if you wanted the health of NPC #3, you would use this:

      (item (3) of [health v] :: list) // outputs 9.25
    

If you wanted to move all NPCs to the right by 20 units, you could loop over every NPC:

      define update NPCs // make this "run without screen refresh" to run instantly
      set [i v] to [1] // variable used for counting, start at index 1
      repeat (length of [x v] :: list) // loop over every item of the list
      replace item (i) of [x v] with ((item (i) of [x v] :: list) + (20)) // move 20 units to the right
      change [i v] by (1) // increase counter to go to the next item
      end
    

The above could be expanded to perform many actions such as changing the x and y values to move towards the player, or changing the health as the NPC takes damage or heals over time.

Here is how you could display them with stamps:

      define render NPCs
      set [i v] to [1]
      repeat (length of [costume v] :: list)
      go to x: (item (i) of [x v] :: list) y: (item (i) of [y v] :: list)
      stamp
      change [i v] by (1)
      end
    

The alternative to a spreadsheet using many lists is to combine them all into one list, with groups of items associated with each NPC:

NPC
Alfa
155
12
80.5
Bravo
-120
-142
100
Charlie
...

Since there are 4 items per NPC, a few changes are needed:

      define render NPCs
      set [i v] to [1]
      repeat ((length of [NPC v] :: list) / (4)) // 4 items per NPC
      go to x: (item ((i) + (1)) of [NPC v] :: list) y: (item ((i) + (2)) of [NPC v] :: list) // add 1 and 2 to get the correct item
      stamp
      change [i v] by (4) // 4 items per NPC
      end
    

Start and stop specific sounds

This is annoying to achieve without clones as the alternative would be separate sprites for each sound.

      set [sound name v] to [ambient_factory_04] // local variable for the clone to inherit
      create clone of [myself v]
      set [sound name v] to []

      when I start as a clone
      play sound (sound name) until done
      delete this clone // make sure to delete the clone after it is no longer needed


      when I receive [stop sound ambient_factory_04 v]
      if <(sound name) = [ambient_factory_04]> then
      delete this clone // delete the clone so it can't continue to play the sound
      end