Showing posts with label fun. Show all posts
Showing posts with label fun. Show all posts

Tuesday, June 14, 2016

Introducing Battlescripts: Bots, Ships, Mayhem!

Programming doesn’t need to be all about building applications, meeting goals, and satisfying project specifications. It can also be about having fun, about enjoying the process of creating something. Many people do treat programming and development of this skill as a form of recreation. At Toptal, we wanted to try out something interesting within our community. We decided to build a bot-vs-bot game platform around Battleship, that is now open-source.
Introducing Battlescripts: Bots, Ships, Mayhem!
Since its initial launch internally, the platform has grabbed the attention of some amazing bot makers within our community. We were really impressed to see that one of the community members, Toptal engineer Quân Lê, even built a tool to debug Battlescripts Bots easily. The announcement also sparked an interest among a few to create their own bot-vs-bot engines, supporting different games types and different rules. Amazing ideas started to stream in from the moment Battlescripts was unveiled. Today, we are happy to make Battlescripts open-source. This gives our community and everyone else an opportunity to explore the code, make contributions, and/or fork it to make something else entirely of it.

Anatomy of Battlescripts

Battlescripts is built using some very simple components. It runs on Node.js and uses some of the most popular and well implemented packages, such as ExpressMongoose, etc. The back-end is in pure JavaScript, as well as the front-end scripts. The only two external dependencies of this application areMongoDB and Redis. User submitted code for bots are run using the “vm” module that comes with Node.js. In production, Docker is used for added safety, but is not a hard dependency of Battlescripts.
battlescripts
The code for Battlescripts is available on GitHub under the BSD 3-clause license. The included README.mdfile has detailed instructions on how to clone the repository and launch the application locally.

Web Server

You will notice that the application has a structure similar to that of simple Express.js web applications. The app.js file bootstraps the server by establishing a connection to the database, registering some common middlewares, and defining some social authentication strategies. Furthermore, all the models and routes are defined within the “lib/” directory. The entirely application requires only a few models: Battle, Bot, Challenge, Contest, Party, and User. Battles between bots are simulated outside of the web server nodes, and is done using Node.js package Kue. This enables us to isolate the engine from the rest of the web application, making it less likely for the battle simulation engine to interfere with the web servers, keeping the web application itself more responsive and stable.

Bots & Engine

Since the bots are expected to be implemented in JavaScript, and that is exactly what we have on our back-end with Node.js, it was easier to build the engine. When it comes to executing user submitted code, one of the biggest challenges is to make sure that the code doesn’t do something malicious on the server, or that any code that is buggy doesn’t interfere with the stability of the overall system. Node.js’s standard library comes with this amazing module that makes part of the this task a very easy. The “vm” module was introduced in order to make it easier for Node.js developers to run untrusted code in a separate context. Although according to the official documentation, it is important to run untrusted code in a separate process - but that is something we do on the production servers. During local development, the “vm” module and the features that it offers work alright.
Make bots fight each other, while it's still legal!

Executing JavaScript

If you want to run some arbitrary JavaScript code in Node.js under a separate context, you can use the “vm” module as follows:
var vm = require(‘vm’)

var ctxObj = {
    result: ‘’
}
vm.runInNewContext(‘ result = “0xBEEF” ’, ctxObj )
console.log(ctxObj); // { result: “0xBEEF” }
Within this “new context”, the code that you run do not even get access to “console.log”, because in that context such a function does not exist. You could, however, expose the “context.log” function of the original context into the new context by passing it as an attribute of “ctxObj”.
In Battlescripts, nodes that simulate battles run each bot under separate Node.js “vm” contexts. The engine takes on the responsibility of syncing the state of the contexts for both bots according to rules of the game.
Running JavaScript code in isolated context is not all that this module does. The “runInNewContext” function accepts an object as the third parameter which can control three additional aspects of this code execution:
  • Filename to be used in generated stack traces relevant to this execution.
  • Whether or not to print errors to stderr.
  • Number of milliseconds to allow the execution to continue before timing it out.
One of the pitfalls of this “vm” module is that it doesn’t provide any means of limiting memory usage. This, along with a few other limitations of the module is worked-around on the server through the use of Docker, and the way the engine nodes are run. The “vm” module, when used very frequently slowly starts to leak memory that is hard to track down and free. Even if the context objects are reused, the memory usage keeps growing. We solved this problem by following a simple strategy. Every time a battle is simulated in a worker node, the node exits. The supervisor program on the production server then restarts the worker node which then becomes ready to handle the next battle simulation in a fraction of a second.

Extensibility

Battlescripts was originally designed around the standard rules of Battleship. The engine within was not very extensible. However, after Battlescripts was launched, one of the most common requests was to introduce newer game types, as users of the application quickly realized that some games are easier to conquer with bots than others. For example, if you compare TicTacToe with Chess, the former has a much smaller state space, making it very easy for bots to come up with a solution that will either win or end a game in draw.
The Battlescripts engine has been recently modified a little to make it easier to introduce newer game types. This can be done by simply following a construct with a handful of hook-like functions. An additional game type, TicTacToe, was added to the codebase since it is easier to follow. Everything relevant to this game type can be found inside the “lib/games/tictactoe.js” file.
However, in this article, we will take a look at the implementation of Battleship game type. Exploration of TicTacToe game code can be left as an exercise for later.

