Java tutorial
/******************************************************************************* * Copyright 2011 See AUTHORS file. * * 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.mygdx.game; import com.badlogic.gdx.ApplicationAdapter; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input.Keys; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.Animation; import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.maps.tiled.TiledMap; import com.badlogic.gdx.maps.tiled.TiledMapTileLayer; import com.badlogic.gdx.maps.tiled.TiledMapTileLayer.Cell; import com.badlogic.gdx.maps.tiled.TmxMapLoader; import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer; import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Pool; import java.util.Arrays; import java.util.Random; /** Super Mario Brothers like very basic platformer, using a tile map build via <a href="http://www.mapeditor.org/">Tiled</a> and a * tileset and sprites by <a href="http://www.vickiwenderlich.com/">Vicky Wenderlich</a></p> * * Shows simple platformer collision detection as well as on-the-fly map modifications through destructable blocks! * @author mzechner */ public class DesertGame extends ApplicationAdapter { final public Random rand = new Random(); /** The player character, has state and state time, */ static class Wanderer { static float WIDTH; static float HEIGHT; static float MAX_VELOCITY = 10f; static float JUMP_VELOCITY = 50f; static float DAMPING = 0.87f; enum State { Standing, Walking, Jumping } enum Facing { North, East, South, West } final Vector2 position = new Vector2(); final Vector2 velocity = new Vector2(); State state = State.Walking; Facing facing = Facing.North; float stateTime = 0; //int facing = 0; boolean facesRight = true; boolean grounded = false; } private TiledMap[] map = new TiledMap[2]; private OrthogonalTiledMapRenderer renderer; private OrthographicCamera camera; private Texture wandererTexture; private Animation standNorth; private Animation standEast; private Animation standSouth; private Animation standWest; private Animation walkNorth; private Animation walkEast; private Animation walkSouth; private Animation walkWest; private Wanderer wanderer; private Pool<Rectangle> rectPool = new Pool<Rectangle>() { @Override protected Rectangle newObject() { return new Rectangle(); } }; private Array<Rectangle> tiles = new Array<Rectangle>(); //private static final float GRAVITY = -2.5f; @Override public void create() { // load the wanderer frames, split them, and assign them to Animations wandererTexture = new Texture("4LinkGreen1.png"); TextureRegion[][] regions = TextureRegion.split(wandererTexture, 24, 32); // Animations (standing, walking) in the four directions standNorth = new Animation(0, regions[0][0]); standEast = new Animation(0, regions[1][0]); standSouth = new Animation(0, regions[2][0]); standWest = new Animation(0, regions[3][0]); walkNorth = new Animation(0.07f, Arrays.copyOfRange(regions[0], 1, 11)); walkNorth.setPlayMode(Animation.PlayMode.LOOP); walkEast = new Animation(0.07f, Arrays.copyOfRange(regions[1], 1, 11)); walkEast.setPlayMode(Animation.PlayMode.LOOP); walkSouth = new Animation(0.07f, Arrays.copyOfRange(regions[2], 1, 11)); walkSouth.setPlayMode(Animation.PlayMode.LOOP); walkWest = new Animation(0.07f, Arrays.copyOfRange(regions[3], 1, 11)); walkWest.setPlayMode(Animation.PlayMode.LOOP); // figure out the width and height of the wanderer for collision // detection and rendering by converting a wanderer frames pixel // size into world units (1 unit == 16 pixels) Wanderer.WIDTH = 1 / 16f * regions[0][0].getRegionWidth(); Wanderer.HEIGHT = 1 / 16f * regions[0][0].getRegionHeight(); // load the map, set the unit scale to 1/16 (1 unit == 16 pixels) map[0] = new TmxMapLoader().load("level1.tmx"); map[1] = new TmxMapLoader().load("level2.tmx"); renderer = new OrthogonalTiledMapRenderer(map[0], 1 / 16f); // create an orthographic camera, shows us 30x20 units of the world camera = new OrthographicCamera(); camera.setToOrtho(false, 30, 20); camera.update(); // create the Wanderer we want to move around the world wanderer = new Wanderer(); wanderer.position.set(20, 20); } @Override public void resize(int width, int height) { } @Override public void render() { // clear the screen Gdx.gl.glClearColor(0.7f, 0.7f, 1.0f, 1); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); // get the delta time float deltaTime = Gdx.graphics.getDeltaTime(); // update the koala (process input, collision detection, position update) updateKoala(deltaTime); // let the camera follow the koala camera.position.x = wanderer.position.x; camera.position.y = wanderer.position.y; camera.update(); // set the tile map rendere view based on what the // camera sees and render the map renderer.setView(camera); renderer.render(); // render the koala renderKoala(deltaTime); } private Vector2 tmp = new Vector2(); private void updateKoala(float deltaTime) { if (deltaTime == 0) return; wanderer.stateTime += deltaTime; // check input and apply to velocity & state if (Gdx.input.isKeyPressed(Keys.Q)) { renderer.setMap(map[1]); } if (Gdx.input.isKeyPressed(Keys.UP) || Gdx.input.isKeyPressed(Keys.W)) { wanderer.facing = Wanderer.Facing.North; wanderer.velocity.y += Wanderer.MAX_VELOCITY / 2; } if (Gdx.input.isKeyPressed(Keys.DOWN) || Gdx.input.isKeyPressed(Keys.S)) { wanderer.facing = Wanderer.Facing.South; wanderer.velocity.y -= Wanderer.MAX_VELOCITY / 2; } if (Gdx.input.isKeyPressed(Keys.LEFT) || Gdx.input.isKeyPressed(Keys.A)) { wanderer.facing = Wanderer.Facing.West; wanderer.velocity.x = -Wanderer.MAX_VELOCITY; //if (koala.grounded) koala.state = Koala.State.Walking; //koala.facesRight = false; } if (Gdx.input.isKeyPressed(Keys.RIGHT) || Gdx.input.isKeyPressed(Keys.D)) { wanderer.facing = Wanderer.Facing.East; wanderer.velocity.x = Wanderer.MAX_VELOCITY; //if (koala.grounded) koala.state = Koala.State.Walking; //koala.facesRight = true; } // clamp the velocity to the maximum if (Math.abs(wanderer.velocity.x) > Wanderer.MAX_VELOCITY) { wanderer.velocity.x = Math.signum(wanderer.velocity.x) * Wanderer.MAX_VELOCITY; } if (Math.abs(wanderer.velocity.y) > Wanderer.MAX_VELOCITY) { wanderer.velocity.y = Math.signum(wanderer.velocity.y) * Wanderer.MAX_VELOCITY; } // clamp the velocity to 0 if it's < 1, and set the state to standign if (Math.abs(wanderer.velocity.x) < 1) { wanderer.velocity.x = 0; if (wanderer.grounded) wanderer.state = Wanderer.State.Standing; } if (Math.abs(wanderer.velocity.y) < 1) { wanderer.velocity.y = 0; if (wanderer.grounded) wanderer.state = Wanderer.State.Standing; } // multiply by delta time so we know how far we go // in this frame wanderer.velocity.scl(deltaTime); // perform collision detection & response, on each axis, separately // if the koala is moving right, check the tiles to the right of it's // right bounding box edge, otherwise check the ones to the left Rectangle koalaRect = rectPool.obtain(); koalaRect.set(wanderer.position.x, wanderer.position.y, Wanderer.WIDTH, Wanderer.HEIGHT); int startX, startY, endX, endY; if (wanderer.velocity.x > 0) { startX = endX = (int) (wanderer.position.x + Wanderer.WIDTH + wanderer.velocity.x); } else { startX = endX = (int) (wanderer.position.x + wanderer.velocity.x); } startY = (int) (wanderer.position.y); endY = (int) (wanderer.position.y + Wanderer.HEIGHT); getTiles(startX, startY, endX, endY, tiles); koalaRect.x += wanderer.velocity.x; for (Rectangle tile : tiles) { if (koalaRect.overlaps(tile)) { wanderer.velocity.x = 0; break; } } koalaRect.x = wanderer.position.x; // if the koala is moving upwards, check the tiles to the top of it's // top bounding box edge, otherwise check the ones to the bottom if (wanderer.velocity.y > 0) { startY = endY = (int) (wanderer.position.y + Wanderer.HEIGHT + wanderer.velocity.y); } else { startY = endY = (int) (wanderer.position.y + wanderer.velocity.y); } startX = (int) (wanderer.position.x); endX = (int) (wanderer.position.x + Wanderer.WIDTH); getTiles(startX, startY, endX, endY, tiles); koalaRect.y += wanderer.velocity.y; for (Rectangle tile : tiles) { if (koalaRect.overlaps(tile)) { // we actually reset the koala y-position here // so it is just below/above the tile we collided with // this removes bouncing :) if (wanderer.velocity.y > 0) { wanderer.position.y = tile.y - Wanderer.HEIGHT; // we hit a block jumping upwards, let's destroy it! //TiledMapTileLayer layer = (TiledMapTileLayer)map.getLayers().get(1); //layer.setCell((int)tile.x, (int)tile.y, null); } else { wanderer.position.y = tile.y + tile.height; // if we hit the ground, mark us as grounded so we can jump wanderer.grounded = true; } wanderer.velocity.y = 0; break; } } rectPool.free(koalaRect); // unscale the velocity by the inverse delta time and set // the latest position wanderer.position.add(wanderer.velocity); wanderer.velocity.scl(1 / deltaTime); // Apply damping to the velocity on the x-axis so we don't // walk infinitely once a key was pressed wanderer.velocity.x *= Wanderer.DAMPING; wanderer.velocity.y *= Wanderer.DAMPING; } private void getTiles(int startX, int startY, int endX, int endY, Array<Rectangle> tiles) { TiledMapTileLayer layer = (TiledMapTileLayer) map[0].getLayers().get(1); rectPool.freeAll(tiles); tiles.clear(); for (int y = startY; y <= endY; y++) { for (int x = startX; x <= endX; x++) { Cell cell = layer.getCell(x, y); if (cell != null) { Rectangle rect = rectPool.obtain(); rect.set(x, y, 1, 1); tiles.add(rect); } } } } private void renderKoala(float deltaTime) { // based on the koala state, get the animation frame TextureRegion frame = null; /* switch (koala.state) { case Standing: frame = stand.getKeyFrame(koala.stateTime); break; case Walking: frame = walk.getKeyFrame(koala.stateTime); break; case Jumping: frame = jump.getKeyFrame(koala.stateTime); break; } */ switch (wanderer.facing) { case North: frame = walkNorth.getKeyFrame(wanderer.stateTime); break; case East: frame = walkEast.getKeyFrame(wanderer.stateTime); break; case South: frame = walkSouth.getKeyFrame(wanderer.stateTime); break; case West: frame = walkWest.getKeyFrame(wanderer.stateTime); break; } // draw the wanderer, depending on the current velocity // on the x-axis, draw the wanderer facing either right // or left Batch batch = renderer.getSpriteBatch(); batch.begin(); if (wanderer.facesRight) { batch.draw(frame, wanderer.position.x, wanderer.position.y, Wanderer.WIDTH, Wanderer.HEIGHT); } else { batch.draw(frame, wanderer.position.x + Wanderer.WIDTH, wanderer.position.y, -Wanderer.WIDTH, Wanderer.HEIGHT); } batch.end(); } }