Hello again,
Day 1
I decided that I would begin with screen navigation for menus. I did this by creating a basic state system which will be in charge of displaying whichever screen is active. This consists of:
1. A 'Screen' interface containing methods such as 'onCreate()', onUpdate() and onRender() etc. This is important since each screen will use these methods.
2. A ScreenManager which holds the current screen being displayed. This has setters and getters for changing the currentScreen.
public class ScreenManager { private static Screen currentScreen; public static void setScreen(Screen screen) { if (currentScreen != null) { currentScreen.dispose(); } currentScreen = screen; currentScreen.create(); } public static Screen getCurrentScreen() { return currentScreen; } }
3. Our screens which will implement the Screen interface! For now I have only set up one screen which is called GameScreen. This is the screen which will display the main game shown in the previous post. Now, all we need to do in our MainGame class is to set our current screen in our ScreenManager to the GameScreen and the manager will take care of calling its render method! Here is what the MainGame class looks like now:
public class MainGame extends ApplicationAdapter { public static final int WIDTH = 1920; public static final int HEIGHT = 1080; public static SpriteBatch sb; public static final float STEP = 1 / 60f; private float accum; @Override public void create() { sb = new SpriteBatch(); ScreenManager.<em>setScreen</em>(new GameScreen()); } @Override public void render() { if (ScreenManager.<em>getCurrentScreen</em>() != null) { accum += Gdx.graphics.getDeltaTime(); while (accum >= STEP) { accum -= STEP; ScreenManager.<em>getCurrentScreen</em>().update(STEP); ScreenManager.<em>getCurrentScreen</em>().render(sb); } } }
As you can see, we set the screen to GameScreen in the onCreate() method and then we call the update and render methods of the current screen sing the manager. This way, we can change the current screen anywhere in the code (e.g. Game over screen when player dies), and the manager will automatically call the update and render methods for that screen.
You may notice that I have an accum and STEP variables set up in this class. These are for making sure that the game doesn't slow down but instead skips frames when things get heavy, hopefully we wont have to worry about this. ;) I also set up my WIDTH and HEIGHT variables as 1920 and 1080 (full-hd). This is something I always do since its good to have your graphics and images exported with a specific resolution in mind. These will later be accessed in other areas of our code eg. cameras and such..
Now I'm going to talk about the GameScreen. Firstly I opened up 'Tiled' and created an extremely basic tile map, exported it as 'test.tmx' and added it to my games assets. (tilesize 100x100, may change this later)
Next I needed to read this map into my code and display it in my GameScreen. The most common way to do this is by using the OrthogonalTiledMapRenderer() class included with LibGDX.
public class GameScreen implements Screen { private BoundedCamera cam, b2dCam; private World world; private TiledMap tileMap; private Box2DDebugRenderer b2dr; private int PPM = 100; private OrthogonalTiledMapRenderer tmr; @Override public void create() { //Setup camera. cam = new BoundedCamera(); cam.setToOrtho(false, MainGame.WIDTH, MainGame.HEIGHT); world = new World(new Vector2(0, 0f), true); b2dr = new Box2DDebugRenderer(); b2dCam = new BoundedCamera(); b2dCam.setToOrtho(false, MainGame.WIDTH / PPM, MainGame.HEIGHT / PPM); //Set tile map using Tiled map path. tileMap = new TmxMapLoader().load("test.tmx"); //Setup map renderer. tmr = new OrthogonalTiledMapRenderer(tileMap); } @Override public void update(float step) { b2dCam.update(); cam.update(); } @Override public void render(SpriteBatch sb) { tmr.setView(cam); tmr.render(); b2dr.render(world, b2dCam.combined); }
This is what the GameScreen looks like now. You can see that I also tooks the liberty of setting up a Box2d world and renderer for myself since I know I will be using these soon for my players physics (next post). I am also using a BoundedCamera which is a custom camera:
public class BoundedCamera extends OrthographicCamera { private float xmin; private float xmax; private float ymin; private float ymax; public BoundedCamera() { this(0, 0, 0, 0); } public BoundedCamera(float xmin, float xmax, float ymin, float ymax) { super(); setBounds(xmin, xmax, ymin, ymax); } public Vector2 unprojectCoordinates(float x, float y) { Vector3 rawtouch = new Vector3(x, y, 0); unproject(rawtouch); return new Vector2(rawtouch.x, rawtouch.y); } public void setBounds(float xmin, float xmax, float ymin, float ymax) { this.xmin = xmin; this.xmax = xmax; this.ymin = ymin; this.ymax = ymax; } public void setPosition(float x, float y) { setPosition(x, y, 0); } public void setPosition(float x, float y, float z) { position.set(x, y, z); fixBounds(); } private void fixBounds() { if(position.x < (xmin + viewportWidth / 2)) { position.x = (xmin + viewportWidth / 2); } if(position.x > (xmax - viewportWidth / 2)) { position.x = (xmax - viewportWidth / 2); } if(position.y < (ymin + viewportHeight / 2)) { position.y = (ymin + viewportHeight / 2); } if(position.y > (ymax - viewportHeight / 2)) { position.y = (ymax - viewportHeight / 2); } } }
The idea of this camera is to make sure that it stays within the boundaries of the tile map. For example, if the player is following our player and we reach an edge of the map, the camera should stop moving in that direction so we dont see past the map.
The game now looks like this:
Incredible I know. :P This is the Bottom-Left edge of the tile map I created in Tiled being rendered on the screen. In the next post I'll probably talk a lot about Box2d, walls, gravity, player physics and moving the camera.
Thanks for reading,
Yellowbyte