com.andgate.pokeadot.GameScreen.java Source code

Java tutorial

Introduction

Here is the source code for com.andgate.pokeadot.GameScreen.java

Source

/*
This file is part of Poke.
    
Poke is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License.
    
Poke is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with Poke.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.andgate.pokeadot;

import com.andgate.pokeadot.util.HSL;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Circle;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.ImageButton;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.utils.Array;

import java.text.DecimalFormat;
import java.util.Iterator;

public class GameScreen implements Screen {
    private final PokeADot game;

    public enum GameState {
        INTRO, PAUSE, RUN, OVER
    }

    private GameState gameState = GameState.INTRO;

    public void setGameState(GameState newGameState) {
        gameState = newGameState;
    }

    InputMultiplexer im;

    Stage introStage;
    Stage pauseMenuStage;
    Stage pauseButtonStage;
    Stage gameOverStage;

    int pokablesExpired;
    Array<Pokable> pokables;

    private float spawnWaitTime;
    private float nextSpawnTime;
    private int spawnCount;

    private static final String HIT_COUNT_TEXT = "Poked";
    private static final String POINTS_TEXT = "Points";
    private static final String GAME_TIME_TEXT = "Time";
    private static final String PAUSE_TEXT = "Pause";
    private static final String INSTRUCTIONS_TEXT = "Don't let them shrink!";
    private static final String POKE_ANYWHERE_TEXT = "Poke circles to start.";
    private static final DecimalFormat doubleDecimal = new DecimalFormat("0.00");

    private static final float GAME_END_PAUSE = 10.0f; // seconds
    private float gameEndWait = 0.0f;
    private static final float TAP_TO_END_START_TIME = 1.0f; //seconds
    private boolean tapToEnd = false;

    public GameScreen(final PokeADot newGame, PokeADot.GameMode newMode) {
        game = newGame;
        game.mode = newMode;

        im = new InputMultiplexer();
        im.addProcessor(new GameInputProcessor());
        Gdx.input.setInputProcessor(im);

        pokables = new Array<Pokable>();
        pokablesExpired = 0;
        forceSpawn();

        buildStages();

        /*im.setProcessors(new Array<InputProcessor>(
            new InputProcessor[]{new GameInputProcessor()}
        ));*/
    }

    private void buildStages() {
        buildIntroStage();
        buildPauseButtonScene();
        buildPauseMenuScene();
        buildGameOverStage();
    }

    private void disposeStages() {
        introStage.dispose();
        pauseMenuStage.dispose();
        pauseButtonStage.dispose();
        gameOverStage.dispose();
    }

    private void buildIntroStage() {
        introStage = new Stage();
        //introStage.getViewport().setCamera(camera);

        Label.LabelStyle labelStyle = new Label.LabelStyle(game.mediumSmallFont, Color.WHITE);
        Label instructionsLabel = new Label(INSTRUCTIONS_TEXT, labelStyle);
        Label pokeAnywhereLabel = new Label(POKE_ANYWHERE_TEXT, labelStyle);

        Table table = new Table();
        table.add(instructionsLabel).center().row();
        table.add(pokeAnywhereLabel).center();

        table.setFillParent(true);

        introStage.addActor(table);
    }

    private void buildPauseButtonScene() {
        pauseButtonStage = new Stage();
        //pauseButtonStage.getViewport().setCamera(camera);

        ImageButton pauseButton = game.createIconButton(Constants.PAUSE_ICON_LOCATION,
                Constants.PAUSE_ICON_DOWN_LOCATION, new ClickListener() {
                    @Override
                    public void clicked(InputEvent event, float x, float y) {
                        gameState = GameState.PAUSE;
                        game.buttonPressedSound.play();
                        im.setProcessors(new Array<InputProcessor>(
                                new InputProcessor[] { pauseMenuStage, new GameInputProcessor() }));
                    }
                });

        Table table = new Table(game.skin);
        table.bottom().left();
        table.add(pauseButton).bottom().left();

        table.setFillParent(true);

        pauseButtonStage.addActor(table);
    }

    private void buildPauseMenuScene() {
        pauseMenuStage = new Stage();
        //pauseMenuStage.getViewport().setCamera(camera);

        ImageButton playButtonWrapper = game.createIconButton(Constants.PLAY_ICON_LOCATION,
                Constants.PLAY_ICON_DOWN_LOCATION, new ClickListener() {
                    @Override
                    public void clicked(InputEvent event, float x, float y) {
                        gameState = GameState.RUN;
                        game.buttonPressedSound.play();
                        im.setProcessors(new Array<InputProcessor>(
                                new InputProcessor[] { pauseButtonStage, new GameInputProcessor() }));
                    }
                });

        ImageButton stopButtonWrapper = game.createIconButton(Constants.STOP_ICON_LOCATION,
                Constants.STOP_ICON_DOWN_LOCATION, new ClickListener() {
                    @Override
                    public void clicked(InputEvent event, float x, float y) {
                        game.buttonPressedSound.play();
                        gameState = GameState.OVER;
                    }
                });

        final Label.LabelStyle pauseLabelStyle = new Label.LabelStyle(game.largeFont,
                new Color(1.0f, 1.0f, 1.0f, 0.7f));
        final Label pauseLabel = new Label(PAUSE_TEXT, pauseLabelStyle);

        Table buttonTable = new Table();
        buttonTable.add(playButtonWrapper).left();
        buttonTable.add(stopButtonWrapper).expandX().right();

        Table table = new Table(game.skin);
        table.add(pauseLabel).center().spaceBottom(25.0f).row();
        table.add(buttonTable).fill();

        table.setFillParent(true);

        pauseMenuStage.addActor(table);
    }

    private void buildGameOverStage() {
        gameOverStage = new Stage();
        //gameOverStage.getViewport().setCamera(camera);

        final Label.LabelStyle largeLabelStyle = new Label.LabelStyle(game.largeFont,
                new Color(1.0f, 0.0f, 0.0f, 0.7f));
        final Label.LabelStyle smallLabelStyle = new Label.LabelStyle(game.smallFont,
                new Color(1.0f, 1.0f, 1.0f, 0.7f));

        final Label gameOverLabel = new Label("Game Over", largeLabelStyle);
        final Label continueLabel = new Label(" ", smallLabelStyle);

        Table table = new Table(game.skin);
        table.add(gameOverLabel).center().row();
        table.add(continueLabel).center().top().row();

        table.setFillParent(true);

        gameOverStage.addActor(table);
    }

    private void buildGameOverContinueStage() {
        gameOverStage = new Stage();
        //gameOverStage.getViewport().setCamera(camera);

        final Label.LabelStyle largeLabelStyle = new Label.LabelStyle(game.largeFont,
                new Color(0.7f, 0.0f, 0.0f, 0.7f));
        final Label.LabelStyle smallLabelStyle = new Label.LabelStyle(game.smallFont,
                new Color(1.0f, 1.0f, 1.0f, 0.7f));

        final Label gameOverLabel = new Label("Game Over", largeLabelStyle);
        final Label continueLabel = new Label("tap to continue", smallLabelStyle);

        Table table = new Table(game.skin);
        table.add(gameOverLabel).center().row();
        table.add(continueLabel).center().top().row();

        table.setFillParent(true);

        gameOverStage.addActor(table);
    }

    @Override
    public void render(float delta) {
        switch (gameState) {
        case INTRO:
            updateIntro();
            renderGraphics();
            break;
        case RUN:
            update(delta);
            renderGraphics();
            break;
        case PAUSE:
            // throw up a pause menu
            renderGraphics();
            break;
        case OVER:
            gameOverUpdate(delta);
            renderGraphics();
            break;
        }
    }

    private void update(float delta) {
        game.gameTime += delta;
        updatePokables(delta);
        clearExpiredCircles();

        modeUpdate(delta);

        stepSpawn(delta);
    }

    private void updateIntro() {
        update(0.0f);

        if (Gdx.input.isTouched()) {
            gameState = GameState.RUN;
            im.setProcessors(
                    new Array<InputProcessor>(new InputProcessor[] { pauseButtonStage, new GameInputProcessor() }));
        }
    }

    private void clearExpiredCircles() {
        Iterator<Pokable> it = pokables.iterator();
        while (it.hasNext()) {
            Pokable pokable = it.next();

            if (pokable.getState() == Pokable.PokableState.DESTRUCT) {
                it.remove();
            }
            if (pokable.getState() == Pokable.PokableState.EXPIRED) {
                it.remove();
                pokablesExpired++;
            }
        }
    }

    private void modeUpdate(float delta) {
        switch (game.mode) {
        case PLAY:
            playUpdate(delta);
            break;
        case PRACTICE:
            practiceUpdate(delta);
            break;
        case NONE:
            gameState = GameState.OVER;
            break;
        }
    }

    private void playUpdate(float delta) {
        if (pokablesExpired > 0) {
            gameState = GameState.OVER;
        }
    }

    private void practiceUpdate(float delta) {
        // Nothing to update, just here to look pretty for now.
    }

    private void updatePokables(float delta) {
        for (Pokable pokable : pokables) {
            pokable.update(delta);
        }
    }

    private void renderGraphics() {
        renderSetup();

        game.shape.begin(ShapeRenderer.ShapeType.Filled);
        renderPokables();
        game.shape.end();

        game.batch.begin();
        renderText();
        if (gameState != GameState.RUN)
            game.batch.draw(game.pauseBG, 0.0f, 0.0f, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); // dims the screen
        game.batch.end();

        game.batch.begin();
        switch (gameState) {
        case INTRO:
            introStage.draw();
            break;
        case RUN:
            pauseButtonStage.draw();
            break;
        case PAUSE:
            pauseMenuStage.draw();
            break;
        case OVER:
            gameOverStage.draw();
            break;
        }
        game.batch.end();
    }

    private void renderPokables() {
        for (Pokable pokable : pokables) {
            pokable.render();
        }
    }

    private void renderText() {
        Color colorBackup = game.smallFont.getColor();
        game.smallFont.setColor(1.0f, 1.0f, 1.0f, 0.5f);

        switch (game.mode) {
        case PLAY:
            renderTimeText();
            break;
        case PRACTICE:
            renderTimeText();
            renderHitsText();
            break;
        }

        game.smallFont.setColor(colorBackup);
    }

    private void renderHitsText() {
        String pointsText = game.hits + "p";

        float pointsTextWidth = game.smallFont.getBounds(pointsText).width;
        float pointsTextHeight = game.smallFont.getBounds(pointsText).height;

        float centerPointsText = Gdx.graphics.getWidth() / 2.0f - pointsTextWidth / 2.0f;
        float bottomPointsText = (Constants.BUTTON_LENGTH * game.ppm) / 2.0f - pointsTextHeight / 2.0f;

        game.smallFont.draw(game.batch, pointsText, centerPointsText, bottomPointsText);
    }

    private void renderTimeText() {
        String formattedTime = String.format("%.2fs", game.gameTime);

        float timeTextWidth = game.smallFont.getBounds(formattedTime).width;
        float timeTextHeight = game.smallFont.getBounds(formattedTime).height;

        float centerTimeText = game.worldWidth * game.ppm / 2.0f - timeTextWidth / 2.0f;
        float topTimeText = game.worldHeight * game.ppm - timeTextHeight / 2.0f;

        game.smallFont.draw(game.batch, formattedTime, centerTimeText, topTimeText);
    }

    private void renderSetup() {
        Gdx.gl20.glClearColor(Constants.BG_COLOR.r, Constants.BG_COLOR.g, Constants.BG_COLOR.b, 1);
        Gdx.gl20.glClear(GL20.GL_COLOR_BUFFER_BIT);

        // tell the camera to update its matrices.
        //camera.update();
        // tell the SpriteBatch to render in the
        // coordinate system specified by the camera.
        //game.batch.setProjectionMatrix(camera.combined);

        //game.batch.enableBlending();
        //game.batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
    }

    private void forceSpawn() {
        newSpawn();
        nextSpawnTime = 0.0f;
        stepSpawn(0.0f);
    }

    private void stepSpawn(float delta) {
        spawnWaitTime += delta;
        if (spawnWaitTime >= nextSpawnTime) {
            for (int i = 0; i < spawnCount; i++)
                spawnPokable();
            newSpawn();
        }
    }

    private void newSpawn() {
        spawnWaitTime = 0.0f;
        nextSpawnTime = MathUtils.random(Constants.MINIMUM_SPAWN_TIME, Constants.MAXIMUM_SPAWN_TIME);
        spawnCount = MathUtils.random(Constants.MINIMUM_SPAWN_COUNT, Constants.MAXIMUM_SPAWN_COUNT);
    }

    private void spawnPokable() {
        Pokable pokable;

        int attempts = 10;
        while (attempts > 0) {
            pokable = generateRandomPokable();

            if (!collidesWithPokables(pokable)) {
                pokables.add(pokable);
                attempts = 0;
            }

            attempts--;
        }
    }

    private Pokable generateRandomPokable() {
        Circle newCircle = new Circle();
        newCircle.radius = MathUtils.random(Constants.MINIMUM_CIRCLE_RADIUS, Constants.MAXIMUM_CIRCLE_RADIUS);
        newCircle.x = MathUtils.random(0.0f + newCircle.radius, game.worldWidth - newCircle.radius);
        newCircle.y = MathUtils.random(Constants.BUTTON_LENGTH + newCircle.radius,
                game.worldHeight - newCircle.radius);

        /*Color newColor = new Color();
        newColor.r = MathUtils.random(0.5f, 1.0f);
        newColor.g = MathUtils.random(0.5f, 1.0f);
        newColor.b = MathUtils.random(0.5f, 1.0f);
        newColor.a = 1.0f;*/

        HSL newHSL = new HSL();
        newHSL.h = MathUtils.random(0.0f, 1.0f);
        newHSL.s = 0.7f;
        newHSL.l = 0.7f;

        Color newColor = newHSL.toRGB();

        float newLifeSpan = MathUtils.random(Constants.MINIMUM_LIFE_TIME, Constants.MAXIMUM_LIFE_TIME);

        return new Pokable(game, newCircle, newColor, newLifeSpan);
    }

    private boolean collidesWithPokables(Pokable testPokable) {
        for (Pokable otherPokable : pokables) {
            if (otherPokable.collidesWith(testPokable)) {
                return true;
            }
        }
        return false;
    }

    private void gameOverUpdate(float delta) {
        // Some last minute updates
        gameEndWait += delta;

        if (gameEndWait >= TAP_TO_END_START_TIME && !tapToEnd) {
            buildGameOverContinueStage();
            tapToEnd = true;
        }

        if (tapToEnd && Gdx.input.isTouched() || gameEndWait >= GAME_END_PAUSE)
            showResultsScreen();
    }

    private void showResultsScreen() {
        switch (game.mode) {
        case PLAY:
            game.setScreen(new PlayResultsScreen(game));
            this.dispose();
            break;
        case PRACTICE:
            game.setScreen(new PracticeResultsScreen(game));
            this.dispose();
            break;
        }
    }

    @Override
    public void resize(int width, int height) {
        game.resize(width, height);
        disposeStages();
        buildStages();
    }

    @Override
    public void show() {
        // start the playback of the background music
        // when the screen is shown
        //music.play();
        // There is no music good enough for poke! HA!
        // Except the music you choose, of course :)
        if (gameState == GameState.PAUSE) {
            setGameState(GameState.RUN);
        }
    }

    @Override
    public void hide() {
        if (gameState == GameState.RUN) {
            setGameState(GameState.PAUSE);
        }
    }

    @Override
    public void pause() {
        if (gameState == GameState.RUN) {
            setGameState(GameState.PAUSE);
        }
    }

    @Override
    public void resume() {
        if (gameState == GameState.PAUSE) {
            setGameState(GameState.RUN);
        }
    }

    @Override
    public void dispose() {
        disposeStages();
    }

    public class GameInputProcessor implements InputProcessor {
        @Override
        public boolean keyDown(int keycode) {
            return false;
        }

        @Override
        public boolean keyUp(int keycode) {
            return false;
        }

        @Override
        public boolean keyTyped(char character) {
            return false;
        }

        @Override
        public boolean touchDown(int pixelX, int pixelY, int pointer, int button) {
            if (gameState == GameState.RUN || gameState == GameState.INTRO) {
                float worldX = (float) pixelX / game.ppm;
                float worldY = game.worldHeight - (float) pixelY / game.ppm;

                boolean miss = true;

                for (Pokable pokable : pokables) {
                    if (pokable.getHittableCircle().contains(worldX, worldY)) {
                        game.hits++;

                        pokable.setState(Pokable.PokableState.HIT);
                        miss = false;

                        float radiusPercent = pokable.getVisualRadius() / Constants.MAXIMUM_CIRCLE_RADIUS;
                        float volume = Constants.DEFAULT_VOLUME;
                        float pitch = Constants.RADIUS_PERCENT_TO_PITCH * radiusPercent
                                + Constants.PITCH_ADJUSTMENT;
                        float pan = Constants.DEFAULT_PAN;
                        game.hitSound.play(volume, pitch, pan);
                    }
                }

                if (miss) {
                    game.missSound.play();
                    game.missed++;
                }
            }

            return false;
        }

        @Override
        public boolean touchUp(int x, int y, int pointer, int button) {
            return false;
        }

        @Override
        public boolean touchDragged(int x, int y, int pointer) {
            return false;
        }

        @Override
        public boolean mouseMoved(int x, int y) {
            return false;
        }

        @Override
        public boolean scrolled(int amount) {
            return false;
        }
    }
}