• Home
  • Who’s Zeke?

ZekeChan.net

Game Development - Build, Learn, Earn

Phaser HTML5 Game Tutorial: Build A Pong Game 5: Moving the Paddles & Adding Collisions

July 28, 2015 By Zeke 5 Comments

In part 4 of this series, we added a game mode and a feature to reset the ball. Now we’ll look at adding some controls to the paddles to make it move and collision detection so the paddles can deflect the ball.

Adding keyboard input

Here we set the paddle movement velocity at a 600 pixels. Next, add the following code to the mainState object:

JavaScript
39
40
41
42
43
44
45
46
47
48
49
var mainState = function(game) {
    this.backgroundGraphics;
    this.ballSprite;
    this.paddleLeftSprite;
    this.paddleRightSprite;
    
    this.paddleLeft_up;
    this.paddleLeft_down;
    this.paddleRight_up;
    this.paddleRight_down;
}

Lines 45-48 will be used to add both the left and right paddles controls with its own up and down input controls. We will add keyboard input to these next by creating a new function called initKeyboard after the initPhysics function.

JavaScript
90
91
92
93
94
95
96
97
98
99
100
101
102
    initPhysics: function () {
        game.physics.startSystem(Phaser.Physics.ARCADE);
        game.physics.enable(this.ballSprite, Phaser.Physics.ARCADE);
        
        this.ballSprite.checkWorldBounds = true;
        this.ballSprite.body.collideWorldBounds = true;
        this.ballSprite.body.immovable = true;
        this.ballSprite.body.bounce.set(1);
    },
    
    initKeyboard: function () {
 
    },

Add the following code in the initKeyboard function:

JavaScript
100
101
102
103
104
105
106
    initKeyboard: function () {
        this.paddleLeft_up = game.input.keyboard.addKey(Phaser.Keyboard.A);
        this.paddleLeft_down = game.input.keyboard.addKey(Phaser.Keyboard.Z);
        
        this.paddleRight_up = game.input.keyboard.addKey(Phaser.Keyboard.UP);
        this.paddleRight_down = game.input.keyboard.addKey(Phaser.Keyboard.DOWN);
    },

For the left paddle, we will assign the A and Z keys to control the up and down movement. For the right paddle, the UP and DOWN arrow keys will be used instead.

The game.input.keyboard.addKey function creates a new Key object with a specific keyCode argument to keep track of when that key is pressed. You can find the complete list of keyCodes in the Phaser.Keyboard class file from lines 579 to 678.

To enable the keyboard when the game starts and disable it during demo mode, add the following code to the enablePaddles function:

JavaScript
137
138
139
140
141
142
143
144
145
    enablePaddles: function (enabled) {
        this.paddleLeftSprite.visible = enabled;
        this.paddleRightSprite.visible = enabled;
        
        this.paddleLeft_up.enabled = enabled;
        this.paddleLeft_down.enabled = enabled;
        this.paddleRight_up.enabled = enabled;
        this.paddleRight_down.enabled = enabled;
    },

Next, we need to call the initKeyboard function from the create function to intialise the keyboard controls:

JavaScript
61
62
63
64
65
66
    create: function () {
        this.initGraphics();
        this.initPhysics();
        this.initKeyboard();
        this.startDemo();
    },

Adding the arcade physics body

Going back to the gameProperties object, we’ll set the constant movement speed for both paddles. Add the following highlighted code:

JavaScript
7
8
9
    paddleLeft_x: 50,
    paddleRight_x: 590,
    paddleVelocity: 600,

Since both paddles function exactly the same, we’ll create a grouped object to manage both paddles simultaneously. Let’s call it paddleGroup and add it to the mainState function:

JavaScript
39
40
41
42
43
44
45
46
47
48
49
50
var mainState = function(game) {
    this.backgroundGraphics;
    this.ballSprite;
    this.paddleLeftSprite;
    this.paddleRightSprite;
    this.paddleGroup;
    
    this.paddleLeft_up;
    this.paddleLeft_down;
    this.paddleRight_up;
    this.paddleRight_down;
}