Battleship

Before taking a look at how the game is implemented, let us take a peek at what a standard bot for Battlescript looks like:
function Bot() {}
Bot.prototype.play = function(turn) {
    // ...
}
That is pretty much it. Every bot is defined as a constructor function with one method “play”. The method is invoked for every turn with one argument. For any game, the argument is an object with one method that allows the bot to make its move for the turn, and can come with some additional attributes representing the game state.
As mentioned earlier, the engine has been modified a little recently. All Battleship specific logic has been pulled out of the actual engine code. As the engine still does the heavy lifting, the code that defines the Battleship game is very simple, and lightweight.
function Battleships(bot1, bot2) {
 return new Engine(bot1, bot2, {
  hooks: {
   init: function() {
    // ...
   },
   play: function() {
    // ...
   },
   turn: function() {
    // ...
   }
  }
 })
}

module.exports = exports = Battleships
Notice how we are merely defining three hook-like functions here: init, play, and turn. Each function is invoked with the engine as its context. The “init” function right as the engine object is instantiated, from within the constructor function. Typically this is where you should prepare all the state attributes of the engine. One such attribute that must be prepared for every game is “grids” and (optionally) “pieces”. This should always be an array with two elements, one for each player, representing the state of the game board.
for(var i = 0; i < this.bots.length; ++i) {
 var grid = []
 for(var y = 0; y < consts.gridSize.height; ++y) {
  var row = []
  for(var x = 0; x < consts.gridSize.width; ++x) {
   row.push({
    attacked: false
   })
  }
  grid.push(row)
 }
 this.grids.push(grid)
 this.pieces.push([])
}
The second hook, “play”, is invoked right before the game begins. This is useful, as this gives us the opportunity to do things like place game pieces on the board on behalf of the bots.
for(var botNo = 0; botNo < this.bots.length; ++botNo) {
 for(var i = 0; i < consts.pieces.length; ++i) {
  var piece = consts.pieces[i]
  for(var j = 0; j < piece.many; ++j) {
   var pieceNo = this.pieces[botNo].length

   var squares = []
   for(var y = 0; y < consts.gridSize.height; ++y) {
    for(var x = 0; x < consts.gridSize.width; ++x) {
     squares.push({
      x: x,
      y: y,
      direction: 'h'
     })
     squares.push({
      x: x,
      y: y,
      direction: 'v'
     })
    }
   }
   var square = _.sample(squares.filter(function(square) {
    var f = {
     'h': [1, 0],
     'v': [0, 1]
    }
    for(var xn = square.x, yn = square.y, i = 0; i < piece.size; xn += f[square.direction][0], yn += f[square.direction][1], ++i) {
     var d = [[0, -1], [0, 1], [-1, 0], [1, 0], [-1, -1], [-1, 1], [1, -1], [1, 1]]
     for(var j = 0; j < d.length; ++j) {
      var xp = xn+d[j][0]
      var yp = yn+d[j][1]
      if(xp >= 0 && xp < 10 && yp >= 0 && yp < 10 && this.grids[botNo][yp][xp].pieceNo >= 0) {
       return false
      }
     }
     if(xn >= consts.gridSize.width || yn >= consts.gridSize.height || this.grids[botNo][yn][xn].pieceNo >= 0) {
      return false
     }
    }
    return true;
   }.bind(this)))

   switch(true) {
    case square.direction === 'h':
     for(var k = square.x; k < square.x+piece.size; ++k) {
      this.grids[botNo][square.y][k].pieceNo = pieceNo
     }
     break

    case square.direction === 'v':
     for(var k = square.y; k < square.y+piece.size; ++k) {
      this.grids[botNo][k][square.x].pieceNo = pieceNo
     }
     break

   }

   this.pieces[botNo].push({
    kind: piece.kind,
    size: piece.size,

    x: square.x,
    y: square.y,
    direction: square.direction,

    hits: 0,
    dead: false
   })
  }
 }
}
This may look a bit overwhelming at first, but the goal that this piece of code achieves is simple. It generates arrays of pieces, one for each bot, and places them on the corresponding grids in a uniform fashion. For every piece, the grid is scanned and every valid position is stored in a temporary array. A valid position is where two pieces do not overlap or share adjacent cells.
Finally, the third and the last hook “turn”. Unlike the other two hooks, this one is a little different. The purpose of this hook is to return an object, which the engine uses as the first argument in invoking the bot’s play method.
return {
 attack: _.once(function(x, y) {
  this.turn.called = true

  var botNo = this.turn.botNo
  var otherNo = (botNo+1)%2

  var baam = false

  var square = this.grids[otherNo][y][x]
  square.attacked = true
  if(square.pieceNo >= 0) {
   baam = true
   this.turn.nextNo = botNo

   var pieceNo = square.pieceNo
   var pieces = this.pieces[otherNo]
   var piece = pieces[pieceNo]
   piece.hits += 1

   if(piece.hits === piece.size) {
    piece.dead = true
    baam = {
     no: pieceNo,

     kind: piece.kind,
     size: piece.size,

     x: piece.x,
     y: piece.y,
     direction: piece.direction
    }
   }

   var undead = false
   for(var i = 0; i < pieces.length; ++i) {
    if(!pieces[i].dead) {
     undead = true
    }
   }
   if(!undead) {
    this.end(botNo)
   }
  }

  this.track(botNo, true, {
   x: x,
   y: y,
   baam: !!baam
  })

  return baam
 }.bind(this))
}
Within this method, we begin by informing the engine that the bot has successfully made a move. A bot that fails to make an attacking move for any game in any turn automatically forfeits the game. Next, in case the move has successfully hit the ship, we determine if the ship has been destroyed completely. In case it was, we return details of the ship that was destroyed, otherwise we return “true” to indicate a successful hit without any additional information.
Throughout these codes, we have encountered some attributes and method names that are available at “this”. These are provided by the Engine object and each have some simple behavioral characteristics:
  • this.turn.called: This begins as false before every turn, and must be set to true to inform the engine that the bot has acted for the turn.
  • this.turn.botNo: This will either be 0 or 1, depending on which bot has played this turn.
  • this.end(botNo): Calling this with a bot number ends the game, and marks the bot as victorious. Calling it with -1 ends the game in a draw.
  • this.track(botNo, isOkay, data, failReason): This is a convenience method that lets you record the move details for the bot, or reason for a failed move. Eventually, these recorded data are used to visualize the simulation on the front-end.
