JavaFX 2 GameTutorial Part 4
This is part four of a six part series related to a JavaFX 2 Game Tutorial. If you’ve missed Part 1, Part 2, or Part 3, I encourage you to go through them before beginning this tutorial. To recap, in Part 3 I gave you a little history of the many classic arcade style games and the different input devices that were used. I then showed you how to create a simple game similar to the famous arcade ‘Asteroids’. The controls (movement of the ship) were, however, more similar to those of the PC game ‘Star Craft’. In Part 3, you should have a good understanding of how to receive input from your keyboard and mouse.
Figure 1 JavaFX 2 Game Tutorial Part 4 |
This tutorial is about tweaking Part 2’s game engine and updating the existing ‘Asteroids’ style game from Part 3 to handle collision detection. In this tutorial, I will briefly talk about sprites and how to handle collision detection. The spaceship will now have the ability to generate a force field to protect itself from enemies and asteroids. This is reminiscent of the classic arcade ‘Asteroids Deluxe’. If you want to run the demo, scroll down and click on the WebStart button below. Please read the requirements before launching the game.
What is a Sprite?
According to Wikipedia, “a sprite is a two-dimensional image or animation that is integrated into a larger scene.” From the Java gaming world view, a sprite is an object containing image frames and additional data based on the context of an actor to be animated onto the scene area. In the days of Walt Disney, when cartoons were drawn with a pencil and paper, the artist produced many drawings that became animations. This example points to the creation of the flip book. I’m sure you’ve created flip books as a kid. I know I did. I used to doodle and make cool animations with all the corners of my notebooks. In our Asteroid type game, I created a sprite object which contains all of the images (ImageView) of the ship pre-rotated just like a flip book. To animate the ship turning, I made the current frame visible and the rest of the frames not visible. Similar to a flip book, it will appear to be rotated about its center (pivot) point. A sprite can also contain other information, such as velocity or health points.
Collision Detection
When actors or sprites animate across the scene, the game engine will check each sprite against other sprites to determine if they have collided into one another. This process should be very efficient especially when you have numerous sprites moving about the screen. There are tradeoffs when it comes to being efficient. Because each cycle in the game loop will check for collision, being more accurate usually degrades your performance. Many games will use the bounding region of the image to determine if two sprites have collided into one another. Some games use rectangles as bounding regions. Shown below in figure 2 are two sprites colliding:
Figure 2 Bounding box as a rectangular collision region. |
I’m sure you’ll know by now that most actors (images) in games don’t appear rectangular when pixels surrounding the actor are transparent. However, the actor or image is indeed rectangular even if the pixels are transparent or not.
Figure 3 depicts an actor image |
Those games which use rectangular bounding regions usually make the bounding box inscribed in the sprite’s image. Shown below in figure 4 two rectangular bounding regions (orange and green) are inscribed in the spaceship image.
Figure 4 Two rectangles used as collision bounding boxes. |
I’m sure you will notice that the nose tip of the ship and wings are not covered by either bounding box. This means that when an asteroid overlaps the unbounded region of the sprite the collision will not occur. Some games use this strategy; you will notice that the sprites’ rectangular bounding regions are small and placed in key areas of the sprite image. Greater precision will be found with better algorithms for polygons and other non-rectangular shapes. In this blog entry, I basically use circles as bounding regions and not rectangles. I could have made each sprite contain an array of collision shapes, but instead, I chose to have only one collision region for each sprite. Each collision region will be a circle shape on the scene graph. For the spaceship, I inscribed the circle based on the center point of the ship with the radius extended to the cock pit of the ship. Shown below in figure 5 the bounded circular collision area of the ship is depicted as a red circle.
Figure 5 The ship’s collision region. |
I chose a circle as the bounding region because of the relative ease to determine the collision of two objects based on the distance formula (Pythagorean theorem) which only requires each sprite’s bounding region’s center point and their radii. After calculating the distance based on the two center points, you will compare the result to see if it is less than or equal to the sum of the two radii. If the result does indeed come out to be less than or equal to the sum of the two radii then a collision has occurred. Figure 6 depicts how the distance formula relates to the two center points of circular bounding regions.
Figure 6 distance formula between two center points. |
The following code creates the main game loop from the GameWorld class:
@Override public void handle(javafx.event.ActionEvent event) { // update actors updateSprites(); // check for collision checkCollisions(); // removed dead things cleanupSprites(); }
The code below creates the checkCollision() method from the GameWorld class:
protected void checkCollisions() { // check other sprite's collisions spriteManager.resetCollisionsToCheck(); // check each sprite against other sprite objects. for (Sprite spriteA : spriteManager.getCollisionsToCheck()) { for (Sprite spriteB : spriteManager.getAllSprites()) { if (handleCollision(spriteA, spriteB)) { // The break helps optimize the collisions // The break statement means one object only hits another // object as opposed to one hitting many objects. // To be more accurate comment out the break statement. break; } } } }
The derived Game World (TheExpanse) class’ implementation of its handleCollision() method:
/** * How to handle the collision of two sprite objects. * * @param spriteA Sprite from the first list. * @param spriteB Sprite from the second list. * @return boolean returns a true if the two sprites have collided otherwise false. */ @Override protected boolean handleCollision(Sprite spriteA, Sprite spriteB) { if (spriteA != spriteB) { if (spriteA.collide(spriteB)) { if (spriteA != myShip) { spriteA.handleDeath(this); } if (spriteB != myShip) { spriteB.handleDeath(this); } } } return false; }
The Sprite Class’ default implementation of its collide() method using the distance formula:
public boolean collide(Sprite other) { if (collisionBounds == null || other.collisionBounds == null) { return false; } // determine it's size Circle otherSphere = other.collisionBounds; Circle thisSphere = collisionBounds; Point2D otherCenter = otherSphere.localToScene(otherSphere.getCenterX(), otherSphere.getCenterY()); Point2D thisCenter = thisSphere.localToScene(thisSphere.getCenterX(), thisSphere.getCenterY()); double dx = otherCenter.getX() - thisCenter.getX(); double dy = otherCenter.getY() - thisCenter.getY(); double distance = Math.sqrt(dx * dx + dy * dy); double minDist = otherSphere.getRadius() + thisSphere.getRadius(); return (distance < minDist); }
The Sprite Class’ default implementation of its handleDeath() method:
public void handleDeath(GameWorld gameWorld) { gameWorld.getSpriteManager().addSpritesToBeRemoved(this); }
The Atom (an asteroid or missile) class will override the handleDeath() method:
public void handleDeath(GameWorld gameWorld) { implode(gameWorld); super.handleDeath(gameWorld); }
JavaFX 2 Sprite and Collision Demo
This simple demo game will be a mix between StarCraft and Asteroids. When using the mouse to navigate the ship, you will notice that the controls will resemble StarCraft’s Battle Cruiser. The objective is to fire your weapon at the spheres before they hit your spaceship or other spheres which implode upon impact. Because this is a simple tutorial or even a game in its early stages of development, the game doesn’t keep track of the score. I encourage you to go to GitHub to download the code and enhance the game. For the sake of brevity, I will not be showing all of the code changes, but I trust you will visit GitHub here: https://github.com/carldea/JFXGen for all the demos and source code.
Requirements:
- Java 7 or later
- JavaFX 2.1 or later
- Windows XP or later (Should be available soon for Linux/MacOS)
A simple Asteroid type game called ‘The Expanse’.
Instructions:
- Right mouse click (on Windows) to fly ship.
- Left mouse click (left click on Windows mouse) to fire weapon.
- Key press ’2? to change to large missiles. (blue circular projectiles)
- Other key press defaults to smaller missiles. (red circular projectiles)
- Space bar key press will toggle a force field to protect the ship from enemies and asteroids.
Click on the Launch button below to start the demo:
Continue to Part 5 of this tutorial.
Related articles
Definition of a sprite: http://en.wikipedia.org/wiki/Sprite_%28computer_graphics%29
Walt Disney: http://en.wikipedia.org/wiki/Walt_Disney
How to make a flip book: http://www.bitrebels.com/design/how-to-create-a-flip-book/
JavaFX’s ImageView : http://docs.oracle.com/javafx/2/api/javafx/scene/image/ImageView.html
Collision detection: http://zetcode.com/tutorials/javagamestutorial/collision/
AABBs Collision detection in Java: http://www.youtube.com/watch?v=JIxV-LXqa1g
Pythagorean theorem: http://en.wikipedia.org/wiki/Pythagorean_theorem
Distance formula: http://en.wikipedia.org/wiki/Distance
Serious game of Asteroids Deluxe (Youtube): http://www.youtube.com/watch?v=6DG-GJENHgg
Reference: JavaFX 2 GameTutorial Part 4 from our JCG partner Carl Dea at the Carl’s FX Blog blog.