Now we’ll add the arcade physics to the paddles. Go to the initPhysics function and add the following code:

JavaScript
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
    initPhysics: function () {
        game.physics.startSystem(Phaser.Physics.ARCADE);
        game.physics.enable(this.ballSprite, Phaser.Physics.ARCADE);
        
        this.ballSprite.checkWorldBounds = true;
        this.ballSprite.body.collideWorldBounds = true;
        this.ballSprite.body.immovable = true;
        this.ballSprite.body.bounce.set(1);
        
        this.paddleGroup = game.add.group();
        this.paddleGroup.enableBody = true;
        this.paddleGroup.physicsBodyType = Phaser.Physics.ARCADE;
        
        this.paddleGroup.add(this.paddleLeftSprite);
        this.paddleGroup.add(this.paddleRightSprite);
 
        this.paddleGroup.setAll('checkWorldBounds', true);
        this.paddleGroup.setAll('body.collideWorldBounds', true);
        this.paddleGroup.setAll('body.immovable', true);
    },

Line 102 creates a new group object. Next, we add some default properties to the group so that any new object added to the group will inherit these properties.

By default, we will want all newly added objects to have its physics body enabled and use the arcade physics system. We do that in lines 103-104.

At lines 106-107, we add our left and right paddle sprites to the group.

Next, we’ll set the same property for all objects in the group by calling the setAll function:

  • Line 109: the checkWorldBounds property is set to true. This will enable collision checking between the paddles and the game world boundaries.
  • Line 110: to prevent both paddles from going outside the world boundaries, we set the physics body property body.collideWorldBounds to true.
  • Line 111: to prevent the paddles from being pushed away when the ball collides with either paddle, we set the physics body property body.immovable property to true.

Looking again at the enablePaddles function, let’s make a minor change to our paddle visibility code and add a new line of code:

JavaScript
154
155
156
157
158
159
160
161
162
    enablePaddles: function (enabled) {
        this.paddleGroup.setAll('visible', enabled);
        this.paddleGroup.setAll('body.enable', enabled);
        
        this.paddleLeft_up.enabled = enabled;
        this.paddleLeft_down.enabled = enabled;
        this.paddleRight_up.enabled = enabled;
        this.paddleRight_down.enabled = enabled;
    },

By using the setAll function, we can easily set the visible and body.enable properties of both paddles to use the  enabled parameter.

Moving the paddles

Next, add the following code to the moveLeftPaddle function:

JavaScript
162
163
164
165
166
167
168
169
170
171
172
173
    moveLeftPaddle: function () {
        if (this.paddleLeft_up.isDown)
        {
            this.paddleLeftSprite.body.velocity.y = -gameProperties.paddleVelocity;
        }
        else if (this.paddleLeft_down.isDown)
        {
            this.paddleLeftSprite.body.velocity.y = gameProperties.paddleVelocity;
        } else {
            this.paddleLeftSprite.body.velocity.y = 0;
        }
    },

Add the following code to the moveRightPaddle function:

JavaScript
175
176
177
178
179
180
181
182
183
184
185
186
    moveRightPaddle: function () {
        if (this.paddleRight_up.isDown)
        {
            this.paddleRightSprite.body.velocity.y = -gameProperties.paddleVelocity;
        }
        else if (this.paddleRight_down.isDown)
        {
            this.paddleRightSprite.body.velocity.y = gameProperties.paddleVelocity;
        } else {
            this.paddleRightSprite.body.velocity.y = 0;
        }
    }

Notice how both the moveLeftPaddle and moveRightPaddle functions are almost identical. Here how it works:

  • First, we check if the up key isDown. If the up key is currently being pressed, we set the y velocity of the paddle physics body to a negative value. A negative y velocity value moves the sprite upwards.
  • If the up key is not being pressed, we then check if the down key is being pressed instead. If the down key is being pressed, we set the y velocity to a positive value which moves the sprite downwards.
  • If neither up nor down keys are being pressed, the last else condition code block will run. This will sets our paddle y velocity to 0 and causes it stop completely.

