Back to project page candy-drop.
The source code is released under:
Copyright (c) 2014, Gregory Martin All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: ...
If you think the Android project candy-drop listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package com.gregfmartin.facetapper.screens; // w w w . j a va 2s. co m import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.InputListener; import com.badlogic.gdx.scenes.scene2d.ui.Image; import com.badlogic.gdx.scenes.scene2d.ui.Label; import com.badlogic.gdx.scenes.scene2d.ui.Table; import com.badlogic.gdx.scenes.scene2d.ui.TextButton; import com.gregfmartin.facetapper.GameCore; import com.gregfmartin.facetapper.entities.CandyBase; import com.gregfmartin.facetapper.entities.CandyDrop; import com.gregfmartin.facetapper.entities.Screen; import com.gregfmartin.facetapper.utils.MathEngine; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * The screen where the game is actually played at. * <p/> * Each level has one objective: tap as many candies as you can before they run out. That being said, the rules are * as follows: * <p/> * 1. Each level has a finite number of candies that will be dropped. These candies will be dropped at random * intervals throughout the course of the level. * 2. Because there are a finite number of candies per level, there is a threshold of candies that the player will * need to tap in order to proceed to the next level. This threshold is determined based on the current level/ * [difficulty setting (not yet implemented)]/total number of candies that will be dropped. * 3. Everything about the creation of the candies is random but also governed by the current level/[difficult * setting (not yet implemented)]. This includes the falling speed and point value. * 4. The player will have a finite number of continues. Here, continues will be treated as "lives" so-to-speak. * The player will start with three continues. A single continue will be used if all of the candies have been * dropped and the player has failed to tap the required number of candies to proceed to the next level. When this * happens, the level will restart which will reset all of the counters for the level save the static level * requirements (total number of candies/tap requirement). The score will be reset to the value that it was at * before the start of the level. * 5. If the player manages to tap enough candies to proceed to the next level, a victory animation will play, wait * for them to tap the screen to proceed to the next level, and will continue on from there. * 6. Level progression will gradually increase the difficulty. Because the values for the candies are capped, the * difficulty can be compensated for in different ways. * * @author Gregory Martin */ public class ArenaScreen extends Screen { /** * Do we update the information in the labels? */ static public boolean STATUS_TEXT_DIRTY = true; // The current drop count private int mDropCounter = 0; // The number of "dead" candies private short mDeadCounter = 0; /* * Pool of candies for the level. The size of this pool is unknown since the size would be determined * by the current level. */ private List<CandyBase> mCandyQueue; private Iterator<CandyBase> mCandyQueueIterator; // The current state of the game private ArenaState mGamePlayState = ArenaState.PREP; // The root container for the UI components that will be used here private Table mPlayUiTable; // The root container for the UI components that will be used in the Lose portion private Table mLoseUiTable; // The root container for the UI components that will be used in the Win portion private Table mWinUiTable; // Label showing the player's current score (top-left) and its style private Label mPlayerScoreLabel; private Label.LabelStyle mPlayerScoreLabelStyle; // Label showing the candies goal for the level (threshold/total) (top-right) and its style private Label mLevelCandiesGoalLabel; private Label.LabelStyle mLevelCandiesGoalLabelStyle; // Label showing the current tapped candies total (bottom-right) and its style private Label mCurrentTappedCandiesLabel; private Label.LabelStyle mCurrentTappedCandiesLabelStyle; // UI components that will be used in the Win table private Image mWinLogo; private TextButton mButtonNextLevel; private TextButton.TextButtonStyle mButtonNextLevelStyle; // UI components that will be used in the Lose table private Image mLoseLogo; private TextButton mButtonContinue; private TextButton.TextButtonStyle mButtonContinueStyle; private Label mContinuesLabel; private Label.LabelStyle mContinuesLabelStyle; // Used to generate BitmapFonts private FreeTypeFontGenerator mFontGen; public ArenaScreen(GameCore gameCore) { super(gameCore); } @Override public void castingCall() { super.castingCall(); // The font generator is needed by the UI - instantiate it first mFontGen = new FreeTypeFontGenerator(Gdx.files.internal("fonts/sawasdee/sawasdee.ttf")); // All of the styles that get applied to elements mPlayerScoreLabelStyle = new Label.LabelStyle(mFontGen.generateFont(30), Color.YELLOW); mLevelCandiesGoalLabelStyle = new Label.LabelStyle(mFontGen.generateFont(30), Color.BLUE); mCurrentTappedCandiesLabelStyle = new Label.LabelStyle(mFontGen.generateFont(30), Color.GREEN); mButtonNextLevelStyle = new TextButton.TextButtonStyle(null, null, null, mFontGen.generateFont(40)); mButtonNextLevelStyle.fontColor = Color.BLACK; mButtonContinueStyle = new TextButton.TextButtonStyle(null, null, null, mFontGen.generateFont(40)); mButtonContinueStyle.fontColor = Color.BLACK; mContinuesLabelStyle = new Label.LabelStyle(mFontGen.generateFont(30), Color.RED); // All of the UI elements mPlayUiTable = new Table(); mWinUiTable = new Table(); mLoseUiTable = new Table(); mPlayerScoreLabel = new Label("Score: ", mPlayerScoreLabelStyle); mLevelCandiesGoalLabel = new Label(String.format("%s / %s", String.valueOf(MathEngine.getInstance().getPlayerTapThreshold()), String.valueOf(MathEngine.getInstance().getTotalCandies())), mLevelCandiesGoalLabelStyle); mCurrentTappedCandiesLabel = new Label("Tapped: ", mCurrentTappedCandiesLabelStyle); mWinLogo = new Image(new Texture(Gdx.files.internal("images/ui/logos/win.png"))); mButtonNextLevel = new TextButton("Next Level", mButtonNextLevelStyle); mLoseLogo = new Image(new Texture(Gdx.files.internal("images/ui/logos/lose.png"))); mButtonContinue = new TextButton("Continue?", mButtonContinueStyle); mContinuesLabel = new Label("Continues: ", mContinuesLabelStyle); // Ensure that the Stage acts as the input processor Gdx.input.setInputProcessor(mStage); } @Override public void rehearsal() { super.rehearsal(); resetPlayUi(); } @Override public void logic(float delta) { super.logic(delta); // Check the current state of the level if(mGamePlayState == ArenaState.PREP) { mDropCounter = 0; resetPlayUi(); // Get the MathEngine to prepare the level MathEngine.getInstance().prepLevel(); // Allocate the candy pool prepareCandyPool(); // Prep work should be complete, transition to the next phase mGamePlayState = ArenaState.PLAY; } else if(mGamePlayState == ArenaState.PLAY) { // Update the contents of the UI if(STATUS_TEXT_DIRTY) { updatePlayStateUi(); } // Update necessary things in the MathEngine MathEngine.getInstance().update(); // Check to see if we can drop a candy if(mDropCounter != MathEngine.getInstance().getTotalCandies()) { if(MathEngine.getInstance().canDropCandy()) { if(mDropCounter < MathEngine.getInstance().getTotalCandies()) { mStage.addActor(mCandyQueue.get(mDropCounter)); mDropCounter++; } } } boolean areCandiesDead = areAllCandiesDead(); // Check for potential cases where the game state would change if(areCandiesDead && MathEngine.getInstance().hasPlayerCrossedTapThreshold()) { mGamePlayState = ArenaState.WIN; } if(areCandiesDead && !MathEngine.getInstance().hasPlayerCrossedTapThreshold()) { mGamePlayState = ArenaState.LOSE; } } else if(mGamePlayState == ArenaState.LOSE) { resetLoseUi(); } else if(mGamePlayState == ArenaState.WIN) { resetWinUi(); } else if(mGamePlayState == ArenaState.RESET) { mDropCounter = 0; resetPlayUi(); // Get the MathEngine to reset the level MathEngine.getInstance().resetLevel(); // Allocate the candy pool prepareCandyPool(); // Prep work should be complete, transition to the next phase mGamePlayState = ArenaState.PLAY; } } @Override public void dispose() { super.dispose(); mFontGen.dispose(); mCandyQueue.clear(); } /** * Change the UI for the Play state */ private void resetPlayUi() { // Clear all of the Actors from the Stage mStage.clear(); // This bubbles down to the Group // Reconfigure the table mPlayUiTable.clear(); mPlayUiTable.setFillParent(true); mPlayUiTable.add(mPlayerScoreLabel).top().left(); mPlayUiTable.add(mLevelCandiesGoalLabel).top().right(); mPlayUiTable.row().expand().colspan(2); mPlayUiTable.add(mCurrentTappedCandiesLabel).bottom().right(); mStage.addActor(mPlayUiTable); } /** * Change the UI for the Win state */ private void resetWinUi() { // Clear all of the Actors from the stage mStage.clear(); // Reconfigure the table mWinUiTable.clear(); mWinUiTable.setFillParent(true); mWinUiTable.add(mWinLogo).top().center(); mWinUiTable.row().expand(); mWinUiTable.add(mButtonNextLevel).center(); mButtonNextLevel.addListener(new InputListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { // Move to the next level mGamePlayState = ArenaState.PREP; return false; } }); mStage.addActor(mWinUiTable); } /** * Change the UI for the Lose state */ private void resetLoseUi() { // Clear all of the Actors from the Stage mStage.clear(); // Reconfigure the table mLoseUiTable.clear(); mLoseUiTable.setFillParent(true); mLoseUiTable.add(mLoseLogo).top().center().colspan(2); mLoseUiTable.row().expand(); mLoseUiTable.add(mContinuesLabel).bottom().left(); mLoseUiTable.add(mButtonContinue).bottom().right(); // TEST - Allow the user to continue regardless of their continue count mButtonContinue.addListener(new InputListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { // Reset the level mGamePlayState = ArenaState.RESET; return false; } }); mStage.addActor(mLoseUiTable); } /** * Allocates (or re-allocates) the pool that's used for the Candies for this level. This function will be called * when the Game State is PREP so it will only be called once before moving onto the PLAY state. It will also * be called if the player loses the level and the level needs reset. */ private void prepareCandyPool() { mCandyQueue = new ArrayList<>(MathEngine.getInstance().getTotalCandies()); /* * Ideally, this block of code will be further broken down to insert candies and other entities based on * mathematical calculations generated from the MathEngine. */ for(int i = 0; i < MathEngine.getInstance().getTotalCandies(); i++) { mCandyQueue.add(CandyDrop.obtain(MathEngine.getInstance().getLevel())); } } /** * Updates the information in the labels that are displayed on the screen during the play state. */ private void updatePlayStateUi() { mPlayerScoreLabel.setText(String.format("Score: %s", String.valueOf(MathEngine.getInstance().getScore()))); mLevelCandiesGoalLabel.setText(String.format("%s / %s", String.valueOf(MathEngine.getInstance().getPlayerTapThreshold()), String.valueOf(MathEngine.getInstance().getTotalCandies()))); mCurrentTappedCandiesLabel.setText(String.format("Tapped: %s", String.valueOf(MathEngine.getInstance().getNumTappedCandies()))); STATUS_TEXT_DIRTY = false; } /** * Run a quick loop through the candy pool to determine if all of them are marked as dead * * @return True if all candies have been marked as dead; False otherwise */ private boolean areAllCandiesDead() { /* for(int i = 0; i < mCandyQueue.size(); i++) { if(!mCandyQueue.get(i).isAlive()) { mDeadCounter++; } } */ mDeadCounter = 0; mCandyQueueIterator = mCandyQueue.iterator(); while(mCandyQueueIterator.hasNext()) { if(!mCandyQueueIterator.next().isAlive()) { // mCandyQueueIterator.remove(); mDeadCounter++; } } if(MathEngine.getInstance().getTotalCandies() == mDeadCounter) { return true; } else { return false; } } // The current state of the game play private enum ArenaState { PREP, PLAY, WIN, LOSE, RESET } }