com.sturdyhelmetgames.dodgethecars.screen.GameScreen.java Source code

Java tutorial

Introduction

Here is the source code for com.sturdyhelmetgames.dodgethecars.screen.GameScreen.java

Source

/*
 * Copyright 2012 Antti Kolehmainen
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.sturdyhelmetgames.dodgethecars.screen;

import java.util.Iterator;
import java.util.Random;

import com.badlogic.gdx.Application.ApplicationType;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Touchpad;
import com.badlogic.gdx.scenes.scene2d.ui.Touchpad.TouchpadStyle;
import com.badlogic.gdx.utils.Array;
import com.sturdyhelmetgames.dodgethecars.DodgeTheCarsGame;
import com.sturdyhelmetgames.dodgethecars.RandomUtil;
import com.sturdyhelmetgames.dodgethecars.assets.Art;
import com.sturdyhelmetgames.dodgethecars.assets.Sound;
import com.sturdyhelmetgames.dodgethecars.entity.BasicEntity;
import com.sturdyhelmetgames.dodgethecars.entity.BasicEntity.Direction;
import com.sturdyhelmetgames.dodgethecars.entity.Car;
import com.sturdyhelmetgames.dodgethecars.entity.GameOverIndicator;
import com.sturdyhelmetgames.dodgethecars.entity.Heart;
import com.sturdyhelmetgames.dodgethecars.entity.PauseIndicator;
import com.sturdyhelmetgames.dodgethecars.entity.PineCone;
import com.sturdyhelmetgames.dodgethecars.entity.Point;
import com.sturdyhelmetgames.dodgethecars.entity.Point.PointType;
import com.sturdyhelmetgames.dodgethecars.entity.Squirrel;
import com.sturdyhelmetgames.dodgethecars.entity.TrafficLight;
import com.sturdyhelmetgames.dodgethecars.events.EventCache;

/**
 * Main game screen.
 * 
 * @author Antti 19.6.2012
 * 
 */
public class GameScreen extends TransitionScreen {

    private static final float TIME_PER_ROUND = 5f;

    /**
     * Number of car "waves"
     */
    private long wave;

    /**
     * How long the game session has been running.
     */
    private float gameTime;

    /**
     * First time attribute that is used to control the speed of {@link Car}
     * spawns.
     */
    private float carSpeedUpTotalTime;

    /**
     * Second time attribute that is used to control the speed of {@link Car}
     * spawns.
     */
    private float carSpeedUpTime;

    /**
     * Time that has passed since last {@link Car} spawn.
     */
    private float carTime;

    /**
     * Thime that has passed since last {@link PineCone} spawn.
     */
    private float pineConeTime;

    /**
     * Tells if the game is paused or not.
     */
    private boolean paused = false;

    /**
     * Tells if the game is over or not.
     */
    private boolean gameOver = false;

    /**
     * Tells how long game over lasts.
     */
    private float gameOverTime = 0f;

    /**
     * List for game objects.
     */
    private final Array<BasicEntity> gameEntities = new Array<BasicEntity>(false, 30);

    /**
     * Pool for cars.
     */
    private final Array<Car> carPool = new Array<Car>(false, 20);

    /**
     * Pool for pinecones.
     */
    private final Array<PineCone> pineConePool = new Array<PineCone>(false, 20);

    public static final float LEVEL_BOUNDARY_X_RIGHT = 21f;
    public static final float LEVEL_BOUNDARY_X_LEFT = -23f;
    public static final float LEVEL_BOUNDARY_Y_TOP = 14f;
    public static final float LEVEL_BOUNDARY_Y_BOTTOM = -15f;

    /**
     * The player character.
     */
    private Squirrel player;

    /**
     * Traffic light.
     */
    private TrafficLight trafficLight;

    /**
     * Game over indicator.
     */
    private final GameOverIndicator gameOverIndicator = new GameOverIndicator();