To get this working, we need to call both the moveLeftPaddle and moveRightPaddle functions within the update function:

JavaScript
69
70
71
72
    update: function () {
        this.moveLeftPaddle();
        this.moveRightPaddle();
    },

Click here to download the source codes up to this point. Here is our current work in progress:

Hitting the ball

In part 1 of this series, I mentioned that the original Pong paddles were divided into 8 segments that determine the return angle of the ball when it collided with the paddles.

Let’s add the following code to the gameProperties object:

JavaScript
7
8
9
10
11
12
    paddleLeft_x: 50,
    paddleRight_x: 590,
    paddleVelocity: 600,
    paddleSegmentsMax: 4,
    paddleSegmentHeight: 4,
    paddleSegmentAngle: 15,

Here, at line 10, we set the maximum segments to a value of 4. This will be used to divide the top half into 4 segments and bottom half into another 4 segments.

Our current paddle graphic height is 32 pixels. To ensure that each segment is the same height, I have divided the 32 pixels into 8 segments with each segment being 4 pixels.

Here is the illustration used from part 1 to shows each segment and its return angle.

pong-deflection-angle

For each segment, there will be an increment of 15 degrees as the ball collides towards to outer edges of the paddles. The centre 2 segments will return the ball at a perfect 0 degree angle while the outer edge will return the ball at a 45 degree angle.

We need another function to perform collision detection between the paddles and balls. Add a new function called collideWithPaddle after the moveRightPaddle function:

JavaScript
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
    moveRightPaddle: function () {
        if (this.paddleRight_up.isDown)
        {
            this.paddleRightSprite.body.velocity.y = -gameProperties.paddleVelocity;
        }
        else if (this.paddleRight_down.isDown)
        {
            this.paddleRightSprite.body.velocity.y = gameProperties.paddleVelocity;
        } else {
            this.paddleRightSprite.body.velocity.y = 0;
        }
    },
    
    collideWithPaddle: function (ball, paddle) {
 
    },

The collideWithPaddle requires 2 parameters to work:

  • The ball parameter which is a reference to the ball sprite.
  • The paddle parameter which is a reference to the paddle sprite.

Now to add in the code to make the collideWithPaddle function work:

JavaScript
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
    collideWithPaddle: function (ball, paddle) {
        var returnAngle;
        var segmentHit = Math.floor((ball.y - paddle.y)/gameProperties.paddleSegmentHeight);
        
        if (segmentHit >= gameProperties.paddleSegmentsMax) {
            segmentHit = gameProperties.paddleSegmentsMax - 1;
        } else if (segmentHit <= -gameProperties.paddleSegmentsMax) {
            segmentHit = -(gameProperties.paddleSegmentsMax - 1);
        }
        
        if (paddle.x < gameProperties.screenWidth * 0.5) {
            returnAngle = segmentHit * gameProperties.paddleSegmentAngle;
            game.physics.arcade.velocityFromAngle(returnAngle, gameProperties.ballVelocity, this.ballSprite.body.velocity);
        } else {
            returnAngle = 180 - (segmentHit * gameProperties.paddleSegmentAngle);
            if (returnAngle > 180) {
                returnAngle -= 360;
            }
            
            game.physics.arcade.velocityFromAngle(returnAngle, gameProperties.ballVelocity, this.ballSprite.body.velocity);
        }
    },

Two variables are declared:

  • The returnAngle variable will be used to calculate the angle the ball will be returned at.
  • The segmentHit variable will be used to determine which segment on the paddle is hit.

The centre two segments will have a segmentHit value of 0 while the outer most segments will have a value of 3.

As it’s possible to exceed the paddleSegmentsMax value of 4, lines 194-198 will check when that happens.

