Over the last couple of guides, we’ve taken a brief look at the concept of Object-Oriented Programming (OOP) and started playing around with Java graphics by coding Mandelbrot fractals. In this article, we’ll combine these two areas to take a look at some basic game development concepts on the way to making a simple Java game.
You’ll find the source code pack for the three projects at our website. Unzip the file and you’ll find three further .zip files inside – one for each project.
You’ll also need the Java 8 Software Development Kit (SDK) and the latest NetBeans IDE (Integrated Development Environment).
Install Java first and then the IDE. Launch the IDE, select ‘File > Import Project > From Zip’, select one of the three .zip files, load it up and run it.
Make sure you muck around with it, too – change the code and see what happens. It comes with no warranty whatsoever.
When I was a kid, I loved drawing little cartoons in my school textbooks, each one just slightly different and then flicking the pages, seeing those drawings come to life.
To be honest, I wasn’t that good at it, but flipbook animations share many of the same basic principles with creating animations on a computer.
Just like a flipbook cartoon, you want to move your digital object fractionally, but consistently, through each frame at regular intervals to give the illusion of movement.
What’s a ball?
Well, it’s a bit more than that because with the addition of two characters (look at the code and you’ll figure it out), you can change it from one ball to 100, each independently moving around the frame.
The other thing is that it doesn’t matter what type of computer you have, the frame rate is always the same – 50 frames per second (50fps).
There are a bucketload of ways you can code this, but on this occasion, we thought it was the perfect opportunity to pull in some recently covered OOP concepts, so we’ll start by asking an odd question: what’s a ball?
Obviously, it’s an object and it’s round, but more specifically, in the context of 2D space, a moving ball has a number of attributes – starting with X-axis and Y-axis coordinates.
It has a colour and, if it’s moving, it has a rate of movement in those two axes. It can also have a function that determines its next position based on its current position and the rate of movement.
All of that is enough for us to create a Java class called Ball, which we’ve added to the bottom of the CollisionTest.java code.
When we create an object from the Ball class, we’re giving it a random start position somewhere in the JFrame, a random movement vector and a random colour.
Open up the CollisionTest.java app and you’ll see this in the CollisionTest() constructor and the balls.add() statement.
Setting the frame rate
To get a smooth animation happening, you need to change the position of the ball at a fixed rate, and in Java, you
can set that rate with the Timer class.
First, we create a TimerListener object, whose job is to monitor a Timer and launch a function when that timer runs out.
Next, we create the timer itself and for that, you create an instance of the Timer class, set the delay in milliseconds (ms) before it fires, the name of the TimerListener attached to run when the timer fires (here, called ‘animate’) and start it.
For silky-smooth animation, you need a frame rate faster than your eyes can resolve, which means at least 30fps. To a point, the faster the rate, the smoother the animation looks, but the faster the computer must operate.
As a compromise, we’ve gone for 50fps, which means a timer delay of 20ms.
Bag of balls
Right near the top of the code, we’ve added an ArrayList of type Ball, which allows us to create the coding equivalent of a bag of balls.
Each ball has its own parameters – position, movement, colour – and using Java’s clever object-indexed for-loop, we can iterate through the ArrayList, update each ball and display the changes to the screen every 20ms.
Look at the TimerListener private class inside the Map class – inside the ActionPerformed() method, there’s the object-indexed for-loop, which basically says, ‘for each ball in the balls ArrayList, run the ball.move() method to update the ball position and when complete, repaint all of the balls on the screen’.
Looking at a single ball, the top-left corner is its origin point – if you’re looking at the Ball class, it’s the x
and y integers (Ball.x and Ball.y).
In our code, the height and width of the ball are both set to 60 pixels. However, the origin of that ball on the JPanel and the rate and direction of movement is set randomly when the Ball object is created and added to the balls ArrayList.
In our code, we start with a 1,024 x 768-pixel JFrame and on top of that, we create a JPanel object called ‘map’ and add it to the JFrame. The balls are drawn on the map JPanel. Like the balls, the top-left of that panel is the origin (0,0) and bottom-right, it’s the furthest reach (1024,768).
The rate and direction of movement is set in the Ball class by two variables: dx and dy. Here, a positive dx number directs the ball moving right, while a negative value moves left. A positive dy number moves the ball down the screen; negative, up. Combine the two and you can move the ball in any direction around the panel.
For example, if (dx, dy) is (-1,2), the ball will move at a rate of one pixel left and two pixels down.
Now for the collision detection, which simply involves detecting when the ball hits one of the JPanel’s four walls. We also need to resolve what happens once a wall is detected.
We’re going to appropriate the Law of Reflection here, which says that the angle of incidence (the angle as the ball approaches the surface) equals the angle of reflection (the angle the ball moves away from the surface).
This also assumes an elastic collision, where no energy is lost in the collision between the ball and the wall, leaving the ball to bounce around forever.
We’re using these derivative dx and dy parameters because it’s easier to deal with the vector movement as separate horizontal (x) and vertical (y) components.
Say, for example, the ball is moving down-right and hits the bottom wall first. Using the Law of Reflection, the horizontal component doesn’t change – the ball keeps moving right.
What does change is the ball hits the wall and begins moving up. With no losses, the ball’s upward rate of movement is the same as when it was moving down and it hit the wall.
It might not be obvious, but we can do the reflection easily by simple negation. Look at the Ball.move() method — if the ball is within the panel, we add the current movement rate (dx) to the current horizontal position (x).
But if the ball has hit either the left or right wall, we negate the horizontal movement component (dx = -dx) and add that to the current horizontal position.
The negation turns a negative into a positive and a positive into a negative, so we can handle changes of direction in either direction here. We also do likewise in the vertical plane with the top and bottom walls, the dy and y positional values.
Do all of this every 20-ms and you get the effect of a ball (or many balls) bouncing around the frame.
Balls to Spitfires
First, we generate the cloud using Paint.net and its cloud render function. We create a 1,024 x 768-pixel image using that render option (‘Effects > Render > Cloud’) and run it through the Sepia filter (‘Adjustments > Sepia’) to create our background.
We’ve also created a stylised top-down view of a Spitfire fighter plane from another image, turned it into a .png image with a transparent background and brought that in as an Image object called ‘spit’.
The Ball class is still there, but now represents the position and movement of a Spitfire image object.
Run that code and you’ll see several Spitfire planes now bouncing around the panel on a sepia cloud. Two very different looks, but the source codes are substantially the same.
Load up the JPong.java project and run it. Essentially, you have a paddle that moves right and left, but instead of having to beat an opponent, your job is to just stop five balls from hitting the bottom wall.
For every ball you get back in the air, you score one point; for every ball you let past, you lose a point.
The addition to this project is the Paddle class. Again, the paddle is just another object with parameters and movement, ripe for modelling as a class. To move the paddle, you use the keyboard right and left arrow keys and to monitor these keys, we add in a KeyListener object.
It monitors the keyboard, so that when a key is pressed, we know which key it is, and based on that, we either add or subtract the dx rate of movement parameter to the paddle’s x position (paddle.x) and update the position on the screen.
The collision detection here is determined by whether each ball comes in contact with the paddle on the ball’s downward movement (we check for this to ensure we don’t incorrectly detect balls having just bounced off the bottom wall).
When a collision is detected, the score parameter stored as part of the Paddle object is incremented if the ball bounces off the paddle and decremented if it’s off the back wall. That score is then displayed on the screen using the g.drawString statement in the Map class.
The basis of the detection is we start with the ball’s current x and y position and compare that to the same of the paddle. If the ball position matches the paddle position, a collision is deemed to have occurred. It’s simple and crude, but it works.
More maths and physics
We’re not about to insult serious game developers and suggest you can start coding blockbuster games by simply reading one article – at best, all we’ve done is scratch the lacquer covering the paint over the primer on top of the surface that is games development.
Even with 2D gaming, there are plenty of other things to learn, from sprites to threads to frame buffering to multi-object impulse-momentum collision resolution.
If you still have it on your phone or tablet, play the first episode again, but this time, look at the interaction between the birds and the building components, and also how those components interact with each other.
This interaction was created using a free physics engine called Box2D (box2d.org). Like many high-performance games, Box2D uses C++ to obtain maximum performance.
Thankfully, you don’t have to go that far to start writing your own basic Java games – even with just the little we’ve done here in the three projects, you’ve got enough kit to start playing around and writing simple games of your own.