2D Multiplayer Game - 2-Player Space Shooter (Difficulty: 5)
-
Introduction
In this tutorial, you will learn to build a space-shooter game that 2 players can play on their own computers.Each player will control a spaceship that can move around within the left or right half of the stage, and they can shoot laser bullets toward each other. Both players start with a health value of 4, and whoever drops to 0 health first will lose the game.
Random asteroids (rocks) will fly down from the top, and if they hit a spaceship, that ship will lose 1 point of health. Spaceships can also shoot at the rocks using lasers, and when a rock explodes, it might leave a random power-up item for a health boost or firepower boost. With more firepower, a spaceship can shoot out lasers more frequently.
The game looks like this:
Note that to work on this project, you must be familiar with clones and message passing.
Step 1 - Remix Project Template
This starter project contains all the game assets you need, so please remix it in your playground:
https://play.creaticode.com/projects/6612868fc7d2146705bf1aee
Here is the content of this project:
- Ship
- Laser
- Rock
- Powerup
- Winner
- Explosion
- Stage with star backdrop. Note that the stage has been expanded to a width of 720, which can be changed in the “Edit” menu, under “canvas size” and “viewport size”.
Also, all the variables to be used below have been defined in this template, so you won’t have to define them again.
Step 2 - Add 2 Buttons for Creating and Joining Games
First, we will go to the “Ship” sprite, and add code to create 2 buttons (from Widgets category), one for creating a new game by player A, and one for joining the game by player B. We should also hide the ship sprite, as we will only be using its clones.
They will look like this when you click the green flag button:
Step 3 - Create the Game
When the “create game” button is clicked by the host player, we will remove all buttons, and then create a multiplayer game:
The game is named “space shooter”, and it is protected by the password “123”. You can change them to any other value.The display name of the host player will be “Player A”, with a role of “A”.
The game will be hosted by the game server named “US East”. It has a capacity of 2, which means it will only have 2 players. The dimensions of the game world are 720 by 360, which is the size of the stage. This means all sprites will not move outside this boundary.
In summary, when this block runs, a game session will be created on the given server under this project’s ID, the game name and the host player name, waiting for the second player to join.
Step 4 - Join the Game
The second player (on another computer) can run the same project and click the “Join Game” button to join the game created by the first player.
The second player needs to use the same game name, host name, game server and password specified above. Otherwise, the game server will not find the given game session. The second player’s display name is “Player B”, with a role of “B”.Note that we are hardcoding the game name/password/display name in this project for simplicity. For a real game project, there can be many game sessions running at the same time, and they will need to have different game names and passwords. So a more common solution is to ask the host player to input the game name and password, and ask the guest players to choose from a list of all existing games to join.
After this block runs, both players will be connected to the same game session.
Step 5 - Clone the Ship Sprite
In this game, each player controls his own spaceship locally. In other words, player A controls a Ship sprite running on his/her computer, and player B controls another Ship sprite running on his/her computer. To make sure they also see each other’s ships, the game server will create a copy of player A’s ship on player B’s computer, and a copy of player B’s ship on player A’s computer:
The copies are often called “remote copies”, since they run on a different computer, while each player’s own sprite is called the “original sprite”.Although we can create one sprite for each player, it would be highly inefficient, since most of the code blocks for the 2 players, such as keypress handling and collision detection, are exactly the same. Therefore, a much better solution is to have all players use the same sprite, but each player has a different clone. The clones can have different positions, costumes and clone IDs, which will help us implement different behaviors.
We can add these 2 blocks to create the clones for both players:
The 2 clones have clone IDs of “A” and “B”, which allow us to run different code blocks later depending on the ID.
Step 6 - Setup Each Clone
When the clone is created for either player, we will show the sprite, since the original sprite is hidden. Then depending on the clone ID is “A” or “B”, we move the clone to the left or right, and switch to the right costume.
As a result, when the first player clicks “Create Game”, he/she will see the yellow spaceship on the left; when the second player clicks “Join Game”, he/she will see the red spaceship on the right:
Step 7 - Add the Clones to the Game
The clones we create are the “original sprites”, since they run on each player’s own computer. The game server doesn’t know about them yet. Now we add them to the game using this block:
This block tells the game server to treat the ship sprite as dynamic, so it will be moveable if we specify its moving speed. Also, we will assume the ship has a circular-shape collider, which will be used to detect collision between the ship and other sprites. The circle is invisible, but you can assume they wrap around the ships like this
After this block runs, the game server will create the remote copies of each spaceship, so we see both sprites on both computers:
Note that there are 2 ways to build and test multiplayer games:-
You can work with another student as a group. One of you will create the project and run it as the host. Share this project so that the other student can run it on his/her computer as the guest player. This is a perfect setup for pair coding, as the 2 students can switch roles by switching seats or computers. Also, on some computers, it might be slow to run 2 playgrounds at the same time.
-
You could also test the program as both players on your computer, using 2 browser tabs. The second tab needs to be in the “incognito” mode, which allows you to log in to CreatiCode using a different account on the same computer. Then, you can run as the host player in one tab and as the guest in the other, as shown above.
Step 8 - A Button for Starting the Game
After both players have joined the game and added their sprites, the host player can click a button to start the game. We can add this new button after the “Create Game” button is clicked:
In a more complete game, we should not show this button until we see there are 2 players in the game. However, for simplicity, we will add this button right away, even if the second player has not joined. We will rely on the player to wait until he/she sees the second spaceship to press this button.
Step 9 - Start the Game
When the host player clicks the “Start Game” button, we need to broadcast a message to both playgrounds. When each spaceship receives this message, it will start the game logic. For now, we will simply remove all the widgets when we receive this message in the Ship sprite:
Note that at this point, there are 3 “Ship” sprites in each player’s playground:
- The original sprite that is hidden
- The clone created by this player
- The clone created by the game server to mirror the movement of the other player’s spaceship.
When we click button 3, all 3 sprites may respond to it. But we only want to handle the button click once, so we should use the “clone ID” to ensure only the original sprite handles it:
Note that the word “original” might be confusing as it is used for 2 meanings:- Every sprite (not its clones) has a clone ID of “originalsprtie”, so we can use that to tell whether a sprite is a clone or the original sprite coming with the project.
- In multiplayer games, the term “original sprite” has a special meaning, which refers to the sprite added to the game session by each player on his/her own computer, and not the copied clones added by the game server on other players’ computers.
In this example, we first make sure the original Ship sprite would respond to the button click, then send the message to the “Original Sprites” only, which is the Ship clone created by each player in their playground.
Step 10 - Show Progress Bars on Top
Next, we will add progress bars for the health and firepower of each ship. For each ship, health starts at 4, and a player loses if his/her ship’s health drops to 0. The firepower starts at 1, and it can increase to up to 4 when the ship picks up powerup rewards.
So we will need to add 4 progress bars, 2 on each side of the stage:
Note that we use the letters “A” and “B” in the name of these progress bars so that we can easily use the ship’s clone ID to determine which bar to update.The bars will look like this:
Note that you can also use a sprite with 4 different costumes to replace the progress bars.
Step 11 - Define the New Custom Block to Handle Keypresses
Now let’s make a new block “handle keys” to allow the players to control their ships:
Step 12 - Update Intended Y Speed
To keep track of the vertical movement speed intended by the player, we can create a new variable “intended y speed”, and update its value based on the key “w” and “s” in a forever loop:
Step 13 - Update Intended X Speed
Similarly, we can use another variable “intended x speed” to keep track of the horizontal movement speed:
Step 14 - Define Range for X Position
In this game, we do not want the players to cross the middle of the stage. We can define the minimum and maximum x positions for each spaceship using 2 variables:
With that, we can then set the ship’s x speed to 0 when it is already at the left or right bounds in the “handle keys” block:
Step 15 - Set Movement Speed
Now we are ready to set the moving speed of the spaceship using this block:
Note that this block not only changes the original sprite’s speed in this player’s playground, but also informs the game server of this change in speed, and the game server will make sure the copies of this sprite on other players’ computers also move at the same speed.
Step 16 - Reduce Number of Messages
The solution above would send the intended speeds to the game server repeatedly in the forever loop, which may not be necessary. On computers with slow Internet connection, too many messages may cause the program to overwhelm the network and cause loss of messages. To reduce the number of messages we send to the game server, we can change it to only update the speed when it changes.
To do that, we can define 2 new variables of “actual x speed” and “actual y speed”, and only set the speed if they are different from the intended speeds:
Now both players can control their spaceships using w/a/s/d keys after the host player starts the game:
You might notice a small lag between the original sprites and their remote copies. This is expected, since it takes some time for the game server to update the remote copies with the change in speed. The good news is that this is acceptable for most games, since the players won’t observe both computers at the same time, so they won’t see this small difference.
Step 17 - Add Ship Attributes
Next, we will add 3 new variables to each ship:
- The “last fire time” is used to keep track of the last time this spaceship has fired a laser. It will be used to control when it can fire again.
- The “ship health” is the health of this ship, which starts at 4
- The “ship firepower” is the firepower of the ship, and the minimum firing interval will be 2 divided by ship firepower. For example, if “ship firepower” is 1, then the ship will have to wait 2 seconds before making the next shot. If “ship firepower” is 4, then it only needs to wait for 0.5 seconds.
Step 18 - Handle the SPACE key
Now we can add the logic in the “handle key” block for the SPACE key. We will first check if the required minimum time has passed since the last time the player fired a shot. We can use a new variable “last fire time” to keep track of the time when the last laser was fired, and check if the required time span (2 seconds divide by the ship’s firepower attribute) has passed by:
Step 19 - Send a Message to Fire the Laser
If the required waiting time has passed, we will broadcast a message to the “Laser” sprite to fire a new shot. Since we are reusing the same Laser sprite for both players, we need to tell the Laser sprite which player is making the shot, and also the starting position of the laser.
The message type is “fire”, and the parameter contains 3 parts separated by a space letter:- clone ID: this is the clone ID of the firing spaceship, which can be “A” or “B”
- x position: this is the x position of the spaceship shifted left or right by 20 steps, which ensures the laser appears at the head of the spaceship.
- y position: the y position of the laser will be the same as the spaceship
Note that we are just sending the message from the “Ship” sprite to the “Laser” sprite on the same computer at this point, and we don’t need to use the game server yet.
Step 20 - Initialize the Laser Sprite
Now we switch to the Laser sprite.
First, we will hide this sprite when the green flag is clicked. We will be creating clones for each laser shot, so the original Laser sprite will remain hidden. We will also use the variable “next laser ID” to keep track of the ID of the next laser to be fired.
Step 21 - Receive the “fire” Message
When the Laser sprite receives the “fire” message, we will first make sure only the original sprite handles it. Otherwise, a clone of the Laser sprite may receive this message as well, which may generate a duplicate laser shot.
We will need to split the parameter attached to this message into 3 parts by the space letter. The first part will be stored in the “Player ID” variable, which will be “A” or “B”. The next 2 parts are stored in “start x” and “start y”, which are the starting positions for the new laser.
Step 22 - Create a Clone of the Laser
Now we can create a clone of the Laser sprite to represent the new shot. We first move the original sprite to the starting position of the new shot, and then create a clone of it at that position. The clone ID of that new clone will be “Player ID” joined with “next laser ID”. For example, for player A’s lasers, its clone ID will be “A 1”, “A 2”, etc.
Step 23 - Display the Clone for the Laser
When the clone of the Laser is created, we will first unhide it, since the original sprite is always hidden. Then we will switch to the correct costume based on whether the clone ID contains “A” or “B”.
Step 24 - Add this Clone to the Game
So far the game server doesn’t know anything about firing the shot or the clone of the Laser. Now it is the time to add this clone to the game:
The Laser is dynamic since it will be flying across the stage. We will use a rectangle shape to approximate the shape of this Laser for collision detection. After this step, the game server will know about this clone, and will also create a copy of it (another clone) on the other player’s computer.
Step 25 - Make this Laser Fly
Next, we will make the Laser fly left or right based on its clone ID:
After this step, both players can start to fire lasers by pressing the SPACE key:
Step 26 - Remove the Laser When It Hits the Edge
Currently, the laser would stop at the edge. We should make it disappear. This can be done very easily using the “when touching” block:
We are using the special value of “Edge” for the first input, which represents the 4 borders of the stage. The action to take is “Self-Destruct”, which makes the laser delete itself. Since we don’t need to do anything else when a bullet hits the world edge, we won’t need to broadcast any message here.Here is the result:
Step 27 - Hit the Opponent Spaceship
When the laser hits the opponent player’s spaceship, it should not pass through it. Instead, it should explode, and the opponent should lose one point in health.
First, we should go to the Ship sprite, and add a message receiver block for a new message type “laser hit ship”:
Next, switch back to the Laser sprite, and set up the touch event using these 2 blocks:
The first input is the Ship sprite. For the second input, we are passing in the letter “A” or “B”, so that player A’s laser would only hurt player B, and vice versa. The action type is “Self-Destruct”, which removes the bullet when it hits the spaceship. Next, we choose to broadcast the “laser hit ship” message, so that we can reduce the spaceship’s health when that message is received.Here is the result:
Note that the “laser hit ship” message will ONLY be received by 2 sprites: the ship clone that’s being hit, and the laser clone that’[censored]ting it. No other sprites will receive this message. So for the next step, we just need to handle this message in those 2 sprites.
Step 28 - Trigger Explosion from Laser
When the Laser clone receives the “laser hit ship” message, it should tell the Explosion sprite to show a new explosion animation. To do that, we first create a new message type “new explosion” in the Explosion sprite:
Next, back in the Laser sprite, we broadcast this message when we receive “laser hit ship”:
The parameter is the x and y positions of the laser, so that we can create the explosion at the same location. Note that we are broadcasting to “All” sprites. This ensures all sprites on all players’ computers can receive this message. We won’t need to add the Explosion sprite to the game at all. The Explosion sprite on each player’s computer will receive this message and show an explosion.
Step 29 - Setup the Explosion Sprite
Now, switch to the Explosion sprite. As usual, we should hide it when the game starts, since we only need to use its clones for each explosion:
Step 30 - Handle the “new explosion” Message
When the Explosion sprite receives the “new explosion” message, it should create a clone of itself at the given location. The parameter is stored in “explosion info”, which contains 2 parts that can be separated by the SPACE character. Note that we need to check that only the original Explosion sprite would do so, since there may be existing explosion clones, and we don’t want them to handle this new explosion message.
Step 31 - Show the Explosion Animation
When the clone is created, we will make sure it is at the front layer, play an explosion sound, switch to the first costume, and display it. Then we repeatedly rotate to the next costume. Lastly, we remove this clone.
Now we will get a nice explosion effect:
Step 32 - Reduce Ship Health
In the Ship sprite, when the clone receives the “laser hit ship” message, it should reduce its health by 1. We can add a guard to check its health is greater than 0 before doing that. The reason is that a ship with health of 1 may receive 2 hits before we stop the game.
Step 33 - Request to Update Health Progress Bar
Next, this ship needs to tell all players to update the displayed health bar for this player. We can first add a new receive block to define the message type “update health”:
Then we can broadcast this message to “All” sprites:
The parameter will include 2 parts: the clone ID of this ship, which is “A” or “B”, and the updated health value of it.
Step 34 - Update Health Progress Bar
To update the health bar value, we first need to make sure only the original Ship sprite on each computer handles this message, so the update is not done in duplicate. We can extract the ID of the ship (“A” or “B”) and its health value from the parameter “info”, then we update the progress bar widget for that ship with the new value:
Now we will see that the health bar of the red ship will be updated from 100% to 75% (4 down to 3) on both playgrounds when it is first hit:
Step 35 - Check for Game Over
When we update the ship’s health, it’s also a good place to check if the ship’s health has dropped to 0 and announce it is “game over”. We will broadcast a local message to the “Winner” sprite to handle game over, with a parameter that’s the ID of the winning player.
Step 36 - Stop the Game
In the Winner sprite, we will need to hide it when the green flag clicks.
When the “game over” message is received, we will play a short sound clip, and show the correct costume of the winning player. After 3 seconds, we stop the entire program.
Here is what it looks like:
By now, the game is fully playable! You have completed a full multiplayer game. Congratulations!For the remaining steps, you will learn to add rocks and powerups to make the game more fun.
Step 37 - The “add rocks” Message
In the Ship sprite, when the player clicks the “start game” button, we will send out the “add rocks” message, so that the Rock sprite can start adding rocks:
In the Rock sprite, we will hide the rock at the game start, and also receive the “add rocks” message:
Step 38 - Create Clones of Rock Repeatedly
The Rock sprite will add a new clone of itself every 2 seconds. Each time it will first go to a new random starting position along the top edge of the stage. Each new clone has an ID of “nextRockID”.
Step 39 - Add Rock Clone to the Game
When the clone of the Rock is created, we will show it on the stage. Then, we add it to the game, so the game server would create a copy of it on the other player’s computer. We will also assign a random speed for this rock, so it falls down at a random speed and direction:
Step 40 - Delete the Rock Clone at Edge
When the rock hits the edge of the stage, it should be deleted. We can use the “Self-Destruct” action to do so, similar to the Laser sprite:
Now the game looks like this:
Step 41 - Rock Hitting the Ship
When a rock hits a spaceship, it should reduce its health by 1, similar to the effect of a laser shot. This will force the player to dodge the rocks.
First, we handle the message “rock hits ship” in the Ship sprite:
Then, in the Rock sprite, we trigger this message whenever the rock hits the ship:
Step 41 - Rock Explodes
When the rock hits a ship, it will self-destruct. We can also show an explosion at that location. This can be done by broadcasting the “new explosion” message:
Now the game looks like this when a rock hits a spaceship:
Step 42 - Laser Hits Rock
The players can also shoot at the rocks. When a laser hits a rock, the rock should explode. We can set this up in the Rock sprite first:
Then in the Laser sprite, we set up the touch event:
Now we can shoot out the rocks:
Step 43 - Rock Produces Powerups
When a laser hits a rock, we should reward the player with a power-up item sometimes. For example, in the Rock sprite, we can broadcast the “add powerup” message 50% of the time, along with the x and y positions of the rock as parameters:
Now switch to the Powerup sprite. We will hide it at game start, and set the ID of next powerup to 0. Then we will receive the “add powerup” message:
Step 44 - Handle the “add powerup” message
To handle the “add powerup” message, we will first make sure only the original Powerup sprite on each computer will process that message, and not any of its clones. This sprite will go to the location specified in the parameter, and then choose between 2 types of powerups randomly: “firepower” or “health”. It will then set the costume according to the powerup, then create a clone of itself. It will also update the next ID by 1:
Step 45 - The Powerup Clone
When the powerup clone is created, we will add it to the game session, so a copy of it appears on the other player’s computer. After 6 seconds, we will remove this clone, if the players haven’t picked it up by then.
Now the game runs like this. The powerup items will be added and then removed in 6 seconds.
Step 46 - Ship Collects Powerups
Next, in the Ship sprite, we will enable the Ship sprite to pick up the power-up items.
First, we receive a new message type of “ship collects powerup”:
Then, we can trigger this message when the Ship touches the Powerup sprite:
The action is “Continue and Collect”, which means the Ship itself will continue its current movement, but the powerup item will be removed.
Step 47 - Increase Ship’s Health Value
When the Ship receives the “ship collects powerup” message, if the powerup type is “health”, then we should increase this ship’s health value by 1 if it is not 4 (the maximum):
Step 48 - Increase Ship’s Firepower Value
On the other hand, if the powerup type is “firepower”, we need to increase the ship’s firepower value.
First, we will add a new message handler in the Ship sprite for “update firepower”, which is similar to “update health”. We extract the ID of the ship and its updated firepower value, then update the corresponding progress bar:
Now we can update the ship’s firepower value and send out this new message when the ship collects a firepower reward:
Further Work
By now the game is working with rocks and power-ups. It can still be further enhanced in many ways, and here are some suggestions:
- Other Powerup Types: For example, speed up the spaceships, new types of weapons, smaller ship sizes, etc.
- More Players: You can extend the game so each side has 2 players playing as a team
- Player-Specified Game Name and Display Name: Instead of always using the same game name and display name, you can allow the host player to specify them when creating a new game, and allow the guest players to choose from a list.
- Click to Shoot: you can allow the player to click anywhere on the stage to shoot the laser in that direction.
-