    /**
     * Pause indicator.
     */
    private final PauseIndicator pauseIndicator = new PauseIndicator();

    /**
     * Holder for score text position.
     */
    private final Vector3 scoreTextPosition = new Vector3(LEVEL_BOUNDARY_X_LEFT, LEVEL_BOUNDARY_Y_TOP - 1f, 0f);

    /**
     * Stage for {@link Touchpad}.
     */
    private final Stage stage;

    /**
     * {@link Touchpad} for touch events.
     */
    private final Touchpad touchpad;

    /**
     * Indicates if touch controls are active or not.
     */
    private boolean isTouchActive = false;

    public GameScreen(final DodgeTheCarsGame game) {
        super(game);

        Gdx.input.setCatchBackKey(true);

        player = new Squirrel();
        gameEntities.add(player);

        trafficLight = new TrafficLight();

        camera.project(scoreTextPosition);

        stage = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true) {
            @Override
            public boolean keyDown(int keyCode) {
                if (gameTime > 3.5f) {
                    if (!paused && !gameOver) {
                        if (keyCode == Keys.P || keyCode == Keys.ESCAPE || keyCode == Keys.BACK
                                || keyCode == Keys.HOME) {
                            pause();
                            return true;
                        }
                    } else if (paused) {
                        if (keyCode == Keys.P || keyCode == Keys.ESCAPE || keyCode == Keys.BACK
                                || keyCode == Keys.C) {
                            resumeGame();
                            return true;
                        } else if (keyCode == Keys.Q) {
                            updateLeaderboard();
                            game.setScreen(new TitleScreen(game));
                            return true;
                        }
                    }

                    if (gameOver) {
                        if (keyCode == Keys.R) {
                            game.setScreen(new GameScreen(game));
                            return true;
                        } else if (keyCode == Keys.Q) {
                            game.setScreen(new TitleScreen(game));
                            return true;
                        }
                    }
                }
                return super.keyDown(keyCode);
            }

            @Override
            public boolean touchUp(int screenX, int screenY, int pointer, int button) {
                final Vector3 position = new Vector3(screenX, screenY, 0f);
                camera.unproject(position);

                if (gameTime > 3.5f) {
                    if (paused) {
                        // very dirty code, sorry :)
                        if (position.x > -7f && position.x < 7f && position.y > 3f && position.y < 6f) {
                            resumeGame();
                            return true;
                        } else if (position.x > -7f && position.x < 7f && position.y < -4f && position.y > -7f) {
                            updateLeaderboard();
                            game.setScreen(new TitleScreen(game));
                            return true;
                        }
                    }
                    if (gameOver) {
                        // very dirty code, sorry :)
                        if (position.x > -7f && position.x < 7f && position.y > 4f && position.y < 7f) {
                            game.setScreen(new GameScreen(game));
                            return true;
                        } else if (position.x > -7f && position.x < 7f && position.y < -4f && position.y > -7f) {
                            game.setScreen(new TitleScreen(game));
                            return true;
                        }
                    }
                }

                return super.touchUp(screenX, screenY, pointer, button);
            }

        };
        Gdx.input.setInputProcessor(stage);
        touchpad = new Touchpad(2.0f, Art.skin.get("touchpad", TouchpadStyle.class));
        touchpad.setBounds(15, 15, 200, 200);
        stage.addActor(touchpad);

