My First Game – Part 10: Pong – Post Mortem

We made a simple Pong clone using HaxeFlixel. Here is a summary of each parts:

Part 1 – Mental Preparation

This isn’t really part of the Pong tutorial, but if you’re making a game with the intention of commercialising your work some day, then it’s best to know what hurdles you have ahead.

The Minimum Viable Product (MVP) is concept that helps, especially when you have too many game ideas and too little time.

Part 2 – Game Design

Before you start coding your Pong game (or any other game), you need to lay down the design. In a perfect world, your version of Pong would have the most realistic physics, with high-definition graphics and amazing sounds, and an amazing title screen.

Unfortunately, we don’t have the time for that. We gotta pick what we want to make as an MVP, and strip out unnecessary things. Once you list down the necessary components and tasks to complete the game, you can gauge how much time it’ll take to finish it.

Part 3 – Setup

For beginners in HaxeFlixel, this is a short guide on getting your project up and ready for coding.

Part 4 – Paddle

Part 5 – Paddle Movement

Part 6 – Second Paddle

Part 7 – The Ball

Part 8 – Score Text

Part 9 – Sound

Parts 4 to 9 are straightforward — you make a Pong clone with as little effort as possible. Nothing fancy like realistic physics or intelligent AI.

While going through the tutorial, you might have had a lot of nagging thoughts in your head, such as “But the ball doesn’t bounce realistically”, or “But the score text is too plain”.

That’s normal, and it’s good to have these thoughts, but they’re not necessary in completing a game. They are part of the polishing phase — after you’re done with the game’s basic features, you start improving it.

I learned the hard way in failing to complete several of my game projects. Often, I find myself thinking “Oh it’ll be great if the game had this feature”, only to be boggled by its complexity, and ultimately making me lose interest in the game completely.

It is, in fact, a challenge to strip a game idea to its bare minimum. So work your way toward an MVP whenever you have this Amazing Idea™ in your head, and polish from there. More often than not, you’ll struggle just to finish your MVP instead of the actual full game.

In the next post, we shall begin a new tutorial — an Asteroids clone. Or rather, it is a study on an existing HaxeFlixel demo – FlxTeroids.

My First Game – Part 9: Pong – Sound

Games feel much better when there is sound, no matter how simple it is. In the case of Pong, we only need one sound — when the ball hits the paddle, or when it hits the screen edge.

Before we add sound to the game, let’s assume you want to create one. You can easily do this by visiting Bfxr.net.

ss8

  1. Click on any sound effect button on the top-left of the app until you hear one that you like.
  2. Click on the effect that you want to export as a sound file on the bottom-left of the app.
  3. Click on “Export Wav” button on the bottom-right of the app to save the audio onto your computer.

Now we have a simple sound file, let’s use it in the game. Before we can use the sound file, we need to add the file to our assets folder.

Note: the assets/music  folder is for background music — because it loops infinitely.  In our case of Pong, we’re adding a sound effect. For that, we can put them in the assets/sounds  folder.

Let’s insert the audio-playing code in Ball.hx  itself:

Here are some notes on the code above:

  • In order to use FlxSound , we need to import  it first, as indicated at line 5.
  • We then create a class variable bounceSound  at line 11.
  • FlxG.sound.load()  will load sound files from the assets/sounds  folder automatically
  • In the line 24, we load the sound when the Ball is created. In Haxe, the sound files can be loaded as a variable name rather than by string — written as AssetPaths.Hit_Hurt5__wav  instead of a string value, e.g.   "assets/sounds/Hit_Hurt5.wav" .
  • Alternatively, instead of bounceSound.play(); , you can use  FlxG.sound.play("assets/sounds/Hit_Hurt5.wav"); .
  • After we have loaded the sound, we can conveniently play the sound by just using  play() , as indicated on 34 and 56.

 

If you test the game now, there will be sound when the ball bounces! Congratulations, you have completed a simple game of Pong, minus the polish and game balancing. It’s not pretty, but it’s entertaining enough for at least a minute. 😛

In our next post, we shall wrap up with a short post-mortem of this Pong clone project and discuss some points when creating a proper game.

My First Game – Part 8: Pong – Score Text

We pretty much have an MVP already by now — Players can bounce the ball back against each other, and the round restarts when the ball goes behind either of the paddles.

Now let’s keep track of the score to see who’s winning. First we need a variable that doesn’t get reset whenever we reload the MenuState  screen.

Note that whenever you create a HaxeFlixel template project, there is a Reg.hx  file in your source folder. You can use this file to store any game data during the game’s lifetime. This is where we’ll store our Pong score data too. We don’t need to modify anything in Reg.hx  because there’s already a scores  variable there.

Remember, we have two players, so we need to keep track of two scores. Where do we handle the scoring? Let’s do it in MenuState.hx , because that’s where most of our code logic is anyway:

There are three parts to note in the code above.

In the first part, by default, uninitialised values are null . In Haxe, there are various different default null values, depending on which target platform you want to build on, as noted in this page. As such, to play safe, I did a quick check on the initial values of the Reg.scores  array. In case the value is not larger than zero, we initialise it to zero.

In the second part, we just load the score values accordingly.

In the third part, I set the position of the scores at an arbitrary position — The top-middle part of the screen. We then create two FlxText  objects (based on the parameters for  new() ) and add them to the stage.