Essentially, this is all that needs to be done on the back-end to implement a game on this platform.

Replaying Games

As soon as a battle simulation ends, the front-end redirects itself to the game replay page. This is where the simulation and results are visualized, and other game related data is displayed.
This view is rendered by the back-end using the “battle-view-battleships.jade” in “views/” with all battle details in context. Re-play animation of the game is done through front-end JavaScript. All the data recorded through the “trace()” method of the engine is available in the context of this template.
function play() {
 $('.btn-play').hide()
 $('.btn-stop').show()

 if(i === moves.length) {
  i = 0
  stop()
  $('.ul-moves h4').fadeIn()
  return
 }
 if(i === 0) {
  $('.ul-moves h4').hide()
  $('table td').removeClass('warning danger')
  $('.count span').text(0)
 }

 $('.ul-moves li').slice(0, $('.ul-moves li').length-i).hide()
 $('.ul-moves li').slice($('.ul-moves li').length-i-1).show()

 var move = moves[i]
 var $td = $('table').eq((move.botNo+1)%2).find('tr').eq(move.data.y+1).find('td').eq(move.data.x+1)
 if(parseInt($td.text()) >= 0) {
  $td.addClass('danger')
 } else {
  $td.addClass('warning')
 }
 ++i

 $('.count span').eq(move.botNo).text(parseInt($('.count span').eq(move.botNo).text())+1)

 var delay = 0
 switch(true) {
  case $('.btn-fast').hasClass('active'):
   delay = 10
   break

  case $('.btn-slow').hasClass('active'):
   delay = 100
   break

  case $('.btn-slower').hasClass('active'):
   delay = 500
   break

  case $('.btn-step').hasClass('active'):
   stop()
   return
 }
 playTimer = setTimeout(function() {
  play()
 }, delay)
}

function stop() {
 $('.btn-stop').hide()
 $('.btn-play').text(i === 0 ? 'Re-play' : ($('.btn-step').hasClass('active') ? 'Next' : 'Resume')).show()

 clearTimeout(playTimer)
}

$('.btn-play').click(function() {
 play()
})
$('.btn-stop').click(function() {
 stop()
})

What Next?

Now that Battlescripts is open source, contributions are welcome. The platform at its current stage is mature, but has a lot of room for improvements. Be it a new feature, a security patch, or even bug fixes, feel free create an issue in the repository requesting it to be addressed, or fork the repository and submit a pull request. And if this inspires you to build something entirely new, be sure to let us know and leave a link to it in the comments section below!
This article was written by Mahmud Ridwan, a Toptal Python developer.

Thursday, May 26, 2016

Ultimate Guide to Processing: Building a Simple Game

In this article, we will implement our own game step-by-step. Each step will be explained in detail. Then, we will port the game to the web.
Building a Simple Game with Processing
Before we begin, here is the code of the DVD logo exercise from the previous part. If you have any questions, be sure to leave a comment.

A Simple Game

The game we will build in this article is sort of a combination of Flappy Bird, Pong and Brick Breaker. The reason I picked a game like this is that it has most of the concepts that beginners struggle with when learning game development. This is based on my experience from when I was a teaching assistant, helping new learners with Processing. These concepts include gravity, collisions, keeping scores, handling different screens and keyboard/mouse interactions. Flappy Pong has all of them in it.
Without using object-oriented programming (OOP) concepts, it is not easy to build complex games, such as platform games with multiple levels, players, entities etc. As we move along, you’ll see how the code gets complicated really fast. I did my best to keep it organised and simple.
I advise you to follow the article, grab the full code, play with them on your own, start thinking about your own game as quickly as possible, and start implementing it.
So let’s begin.