The -1 is added at the end of the calculation at lines 195 and 197 to limit the values of segmentHit to a range of -3 to 3. This final value is used to calculate the returnAngle of the ball by multiplying it by the the paddleSegmentAngle value of 15.

The next set of conditions from lines 200-210 checks whether the ball has hit the left or right paddle. The first if condition checks if the paddle is on the left side of the game world while the second condition checks if the paddle is on the right.

If the right paddle is hit, we need to offset the angle by 180 degrees to give us the correct angle in returning the ball sprite.

Here is an illustration of how to return angle is calculated:

pong-05b

After calculating the return angle of the ball, we need to reset the velocity and angle for the ball to bounce off the paddles. In lines 202 and 209, we use the arcade physics velocityFromAngle function to calculate the new velocity of the ball. Here, we use 3 arguments:

  • The angle: value is in degrees.
  • The speed: the velocity to move at.
  • The point: the Point object to apply the x and y properties to. In this case we apply it to the ball’s arcade physics body.

One final line of code to complete this step. In the update function, add this line of code:

JavaScript
72
73
74
75
76
    update: function () {
        this.moveLeftPaddle();
        this.moveRightPaddle();
        game.physics.arcade.overlap(this.ballSprite, this.paddleGroup, this.collideWithPaddle, null, this);
    },

At line 75, we call the arcade physics overlap function to check if the ball sprite overlaps the paddles in the paddle group. Here, we use 5 arguments:

  • The object1: The first object or array of objects to check. We use our ball sprite for the first object.
  • The object2: The second object or array of objects to check. Here we use our paddle group that contains both paddles.
  • The overlapCallback: An optional callback function that is called if the objects overlap. The two objects from the first two arguments will be passed to the collideWithPaddle function in the same order.
  • The processCallback: A callback function that lets you perform additional checks against the two objects if they overlap. We only use this if we need to perform any additional verification before the overlapCallback function is called. As there is no need for this, we set it to null.
  • The callbackContext: The context in which to run the callbacks. This will be needed later on to apply any further modifications or calculations in the collideWithPaddle function.

Here’s our current work in progress:

Click here to download the source codes.

So far so good. In the next step, we’ll look at adding scoring and resetting the game when a player has won.

Filed Under: Tutorials Tagged With: beginner, html5, javascript, phaser, pong, retro

Comments

  1. nd says

    September 9, 2015 at 12:14 pm

    Hey

    Thanks for the Tuts 🙂 very helpful.

    Please update the “collideWithPaddle” from:

    game.physics.arcade.velocityFromAngle(returnAngle, this.ballVelocity, this.ballSprite.body.velocity);

    to:

    game.physics.arcade.velocityFromAngle(returnAngle, gameSettings.ballVelocity, this.ballSprite.body.velocity);

    As in your code.

    Cheers

    Reply
    • nd says

      September 9, 2015 at 12:17 pm

      also for the left paddle 🙂

      Reply
      • Zeke says

        September 22, 2015 at 4:06 am

        Hey nd, thanks for noticing the mistake. I’ve updated my article accordingly =)

        Reply
        • Scott says

          December 15, 2015 at 6:33 am

          Should be gameProperties, not gameSettings

          Reply
          • Zeke says

            December 15, 2015 at 10:06 am

            Hey Scott, thanks for noticing that error. Correction made =)

Leave a Reply Cancel reply

Your email address will not be published.

Top 5 Posts

  • Asteroids HTML5 Game Tutorial Part 1: Project Setup & Moving The Player Ship
  • Asteroids HTML5 Game Tutorial Part 5: Scoring, levels, sounds & invulnerability
  • Phaser HTML5 Game Tutorial: Build A Pong Game 1: Project Setup
  • Asteroids HTML5 Game Tutorial Part 4: The player ship splits asteroids
  • Asteroids HTML5 Game Tutorial Part 2: Single Screen Game World & Shooting

Copyright © 2018 · ZekeChan.net