If you test the game now, you will notice that the score is always zero, no matter who wins. That’s because we haven’t added any code to manipulate the score. Let’s do that now. In Ball.hx , modify the code for update() :

Note that we have split the switchState  code into two parts — If the ball exits the screen on the left, it means Player 2 (on the right) gets a point. If the ball exits on the right, Player 1 (on the left) gets the point.

Now if you test the game, the score will update correctly:

ss7

Before we can pass this off as a somewhat completed game, we will add one finishing touch to it — sound effects. We’ll cover that in the next post.

My First Game – Part 7: Pong – The Ball

Now that we have two paddles that can be moved, we will need a ball. Here’s what the ball needs to do:

  • The ball always starts at the middle of the screen.
  • The ball automatically moves when the round starts.
  • If ball touches the top or bottom of the screen, bounce it
  • If ball touches the paddle, bounce it
  • If the ball touches the left or right of the screen, restart the round.

Note that we will not handle player scoring (when ball touches left/right side of the screen) yet, because that would be defined under game rules rather than the ball’s characteristics. Okay so let’s begin!

First, we create a new Haxe file, Ball.hx, in the source folder. Again, we copy the template FlxSprite  code from the cheat sheet page into Ball.hx , and rename the class to “Ball” to match the file name:

Similar to Paddle.hx , we add a piece of code to set the image of the ball — in this case, a circle instead of square:

In the code above, we use makeGraphic  to create a white square that is sized 30×30. Then we use FlxSpriteUtil  to draw a green circle at (X=15, Y=15) position within the square, at 15-pixel radius.

Note the  0xFF00FF00 . It’s hex code for the color — the first two digits being the opacity (00 being complete transparent, FF being completely opaque), and the last 6 digits represent the RGB (Red, Green, Blue) values.

Note that FlxSpriteUtil  only draws shapes rather than create shape objects, thus the need to draw a square before drawing a circle.

Now that we have the Ball class, we have to add it into the game, similar to the paddles. Go back to MenuState.hx  and add the following code into create() :

If you test the game now, you’ll have a green circle (with a white box behind it), but it doesn’t move. Let’s give it some movement by setting its velocity:

Now the ball moves, but it disappears off the screen. We need to bounce the ball when it hits the screen bounds. Let’s add some code to do that in update() :

In the code above, if the ball hits the upper or lower bounds of the screen, we invert the y-velocity to simulate bouncing. Then, it checks if the ball hits the left or right bounds of the screen — if it does, we reload the screen by using switchState() .

If you test the game now, you will notice the ball bouncing correctly — but it goes through the paddle. We need the ball to bounce when it hits the paddle too.

To do that, we add a new function and variable in Ball.hx  — which we will access from  MenuState.hx  later:

In the code above, we have a class variable hitPaddle  to store a reference to the paddle that the ball has bounced against. Why do we need to store a reference of the paddle? We’ll get back to this in a bit.

Back in MenuState.hx , we add two lines of code into update() , to check for collision between the ball and the paddles:

Note the third parameter, onHitPaddle  is a callback — when a ball overlaps a paddle, the callback function will be executed. Let’s write the callback code into MenuState.hx  as well:

If you’re wondering how the callback function is written, you can check out the HaxeFlixel API for overlap() :

http://api.haxeflixel.com/flixel/FlxG.html#overlap

Note the thisBall.bounce(thisPaddle) — Remember we talked about storing a reference of the paddle when the ball bounces of it? Logically, the ball would invert x-velocity once it touches the paddle. Unfortunately, the ball may overlap the paddle for more than once (e.g. it overlaps for 2 or 3 frames). This would make the ball invert its x-velocity indefinitely because its body is still overlapping the paddle.

If you want to see this “bug” in action, you can just remove the following highlighted code from Ball.hx and test the game:

As such, to prevent the bug, we store a reference to the paddle, which is like saying “I’ve bounced off this paddle before. If I’m bouncing off it again, I don’t need to invert my x-velocity“. It’s not a fool-proof way, but it works for Pong.

Now, if you test your game, it should bounce back and forth between the paddles correctly, and reset the stage of it goes beyond the left or right side of the screen.

ss6

Yes, it’s a little rough on the edges, but right now we’re only concerned with making a playable game. We can always polish the graphics or handle the ball-bouncing code more elegantly later on.

In the next post, we shall handle player scoring. It’s not going to be overly fancy — so it’ll be fairly quick. :)

My First Game – Part 6: Pong – Second Paddle

So far we have a paddle on the left, that can be moved with the UP and DOWN keys.

What if we want to add a second player? We can just make a quick modification to the MenuState.hx :

Oops, for the left paddle, we’re controlling it using UP and DOWN keys. What about the right paddle? Let’s modify the code a little…

Now we have two paddles on the screen! Note that the code is not optimised — it’s not adhering to the DRY (Don’t Repeat Yourself) methodology, but for now it’s excusable because we’re still learning.

Now if you test the game, you will see two paddles which can be moved by the W/S and UP/DOWN keys.

ss5

Now that we have two paddles, we will need the ball. In our next post, we shall create the Pong ball, inclusive of the logic to keep the ball within the screen boundaries and for the ball to bounce off the Paddles.