Building Flappy Pong

Step #1: Initialize & Handle Different Screens

The first step is to initialize our project. For starters, we will write our setup and draw blocks as usual, nothing fancy or new. Then, we will handle different screens (initial screen, game screen, game over screen etc.). So the question arises, how do we make Processing show the correct page at the correct time?
Accomplishing this task is fairly simple. We will have a global variable that stores the information of the currently active screen. We then draw the contents of the correct screen depending on the variable. In the draw block, we will have an if statement that checks the variable and displays the contents of the screen accordingly. Whenever we want to change the screen, we will change that variable to the identifier of screen we want it to display. With that said, here is what our skeleton code looks like:
/********* VARIABLES *********/

// We control which screen is active by settings / updating
// gameScreen variable. We display the correct screen according
// to the value of this variable.
//
// 0: Initial Screen
// 1: Game Screen
// 2: Game-over Screen

int gameScreen = 0;

/********* SETUP BLOCK *********/

void setup() {
  size(500, 500);
}


/********* DRAW BLOCK *********/

void draw() {
  // Display the contents of the current screen
  if (gameScreen == 0) {
    initScreen();
  } else if (gameScreen == 1) {
    gameScreen();
  } else if (gameScreen == 2) {
    gameOverScreen();
  }
}


/********* SCREEN CONTENTS *********/

void initScreen() {
  // codes of initial screen
}
void gameScreen() {
  // codes of game screen
}
void gameOverScreen() {
  // codes for game over screen
}


/********* INPUTS *********/

public void mousePressed() {
  // if we are on the initial screen when clicked, start the game
  if (gameScreen==0) {
    startGame();
  }
}


/********* OTHER FUNCTIONS *********/

// This method sets the necessery variables to start the game  
void startGame() {
  gameScreen=1;
}
This may look scary at first, but all we did is build the basic structure and separate different parts with comment blocks.
As you can see, we define a different method for each screen to display. In our draw block, we simply check the value of our gameScreen variable, and call the corresponding method.
In the void mousePressed(){...} part, we are listening to mouse clicks and if the active screen is 0, the initial screen, we call the startGame() method which starts the game as you’d expect. The first line of this method changes gameScreen variable to 1, the game screen.
If this is understood, the next step is to implement our initial screen. To do that, we will be editing the initScreen() method. Here it goes:
void initScreen() {
  background(0);
  textAlign(CENTER);
  text("Click to start", height/2, width/2);
}
Now our initial screen has a black background and a simple text, “Click to start”, located in the middle and aligned to the center. But when we click, nothing happens. We haven’t yet specified any content for our game screen. The method gameScreen() doesn’t have anything in it, so we aren’t covering the previous contents drawn from the last screen (the text) by having background() as the first line of draw. That’s why the text is still there, even though the text() line is not being called anymore (just like the moving ball example from the last part which was leaving a trace behind). The background is still black for the same reason. So let’s go ahead and begin implementing the game screen.
void gameScreen() {
  background(255);
}
After this change, you will notice that the background turns white & the text disappears.

Step #2: Creating the Ball & Implementing Gravity

Now, we will start working on the game screen. We will first create our ball. We should define variables for its coordinates, color and size because we might want to change those values later on. For instance, if we want to increase the size of the ball as the player scores higher so that the game will be harder. We will need to change its size, so it should be a variable. We will define speed of the ball as well, after we implement gravity.
First, let’s add the following:
...
int ballX, ballY;
int ballSize = 20;
int ballColor = color(0);
...
void setup() {
  ...
  ballX=width/4;
  ballY=height/5;
}
...
void gameScreen() {
  ...
  drawBall();
}
...
void drawBall() {
  fill(ballColor);
  ellipse(ballX, ballY, ballSize, ballSize);
}
We defined the coordinates as global variables, created a method that draw the ball, called fromgameScreen method. Only thing to pay attention to here is that we initialized coordinates, but we defined them in setup(). The reason we did that is we wanted the ball to start at one fourth from left and one fifth from top. There is no particular reason we want that, but that is a good point for the ball to start. So we needed to get the width and height of the sketch dynamically. The sketch size is defined in setup(), after the first line. width and height are not set before setup() runs, that’s why we couldn’t achieve this if we defined the variables on top.

Gravity

