Note: A completed project file for this tutorial can be found at the end of this article.
In this tutorial, we will create a Pong-like game from the ground up. We will be covering some more advanced features and behaviors in hyperPad, so this tutorial assumes some basic knowledge with the hyperPad editor. If you think you need to familiarize yourself more with making a game from scratch, you can check out the Complete Guide - Creating a Platform Game tutorial first.
Pong is a simple two-player game that consists of only two paddles and a ball, and can be thought of as a simple digital version of table tennis. Each player controls a paddle, both on opposite sides of the table. The paddles can move only forwards and backwards, hitting the ball towards the opposing player. If a player misses the ball, the other player gains a point. The first player to 11 points wins!
For our Pong game, we will include the option of playing in 1 Player mode (Player vs. AI), 2 Player mode (Player vs. Player), or 0 Player "Watch" mode (AI vs. AI).
Part 1: Project Setup and Main Menu
Let's get started by creating a new project.
Open the Projects menu in hyperPad and tap New Project. Give your project a name and tap Next. Orientation can be left default as landscape mode. For this tutorial, Device Support is set to iPad 4:3. On the next screen, select "Bird's Eye View" and leave the rest as default.
Now that we have a new project ready to go, let's quickly get started on setting up a main menu before jumping straight into the game itself. First thing we'll do is rename our scene to "Main Menu".
Tap the Project/Scene Menu button and tap the gear button beside our "Scene 1".
Tap the Settings option and rename our scene to "Main Menu". Tap Save to save and close the settings window.
Next, to stay traditional to early retro games like arcade Pong, we can make our background colour black.
Tap the Scene Settings button and select Background. Tap the "Change Colour" button and set it to black (#000000FF).
Now let's add a title label to our main menu. Tap the Special Objects button and select Label.
Enter the name of your game in the text field ("hyperPong" was entered for this tutorial), set the font size to something large but not too big. For a "hyperPong" title, 72 points is a good size. Change the colour to white (#FFFFFFFF) and place it in the centre of the menu near the top.
Now we have a main menu scene to populate with buttons of all our modes of play. We'll leave the menu as is for now, and we'll finish it up after we have a playable game.
Part 2: Setting Up the Game Scene and Paddle
Tap the Project Menu button and tap "Add Scene". Name it "Game" and tap Create. Now in the Game scene, you should see an empty canvas. Like we did with the Main Menu scene, go ahead and tap the Scene Settings button again and change the background to black (#000000FF).
Let's get to work creating our player paddles. Create an empty object by going to the Special Objects button and selecting "Empty Object". Change its colour to white (#FFFFFFFF) and make sure the opacity slider is fully on.
Rename the object to "Player-1" and set it's physics mode to Physics, and leave Passable enabled.
The reason why we'll keep Passable enabled is so we have greater control over how the ball will interact with the paddle when it hits it. We'll want to be able to control the speed and direction the ball goes manually.
In the Player-1 object properties, go to the Transform tab and set the scale X to 25% and move it to the left of the game field.
Next, we'll add some controls to our paddle so it can move. With the paddle selected, go to its behaviour editor by going back to the Physics tab.
Place down a Joystick Analog behaviour, found under the Interaction section (if you can't find it, you may have to tap the Advanced tab). This will automatically create a joystick for you in the scene editor.
But we don't want to only allow the paddle to move by joystick movement. We also want it so an AI can perform movements, too.
Under the Custom section, place down a Box Container. This will store what direction the paddle will move. That way, both a player controlling a joystick or an AI performing calculations can move by changing this one value. Its default value should be set to 0. It's also a good idea to give it a good name so it's easier to work with, "Move Direction" seems fitting.
With the Box Container selected, press the "Change Input Field" button and tap Yes. This will create a Set Input Field behaviour for the box container so we can change its value when we need to. Connect the Set Input Field to the Joystick Analog.
With the Set Input Field behaviour selected, drag the output value from the Joystick Analog into the New Value field. Select the "y" property. Now, the Move Direction box container will track how far the player moves the joystick up or down (y-axis).
Now that we're tracking how far the joystick moves up or down, we also need to reset it to zero if the player lets go of the joystick. Otherwise, the paddle will keep moving in the direction it last remembers.
Under the Interaction section, add a Stopped Touching behaviour, and tap it to open its properties. By default it's tracking when the player stops touching the paddle, but we want to track when the player stops touching the joystick.
Tap the object property to change it, and select the joystick. You may need to select the Global UI layer to be able to pick it. When it's selected, tap the check button. The behaviour should now be showing the joystick as the tracked object.
Tap on the Set Input Field we made earlier and duplicate it. With the duplicate Set Input Field, connect it to the Stopped Touching behaviour and set its New Value property to 0.
Now our Move Direction box container variable will track up and down movements of the joystick and reset itself to 0 when the joystick is let go. That gives us an excellent way to tell our paddle how to move, so let's continue and give our paddle some movement.
Under the Custom section, drop down a Timer behaviour. In its properties, set the interval to 0. This timer will execute as often as it can (once per frame), which gives us the opportunity to update the paddle's movement very frequently.
Before setting the paddle's velocity, we want to make sure the value for the Move Direction box container is clamped within a reasonable range, just in case it goes too high or too low. Otherwise, our paddle will move too quickly in some cases.
Under the Logic section, place down a Minimum behaviour and a Maximum behaviour. Connect the Minimum behaviour to the timer, and the Maximum behaviour to Minimum. You should have something that looks like this:
In the Minimum behaviour's properties, set the first value to the output value of the Move Direction behaviour, and set the second value to 1.
The Minimum behaviour will output the smallest number it's given. So, if the Move Direction value goes higher than 1, it will output 1. Otherwise, it will simply output the value for Move Direction.
In the Maximum behaviour's properties, set the first value to the result of Minimum, and set the second value to -1.
The Maximum behaviour will output the biggest number it's given. So, if the Move Direction value goes lower than -1, it will output -1. Otherwise, it will output the value of Minimum (which is either 1 or Move Direction, which ever is lower).
What this all means, is that the resulting value of Maximum is effectively the value of Move Direction clamped between -1 and +1. This gives us full control over how fast the paddle can possibly move!
A range from -1 to +1 is easy to work with, but will be too slow if we directly use that as the paddle velocity.
Under the Logic section again, place down a Multiply Values behaviour and connect it to the Maximum behaviour. For the first value, set it to the resulting output of the Maximum behaviour. For the second value, we can set it to a good "max speed" value.
For now, set it to 20. If you think later on that the paddle moves too slow or too quickly, you can just set this number to a higher or lower number.
The Multiple Values behaviour now gives us a good velocity range to work with, from as low as -20 to as high as +20.
Now's a good time to set the paddle's velocity. Under the Physics section, add a Set Velocity behaviour and connect it to the Multiply Values behaviour. Set the "x" field to 0 and the "y" field to the result output of the Multiply Values behaviour.
Try it out! When you play the game, the paddle should be moving as you move the joystick up and down.
As you probably have noticed during your testing, nothing is stopping the paddle from leaving the screen. In the next part, we'll add some boundaries to keep everything in view.
Part 3: Game Boundaries
Before we continue, let's use some foresight and consider good game design. The player paddle won't be in the centre of the screen, but closer to the edges as we further piece our game together. This will become an issue as we have UI elements (i.e. joysticks) that will overlap with our game elements (i.e. paddles).
Instead of using the whole screen as the playable area, we should inset the boundaries so that our paddle doesn't go under the joystick where our fingers are, where we can't easily see it. That way we won't lose track of our paddle near the bottom of the playable area. We can also use this inset as a hard boundary to keep our objects from leaving the playable field.
Tap the Special Objects button and create a new Empty Object. Tap the new empty object to open its properties.
Keep it's physical mode set to Wall, and disable Passable. Set it's colour so we can visually see it, an opaque dark grey (#1F1F1FFF) fits nicely. Set friction to 0% and bounce to 100%.
Now in the Transformation tab of the wall, set the Y scale to 200%, and move it to the top of the screen area. Now stretch it so it fits the full width of the screen area. You can also turn on the grid snap mode to help you place it accurately.
Now duplicate the wall and move the copy to the bottom.
Duplicate again and set its scale X to 200% instead. Then stretch the height to fit the screen and put it to the left of the screen.
And again, duplicate the vertical wall and move it to the right of the screen. You should now have a fully enclosed game field:
When you play the game now, you'll probably immediately notice that you can move through the walls as if they weren't there. This is due to two reasons; we've set the paddle to be "Passable" and we're forcing the paddle's velocity every frame. This is fine, we'll just need to implement a way to stop the paddle from moving into the wall.
What we'll do to implement that behaviour is check the player's position against the wall's position. If that's true, then move the player back into the playable field.
To make it a bit easier for us to implement that, tap the top wall and change the Y anchor point to 0%. This will anchor the wall's position to the very bottom of itself. You may need to unlock the anchor fields by tapping on the Lock icon.
Similarly with the bottom wall, change the bottom wall's Y anchor point to 100% this time. This will anchor the bottom wall's position to the very top of itself.
Now we're prepared to implement our bounds-checking behaviours. Tap on the paddle and open its behaviour editor.
Start by placing down a Get Bounding Box from the Logic section. It should be set to use the paddle by default. This behaviour will give us the bottom/left coordinates and the size of the paddle.
Next, drop down a Add Values under the Logic section and connect it to the Get Bounding Box behaviour. In the Add Values properties, set the first value to the Get Bounding Box result "y" and the second value to the result "height".
The result of this addition will be the position of the top of our paddle. For readability, you may rename the behaviour to something useful, such as "Add Values - Paddle Top", so we can clearly see what it's accomplishing.
Now we'll check if the paddle is moving into either the top or bottom walls. Under the Transform section, drop down two Get Position behaviours and connect them to the Add Values behaviour in parallel.
Set the first Get Position behaviour to track the bottom wall and set the second Get Position behaviour to track the top wall. You can also rename these behaviours for readability. You should now have something similar to this:
For each Get Position behaviour, drop and connect a Subtract Values behaviour from the Logic section.
In the properties for the Subtract Values connected to the Get Position for the bottom wall, set the first value to the Get Position Bottom Wall result "position_y". Then set the second value to the Get Bounding Box "y".
Now for the properties in the other Subtract Values connected to the Get Position for the top wall, set the first value to Get Position Top Wall result "position_y". Then set the second value to the Add Values - Player Top result.
These subtractions will tell us how far we have to "push" the paddle back to keep it in the playing field. Now we just need to check whether we have to or not.
Under the logic section, drop and connect an If behaviour for both behaviour branches.
For the If behaviour connected to the Bottom Wall branch, set the first value to the result of the connected Subtract Values behaviour. Set the condition to > ("Is Greater Than") and the second value to 0.
For the If behaviour connected to the Top Wall branch, set the first value to the result of the connected Subtract Values behaviour. Set the condition to < ("Is Less Than") and the second value to 0.
Both of these If statements will continue execution if the paddle surpasses each wall respectively.
If the paddle surpasses a wall, we want to forcibly stop the paddle from moving any further by resetting the paddle's velocity to 0, and then push it back into the field.
Under the Physics section, place down and connect a Set Velocity behaviour for each branch. On both, set their Velocity X/Y values to 0.
Now we can push the paddle back into play. Under the Transform section, place down a Move By behaviour for each branch.
In each Move By behaviour, leave the Move By X values at 0 and set the Move By Y values to the result of the Subtract Values in their respective branches. Then set the duration length to 0 seconds (instantly).
This entire section, when executed, will now check to see if the paddle is inside a wall, how far it's inside a wall, and push it out if needed. Now we just need to execute this chain of behaviours when ever the paddle moves.
For simplicity, at the very top of this chain of behaviours, add a Behaviour Bundle from the Custom section and connect it above the Get Bounding Box. You can rename it to something useful, such as "Check Bounds".
Note: The "Behaviour Bundle" behaviour doesn't do anything itself, and is only used here to mark a "starting point" for checking if the paddle is out of bounds.
Next, under the Custom section place down a Execute Behaviour. In its properties, tap on the "Select Behaviour" field and choose the "Check Bounds" behaviour we just made. Now move the Execute Behaviour under where we originally set the velocity of the paddle for paddle movement. Connect the Execute Behaviour to the Set Velocity behaviour from the original paddle movement chain.
Now, every time the paddle updates its movement velocity, the Execute Behaviour will "jump" execution over to our new Check Bounds behaviour chain, where it will check if the paddle moved out of bounds!
Test it out and see it in action!
One last thing to do now before moving on, let's move the paddle to the left side of the field.
Now that we have some room, let's build a ball!
Part 4: Making the Ball
Back in the scene editor, create an empty object. Set its physics mode to Physics and disable Passable, so the ball can move and physically collide with the boundary walls. Set the friction to 0% and the bounce to 100%. Set the colour to white (#FFFFFFFF). For simplicity, you can also rename this object to "Ball".
In the transformation tab, set the scale to X=25% and Y=25%, and the position to X=50% and Y=50%.
Next, open the behaviour editor for the ball and we'll give it some movement. Under the Custom section, drop down a Wait behaviour and set it to 3 seconds.
Then, place a Random Number behaviour from the Logic section and connect to the Wait behaviour. For the first value put -1.0 and for the second value put 1.0. Be sure to include the ".0", that way it will randomly pick a number between -1.0 and 1.0 including decimal places, otherwise it will only choose whole integers. This will serve as the random "pitch direction" when the ball first launches.
Next, drop and connect an Apply Force behaviour from the Physics tab. Set Force X=-1, and set Force Y to the result of the Random Number behaviour. This will serve as the direction the ball will move, but 1 newton is not a lot of force, so we should change the multiplier, too. Set the multiplier to 150 and test the game to see if it's too fast or slow. If so, you can change this multiplier to your liking. The ball should start moving after 3 seconds when playing, as if it's been served.
When testing, the ball should bounce around against the walls, but pass through the paddle. That's normal, as we have yet to implement the paddle collision behaviours on the ball. You may have also noticed the ball can sometimes start spinning, which we don't want so let's fix that.
Back in the ball's behaviour editor, place down anywhere a Lock Rotation behaviour under the Transform section and test again. Now, the ball won't spin out of control anymore.
Before we begin adding behaviours for colliding with the paddle, back in the scene editor tap on the paddle and in the paddle properties go to the tags tab. In the text field, type in "Paddle" and press the + button to add the tag to the paddle.
Now return to the ball's behaviour editor. Under the Object section, drop down anywhere a Collided behaviour. In that behaviour's properties, tap on the Tags tab and select the Paddle tag we created. Now, whenever the ball hits any object with the tag "Paddle", this event will execute.
What we'll want to do, is take the ball's current velocity and flip it's X velocity. That way if the ball was moving left it will start moving right, and vice versa. We also want the ball to move a tiny bit faster than previously, so as the round goes on the ball will slowly get faster.
Connected to the Collided behaviour, drop down a Get Velocity behaviour from the Physics section. Afterwards, connect a Multiply Values behaviour from the Logic tab. Set the first value to the result "velocity_x" from Get Velocity. For the second value, set the value to -1.03. The result of this multiply value will be the negative x-velocity of the ball, as well as a 3% speed increase. So let's apply that change next.
Connected to the Multiply Values behaviour, drop down a Set Velocity behaviour from the Physics section. Set its X velocity to the result of the Multiply Values and leave the Y velocity as current velocity.
Now when you play, the ball should now bounce around, hit the paddle and reverse direction with a tiny, almost unnoticeable speed increase
It's all starting to come together now! The ball and paddle moves around nicely, but the ball's pitch doesn't change. When the ball hits a paddle it should change the pitch of the ball, too; if the ball hits closer to the top of the paddle, then it should pitch up, and if the ball hits closer to the bottom of the paddle, then it should pitch down. This will add a layer of complexity, as well as skill since the player(s) will have some control where the ball will go.
To find out how heavy we should pitch the ball, we'll need to get the difference in position between the ball and the paddle. Under the Transform section, place down two Get Position behaviours and connect them in series under the Set Velocity behaviour. The first Get Position will stay tracking the Ball by default. Change the second Get Position object to the output "ObjectB_ID" from the Collided event behaviour.
Note: "ObjectB" in this case would be the colliding paddle as it is the second object related to the collision event, whereas the first object ("ObjectA") would be the ball.
Drop and connect a Subtract Values behaviour from the Logic section. Set the first value to the result "position_y" of the first Get Position tracking the ball. Set the second value to the result "position_y" of the second Get Position tracking the colliding paddle.
Afterwards, drop and connect an Apply Force behaviour from the Physics section. Set the force values X to 0 and Y to the result of the Subtract Values. The position difference alone won't be very strong so we'll also have to modify the multiplier. Try a multiplier of 50 and see how it feels in game.
While testing, the ball should now change it's pitch depending on where you hit the ball on your paddle. The ball should pitch further up if it hits near the top of the paddle and should pitch down if it hits near the bottom of the paddle. If your ball is pitching in the wrong direction, try swapping the values in the Subtract Values behaviour and that should resolve the problem.
We're almost done fully implementing the movement of the ball! There is still one minor issue; there's no limit how fast the ball can go! In fact, after playing for a while without resetting, the ball can move so quickly it can actually pass through objects. We should implement a clamp on the ball's velocity so it can't reach this incredibly high speed.
After the Apply Force behaviour, place down and connect a Get Velocity behaviour from the Physics section to get the new velocity of the ball. Under that, drop down and connect a Maximum behaviour and a Minimum behaviour in series from the Logic section.
In the Maximum properties, set the first value to the result "velocity_x" from Get Velocity, then set the second value to -25. This will result with which ever value is the greatest.
In the Minimum properties, set the first value to the result of the Maximum, and the second value to 25. This will result with which ever is lesser, effectively giving us a clamped value of the ball's X velocity between -25 and 25. No higher and no lower!
Now we need to do the same for the ball's Y velocity. Drop and connect another set of Maximum and Minimum in series. In the new Maximum properties, set the first value to the result "velocity_y" from Get Velocity, then set the second value to -10.
In the new Minimum properties, set the first value to the result of the new Maximum, and the second value to 10. This will result in a clamped Y velocity between -10 and 10. Feel free to change these values later on if you prefer!
Now that we have the clamped velocity values for the ball, let's apply them now. Drop down and connect a Set Velocity behaviour from the Physics tab. In its properties, set the X velocity to the result of the first Minimum clamping the X velocity, then set the Y velocity to the result of the second Minimum clamping the Y velocity.
Now, no longer how long a match goes for, the ball won't go to extreme speeds. The Y velocity is also clamped so the ball won't pitch too much.
Part 5: Paddle Artificial Intelligence
Though it would be fine to duplicate the paddle and create a second playable paddle, wouldn't it be cool to have the option to fight an AI instead of 2-player? Of course it would! In this part, we'll look into giving the paddle AI and giving the user the option to either play in 2-player mode or against an AI.
Using the system we set up for player movement - that is changing the Move Direction box container - implementing an AI is pretty straight forward. Instead of using a joystick, the paddle will change its Move Direction depending if the ball is above or below it.
We will also add some entropy to the Move Direction as well, or in other words, some randomness. Doing this, we'll give a little bit of a human-like characteristic to the AI. The AI won't feel quite as robotic and won't simply play the game perfectly.
Here's how we'll define our AI:
- The AI will move only if the ball is coming towards them.
- Move Direction is simply the difference between the ball and paddle's Y (vertical) position, plus some entropy.
- When ever the ball hits anything (a paddle, wall, or anything else), the entropy of the paddle's AI will change.
- Entropy is based on the speed of the ball; the faster the ball moves, the higher the entropy value and therefore the more inaccurate the paddle will be.
Sounds simple enough, let's get to work.
Tap on the paddle and enter its behaviour editor. Find an empty spot and drop down a new Timer behaviour from the Custom section. Set it's timer for 0 seconds. For readability, you can change it's name to something useful like "AI Think".
Note: If you receive a message here or in the future about having too many timers with 0 seconds, you can safely ignore it. We won't have very many in our project.
First, we want to set the Move Direction to 0, that way the paddle stops moving when we're not supposed to move at all. Tap on the Move Direction box behaviour we made in Part 2, and tap the Change Input Field button to create a new Set Input Field behaviour. Connect the Set Input Field behaviour to the AI Think Timer and set its New Value property to 0.
Under the Transform section, drop down two Get Position behaviours and connect them in series from the second execution connection of the AI Think Timer. Set one of them to track the Ball, and leave the other tracking the paddle. For better readability, you can rename these behaviours as well. You should have something like this:
This way, the Move Direction value is set to 0 first before it continues to the rest of the AI behaviours.
Next, drop and connect a Get Velocity behaviour from the Physics section and have it track the Ball. With these three behaviours, we have all the data we need to check if the ball is coming towards the paddle.
Drop and connect an If behaviour from the Logic section. Set the first value to the result "position_x" from the Get Position behaviour for the paddle. Set the condition to "Is Less Than" (<), and set the second value to the result "position_x" from the Get Position behaviour for the ball.
If this condition is true, that means the paddle is to the left of the ball. In other words, the ball is to the right of the paddle.
We also need a branch for when the ball is to the right of the paddle, too. Drop and snap another If behaviour onto the right side of the If behaviour. This will convert it into an "Else If" behaviour, and will execute if the first If behaviour is false. Its properties can be left as-is.
Now that we have two branches for whether the ball is to the left or right of the paddle, we need to check if the ball is moving towards the paddle.
Drop down another If behaviour and connect it to the first If behaviour. Set the first value to the result "velocity_x" from the Get Velocity behaviour for the ball. Set the condition to "Is Less Than" (<), and set the second value to 0. This If behaviour will be true if the ball is moving towards the left, which is exactly what we want because the If behaviour from before is true when the ball is to the right of the paddle! Therefore, the ball must be coming towards the paddle.
Now we need to do the same for the "Else If" branch. Tap on the new If behaviour and duplicate it, then connect it to the Else If behaviour. Set its condition to "Is Greater Than" (>). Now, if the ball is to the left of the paddle, this If behaviour will run and check if the ball is moving to the right. If that's true, the ball must be coming towards the paddle as well.
If execution can make it past this barrage of if statements, that means the AI is allowed to move! Now we need to update the Move Direction depending on where the ball is vertically.
Under the Logic section, drop down a Subtract Values behaviour. Connect both If behaviours to the same Subtract Values behaviour. Set the first value to the result "position_y" from the Get Position for the ball, then set the second value to the result "position_y" from the Get Position for the paddle.
You should have something that looks like this:
This will give us the distance between the paddle and the ball on the vertical axis. If we use this value, that means the paddle will try to move faster if the ball is far above or below the paddle. All we need to do now is set the value in the Move Direction box container!
Tap on the Move Direction box container and make another Set Input Field behaviour by pressing the Change Input Field button. Move and connect it to the Subtract Values, and set its New Value property to the result of the Subtract Values behaviour.
Test it out and the paddle should now come alive all on its own!
You may have noticed while watching the AI play, it appears to be playing very well. Our AI is currently set up to play about as perfectly as it can, which is an issue if we want to give human players a decent chance at winning. This is where adding entropy comes in; giving some randomness to our AI will make it less accurate, less robotic, and less of a pain for humans to play against.
Back in the paddle's behaviour editor, drop down another Box Container from the Custom section, and call it "Entropy" for readability. Set its default value to 0. Next, drop down an Add Values behaviour from the Logic section.
Disconnect the Subtract Values from the Set Input Field we just recently made and instead connect the Add Values between them. In the Add Values properties, set the first value to the result of Subtract Values, and set the second value to the result of the Entropy Box Container.
In the Set Input Field properties, change its New Value property from the Subtract Values result to the Add Values result.
Now, the Move Direction of the paddle will add the value of the Entropy box container, so all we have to do is store some random value in Entropy and it will be added into the paddle's Move Direction, making it less accurate. Let's keep going.
As we detailed earlier in this part, we want the entropy to change when the ball hits anything, like a paddle or a wall. This will attempt to emulate how a human would have to (inaccurately) reevaluate where the ball will go anytime its course changes.
Find an empty spot and drop down a Collided event behaviour from the Object section. For the first object, select the Ball as the tracked object. Leave the second object as-is. All other settings can be left alone as well. This event behaviour will execute when ever the ball collides with any object.
What we also detailed earlier in this part, is that we want the entropy to be based on the velocity of the ball as well. This will attempt to emulate how a human would also evaluate the course of the ball more inaccurately as it moves faster.
What we'll do is set the base entropy to a value between the negative Y-axis velocity and positive Y-axis velocity of the ball. Drop down and connect a Get Velocity behaviour from the Physics section and set it to track the Ball object.
Drop and connect a Subtract Values from the Logic section. Set the first value to 0 and set the second value to the "velocity_y" result from Get Velocity. This will give us the negative Y-axis velocity of the ball.
Next, drop and connect a Random Number behaviour from the Logic section. Set the first value to the result "velocity_y" from Get Velocity, and set the second value to the Subtract Values result. This will give us a number between negative velocity_y and positive velocity_y.
We could use the result of random number as the entropy value as-is, but the paddle will be wildly inaccurate. So, we need to scale it down a bit.
Drop and connect a Divide Values from the Logic section. Set the first value to the result of Random Number, and set the second value to 5. The bigger the second value is, the more accurate the paddle will be. Inversely, the smaller second value is, the less accurate the paddle will be.
Note: If you prefer, you may change this value to make the AI easier or more difficult. This value could also be changed depending on a selected difficulty level.
Now let's update the Entropy box container. Tap on the Entropy box container and tap the Change Input Field button to make a Set Input Field for Entropy. Connect the Set Input Field to the Divide Values and set its new value to the result of Divide Values.
Now when you play the game, the AI doesn't play as robotic anymore. It will make more mistakes, and won't only try to hit the ball dead-center.
And with that, we've checked all the boxed we wanted for creating an AI. In the next part, we'll duplicate the paddle and set up the different play modes.
Part 6: Second Paddle and Playable Modes
In this section, we're going to set up the ability to select different playing modes (such as Player vs. Player, Player vs. AI, and AI vs. AI), as well as setting up the second paddle.
In the scene editor, select the Global UI layer. Tap the Create Special Object button and create a label object with the text "Game Settings". Move the label off-screen where the player won't see it.
We'll use this object to contain some game settings. Since the label is on the Global UI, it is automatically deleted when moving between scenes. Attributes that we store on this label won't be reset when we move from scene-to-scene, making it a great way to store data we want to keep.
Note: The "Refresh" setting found on some Scene loading behaviours will cause all objects to be reloaded, including Global UI objects and attributes. Be sure to remember this when you make Global UI objects in your own projects!
With the Game Settings object selected, go to the attributes tab and create two attributes. One called "Player 1 AI" and the second "Player 2 AI". Set the Player 1 AI attribute to 0, and set the Player 2 AI attribute to 1.
We'll use these settings to set the paddles to "Player mode" or "AI mode". For instance, if Player 1 AI is set to 0, that means Player 1 is a player, not an AI. If Player 2 AI is set to 1, that means Player 2 is an AI. We set these default values so that we can test the game right from the editor.
Now, go back to the Main layer and enter the paddle's behaviours again. We'll add some behaviours to disable certain paddle behaviours depending on whether it's supposed to be a Player or an AI.
Find an empty spot and drop down a Get Attribute from the Objects section. In its properties, set the object field to track the Game Settings label. You will need to select the Global UI layer from the object selection screen to pick it.
Set the Attribute Key Type to Predefined, and set the key to "Player 1 AI". This behaviour will get the value set the game settings for player 1.
Next, drop down and connect an If behaviour from the Logic section. Set the first value to the result "attributeValue" of Get Attribute. If it's not already set, change the condition to "Is Equal To" (=) and set the second value to 0. This If branch will execute if this paddle is supposed to be a Player, not an AI.
In the Custom section, drop down and connect a Behaviour Off behaviour. Set the selected behaviour to the AI Think timer behaviour. When this behaviour executes, it will disable the AI Think timer, meaning only the joysticks will change the Move Direction of the paddle.
Now we need to do something similar for the AI mode. For that, we need an Else If behaviour. Drop down another If behaviour and snap it to the right side of the last If behaviour, transforming it into an Else If. No need to change any of its properties.
In the Custom section again, drop down and connect two Behaviour Off behaviours to the Else If. Set one of them to the Joystick Analog behaviour, and set the second one to the Stopped Touching behaviour.
Now if the paddle is set to be an AI, it will disable the events for moving the joystick, only allowing the AI Think timer to control movement. This is pretty close to what we want, but we should also hide the joystick since it's not doing anything.
In the Transform section, drop and connect a Hide Graphic behaviour. Set the object to the joystick. You may have to select the Global UI layer to select it.
Since we're hiding the graphic here, we should also add a way to show it again if the paddle changes back to player mode. Drop down a Show Graphic behaviour from the Transform section, and connect it to the Behaviour Off for the AI Think timer. Set the object to the same joystick.
At this point, we're now ready to finally create the second paddle. Tap on the Player 1 paddle hit the duplicate button, and move it to the right side of the playable area. Turn on the grid to keep everything lined up. Its name should be "Player-2".
Now we need to duplicate the joystick so it can have its own separate controls. Select the Global UI layer and tap on the joystick. Duplicate it and move it to the right as well.
Now we need to update a few behaviours in the Player-2 paddle. Back on the Main layer, tap on the Player-2 paddle and open its behaviour editor.
Tap on the Get Attribute we made just recently. In its properties, change the key from "Player 1 AI" to "Player 2 AI"
Then, on both the Show Graphic and Hide Graphic we also recently made, change both to track the second joystick we just made.
Finally, on both the Joystick Analog and Stopped Touching event behaviours we made all the way back in Part 2, change both to track the second joystick.
Now when you play the game, you should be able to hit the ball back and forth with an AI on the other side! (Remember, we set "Player 2 AI" in game settings to 1, so it will activate its AI mode and disable player controls.)
With the ability to set paddles as player-playable or as AI, we can now update our main menu buttons. Go the scene menu and select Main Menu to open the menu scene we made in Part 1.
Tap the Create Special Object button and create a new label. Change its colour to white (#FFFFFFFF) to match the theme and center it on screen. Change the text to "Play 1 Player". Set the text alignment to be centered as well.
We need to add some behaviours so it loads the game with the right settings. We want this button to enable Player vs. AI mode, where Player-1 is the player and Player-2 is the AI. With the new label selected, enter its behaviour editor.
Let's begin by adding a Started Touching event behaviour from the Interaction section. It should already be tracking our label.
Then, drop and connect a Set Attribute behaviour from the Object section. Set the tracked object to the Game Settings label, you may have to select the Global UI layer to pick it. Set the attribute key type to Predefined and pick the "Player 1 AI" attribute. Since this button will play the game in Player vs. AI mode, set the attribute value to 0 so it doesn't turn the Player-1 paddle into an AI.
Next, duplicate the Set Attribute behaviour and connect it. Set its key to "Player 2 AI" and change the attribute value to 1. This will make the Player-2 paddle controlled by the AI.
Lastly, under the Scene section, drop and connect a Load Scene behaviour. Set the selected scene to the Game scene. Leave the Refresh option off, as that will reset our Game Settings attributes. If you like, feel free to select a Transition animation.
Back out of the behaviour editor for that label to return to the Menu scene. With our Play 1 Player button selected, duplicate and move the copy under the other label. Change the text to "Play 2 Player".
Enter the new label's behaviour editor and change the attribute values so both "Player 1 AI" and "Player 2 AI" are set to 0. This button will start the game with both paddles as human-playable.
Back out of the behaviour editor for that label to return to the Menu scene. With our Play 2 Player button selected, duplicate and move the copy under the other label. Change the text to "Watch AI".
Enter the new label's behaviour editor and change the attribute values so both "Player 1 AI" and "Player 2 AI" are set to 1. This button will start the game with both paddles as AI, so you can watch them play against each other.
Back out of the behaviour editor for that label to return to the Menu scene. Test out the game to make sure that all play modes are working properly.
Play 1 Player should set the left paddle as playable, and right paddle as AI.
Play 2 Player should set the left and right paddle as playable.
Watch AI should set the left and right paddle as AI.
Part 7: Scoring and Winning Conditions
Our Pong clone is looking very promising so far. In this part we'll add a way for players (or AIs) to score on each other and win matches. Open the scene menu and open up the Game scene.
Tap on the ball and open its behaviour editor. We need to track when the ball hits the wall to the left or right, then give points to the player who scored.
Under the Object section, drop down two Collided behaviours. On one of them, the first object should be the Ball by default, and set the second object to the wall on the left. On the other Collided event behaviour, the first object should also be the Ball by default, and set the second object to the wall on the right.
Next, drop down two Broadcast Message behaviours from the Custom section. Connect each to their own Collided behaviour. For the Broadcast Message behaviour connected to the Collided event for the left wall, change the event key in its properties to "player 2 scored". For the other, set it to "player 1 scored".
Later on, we'll need these messages to give our players points.
Now we need to stop the ball from moving and reset the ball to its original position. Under the Physics section, drop down a Set Velocity behaviour. Connect it to both broadcast messages, and set the velocity properties X=0 and Y=0. You should have something that looks like this:
Drop down and connect a Move to Point behaviour from the Transform section. Set the position type to Relative Position (%) and change the point values to X=50% and Y=50%, enable screen coordinates. Then, set the duration to 0 seconds so it moves instantly.
We will also need to announce that the game needs to be reset, so that we can reset the serve timer when a player scores.
Drop down and connect a Broadcast Message, and set the event key to "reset".
In the Ball's behaviour editor, look for the Wait (3 seconds) behaviour we made in Part 4 that serves the ball when the game starts. Above it, drop and connect a Receive Message behaviour from the Custom section, and set the event key to "reset".
Now, our ball will get served every time the game is reset. But, now our ball isn't being served when the match first starts. We need to execute the ball serving behaviours when the ball is created as well. We can do this by connecting a non-event behaviour to the wait as well. Drop down a Behaviour Bundle from the Custom section and connect it to the Wait behaviour alongside the Receive Message behaviour. You should have something like this:
When you test the game, the ball should reset when ever it hits the left or right wall. It should then serve after 3 seconds.
Since we're now broadcasting messages when a player scores, let's create a score board. Close the behaviour editor and return to the scene editor.
Select the Scene UI layer and tap on the Create Special Object button and create a new label. Change its color to white (#FFFFFFFF) and move it to the top left of the field. Set the text to "0", use 72pt size, and use center alignment.
This label will keep track of Player-1's score. Enter its behaviour editor an we'll add some scoring behaviours.
Under the Custom section, drop down a Receive Message. Set the event key to "player 1 scored". Next, under the UI section, drop and connect an Add to Score behaviour. Set the Add By property to 1 and change the Event When Score Reaches property to 11.
Under the Scene section, drop down and connect a Load Overlay behaviour. Set the overly to the pre-built Game Over overlay. When the score reaches 11 points, the Add to Score behaviour continues execution and loads the Game Over screen, marking the end of the match.
Exit the behaviour editor and duplicate our score label. Move it to the right side for Player-2.
In the behaviour editor for the new label, change the Receive Message event to "player 2 scored".
When you test the game, you should see the score board count up by 1 when a player scores. If the left wall is hit, Player-2 scores. If the right wall is hit, Player-1 scores. Once a score reaches 11 points, the Game Over screen will show.
Next, let's add a count down timer on the screen for counting down the ball serve.
Create another label and place it in the center of the screen. Set the colour to white (#FFFFFFFF) and clear the text field so it's empty.
Open the label's behaviour editor. Under the Custom section, drop down a Receive Message and set the event key to "reset". Next, under the UI section, drop down and connect a Count Down behaviour and set the time to 3 seconds.
Last, also from the UI section, drop and connect a Set Label and leave the text field blank so no text will be shown after the count down.
When the ball is reset, we'll see a 3-2-1 count down for serving. We also want to see it when we first start the match as well. Just like what we did with the ball's serving behaviours, drop down a Bundle Behaviour and connect it to the Count Down behaviour with the Receive Message behaviour. You should have something like this:
When you test the game, you should now see a 3-2-1 count down for when the ball is being served.
Next, let's change the Game Over menu to say something more awarding than a "Game Over" message, as well as some other tweaks. In the scene editor, open the scene menu and expand the "Overlays" section. You should see the pre-made "Game Over" overlay. Click the gear button and enter its Settings. Change its name to "Player 1 Wins"
Next, tap on the overlay name in the scene menu to open it.
First, tap on the Restart label and delete it. Next, tap on the Main Menu label and change the text to "End Match". This will only give us the option to return to the main menu, since restarting the scene will also reset the game settings, and we don't want that.
Then, tap on the big Game Over label and change its text to "Player 1 Wins!". Lastly, change the colours of the labels to white (#FFFFFFFF) to match the rest of the theme of our game. The overlay should now look something like this:
Now let's make an overlay for Player 2. Open the scene menu and tap the gear button next to "Player 1 Wins" overlay. Choose Duplicate to make a copy of the overlay. In the scene menu again, click the gear button next to the duplicate overlay and change its name to "Player 2 Wins".
In the Player 2 Wins overlay, change the big label from "Player 1 Wins!" to "Player 2 Wins!".
Now we need to update our scoring behaviours to open the right overlay. In the scene menu, expand the "Scenes" section and open the Game scene.
Tap on the Player-1 score board label and open its behaviour editor. Ensure that the Load Overlay is set to open the "Player 1 Wins" overlay.
Return to the scene editor and open the behaviour editor for the Player-2 score board label. Tap on the Load Overlay behaviour and change its selected overlay to "Player 2 Wins".
When you play the game, now it should show "Player 1 Wins!" when Player-1 gets 11 points, and "Player 2 Wins!" when Player-2 gets 11 points.
We're almost done with this part of the tutorial! One more detail we should add in our game is changing the serve direction of the ball. Currently, the ball will always get served towards Player-1. At the beginning of the match, the ball should randomly be served left or right. When a player scores, the ball should be served towards them.
Back in the scene editor, tap on the ball and open its behaviour editor. Drop down anywhere a Box Container from the Custom section, and rename it "Serve Direction" for easier readability.
Next, drop down anywhere a Execute Sequence behaviour from the Logic section. In its properties, change the Sequence Type to Random.
This will be our decider for which direction the ball will be served at the beginning of the game. Tap on the Serve Direction box container and create two Set Input Field behaviours by tapping the Change Input Field button twice. Connect both to the Execute Sequence in parallel. On one of the Set Input Field behaviours, change the New Value to -1.
On the other, set the New Value to 1.
When the game starts, it will set the Serve Direction to -1 (serve left) or 1 (serve right) randomly. Find the Apply Force behaviour in the ball serving behaviour chain, and set its Force-X value to the result of the Serve Direction box container.
Test the game a few times and the ball should get served either left or right at random at the start of the game.
Now we just need to change the serve direction depending on who scores. Create another Set Input Field from the Serve Direction box container and find the player-wall collision behaviours. Make some room and connect it to the "player 2 scored" Broadcast Message behaviour. In the properties of the Set Input Field, set the New Value to 1. This will change the serve direction to serve towards Player-2.
Duplicate the Set Input Field, connect it to the other "player 1 scored" Broadcast Message behaviour and change its New Value to -1. This will change the serve direction to serve towards Player-1.
That's it for this part of the tutorial! When Player-1 scores, the ball should be served left towards them, and when Player-2 scores, the ball should be served right towards them. Additionally, players now have the ability to score on each other and eventually win or lose.
Part 8: Pause Menu, Quit, and Final Touches
At this point, we have just about everything we need to call our game complete. It's only missing a couple little features like an in-game Pause button and a graceful way to quit the game from the main menu. Lastly, we should hide the joysticks while on the main menu as we don't need them there. After that, we'll call it a job well done.
In the scene editor for the Game scene, select the Scene UI layer and tap the Create Special Object button to create a new label. Make its colour white (#FFFFFFFF) and place at the top of the screen. Change the text to "Pause". Change its alignment to center as well.
Open its behaviour editor and place down a Started Touching behaviour from the Interaction section. Connected under it, place down a Load Overlay behaviour and set the selected overlay to the pre-made Pause Menu. Ensure the the Pause option is enabled. Feel free to pick a transition style if you prefer.
Let's also tweak the Pause Menu a bit. Return to the scene editor and open the scene menu. Expand the Overlays section and open the Pause Menu overlay.
Tap on the Restart label and delete it, as we did with the player winning overlays. Change all the label colours to white (#FFFFFFFF) and change the Main Menu label text to "End Match".
When you test the game, the in-game Pause button should now open the Pause Menu overlay, which will also pause the game in the background. Resume should continue the game where it left off and the End Match button should take you back to the main menu scene.
Next, let's make some finishing touches to the Main Menu scene. Back in the scene editor, open the scene menu and expand the Scenes section. Select the Main Menu to open it.
Let's create a "Quit" button. Make some room for another button and duplicate one of the Play mode buttons. Move it to the bottom of the list and change its text to "Quit Game". When that's done, open its behaviour editor.
In the behaviour editor, delete all behaviours except for the Started Touching behaviour so it's the only one left.
Under the Scene section, drop and connect a Quit Project behaviour. When you test the main menu, the Quit Game button should now end the game.
Lastly, let's hide the joysticks from the main menu. Tap the title label and open its behaviour editor. We'll sneak some behaviours in there to hide the joysticks.
In the behaviour editor, drop down two Hide Graphic behaviours from the Transform section. For one, set the object to one of the joysticks. For the other Hide Graphic behaviour, set the object for the other joystick. While selecting the joysticks, you may need to first select the Global UI layer to pick them.
Now when you test the menu, there should be no more joysticks shown there! When you play the game, they should come back as the paddles in-game are told to show or keep hiding the joysticks depending on whether they're an AI or human-playable paddle.
Congratulations! You've made a fairly convincing clone of the famous Pong game.
It was a lot to take in, but if you've made it this far you should have an understanding of a wide range of game design topics; from creating artificial intelligence to dynamically altering scenes based on settings and more. This knowledge can be applied to any and all types of games to make your games really stand out.
There's always more that can be done to continue improving on what we have so far...
Feel free to add your own graphics and sound effects and continue practicing new skills. As an exercise, try to implement difficulty levels (Easy, Normal, Hard, etc) when choosing to play with AI paddles using the existing foundation we have created for our AI. (Hint: See the end of Part 4 of this tutorial!)
A completed project file for this tutorial can be found here: Download Tutorial Project