We have all the pieces for the game, now we have to put them all together.
In the game loop we have to update sprite objects of various types. We keep references to them in dedicated lists.
ListbackgroundList = new ArrayList<>(); List cloudList = new ArrayList<>(); List enemyList = new ArrayList<>(); List playerBulletList = new ArrayList<>(); List playerList = new ArrayList<>();
ps: please excuse the html tags that blogspot adds for an unknown reason
We create the game and start it ...
// create game level (background, player, etc) createLevel( scene); // add nodes which display debug information createDebugOverlay(); // create game loop createGameLoop(); // start the game startGame();
... using these methods ...
private void createLevel( Scene scene) {
// load game assets
loadResources();
// create level structure
createBackground();
// create player, including input controls
createPlayer( scene);
}
private void createBackground() {
Image image = backgroundImage;
double x = 0;
double y = -image.getHeight() + Settings.SCENE_HEIGHT;
Background background = new Background( backgroundLayer, backgroundImage, x, y, Settings.BACKGROUND_SPEED);
backgroundList.add( background);
}
private void createPlayer( Scene scene) {
// player input
Input input = new Input( scene);
// register input listeners
input.addListeners();
Image image = playerShipImage;
// center horizontally, position at 70% vertically
double x = (Settings.SCENE_WIDTH - image.getWidth()) / 2.0;
double y = Settings.SCENE_HEIGHT * 0.7;
// create player
Player player = new Player(playfieldLayer, playerShipImage, x, y, 0, 0, 0, 0, Settings.PLAYER_SHIP_HEALTH, 0, Settings.PLAYER_SHIP_SPEED, input);
// register player
playerList.add( player);
}
We add more attributes to the settings class
public class Settings {
public static double SCENE_WIDTH = 400;
public static double SCENE_HEIGHT = 800;
public static double BACKGROUND_SPEED = 0.6;
public static double PLAYER_SHIP_SPEED = 4.0;
public static double PLAYER_SHIP_HEALTH = 100.0;
public static int ENEMY_SPAWN_RANDOMNESS = 50;
public static int CLOUD_SPAWN_RANDOMNESS = 100;
}
We spawn the clouds at random intervals ...
private void spawnClouds( boolean random) {
if( random && rnd.nextInt(Settings.CLOUD_SPAWN_RANDOMNESS) != 0) {
return;
}
Pane layer;
Image image;
double speed;
double opacity;
double x;
double y;
// determine random layer
// clouds in the upper layer are less opaque than the ones in the lower layer so that the player ship is always visible
// and the upper layer clouds are faster
if( rnd.nextInt(2) == 0) {
layer = lowerCloudLayer;
opacity = 1.0;
speed = rnd.nextDouble() * 1.0 + 1.0;
} else {
layer = upperCloudLayer;
opacity = 0.5;
speed = rnd.nextDouble() * 1.0 + 2.0;
}
// determine random image
image = cloudImages[ rnd.nextInt( cloudImages.length)];
// set position horizontally so that half of it may be outside of the view, vertically so that it enters the view with the next movement
x = rnd.nextDouble() * Settings.SCENE_WIDTH - image.getWidth() / 2.0;
y = -image.getHeight();
// create a sprite
Cloud cloud = new Cloud( layer, image, x, y, speed, opacity);
// manage sprite
cloudList.add( cloud);
}
... and the enemies ...
private void spawnEnemies( boolean random) {
if( random && rnd.nextInt(Settings.ENEMY_SPAWN_RANDOMNESS) != 0) {
return;
}
// image
Image image = enemyImage;
// random speed
double speed = rnd.nextDouble() * 1.0 + 1.0;
// x position range: enemy is always fully inside the screen, no part of it is outside
// y position: right on top of the view, so that it becomes visible with the next game iteration
double x = rnd.nextDouble() * (Settings.SCENE_WIDTH - image.getWidth());
double y = -image.getHeight();
// create a sprite
Enemy enemy = new Enemy( playfieldLayer, image, x, y, 0, 0, speed, 0, 1,1);
// manage sprite
enemyList.add( enemy);
}
... and the bullets whenever the player demands it.
private void spawnPrimaryWeaponObjects( Player player) {
player.chargePrimaryWeapon();
if( player.isFirePrimaryWeapon()) {
PlayerBullet playerBullet;
Image image = playerBulletImage;
// primary weapon gives horizontal center of the cannon, but we also need to consider the center of the bullet
double x = player.getPrimaryWeaponX() - image.getWidth() / 2.0;
double y = player.getPrimaryWeaponY();
double spread = player.getPrimaryWeaponBulletSpread();
double count = player.getPrimaryWeaponBulletCount();
double speed = player.getPrimaryWeaponBulletSpeed();
// create sprite
playerBullet = new PlayerBullet( bulletLayer, image, x, y, 0, -speed);
playerBulletList.add( playerBullet);
// left/right: vary x-axis position
for( int i=0; i < count / 2.0; i++) {
// left
playerBullet = new PlayerBullet( bulletLayer, image, x, y, -spread * i, -speed);
playerBulletList.add( playerBullet);
// right
playerBullet = new PlayerBullet( bulletLayer, image, x, y, spread * i, -speed);
playerBulletList.add( playerBullet);
}
player.unchargePrimaryWeapon();
}
}
We then have to process various methods like move() on every sprite. We create helper methods which do that for us. There's an alternative to the for loop by using forEach in Java 8, but we keep the for loop for now. That way the game remains playable with Java 7. The main advantage of Java 8 would be how easy it is to parallelize code by using streams. Maybe we'll come back to that later. There's no performance problem, so let's not make things too complicated for now.
private void moveSprites( List spriteList) {
for( SpriteBase item: spriteList) {
item.move();
}
}
private void updateSpritesUI( List spriteList) {
for( SpriteBase item: spriteList) {
item.updateUI();
}
}
private void checkRemovability( List spriteList) {
for( SpriteBase item: spriteList) {
item.checkRemovability();
}
}
In case you are curious, instead of a method with the for loop, you could as well write in Java 8
spriteList.forEach( player -> player.move());
Now we invoke these methods in the game loop
private void createGameLoop() {
gameLoop = new AnimationTimer() {
@Override
public void handle(long l) {
// player AI (player input)
// ---------------------------
for( Player player: playerList) {
player.processInput();
}
// add random sprites
// --------------------------
spawnClouds( true);
spawnEnemies( true);
// spawn bullets of players
// ---------------------------
for( Player player: playerList) {
spawnPrimaryWeaponObjects( player);
}
// move sprites internally
// ---------------------------
moveSprites( playerList);
moveSprites( backgroundList);
moveSprites( playerBulletList);
moveSprites( cloudList);
moveSprites( enemyList);
// move sprites on screen
// ---------------------------
updateSpritesUI(playerList);
updateSpritesUI(backgroundList);
updateSpritesUI(playerBulletList);
updateSpritesUI(cloudList);
updateSpritesUI(enemyList);
// check if sprite can be removed
// ------------------------------
checkRemovability( playerBulletList);
checkRemovability( cloudList);
checkRemovability( enemyList);
// remove removables from list, layer, etc
// ------------------------------
removeSprites( playerBulletList);
removeSprites( cloudList);
removeSprites( enemyList);
// calculate fps
// ---------------------------
updateFps();
// show debug information (fps etc)
// --------------------------------
updateDebugOverlay();
}
};
}
And that was about it. We now have a game engine with which we can proceed.
Finally here's a screenshot about what the game looks like.
Maybe not much of a difference to you compared to the prototype, but you'll see how easy it will be to add other kinds of stuff. Next time we spawn heat seeking missiles and make things go booom.

No comments:
Post a Comment