Now implementing gravity is actually the easy part. There are only a few tricks. Here is the implementation first:
...
float gravity = 1;
float ballSpeedVert = 0;
...
void gameScreen() {
  ...
  applyGravity();
  keepInScreen();
}
...
void applyGravity() {
  ballSpeedVert += gravity;
  ballY += ballSpeedVert;
}
void makeBounceBottom(float surface) {
  ballY = surface-(ballSize/2);
  ballSpeedVert*=-1;
}
void makeBounceTop(float surface) {
  ballY = surface+(ballSize/2);
  ballSpeedVert*=-1;
}
// keep ball in the screen
void keepInScreen() {
  // ball hits floor
  if (ballY+(ballSize/2) > height) { 
    makeBounceBottom(height);
  }
  // ball hits ceiling
  if (ballY-(ballSize/2) < 0) {
    makeBounceTop(0);
  }
}
And the result is:
Hold your horses, physicist. I know that’s not how gravity works in real life. The variable we defined as gravity is just a numeric value that we add to ballSpeedVert on every loop. And ballSpeedVert is the vertical speed of the ball, which is added to the Y coordinate of the ball (ballY) on each loop. We watch the coordinates of the ball and make sure it stays in the screen. If we didn’t, the ball would fall to infinity. For now, our ball only moves vertically. So we watch the floor and ceiling boundries of the screen. With keepInScreen() method, we check if ballY (+ the radius) is less than height, and similarly ballY (- the radius) is more than 0. If the conditions don’t meet, we make the ball bounce (from the bottom or the top) with makeBounceBottom() and makeBounceTop() methods. To make the ball bounce, we simply move the ball to the exact location where it had to bounce and multiply the vertical speed (ballSpeedVert) with -1(multiplying with -1 changes the sign). When the speed value has a minus sign, adding Y coordinate the speed becomes ballY + (-ballSpeedVert), which is ballY - ballSpeedVert. So the ball immediately changes its direction with the same speed. Then, as we add gravity to ballSpeedVert and ballSpeedVert has a negative value, it starts to get close to 0, eventually becomes 0, and starts increasing again. That makes the ball rise, rise slower, stop and start falling.
There is a problem though - the ball keeps bouncing. If this was a real world scenario, the ball would have faced air resistance and friction every time it touches a surface. That’s the behaviour we want for our game, so implementing this is easy. We add the following:
...
float airfriction = 0.0001;
float friction = 0.1;
...
void applyGravity() {
  ...
  ballSpeedVert -= (ballSpeedVert * airfriction);
}
void makeBounceBottom(int surface) {
  ...
  ballSpeedVert -= (ballSpeedVert * friction);
}
void makeBounceTop(int surface) {
  ...
  ballSpeedVert -= (ballSpeedVert * friction);
}
And what we get is:
As the name suggests, friction is the surface friction and airfriction is the friction of air. So obviously, friction has to apply each time the ball touches any surface. airfriction however has to apply constantly. So that’s what we did. applyGravity() method runs on each loop, so we take away 0.0001percent of its current value from ballSpeedVert on every loop. makeBounceBottom() and makeBounceTop()methods run when the ball touches any surface. So in those methods, we did the same thing, only this time with friction.

Step #3: Creating the Racket

Now we need a racket for the ball to bounce on. We should be controlling the racket. Let’s make it controllable with the mouse. Here is the code:
...
color racketColor = color(0);
float racketWidth = 100;
float racketHeight = 10;
...
void gameScreen() {
  ...
  drawRacket();
  ...
}
...
void drawRacket(){
  fill(racketColor);
  rectMode(CENTER);
  rect(mouseX, mouseY, racketWidth, racketHeight);
}
We defined the color, width and height of the racket as a global variable, we might want them to change during gameplay. We implemented a method drawRacket() which does what its name suggests. We set the rectMode to center, so our racket is aligned to the center of our cursor.
Now that we created the racket, we have to make the ball bounce on it.
...
int racketBounceRate = 20;
...
void gameScreen() {
  ...
  watchRacketBounce();
  ...
}
...
void watchRacketBounce() {
  float overhead = mouseY - pmouseY;
  if ((ballX+(ballSize/2) > mouseX-(racketWidth/2)) && (ballX-(ballSize/2) < mouseX+(racketWidth/2))) {
    if (dist(ballX, ballY, ballX, mouseY)<=(ballSize/2)+abs(overhead)) {
      makeBounceBottom(mouseY);
      // racket moving up
      if (overhead<0) {
        ballY+=overhead;
        ballSpeedVert+=overhead;
      }
    }
  }
}
And here is the result:
So what watchRacketBounce() does is it makes sure that the racket and the ball collides. There are two things to check here, which is if the ball and racket lined up both vertically and horizontally. The first if statement checks if the X coordinate of the right side of the ball is greater than the X coordinate of the left side of the racket (and the other way around). If it is, second statement checks if the distance between the ball and the racket is smaller than or equal to the radius of the ball (which means they are colliding). So if these conditions meet, makeBounceBottom() method gets called and the ball bounces on our racket (at mouseY, where the racket is).
Have you noticed the variable overhead which is calculated by mouseY - pmouseYpmouseX and pmouseYvariables store the coordinates of the mouse at the previous frame. As the mouse can move very fast, there is a good chance we might not detect the distance between the ball and the racket correctly in between frames if the mouse is moving towards the ball fast enough. So, we take the difference of the mouse coordinates in between frames and take that into account while detecting distance. The the faster mouse is moving, the greater distance is acceptable.
We also use overhead for another reason. We detect which way the mouse is moving by checking the sign of overhead. If overhead is negative, the mouse was somewhere below in the previous frame so our mouse (racket) is moving up. In that case, we want to add an extra speed to the ball and move it a little further than regular bounce to simulate the effect of hitting the ball with the racket. If overhead is less than0, we add it to ballY and ballSpeedVert to make the ball go higher and faster. So the faster the racket hits the ball, the higher and faster it will move up.

