Hello again! We are going to take our icicle mayhem game we just made (our whatever version you happened to have created!) and add some more advanced stuff to it, such as a main menu, achievements, and animations. We are going to build right off of the last post, so please visit that if you wish to get your game up to date for this tutorial.
Step 1:
Adding a Main Menu! This is like the trunk to the tree of life, this is where we can branch off to many things, such as achievements, easy mode, hard mode, settings, high scores, etc, etc. To start we are going to create two java classes called MainMenuScreen and AchievementScreen, and fill them with the bare bones needed to make a “screen” work in libgdx, I’m also including the same background in this as the GameScreen as it will work well for our main menu. (For achievementScreen, just change the word MainMenuScreen to AchievementScreen in the two locations it appears).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
|
public class MainMenuScreen implements Screen,InputProcessor { //Creates a reference to hold our "passed" game. private MyGdxGame game;
//Defining the camera to be used private OrthographicCamera camera;
//Texture for the cave background private Texture caveBackground = new Texture("cavebackground.png");
public MainMenuScreen(MyGdxGame game) { //assigns the game we passed to the placeholder this.game = game; //creates the camera camera = new OrthographicCamera(); //Sets the screen to be 800 width by 480 height camera.setToOrtho(false, 480, 800); //very important, lets us use the Input methods! Gdx.input.setInputProcessor(this); } @Override public void render(float deltaTime){ //Sets the color to be applied after clearing the screen (R,G,B,A) Gdx.gl.glClearColor(0,0,255,1); //Clears the screen Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); //updates camera view camera.update(); //makes the camera fit onto the available screen game.gameBatch.setProjectionMatrix(camera.combined); //Starts gameBatch for drawing game.gameBatch.begin(); //All drawing needs to be done here //Draws the Cave Background starting at x = 0, y = 0 game.gameBatch.draw(caveBackground,0,0); //Ends the gameBatch after drawing game.gameBatch.end(); }
//This method gets called when a key is pressed down public boolean keyDown (int keycode) { return false; }
//happens upon lifting finger off of key public boolean keyUp (int keycode) { return false; }
//called when the person lifts their finger off of the screen public boolean touchUp (int x, int y, int pointer, int button) { return false; }
public boolean touchDown(int screenX, int screenY, int pointer, int button) { return false; }
public boolean keyTyped (char character) { return false; }
public boolean touchDragged (int x, int y, int pointer) { return false; }
public boolean mouseMoved (int x, int y) { return false; }
public boolean scrolled (int amount) { return false; }
@Override public void resize(int width, int height){
}
@Override public void show(){
}
@Override public void hide(){
}
@Override public void pause(){
}
@Override public void resume(){
}
@Override public void dispose(){
} }
|
Now, we need to actually open this screen first, before the GameScreen, to do this, go to the MyGdxGame.Java class and edit the portion where we “setScreen” in the create function to the following:
1 2
|
//this will set the screen to our next class "MainMenuScreen" this.setScreen(new MainMenuScreen(this));
|
This will now set our first screen of the game to our MainMenuScreen. Now we need to add some options/buttons to the screen. One drawback right now is that the buttons I will be creating will be graphically-tactile free.. as in, once your finger touches the button, it does not visually depress. We can make an animation play when the button is pressed, and maybe ill include that latter but for now, we will keep them static.
mainmenuscreen.png:
Now we are going to create a new variable in the MainMenuScreen to use our Texture:
1 2
|
//Texture for button Overlay private Texture menuTexture = new Texture("mainmenuscreen.png");
|
Adding our texture to draw in the render section after we draw the cavebackground:
1 2
|
//draws our button overlay game.gameBatch.draw(menuTexture,0,0);
|
We are now going to create two rectangles the same shape as the button to use, we define them as follows:
1 2 3 4
|
//Rectangle for our play button private Rectangle playButton = new Rectangle(70,505,355,73); //Rectangle for our achievement button private Rectangle achievementButton = new Rectangle(74,340,355,73);
|
Now that we have the buttons on screen, we need to be able to press them. Thus we will go into the touchDown function in MainMenuScreen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
//function is called upon the screen being pressed public boolean touchDown(int screenX, int screenY, int pointer, int button) { //this changes the screenX and screenY from the pixel number on the phone, to the pixel //number associated with our game. float xPos = (float)screenX/Gdx.graphics.getWidth()*480; float yPos = 800-(float)screenY/Gdx.graphics.getHeight()*800;
//if click was in our play button if(playButton.contains(xPos,yPos)){ //Tells MyGdxGame.java to set the screen now to our GameScreen game.setScreen(new GameScreen(game)); }
//if click was in our achievement screen if(achievementButton.contains(xPos,yPos)){ //Tells MyGdxGame.java to set the screen now to our AchievementScreen game.setScreen(new AchievementScreen(game)); } return false; }
|
Now we are able to enter the AchievementScreen and GameScreen, but we are lacking the ability to be able to go back to the main menu. Thus we will have to rework some of the GameScreen’s end game code to allow the player to either replay the level or return to the main menu to look at achievements or other future add-ons. To start, I edited the old gameOver.png to include our buttons.
gameOver.png:
We will once again define two new rectangles in our GameScreen class for the buttons:
1 2 3 4
|
//Rectangle for our play button private Rectangle replayButton = new Rectangle(62,425,340,62); //Rectangle for our achievement button private Rectangle mainMenuButton = new Rectangle(62,264,340,62);
|
Then we are going to edit the checkScreenPress() function code to down to just directions:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
//checks if the screen is pressed public void checkScreenPress(){ //if screen is pressed if(Gdx.input.isTouched()) { //gets the location of the press, and determines which half of the screen was pressed if ((float) Gdx.input.getX() / Gdx.graphics.getWidth() <= .5) { //if left half of screen, go left character.goLeft(); } else { //if not on the left half of screen, it must be going right character.goRight(); } } }
|
Then we are going to dump the gameOver handling and button selection into the touchDown function as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
//called when screen is touched public boolean touchDown(int screenX, int screenY, int pointer, int button) { //only work when game is Over if(gameOver){ //this changes the screenX and screenY from the pixel number on the phone, to the pixel //number associated with our game. float xPos = (float)screenX/Gdx.graphics.getWidth()*480; float yPos = 800-(float)screenY/Gdx.graphics.getHeight()*800;
//if click was in our replay button if(replayButton.contains(xPos,yPos)){ //resets char x position character.x = 240; //clears icicles by creating new array icicleArray = new Array<Icicle>(); //resets vars gameOver = false; //if score was higher than the high score if(score > highScore){ //write value of score to our file file.writeString(String.valueOf(score),false); //set our in-game high score to our new score highScore = score; } //resets score score = 0;
} //if click was in our main menu button if(mainMenuButton.contains(xPos,yPos)){ //Tells MyGdxGame.java to set the screen now to our MainMenuScreen game.setScreen(new MainMenuScreen(game)); } }
return false; }
|
And then we have fully functional menu menu and gameScreen buttons to travel back and forth:
Then the last final step is to add a back arrow button to the achievement screen. We are not going to use the button style in the previous two screens because we would prefer to have pictures of achievements displayed on the screen. Here is the back arrow button i quickly designed:
backarrow.png:
Now we will add it as a texture to our AchievementScreen and create a rectangle for it:
1 2 3 4
|
//Texture for the back arrow private Texture backArrow = new Texture("backarrow.png"); //Rectangle for our back arrow private Rectangle backArrowButton = new Rectangle(0,800-backArrow.getHeight(),backArrow.getWidth(),backArrow.getHeight());
|
Draw it in our render function after cavebackground:
1 2
|
//draws our back arrow game.gameBatch.draw(backArrow,0,800-backArrow.getHeight());
|
And then finally adding code to the touchDown function to work when the screen is touched:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
//called when screen is touched public boolean touchDown(int screenX, int screenY, int pointer, int button) { //this changes the screenX and screenY from the pixel number on the phone, to the pixel //number associated with our game. float xPos = (float)screenX/Gdx.graphics.getWidth()*480; float yPos = 800-(float)screenY/Gdx.graphics.getHeight()*800;
//if click was in our play button if(backArrowButton.contains(xPos,yPos)){ //Tells MyGdxGame.java to set the screen now to our GameScreen game.setScreen(new MainMenuScreen(game)); } return false; }
|
And bam! we have all of the screens functional to go back and forth with. We will be stopping here with just these two screens, but you can easily envision how you would be able to add more screens later:
Step 2:
Creating achievements! The main aspect of achievements that needs to be worked in, is in the fact that you must save the achievements much like you do for the high score, so that we are able to save which achievements have been completed. So to help encompass that for us, we will be creating an AchievementManager.Java class as follows that stores our achievements, loads achievements, and saves achievements:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
|
public class AchievementManager {
//creates a reference to a local file called achievements FileHandle file = Gdx.files.local("achievements.txt");
//list of variables in order in the file //achievement1 = score 10 points public boolean achievement1 = false; //achievement2 = score 20 points public boolean achievement2 = false; //achievement3.. you see where this is going 30 points.. public boolean achievement3 = false; public boolean achievement4 = false; public boolean achievement5 = false;
public AchievementManager(){ //loads achievements loadAchievements(); }
//this loads the achievements public void loadAchievements(){ //if file exists if(file.exists()) { try { //creates a buffered reader BufferedReader bufferedReader = file.reader(254); //reads each of our 5 lines as booleans achievement1 = Boolean.parseBoolean(bufferedReader.readLine()); achievement2 = Boolean.parseBoolean(bufferedReader.readLine()); achievement3 = Boolean.parseBoolean(bufferedReader.readLine()); achievement4 = Boolean.parseBoolean(bufferedReader.readLine()); achievement5 = Boolean.parseBoolean(bufferedReader.readLine()); //closes the buffered reader bufferedReader.close(); } catch (IOException e){ } } }
//updates achievements and writes files public void update(int score){
//I am sure there is better ways than doing this, but anyways.. checking if you //beat any achievements then setting them true. if(score > 10){ if(score > 20){ if(score > 30){ if(score > 40){ if(score > 50){ achievement5 = true; } achievement4 = true; } achievement3 = true; } achievement2 = true; } achievement1 = true; }
//writes file file.writeString( //writes each achievement with including a line break between each String.valueOf(achievement1) + "n" + String.valueOf(achievement2) + "n" + String.valueOf(achievement3) + "n" + String.valueOf(achievement4) + "n" + String.valueOf(achievement5) ,false); }
}
|
Now that we have a manager to store and load our achievements, lets improve the AchievementScreen.Java class now so that we can display the achievements. To showcase the achievements, we will be using a very simple 5 static achievement boxes that we can overlay onto the screen. The textures are as follows:
achievementlayer1.png
achievementlayer2.png:
redX.png:
So how this is going to work, is that we will draw layer 1, then redX’s over the achievements that have not been unlocked, then draw layer 2. So to start, here are the new variables that we need to define in AchievementScreen:
1 2 3 4 5 6 7
|
//creates a achievement manager object private AchievementManager achievementManager = new AchievementManager();
//creates textures for achievement displays private Texture achievementLayer1 = new Texture("achievementlayer1.png"); private Texture achievementLayer2 = new Texture("achievementlayer2.png"); private Texture redX = new Texture("redX.png");
|
Then we will need to draw them, the render method and function call needed for this are both shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
@Override public void render(float deltaTime){ //Sets the color to be applied after clearing the screen (R,G,B,A) Gdx.gl.glClearColor(0,0,255,1); //Clears the screen Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); //updates camera view camera.update(); //makes the camera fit onto the available screen game.gameBatch.setProjectionMatrix(camera.combined); //Starts gameBatch for drawing game.gameBatch.begin(); //All drawing needs to be done here //Draws the Cave Background starting at x = 0, y = 0 game.gameBatch.draw(caveBackground,0,0); //draws our back arrow game.gameBatch.draw(backArrow,0,800-backArrow.getHeight()); //draws the achievements drawAchievements(); //Ends the gameBatch after drawing game.gameBatch.end(); }
public void drawAchievements(){ game.gameBatch.draw(achievementLayer1,0,0); if(!achievementManager.achievement1){ game.gameBatch.draw(redX,52,459); } if(!achievementManager.achievement2){ game.gameBatch.draw(redX,185,459); } if(!achievementManager.achievement3){ game.gameBatch.draw(redX,322,459); } if(!achievementManager.achievement4){ game.gameBatch.draw(redX,116,333); } if(!achievementManager.achievement5){ game.gameBatch.draw(redX,253,333); } game.gameBatch.draw(achievementLayer2,0,0); }
|
We then also have to have a way to update the achievements during game play. Thus, in GameScreen we will need to define a new AchievementManager as so:
1 2
|
//creates an achievement manager private AchievementManager achievementManager = new AchievementManager();
|
and finally need to do some re-coding of the touchDown() method in game screen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
|
//called when screen is touched public boolean touchDown(int screenX, int screenY, int pointer, int button) { //only work when game is Over if(gameOver){ //this changes the screenX and screenY from the pixel number on the phone, to the pixel //number associated with our game. float xPos = (float)screenX/Gdx.graphics.getWidth()*480; float yPos = 800-(float)screenY/Gdx.graphics.getHeight()*800;
//if click was in our replay button if(replayButton.contains(xPos,yPos)){ //resets char x position character.x = 240; //clears icicles by creating new array icicleArray = new Array<Icicle>(); //resets vars gameOver = false; //if score was higher than the high score if(score > highScore){ //write value of score to our file file.writeString(String.valueOf(score),false); //set our in-game high score to our new score highScore = score; } //updates achievements achievementManager.update(score); //resets score score = 0; } //if click was in our main menu button if(mainMenuButton.contains(xPos,yPos)){ //updates achievements achievementManager.update(score); //Tells MyGdxGame.java to set the screen now to our MainMenuScreen game.setScreen(new MainMenuScreen(game)); //if score was higher than the high score if(score > highScore){ //write value of score to our file file.writeString(String.valueOf(score),false); //set our in-game high score to our new score highScore = score; } } }
return false; }
|
and hopefully you are now about to achieve some of those really really really hard achievements!
Step 3:
Adding your first animation!
We will be adding an animation that is already completed with an .atlas file. To create an animation sprite sheet along with a texture mapping file, please view my blog post on this (in the making…), or other tutorials online. Here is the sprite sheet for our walking actions:
……To be continued later….