In order to check for collisions between the bullets and the enemy ships, as well as your ship and the enemy ships, you will need to know which sprites are enemies. You could create a list of enemies at the start of each level and use that to check against. But that's a little too manual, and also problematic: if you added or deleted more enemies in a level, the list would need to be updated to prevent errors. Creating the Enemy List Instead, of manually creating and updating the list, you can have the enemy ships themselves update the list. We'll call this list enemyList, and it will be cleared at the start of each level. Each enemy sprite will then add its sprite number to the list, within the beginSprite handler of the enemy_move behavior. 1. | Make sure the internal cast is active, then double-click the Score's behavior channel at frame 10. Replace the default exitFrame handler with the following beginSprite handler:
on beginSprite me _global.enemyList = [] end This will clear the global enemy list at the start of level 1. You need to now instance the behavior into the first frame of levels 2 and 3.
| 2. | Name the script clear_enemyList and close the script window. Drag the new script from the cast and drop it in the Score's behavior channel at frame 30. Drag it once more and drop it onto frame 50.
The enemyList, global variable, will now be cleared at the start of each level.
| 3. | Right-click on any one of the enemy ships in level 1, and select Script from the context menu. Add the following line of Lingo as the last line in the beginSprite handler:
_global.enemyList.add(me.spriteNum) Because all the enemy ships have the same behavior attached, it doesn't matter which sprite you choose to modify. Let's see how this works.
| 4. | Rewind and play the movie. Open the Message window and enter the following:
trace(enemyList) -- [4, 5, 6, 7, 8, 9, 10, 11, 12, 13] There you go! All the enemy ships have added their sprite numbers to the enemyList automatically. The asteroids have a different move behavior, so you'll also need to add the line of Lingo to the asteroids' behavior.
| 5. | Stop the movie and right-click one of the asteroids in level 3. Choose Script from the context menu. Add the following line as the last line in the beginSprite handler, and then close the script window.
_global.enemyList.add(me.spriteNum) The enemyList will now be properly updated no matter how many enemies you have per level, and no matter which level you're on. You can now add the collision test to the bullets' move behavior so the bullets will know when they've collided with an enemy ship.
| Checking for Bullet Collisions The next step in this process is having the bullets check for collisions against each of the sprites in the enemyList. To do this you will need to use a repeat loop to iterate through the list, checking each sprite for a collision as you do. For this, Lingo offers a special form of the repeat function that will allow you to process only the entries in a list. Look at the following chunk of code: on listTest myList = [1, 5, 9, 11] repeat with cnt in myList trace(cnt) end repeat end The output this method would produce, in the Message window, is as follows: -- 1 -- 5 -- 9 -- 11 As you can see, using this form of the repeat loop allows you to iterate through only those items specifically in the list. You can now use this to add a similar repeat loop to the move bullet behavior. 1. | Right-click on any of the bullet sprites and select Script from the context menu. Add the following repeat loop to the enterFrame handler. Add the repeat loop immediately after the line that increments the locH as shown:
sp.locH = sp.locH + amtToMove -this line is already present repeat with cnt in _global.enemyList hitEnemy = sp.intersects(cnt) if hitEnemy then trace("collision") end if end repeat With the repeat loop in place, the bullets will now check for collisions with the enemy ships, on each frame. But that means that every bullet on screen will need to iterate through the list on every frame. Seems pretty processor intensive, doesn't it? It is, but this is what repeat loops are designed to do: execute very fast. The trace is there just for testing so that you can tell a collision has occurred, and will be replaced later in this lesson. For now, let's see how this is working.
| 2. | Open the Message window, then rewind and play the movie. Fire some bullets and observe the results.
There are a couple of items you should be aware of. First of all, note that when a bullet hits an enemy you will most likely see multiple occurrences of the word "collision" in the Message window. This is because the code is running fast enough that multiple collisions happen as the bullet sprite passes through the enemy ship sprite. When you're keeping score, you don't want multiple collisions to occur, or the scoring won't be accurate. The next problem is that when a bullet hits a ship, it goes right through it and can go on to hit other ships in its path. You can overcome both of these problems by simply moving the bullet off screen once a collision has occurred.
| 3. | Stop the movie, then right-click a bullet sprite and select Script from the context menu. Replace the trace("collision") line in the repeat loop with the following three lines:
sp.locH = 850 sendSprite(cnt, #explode) exit repeat The first line moves the bullet off the Stage and prevents multiple collisions. Because the bullet is positioned to a locH of 850, the conditional test that follows will set its moving property to false, readying it to be used again:
if sp.locH > 820 then moving = false end if The second line calls the explode method within whatever enemy ship was hit by the bullet.
The explode method, which you will create next, does the job of removing the enemy ship from the Stage and removing the ship's sprite number from the enemyList. In the next section of this lesson you'll further modify the code to add a visible explosion as well as a sound.
Finally, because you're running the explode method which is going to eventually remove the sprite you've hit from the enemyList, an exit repeat is used to stop the loop from continuing to execute. Because you're using the enemyList as part of the repeat loop, if you delete an item from the list while in the loop you will receive a script error.
| 4. | Open the enemy_move behavior for editing by right-clicking any of the enemy ships and selecting Script from the context menu. Add the three property declarations at the top of the script:
property exploding, explodeCount, origMember, startLoc Eventually, when a bullet hits an enemy, the enemy should be destroyed in a fiery explosion. That explosion will be a film loop containing 20 frames of animation. Because of the way film loops work there's no way to know where they are in their animation. Therefore, these two properties will work in conjunction to process the ship's explosion. The exploding property will be used as a true/false flag telling the behavior's enterFrame handler whether or not the ship is exploding. The explodeCount will begin at 1 and then be incremented by 1 on each frame. When the counter reaches 20 you know the film loop has completed one cycle, so you can remove the ship from the Stage, modify the enemyList, and do whatever else needs to be done.
The origMember property will be used to reset the ship back to its original member after the explosion has finished. You'll see how this works in a moment.
| 5. | Initialize the exploding, origMember, and startLoc properties within the beginSprite handler. You can make these three lines the last three lines in the handler.
origMember = sp.member exploding = false startLoc = sp.loc
| 6. | Add the on explode method at the bottom of the behavior:
on explode me exploding = true explodeCount = 1 _global.enemyList.deleteOne(me.spriteNum) end Recall that when a bullet hits an enemy ship, it uses sendSprite to call the explode method within the behavior attached to the ship. This method sets the two properties so they are processed within the behavior's enterFrame handler, which you will modify next.
Note too that the sprite's spriteNum is deleted from the enemy list using Lingo's deleteOne method. The deleteOne method will simply delete the first occurrence of the value within the specified list. This will prevent collisions with the enemy sprite as it explodes over the next 20 frames. You also need to remove the enemies from this list so you will know when the level has been cleared. When the list is empty, all enemies have been killed and you can start the next level.
| 7. | Add the following conditional test to the end of the enterFrame handler:
if exploding then explodeCount = explodeCount + 1 if explodeCount = 20 then sp.locV = -2000 sp.member = origMember exploding = false end if end if This should start to make sense now. Because exploding is false at the start, this code is bypassed until the explode method is called and exploding is set to true. Once true, the explodeCount property will be incremented by 1 until it reaches 20. When it doesand the explosion film loop has played throughthe enemy ship's locV property is set to -2000, effectively removing it from the Stage.
The sprite's member is then set back to its original member. This isn't necessary now, but it will be once you add the explosion effect. The exploding property is then reset to false so the conditional stops executing.
| 8. | Rewind and play the movie to see how it's progressing. Stop it when you're through testing.
Now as you fire, enemy ships are removed as you hit them. Also, the bullet no longer passes through a ship after hitting it. Next, you need to replicate some of what you just did so that the asteroids will also explode when hit with a bullet.
| 9. | Right-click one of the asteroids and select Script from the context menu. Add the following property declaration at the top of the script:
property explodeCount, exploding, origMember, startLoc
| 10. | Add the following three lines to the end of the beginSprite handler to initialize the properties:
startLoc = sp.loc origMember = sp.member exploding = false
| 11. | Add the explode handler to the behavior:
on explode me exploding = true explodeCount = 1 _global.enemyList.deleteOne(me.spriteNum) end
| 12. | Add the conditional test to the end of the enterFrame handler:
if exploding then explodeCount = explodeCount + 1 if explodeCount = 20 then exploding = false sp.locV = -2000 sp.member = origMember end if end if The asteroids will now be removed from the Stage when hit with a bullet. They are also ready to explode when hit, which you will arrange in the next section.
| 13. | Save the movie before continuing.
| Checking for Ship Collisions In the same manner as checking the bullets for collisions with the sprites in the enemyList, you'll want the player's ship to do the same thing. After all, if you collide with an enemy ship, your ship should be destroyed. At the same time, you'll want to reset the enemy ships to their start locations so you can restart the level. 1. | Right-click the player ship and select Script from the context menu. Add these three property declarations at the top of the script:
property exploding, explodeCount, origMember, startLoc These three properties perform the same functions they did within the move_enemy script. The exploding property, which is false until the ship explodes, is a state (or flag) variable that allows the frame script to process either the film loop explosion or the collision checks. As you know, the explodeCount is used as a frame counter as a way to stop the explosion film loop. Finally, origMember stores a reference to the original ship_loop cast member so that when the explosion is finished, the sprite can be reset so that it shows the original film_loop, and not the explosion.
| 2. | Initialize the exploding and origMember properties within the beginSprite handler as follows. Add these two lines of code at the end of the handler:
origMember = sp.member exploding = false startLoc = sp.loc Next, you'll need to add a conditional test to the behavior's enterFrame handler so that it either checks for collisions or processes the explosion once a collision has occurred.
| 3. | Add the following if statement to the end of the enterFrame handler:
if exploding then explodeCount = explodeCount + 1 if explodeCount = 20 then exploding = false sp.loc = startLoc sp.member = origMember repeat with cnt in _global.enemyList sendSprite(cnt, #reset) end repeat end if else repeat with cnt in _global.enemyList if sp.intersects(cnt) then exploding = true explodeCount = 1 end if end repeat end if Because exploding is false until a collision occurs, the repeat loop will iterate through the enemyList and check the ship's sprite against the sprites in the list. When a collision occurs, exploding is set to true and explodeCount is set to 1. Then, on the next frame, the if statement is executed and exploding is true, causing explode count to be incremented by 1. As soon as it reaches 20, signaling the end of the explosion film loop, exploding is set to false, and the ship is placed back at the left edge of the screen by setting its loc to the startLoc. The sprite's member is then set back to the original member. Finally, a repeat loop iterates the sprites in the enemyList and calls the reset handler within each one of them.
The reset handler has yet to be written, but you can probably guess what it will do. When a collision occurs between the player's ship and an enemy ship or asteroid, the enemies should be reset back to their starting positions or the player could start out in a position like this:
Now let's create the reset handler within the two behaviors and then test the game.
| 4. | Close the script window, right-click one of the enemy ship sprites, and select Script from the context menu. Create the reset method at the end of the script:
on reset me sp.loc = startLoc end As you can see, when reset is called, it simply positions the sprite back to its starting location, as defined in the beginSprite handler.
| 5. | Copy the reset handler you just created by highlighting it and then pressing Ctrl/Command+C. Close the script window and right-click one of the asteroids. Select script from the context menu. Paste the reset handler at then end of the script by pressing Ctrl/Command+V.
With the reset in place, the asteroids will also return to their original cast members after exploding.
| 6. | Close the script window, then rewind and play the movie.
You're now able to collide with the enemies as well as shoot them down. Although you don't yet progress to the next level when you've shot down all the enemies, you can jump to the different levels using the Message window.
| 7. | Open the Message window and enter go commands to move between the three levels:
_movie.go("level3") _movie.go("level2")
| 8. | When you're through testing, stop and save the movie.
It's now time to add some excitement to the game by adding a visual explosion as well as an audio one.
| |