Step #4: Horizontal Movement & Controlling the Ball

In this section, we will add horizontal movement to the ball. Then, we will make it possible to control the ball horizontally with our racket. Here we go:
...
// we will start with 0, but for we give 10 just for testing
float ballSpeedHorizon = 10;
...
void gameScreen() {
  ...
  applyHorizontalSpeed();
  ...
}
...
void applyHorizontalSpeed(){
  ballX += ballSpeedHorizon;
  ballSpeedHorizon -= (ballSpeedHorizon * airfriction);
}
void makeBounceLeft(float surface){
  ballX = surface+(ballSize/2);
  ballSpeedHorizon*=-1;
  ballSpeedHorizon -= (ballSpeedHorizon * friction);
}
void makeBounceRight(float surface){
  ballX = surface-(ballSize/2);
  ballSpeedHorizon*=-1;
  ballSpeedHorizon -= (ballSpeedHorizon * friction);
}
...
void keepInScreen() {
  ...
  if (ballX-(ballSize/2) < 0){
    makeBounceLeft(0);
  }
  if (ballX+(ballSize/2) > width){
    makeBounceRight(width);
  }
}
And the result is:
The idea here is the same as what we did for vertical movement. We created a horizontal speed variable, ballSpeedHorizon. We created a method to apply horizontal speed to ballX and take away the air friction. We added two more if statements to the keepInScreen() method which will watch the ball for hitting the left and right edges of the screen. Finally we created makeBounceLeft() and makeBounceRight() methods to handle the bounces from left and right.
Now that we added horizontal speed to the game, we want to control the ball with the racket. As in the famous Atari game Breakout and in all other brick breaking games, the ball should go left or right according to the point on the racket it hits. The edges of the racket should give the ball more horizontal speed whereas the middle shouldn’t have any effect. Code first:
void watchRacketBounce() {
  ...
  if ((ballX+(ballSize/2) > mouseX-(racketWidth/2)) && (ballX-(ballSize/2) < mouseX+(racketWidth/2))) {
    if (dist(ballX, ballY, ballX, mouseY)<=(ballSize/2)+abs(overhead)) {
      ...
      ballSpeedHorizon = (ballX - mouseX)/5;
      ...
    }
  }
}
Result is:
Adding that simple line to the watchRacketBounce() did the job. What we did is we determined the distance of the point that the ball hits from the center of the racket with ballX - mouseX. Then, we make it the horizontal speed. The actual difference was too much, so I gave it a few tries and figured that one-tenth of the value feels the most natural.

Step #5: Creating the Walls

