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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
package; import flixel.FlxSprite; import flixel.FlxG; class Ball extends FlxSprite { public function new() { super(); } override public function update():Void { super.update(); } override public function destroy():Void { super.destroy(); } } |
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:
|
public function new(X:Float, Y:Float) { super(X, Y); makeGraphic(30, 30, 0xFFFFFFFF); FlxSpriteUtil.drawCircle(this, 15, 15, 15, 0xFF00FF00); setSize(30, 30); } |
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() :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
// class variables var paddle:Paddle; var paddle2:Paddle; var ball:Ball; /** * Function that is called up when to state is created to set it up. */ override public function create():Void { super.create(); paddle = new Paddle(10, 50); add(paddle); paddle2 = new Paddle(600, 50); add(paddle2); ball = new Ball(FlxG.stage.stageWidth/2, FlxG.stage.stageHeight/2); add(ball); } |
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:
|
public function new(X:Float, Y:Float) { super(X, Y); makeGraphic(30, 30, 0x00FFFFFF); FlxSpriteUtil.drawCircle(this, 15, 15, 15, 0xFF00FF00); setSize(30, 30); this.velocity.x = 200; this.velocity.y = -200; } |
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() :
|
override public function update():Void { super.update(); if (this.y < 0 || this.y + this.height > FlxG.stage.stageHeight) { this.velocity.y *= -1; } if (this.x + this.width < 0 || this.x > FlxG.stage.stageWidth) { FlxG.switchState(new MenuState()); } } |
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:
|
var hitPaddle:Paddle; public function bounce(paddle:Paddle):Void { if (hitPaddle != paddle) { this.velocity.x *= -1; hitPaddle = paddle; } } |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
|
override public function update():Void { super.update(); FlxG.overlap(ball, paddle, onHitPaddle); FlxG.overlap(ball, paddle2, onHitPaddle); if (FlxG.keys.pressed.W) { // move paddle up paddle.y -= 5; } if (FlxG.keys.pressed.S) { // move paddle down paddle.y += 5; } if (FlxG.keys.pressed.UP) { // move paddle up paddle2.y -= 5; } if (FlxG.keys.pressed.DOWN) { // move paddle down paddle2.y += 5; } if (paddle.y < 0) { paddle.y = 0; } if (paddle.y > FlxG.stage.stageHeight - paddle.height) { paddle.y = FlxG.stage.stageHeight - paddle.height; } if (paddle2.y < 0) { paddle2.y = 0; } if (paddle2.y > FlxG.stage.stageHeight - paddle2.height) { paddle2.y = FlxG.stage.stageHeight - paddle2.height; } } |
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:
|
function onHitPaddle(thisBall:Dynamic, thisPaddle:Dynamic):Void { thisBall.bounce(thisPaddle); } |
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:
|
public var hitPaddle:Paddle; public function bounce(paddle:Paddle):Void { if (hitPaddle != paddle) { this.velocity.x *= -1; //hitPaddle = paddle; } } |
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.

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. 🙂