I Wrote a Pong Game with My Son using Cocos Creator

My son (Toke, 7 years old) and I have long talked about making our own computer game.

I have four reasons for doing so.

  • I would like him to know a bit about what I do all day
  • I would like him to be interested in games from a makers standpoint (learning instead of just consuming)
  • To spend quality time with my favourite son (yeah, he is the only son… but still)
  • I have always wanted to make a game

Inital work

I started looking at a few different ways we could make the game. If I had done it myself I would probably just have programmed it from scratch. But when having a 7-year-old on the project as well, I needed something that was a bit more visual.

I looked at different game engines and decided on the rather new Cocos Creator. (Mostly because I like the cocos2d-x library). I looked at a lot of different engines. But on a features/stability matrix only Cocos Creator seemed good enough (except maybe Unity and Unreal Engine - however they seemed like overkill)

First import the graphics

I cheated a bit and googled for paddles, balls and backgrounds. (And must admit we have not checked the copyright of the images we “stole” - but since this is never published anywhere I think we are safe)

The first thing we did together was import the graphics into the engines editor, and began placing and naming the parts.

Make something move

Then we decided to make the ball move.

I introduced my son to code writing - and created a script, and attached it to the ball as a component.

cc.Class({
    extends: cc.Component,

    properties: {
       speed: 200,
       x_direction: 1,
       y_direction: 1
    },

    onLoad: function () {
    },

    update: function (dt) {
        this.node.x += dt * this.speed * this.x_direction;
        this.node.y += dt * this.speed * this.y_direction;
    },
});

Bounce the ball of the walls

Next up we introduced collisions. Otherwise the ball would just go off the screen and go the same way forever.

We created a 1px box collider on all walls, and a circle collider on the ball. We tagged the top and bottom wall with one tag, the left wall with another and the right wall with yet another tag.

Then we changed the script for the ball.

cc.Class({
    extends: cc.Component,

    properties: {
       speed: 200,
       x_direction: 1,
       y_direction: 1
    },

    onLoad: function () {
        cc.director.getCollisionManager().enabled = true;
    },

    onCollisionEnter: function(other) {
        this.speed += 20;
        // Top or bottom wall
        if(other.tag == 1) {
            this.y_direction *= -1;
        }
        // Left or right wall
        if(other.tag == 2 || other.tag == 3) {
            this.x_direction *= -1;
        }
        // Anything else (in this case, just the paddles)
        if(other.tag == 0) {
            this.x_direction *= -1;
        }
    },

    // called every frame, uncomment this function to activate update callback
    update: function (dt) {
        this.node.x += dt * this.speed * this.x_direction;
        this.node.y += dt * this.speed * this.y_direction;
    },
});

Now we have ball bouncing around, and increasing it’s speed each time it hits something

Next up: Make it a game

Next up we added scores for each player - they are just labels initialized to zero. We created a new script and attached it to the root node.

This script has links to almost everything else in the game, and is now also responsible for instancing a new ball.

Instancing the ball

We changed the ball, with it’s attached script to a prefab, which is instanced each time the game starts, and each time a wall is hit.

cc.Class({
    extends: cc.Component,

    properties: {
        ball: {
            default: null,
            type: cc.Prefab
        },
        root: {
            default: null,
            type: cc.Node
        },
        player1Score: {
            default: null,
            type: cc.Label
        },
        player2Score: {
            default: null,
            type: cc.Label
        },
        player1ScoreValue: 0,
        player2ScoreValue: 0
    },

    getRandomPosition: function() {
        return cc.p(cc.randomMinus1To1() * this.randomRange.x, cc.randomMinus1To1() * this.randomRange.y);
    },

    createBall: function () {
        this.current_ball = cc.instantiate(this.ball);
        this.current_ball.parent = this.root;
        var movementComponent = this.current_ball.getComponent('Ball');
        movementComponent.game = this;
        movementComponent.speed = cc.p(250, 500).x;
        movementComponent.x_direction = cc.randomMinus1To1();
        movementComponent.y_direction = cc.randomMinus1To1();
        this.current_ball.position = this.getRandomPosition();
    },

    destroyBall: function() {
      this.current_ball.destroy();
    },

    updateScore: function() {
        this.player1Score.string = this.player1ScoreValue;
        this.player2Score.string = this.player2ScoreValue;
    },

    hitLeftWall: function() {
        this.player2PointValue++;
        this.destroyBall();
        this.createBall();
        this.updateScore();
    },
    hitRightWall: function() {
        this.player1PointValue++;
        this.destroyBall();
        this.createBall();
        this.updateScore();
    },

    onLoad: function () {
        this.randomRange = cc.p(300, 200);
        this.createBall();
    },
});

and then we updated the balls script to call the game script whenever we hit a wall.

    onCollisionEnter: function(other) {
        this.fart += 20;
        if(other.tag == 1) {
            this.y_retning *= -1;
        }
        if(other.tag == 2) {
            this.game.hitRightWall();
        }
        if(other.tag == 3) {
            this.game.hitLeftWall();
        }
        if(other.tag == 0) {
            this.x_retning *= -1;
        }
    },

and finally we added a script to each of the paddles to allow them to move.

Left paddle

cc.Class({
    extends: cc.Component,

    properties: {
        direction: 0,
        speed: 250
    },

    // use this for initialization
    onLoad: function () {
        cc.eventManager.addListener({
            event: cc.EventListener.KEYBOARD, 
            onKeyPressed: this.onKeyPressed.bind(this),
            onKeyReleased: this.onKeyReleased.bind(this),
        }, this.node);
        
    },
    
    onKeyPressed: function(keyCode, event) {
        switch(keyCode) {
          case cc.KEY.w:
            this.direction = 1;
            break;
          case cc.KEY.s:
            this.direction = -1;
            break;
        }
    },
    
    onKeyReleased: function(keyCode, event) {
         switch(keyCode) {
          case cc.KEY.w:
          case cc.KEY.s:
            this.direction = 0;
            break;
        }
    },

    update: function (dt) {
        this.node.y += this.direction * this.speed * dt;
    },
});

The right paddle is exactly the same - except that the keys used there is “up” and “down” instead of “w” and “s”.

Conclusion

We had fun. But maybe my son had most fun trying to beat me at pong when we actually playtested the game.

Finally: A screenshot.

Pong Screenshot