Our sketch is starting to look more like a game with each step. In this step, we will add walls moving towards the left, just like in Flappy Bird:
...
int wallSpeed = 5;
int wallInterval = 1000;
float lastAddTime = 0;
int minGapHeight = 200;
int maxGapHeight = 300;
int wallWidth = 80;
color wallColors = color(0);
// This arraylist stores data of the gaps between the walls. Actuals walls are drawn accordingly.
// [gapWallX, gapWallY, gapWallWidth, gapWallHeight]
ArrayList<int[]> walls = new ArrayList<int[]>();
...
void gameScreen() {
  ...
  wallAdder();
  wallHandler();
}
...
void wallAdder() {
  if (millis()-lastAddTime > wallInterval) {
    int randHeight = round(random(minGapHeight, maxGapHeight));
    int randY = round(random(0, height-randHeight));
    // {gapWallX, gapWallY, gapWallWidth, gapWallHeight}
    int[] randWall = {width, randY, wallWidth, randHeight}; 
    walls.add(randWall);
    lastAddTime = millis();
  }
}
void wallHandler() {
  for (int i = 0; i < walls.size(); i++) {
    wallRemover(i);
    wallMover(i);
    wallDrawer(i);
  }
}
void wallDrawer(int index) {
  int[] wall = walls.get(index);
  // get gap wall settings 
  int gapWallX = wall[0];
  int gapWallY = wall[1];
  int gapWallWidth = wall[2];
  int gapWallHeight = wall[3];
  // draw actual walls
  rectMode(CORNER);
  fill(wallColors);
  rect(gapWallX, 0, gapWallWidth, gapWallY);
  rect(gapWallX, gapWallY+gapWallHeight, gapWallWidth, height-(gapWallY+gapWallHeight));
}
void wallMover(int index) {
  int[] wall = walls.get(index);
  wall[0] -= wallSpeed;
}
void wallRemover(int index) {
  int[] wall = walls.get(index);
  if (wall[0]+wall[2] <= 0) {
    walls.remove(index);
  }
}
And this resulted in:
Even though the code looks long and intimidating, I promise there is nothing hard to understand. First thing to notice is ArrayList. For those of you who don’t know what an ArrayList is, it is just an implementation of list that acts like an Array, but it has some advantages over it. It is resizeable, it has useful methods like list.add(index)list.get(index) and list.remove(index). We keep the wall data as integer arrays within the arraylist. The data we keep in the arrays are for the gap between two walls. The arrays contain the following values:
[gap wall X, gap wall Y, gap wall width, gap wall height]
The actual walls are drawn based on the gap wall values. Note that all these could be handled better and cleaner using classes, but since the use of Object Oriented Programming (OOP) is not in the scope of this article, this is how we’ll handle it. We have two base methods to manage the walls, wallAdder() and wallHandler.
wallAdder() method simply adds new walls in every wallInterval millisecond to the arraylist. We have a global variable lastAddTime which stores the time when the last wall was added (in milliseconds). If the current millisecond millis() minus the last added millisecond lastAddTime is larger than our interval value wallInterval, it means it is now time to add a new wall. Random gap variables are then generated based on the global variables defined at the very top. Then a new wall (integer array that stores the gap wall data) is added into the arraylist and the lastAddTime is set to the current millisecond millis().
wallHandler() loops through the current walls that is in the arraylist. And for each item at each loop, it calls wallRemover(i)wallMover(i) and wallDrawer(i) by the index value of the arraylist. These methods do what their name suggests. wallDrawer() draws the actual walls based on the gap wall data. It grabs the wall data array from the arraylist, and calls rect() method to draw the walls to where they should actually be. wallMover() method grabs the element from the arraylist, changes its X location based on thewallSpeed global variable. Finally, wallRemover() removes the walls from the arraylist which are out of the screen. If we didn’t do that, Processing would have treated them as they are still in the screen. And that would have been a huge loss in performance. So when a wall is removed from the arraylist, it doesn’t get drawn on subsequent loops.
The final challenging thing left to do is to detect collisions between the ball and the walls.
void wallHandler() {
  for (int i = 0; i < walls.size(); i++) {
    ...
    watchWallCollision(i);
  }
}
...
void watchWallCollision(int index) {
  int[] wall = walls.get(index);
  // get gap wall settings 
  int gapWallX = wall[0];
  int gapWallY = wall[1];
  int gapWallWidth = wall[2];
  int gapWallHeight = wall[3];
  int wallTopX = gapWallX;
  int wallTopY = 0;
  int wallTopWidth = gapWallWidth;
  int wallTopHeight = gapWallY;
  int wallBottomX = gapWallX;
  int wallBottomY = gapWallY+gapWallHeight;
  int wallBottomWidth = gapWallWidth;
  int wallBottomHeight = height-(gapWallY+gapWallHeight);

  if (
    (ballX+(ballSize/2)>wallTopX) &&
    (ballX-(ballSize/2)<wallTopX+wallTopWidth) &&
    (ballY+(ballSize/2)>wallTopY) &&
    (ballY-(ballSize/2)<wallTopY+wallTopHeight)
    ) {
    // collides with upper wall
  }
  
  if (
    (ballX+(ballSize/2)>wallBottomX) &&
    (ballX-(ballSize/2)<wallBottomX+wallBottomWidth) &&
    (ballY+(ballSize/2)>wallBottomY) &&
    (ballY-(ballSize/2)<wallBottomY+wallBottomHeight)
    ) {
    // collides with lower wall
  }
}
watchWallCollision() method gets called for each wall on each loop. We grab the coordinates of the gap wall, calculate the coordinates of the actual walls (top and bottom) and we check if the coordinates of the ball collides with the walls.

Step #6: Health and Score