        // if touch-based device, activate touch controls
        if (ApplicationType.Android.equals(Gdx.app.getType()) || ApplicationType.iOS.equals(Gdx.app.getType())) {
            isTouchActive = true;
        }
    }

    /**
     * Activates game over screen.
     */
    private void activateGameOver() {
        gameOver = true;

        if (Sound.musicHappyGoLucky.isPlaying()) {
            Sound.musicHappyGoLucky.stop();
        }
        Sound.soundSquirrelDie.play();
        updateLeaderboard();
    }

    /**
     * Update leaderboard with player score.
     */
    private void updateLeaderboard() {
        EventCache.updateLeaderboard.eventValue = player.score;
        game.fireEvent(EventCache.updateLeaderboard);
    }

    @Override
    protected void updateScreen(float fixedStep) {
        super.updateScreen(fixedStep);

        // check input and after that update entities
        checkInput(fixedStep);

        if (paused) {
            pauseIndicator.update(fixedStep);
        } else {
            // touch controls if on android
            if (isTouchActive && player.isAlive()) {
                stage.act(fixedStep);
                final float knobX = touchpad.getKnobX() - 100;
                final float knobY = touchpad.getKnobY() - 100;
                player.acceleration.x = knobX;
                player.acceleration.y = knobY;
            }

            // cumulate timers
            gameTime += fixedStep;
            carSpeedUpTotalTime += fixedStep;

            trafficLight.update(fixedStep);

            if (gameOver) {
                gameOverTime += fixedStep;
                if (gameOverTime > 2f) {
                    gameOverIndicator.update(fixedStep);
                }
            }

            if (gameTime > 3f) {
                if (!Sound.musicHappyGoLucky.isPlaying() && player.isAlive()) {
                    Sound.musicHappyGoLucky.play();
                }

                carSpeedUpTime += fixedStep;
                carTime += fixedStep;
                pineConeTime += fixedStep;

                // add some score when player is alive
                if (player.isAlive()) {
                    player.score += (fixedStep * 90);
                }

                wave = Math.round(gameTime / TIME_PER_ROUND) + 1;
                carSpeedUpTime = wave % 3 == 0 ? (carSpeedUpTotalTime * wave) / 5000 + 3.5f : 2f;
                if (carSpeedUpTime > 4.2f) {
                    carSpeedUpTotalTime = 2f;
                }

                // randomly spawn cars
                if (carTime > 4f + MathUtils.random(5f) - carSpeedUpTime) {
                    carTime = 0f;

                    // from which side the car drives
                    final boolean side = getRandom().nextBoolean();
                    final float carX = side ? 35f : -35f;
                    final float carY = getRandom().nextInt(32) - 16;
                    final Direction carDirection = side ? Direction.LEFT : Direction.RIGHT;
                    final int carColor = getRandom().nextInt(3);
                    final float maxVelocity = RandomUtil.oneOfFive() ? 20f : BasicEntity.VELOCITY_MAX;

                    Car car = null;
                    if (carPool.size < 1) {
                        // pool didn't have spare cars, create a new one
                        car = new Car(carX, carY, carDirection, carColor, maxVelocity);
                    } else {
                        // pool had cars, fetch one and reset
                        car = carPool.get(0);
                        car.reset(carX, carY, carDirection, carColor, maxVelocity);
                        carPool.removeIndex(0);
                    }
                    gameEntities.add(car);
                }

                // randomly spawn pinecones and hearts
                if (pineConeTime > 2f + MathUtils.random(5f)) {
                    pineConeTime = 0f;

                    // randomize new powerup position
                    final float powerupX = getRandom().nextInt(44) - 22;
                    final float powerupY = getRandom().nextInt(28) - 14;

                    if (RandomUtil.oneOfSeven()) {
                        // TODO: pool hearts for less GC
                        Heart heart = new Heart(powerupX, powerupY);
                        gameEntities.add(heart);
                    } else {
                        // randomize new pinecone color
                        int color = PineCone.PINECONE_COLOR_BRONZE;
                        if (RandomUtil.oneOfThree()) {
                            color = PineCone.PINECONE_COLOR_SILVER;
                        } else if (RandomUtil.oneOfFive()) {
                            color = PineCone.PINECONE_COLOR_GOLD;
                        }

                        PineCone pineCone = null;
                        if (pineConePool.size < 1) {
                            // pool didn't have spare pinecones, create a new
                            // one
                            pineCone = new PineCone(powerupX, powerupY, color);
                        } else {
                            // pineConePool.sort();
                            // pool had pinecones, fetch one and reset
                            pineCone = pineConePool.get(0);
                            pineCone.reset(powerupX, powerupY, color);
                            pineConePool.removeIndex(0);
                        }

                        gameEntities.add(pineCone);
                    }
                }

                // update entities
                for (Iterator<BasicEntity> i = gameEntities.iterator(); i.hasNext();) {
                    BasicEntity entity = i.next();
                    entity.update(fixedStep);

                    if (entity instanceof Car) {
                        Car car = (Car) entity;

                        // if car hits player, squirrel takes damage
                        if (car.hit(player)) {
                            if (!player.isDamaged && player.health > 1) {
                                Sound.soundSquirrelHit.play();
                            }
                            if (player.takeDamage()) {
                                activateGameOver();
                            }
                        }

                        // if car is out of the limits send it to pool
                        if (car.x < -36f || car.x > 36f) {
                            carPool.add(car);
                            i.remove();
                        }
                    }
                    if (entity instanceof PineCone) {
                        PineCone pineCone = (PineCone) entity;

                        // if pine cone hits player, "collect" it
                        if (player.isAlive() && pineCone.hit(player)) {
                            pineCone.collected = true;
                            player.score += (500 * (pineCone.color + 1));
                            // TODO: pool point objects
                            if (pineCone.color == PineCone.PINECONE_COLOR_BRONZE) {
                                gameEntities.add(new Point(pineCone.x, pineCone.y, PointType.POINT_500));
                            } else if (pineCone.color == PineCone.PINECONE_COLOR_SILVER) {
                                gameEntities.add(new Point(pineCone.x, pineCone.y, PointType.POINT_1000));
                            } else if (pineCone.color == PineCone.PINECONE_COLOR_GOLD) {
                                gameEntities.add(new Point(pineCone.x, pineCone.y, PointType.POINT_1500));
                            }
                        }

                        // if pine cone is collected send it to pool
                        if (!pineCone.isAlive() || pineCone.collected) {
                            if (pineCone.collected) {
                                Sound.soundCollect.play();
                            }
                            pineCone.collected = false;
                            pineConePool.add(pineCone);
                            i.remove();
                        }
                    }
                    if (entity instanceof Heart) {
                        Heart heart = (Heart) entity;
                        // if heart hits player, "collect" it and get score and
                        // health
                        if (player.isAlive() && heart.hit(player)) {
                            heart.collected = true;
                            player.score += 500;
                            player.replenishHealth();
                            // TODO: pool points
                            gameEntities.add(new Point(heart.x, heart.y, PointType.POINT_500));
                        }

                        if (!heart.isAlive() || heart.collected) {
                            if (heart.collected) {
                                Sound.soundCollect.play();
                            }
                            i.remove();
                        }
                    }
                    if (entity instanceof Point) {
                        Point point = (Point) entity;
                        if (!point.isAlive()) {
                            i.remove();
                        }
                    }
                }

            }
            // sort order of entities for drawing, pretty lame solution
            gameEntities.sort();
        }
    }

    @Override
    public void renderScreen(float delta) {
        spriteBatch.getProjectionMatrix().set(camera.combined);
        spriteBatch.begin();

        // render the background
        spriteBatch.draw(Art.backgroundTexture, -24, -46.5f, 48, 64);

        // if player is dead, render it underneath everything
        if (!player.isAlive()) {
            player.render(spriteBatch, delta);
        }

        // render the entities except squirrel when it's dead
        for (BasicEntity entity : gameEntities) {
            if (!(entity instanceof Squirrel) || ((Squirrel) entity).isAlive()) {
                entity.render(spriteBatch, delta);
            }
        }

        // render hud
        for (int i = 0; i < Squirrel.HEALTH_MAX; i++) {
            if (i < player.health) {
                spriteBatch.draw(Art.heartTex, LEVEL_BOUNDARY_X_LEFT + i * 2f, LEVEL_BOUNDARY_Y_TOP - 1f, 2f, 2f);
            } else {
                spriteBatch.draw(Art.heartBlackTex, LEVEL_BOUNDARY_X_LEFT + i * 2f, LEVEL_BOUNDARY_Y_TOP - 1f, 2f,
                        2f);
            }
        }

        trafficLight.render(spriteBatch, delta);

        if (gameOver) {
            gameOverIndicator.render(spriteBatch, delta);
        }
        if (paused) {
            pauseIndicator.render(spriteBatch, delta);
        }

        spriteBatch.end();

        if (isTouchActive) {
            stage.draw();
        }

        // render fps and additional stuff for debugging
        spriteBatch.getProjectionMatrix().set(normalProjection);
        spriteBatch.begin();

        Art.scoreFont.draw(spriteBatch, String.valueOf(player.score), scoreTextPosition.x, scoreTextPosition.y);

        if (isDebug()) {
            Art.debugFont.draw(spriteBatch, "Score: " + player.score, 0, 40);

            int numOfCarsInPlay = 0;
            int numOfCarsInPool = 0;
            for (BasicEntity entity : gameEntities) {
                if (entity instanceof Car) {
                    numOfCarsInPlay++;
                }
            }
            for (Car car : carPool) {
                if (car != null) {
                    numOfCarsInPool++;
                }
            }
            Art.debugFont.draw(spriteBatch, "Number of cars in play: " + numOfCarsInPlay, 0, 60);
            Art.debugFont.draw(spriteBatch, "Number of cars in pool: " + numOfCarsInPool, 0, 80);

            int numOfPineConesInPlay = 0;
            int numOfPineConesInPool = 0;
            for (BasicEntity entity : gameEntities) {
                if (entity instanceof PineCone) {
                    numOfPineConesInPlay++;
                }
            }
            for (PineCone pineCone : pineConePool) {
                if (pineCone != null) {
                    numOfPineConesInPool++;
                }
            }

            Art.debugFont.draw(spriteBatch, "Number of pine cones in play: " + numOfPineConesInPlay, 0, 100);
            Art.debugFont.draw(spriteBatch, "Number of pine cones in pool: " + numOfPineConesInPool, 0, 120);
            Art.debugFont.draw(spriteBatch, "Game speed up time: " + carSpeedUpTime, 0, 140);
            Art.debugFont.draw(spriteBatch, "Round: " + wave, 0, 160);

        }

        spriteBatch.end();

        renderFadeIn(delta);
    }

    /**
     * Processes player movement keys.
     * 
     * @param fixedStep
     */
    private void checkInput(float fixedStep) {
        if (!paused) {
            if (player.isAlive()) {
                // process player movement keys
                if (Gdx.input.isKeyPressed(Keys.UP)) {
                    player.direction = Direction.UP;
                    player.acceleration.y = BasicEntity.ACCELERATION_MAX;
                }
                if (Gdx.input.isKeyPressed(Keys.DOWN)) {
                    player.direction = Direction.DOWN;
                    player.acceleration.y = -BasicEntity.ACCELERATION_MAX;
                }
                if (Gdx.input.isKeyPressed(Keys.RIGHT)) {
                    player.direction = Direction.RIGHT;
                    player.acceleration.x = BasicEntity.ACCELERATION_MAX;
                }
                if (Gdx.input.isKeyPressed(Keys.LEFT)) {
                    player.direction = Direction.LEFT;
                    player.acceleration.x = -BasicEntity.ACCELERATION_MAX;
                }
            }
        }

    }

    @Override
    public void pause() {
        super.pause();
        paused = true;
        pauseIndicator.reset();
    }

    public void resumeGame() {
        paused = false;
    }

    /**
     * Return a {@link Random} instance.
     * 
     * @return Random instance.
     */
    private Random getRandom() {
        return RandomUtil.getRandom();
    }

    @Override
    public void dispose() {
        super.dispose();
        stage.dispose();
    }

}