Now that we can detect the collisions of the ball and the walls, we can decide on the game mechanics. After some tuning to the game, I managed to make the game somewhat playable. But still, it was very hard. My first thought on the game was to make it like Flappy Bird, when the ball touches the walls, game ends. But then I realised it would be impossible to play. So here is what I thought:
There should be a health bar on top of the ball. The ball should lose health while it is touching the walls. With this logic, it doesn’t make sense to make the ball bounce back from the walls. So when the health is 0, the game should end and we should switch to the game over screen. So here we go:
int maxHealth = 100;
float health = 100;
float healthDecrease = 1;
int healthBarWidth = 60;
...
void gameScreen() {
  ...
  drawHealthBar();
  ...
}
...
void drawHealthBar() {
  noStroke();
  fill(236, 240, 241);
  rectMode(CORNER);
  rect(ballX-(healthBarWidth/2), ballY - 30, healthBarWidth, 5);
  if (health > 60) {
    fill(46, 204, 113);
  } else if (health > 30) {
    fill(230, 126, 34);
  } else {
    fill(231, 76, 60);
  }
  rectMode(CORNER);
  rect(ballX-(healthBarWidth/2), ballY - 30, healthBarWidth*(health/maxHealth), 5);
}
void decreaseHealth(){
  health -= healthDecrease;
  if (health <= 0){
    gameOver();
  }
}
And here is a simple run:
We created a global variable health to keep the health of the ball. And then created a method drawHealthBar() which draws two rectangles on top of the ball. First one is the base health bar, other is the active one that shows the current health. The width of the second one is dynamic, and is calculated with healthBarWidth*(health/maxHealth), the ratio of our current health with respect to the width of the health bar. Finally, the fill colors are set according to the value of health. Last but not the least, scores:
...
void gameOverScreen() {
  background(0);
  textAlign(CENTER);
  fill(255);
  textSize(30);
  text("Game Over", height/2, width/2 - 20);
  textSize(15);
  text("Click to Restart", height/2, width/2 + 10);
}
...
void wallAdder() {
  if (millis()-lastAddTime > wallInterval) {
    ...
    // added another value at the end of the array
    int[] randWall = {width, randY, wallWidth, randHeight, 0}; 
    ...
  }
}
void watchWallCollision(int index) {
  ...
  int wallScored = wall[4];
  ...
  if (ballX > gapWallX+(gapWallWidth/2) && wallScored==0) {
    wallScored=1;
    wall[4]=1;
    score();
  }
}
void score() {
  score++;
}
void printScore(){
  textAlign(CENTER);
  fill(0);
  textSize(30); 
  text(score, height/2, 50);
}
We needed to score when the ball passes a wall. But we need to add maximum 1 score per wall. Meaning, if the ball passes a wall than goes back and passes it again, another score shouldn’t be added. To achieve this, we added another variable to the gap wall array within the arraylist. The new variable stores 0 if the ball didn’t yet pass that wall and 1 if it did. Then, we modified the watchWallCollision() method. We added a condition that fires score() method and marks the wall as passed when the ball passes a wall which it has not passed before.
We are now very close to the end. The last thing to do is implement click to restart on the game over screen. We need to set all the variables we used to their initial value, and restart the game. Here it is:
...
public void mousePressed() {
  ...
  if (gameScreen==2){
    restart();
  }
}
...
void restart() {
  score = 0;
  health = maxHealth;
  ballX=width/4;
  ballY=height/5;
  lastAddTime = 0;
  walls.clear();
  gameScreen = 0;
}
Let’s add some more colors.
Voila! We have Flappy Pong!
Full code for the game can be found here.

Porting Processing Code to the Web Using p5.js

p5.js is a JavaScript interpreter for Processing. It is not a simple library that is capable of running Processing code, instead, p5.js takes in pure JavaScript codes. Our task is to convert Processing code into JavaScript following p5.js format. The library has a set of functions and a syntax similar to Processing, and we have to do certain changes to our code to make them work in JavaScript. Even if you are not aJavaScript developer, the changes are very trivial and you should be able to follow along just fine.
First of all, we need to create a simple index.html and add p5.min.js to our header. We also need to create another file called flappy_pong.js which will house our converted code.
<html>
 <head>
  <title>Flappy Pong</title>
  <script tyle="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.19/p5.min.js"></script>
  <script tyle="text/javascript" src="flappy_pong.js"></script>
  <style>
   canvas {
    box-shadow: 0 0 20px lightgray;
   }
  </style>
 </head>
 <body>
 </body>
</html>
Our strategy while converting the code should be copying and pasting all our code into flappy_pong.jsand then making all the changes. And that’s what I did. And here are the steps I took to update the code:
  • Javascript is an untyped languages (there are no type declarations like int and float). So we need to change all the type declarations to var.
  • There is no void in Javascript. We should change all to function.
  • We need to remove the type declarations of arguments from function signatures. (ie. void wallMover(var index) { to function wallMover(index) { )
  • There is no ArrayList in JavaScript. But we can achieve the same thing using JavaScript arrays. We make the following changes:
    • ArrayList<int[]> walls = new ArrayList<int[]>(); to var walls = [];
    • walls.clear(); to walls = [];
    • walls.add(randWall); to walls.push(randWall);
    • walls.remove(index); to walls.splice(index,1);
    • walls.get(index); to walls[index]
    • walls.size() to walls.length
  • Change the declaration of the array var randWall = {width, randY, wallWidth, randHeight, 0}; to var randWall = [width, randY, wallWidth, randHeight, 0];
  • Remove all public keywords.
  • Move all color(0) declarations into function setup() because color() will not be defined before setup() call.
  • Change size(500, 500); to createCanvas(500, 500);
  • Rename function gameScreen(){ to something else like function gamePlayScreen(){ because we already have a global variable named gameScreen. When we were working with Processing, one was a function and the other was an int variable. But JavaScript confuses these since they are untyped.
  • Same thing goes for score(). I renamed it to addScore().
Full Javascript code can be found here.

Conclusion

In this article, I tried to explain how to build a very simple game with Processing. However what we did in this article is just the tip of the iceberg. With Processing, just about anything can be achieved. In my opinion, it’s the best tool to program what you’re imagining. My actual intention with this article is rather than teaching Processing and building a game, to prove that programming isn’t that hard. Building your own game isn’t just a dream. I wanted to show you that with a little effort and enthusiasm, you can do it. I really hope these two articles inspire everyone to give programming a shot.
This is the second part of the ultimate guide to Processing. In the first part, I tried to give a basic walkthrough to the Processing language. If you haven’t read the first part, you can find it here.
This article was written by OGUZ GELAL, a Toptal JavaScript developer.