Cars, skids, dust and smoke

I have been working again on a topdown car prototype, recently adding skid marks, dust and exhaust smoke to the car to improve the look and feel of the game.

dust_skid_smoke

Box2D is being used for the car physics, the car images are rendered using the position of the Body used for the vehicle. When its decided to render any decals (smoke, dust or skids) the Body could be at any angle. If the distance from the centre of the car to an edge is known then its possible to calculate its position (applies to front right/left and back right/left). When a new vehicle is created the radius of the circle that would hold the rectangle is calculated:

Screen Shot 2018-08-03 at 00.23.53

formula

x and y will be known when the car is created or could even be (0,0), x2 will be x + half the width of the car and y2 is y + half the length of the car, this radius value is held in the Car class. Math.pow and Math.sqrt are useful functions to complete the calculation.

Adding decals
When decals are added to be rendered the x and y positions can be calculated using the distance (radius) from the centre of the cars current position given an angle. The cars current angle must be taken into account

angle = car.body.getAngle() + angle;
decalX =  radius * cos(angle);
decalY radius * sin(angle);

Currently I am adding decals at the front two or back two wheels but hard coding the angles, in radians:

  • Front Right is 4.1f
  • Front Left is 5.1f
  • Back Right is 2.1f
  • Back Left is 1f

These values along with the cars current angle make sure the affects are rendered in the correct corners of the rectangle which is the car.

The smoke is rendered always at the back left of the car, dust is added to either the front, back or all tires, when the car is pulling off the dust is added based on the car being front or rear wheel powered. To add some realism to the dust 10% of the cars velocity is applies to the dust entity so while it is active it has some movement.

Next I will Calculate the angle to each of the corners of the rectangle rather than hard coding them, this tutorial will be updated once that is in place, I will also add some more detail on how I achieved the simple smoke and dust affects.

Advertisements

LibGDX Tutorial series update #gamedev

loading.gif

I have been working recently on a game called EvoScape and producing tutorials as I progress it.

After making what would start out as simple save / load functionality many commits and changes were made that would need explaining for part 12 to be useful. I feel there is too much code to explain and it wont be useful to those who read it.

There are many tutorial ideas I have but feel it would be easier for people to follow if I were to create smaller or new projects rather than adding to EvoScape.

I will look back at the project and blog about changes made to it but not rely on this for all of the tutorials I will continue to write.

I have squashed the latest commits for part 12 into one large commit: EvoScape latest branch

If you are interested in the code changes please take a look, I have changed the chunk system to now deal with a 9 chunks (3×3) each chunk consisting of 32×32 tiles. GSON is used to convert the entities and chunks into JSON which is compressed and stored in a text file. I had some issues loading the JSON directly back into classes so wrote a custom loader. I will blog on some of the ideas in this commit as individual blog posts and in separation from the game soon.

Thanks for following this tutorial series and I hope you call back to check out the next set of new posts.

Making a LibGDX Roguelike Survival Game Part 11 – Custom menu / buttons #gamedev

Welcome to the 11th part in this game from scratch series, in this tutorial we look at changing the projection of our sprite batch to allow us to render a heads up display / menu and also create the classes which will allow us to generate interactive menus. The full source code is available on Github.

menu_tutorial

New Images

  • core/assets/gui/main_background.png
  • core/assets/gui/pink_button.png
  • core/assets/gui/square_menu.png
  • core/assets/gui/icons/build.png
  • core/assets/gui/icons/close_menu.png
  • core/assets/gui/icons/resources.png
  • core/assets/gui/icons/settings.png
  • core/assets/gui/selector.png

New classes

  • core/src/uk/co/carelesslabs/ui/BuildMenu.java
  • core/src/uk/co/carelesslabs/ui/Button.java
  • core/src/uk/co/carelesslabs/ui/Menu.java
  • core/src/uk/co/carelesslabs/ui/OnClickListener.java
  • core/src/uk/co/carelesslabs/ui/SquareMenu.java

Updated

  • core/src/uk/co/carelesslabs/Media.java
  • core/src/uk/co/carelesslabs/gameclass.java
  • core/src/uk/co/carelesslabs/Enums.java
  • core/src/uk/co/carelesslabs/Control.java

Custom menu

In this tutorial we will look at creating a custom menu for our game, this eventually will
allow the player to access/view build, settings and inventory. There are libraries available to handle all sorts of inputs (buttons, text boxes etc) such as Scene2D, but when the menu is basic and for learning LibGDX its interesting and sometimes simpler to create your own.

Rendering the menu

Whereas the main game camera moves around the map and can zoom / shake etc the menu view will need to be fixed.

The projection set for game screen is not fit for displaying the menu. Initially I created a new spriteBatch for the HUD (Heads Up Display) which was created using the screen size, this seemed less efficient than having just one spriteBatch, to use only one we will need to update the Matrix of the spriteBatch before rendering the HUD.

We can achieve this by updating the projection of the spriteBatch prior to rendering.

gameclass.java

// new class variable
Matrix4 screenMatrix;

@Override
public void create() {
    ...
    // Setup Matrix4 for HUD
    screenMatrix = new Matrix4(batch.getProjectionMatrix().setToOrtho2D(0, 0, control.screenWidth, control.screenHeight));

...

@Override
public void render () {
  ...
  // GUI
  batch.setProjectionMatrix(screenMatrix);

The screenMatrix will allow us to change to spriteBatch back to a view the size of the screen, co-ordinates 0,0 will be the bottom left of the screen. With the view being the same size as the screen it will be easy to position menu items as we can access the screen width and height.

Menu

Menu.java

The new menu class has a texture which is the background for the menu, a postiion, width and height used when rendering, a Rectanlge (hitbox)
and an array of buttons.

package uk.co.carelesslabs.ui;

import java.util.ArrayList;
import uk.co.carelesslabs.Enums;
import uk.co.carelesslabs.Enums.MenuState;
import uk.co.carelesslabs.entity.Entity;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;

public class Menu {
    public String name;
    public Vector2 pos;
    public Texture texture;
    public float width;
    public float height;
    public float scale;
    public MenuState state;
    public float time;
    public float coolDown;
    public Rectangle hitbox;
    public ArrayList<Button> buttons;

    public Menu(float x, float y, float scale, Texture texture){
        pos = new Vector2(x,y);
        this.texture = texture;
        width = texture.getWidth() * scale;
        height = texture.getHeight() * scale;
        buttons = new ArrayList<Button>();
        hitbox = new Rectangle(x,y,width,height);
        setActive();
    }

    // Render the texture and all of the button textures
    public void draw(SpriteBatch batch){
          if(texture != null) batch.draw(texture, pos.x, pos.y, width, height);
          for(Button b : buttons){
              b.draw(batch);
          }
    }

    // If the player has clicked the mouse then processedClick will be true
    // We check if the mouse position is contained within any of the button Rectangles
    public boolean checkClick(Vector2 pos, boolean processedClick){
        boolean processed = false;
        if(!processedClick){
            if(hitbox.contains(pos)){
                System.out.println("Hit: " + name);
            }

            // Check if a button has been clicked
            for(Button b : buttons){
                if(b.hitbox.contains(pos)){
                    if (b.listener != null) b.listener.onClick(b);
                    processed = true;
                    break;
                }
            }
        } else {
            return processedClick;
        }

        return processed;
    }

    // If the mouse is inside of the menu then check if its also inside of a button
    // When the mouse is inside a button then set its state to hovering
    // Else set all buttons to idle
    public void checkHover(Vector2 pos){
        if(hitbox.contains(pos)){
            // Check if a button is being hovered over
            for(Button b : buttons){
                if(b.hitbox.contains(pos)){
                    b.state = Enums.EnityState.HOVERING;
                } else {
                    b.state = Enums.EnityState.IDLE;
                }
            }
        } else {
            for(Button b : buttons){
              b.state = Enums.EnityState.IDLE;
            }
        }
    }

    // A function to add multiply buttons to our menu
    // It is possible to add any size grid of buttons with a certain sized padding
    public void addButtons(float offset, int columns, int rows, Texture texture, Texture select, int scale) {
        for(int i = 0; i < columns; i++){
            for(int j = 0; j < rows; j++){
                float bx = pos.x + (offset + ((i+1)*offset) + (i * texture.getWidth())) * 2;
                float by = pos.y + (offset + ((j+1)*offset) + (j * texture.getHeight())) * 2;
                float width = texture.getWidth() * 2;
                float height = texture.getHeight() * 2;

                Entity selector = new Entity();
                selector.texture = select;
                selector.width = selector.texture.getWidth() * scale;
                selector.height = selector.texture.getHeight() * scale;
                selector.pos.x = bx - ((selector.width - width) / 2);
                selector.pos.y = by - ((selector.height - height) / 2);

                buttons.add(new Button(bx, by, width, height, texture, selector));
            }
        }
    }

    // Check if the menu is active
    public boolean isActive(){
        return state == Enums.MenuState.ACTIVE;
    }

    // Set meny to active
    public void setActive(){
        state = Enums.MenuState.ACTIVE;
    }

    // Set menu to inactive
    public void setInactive(){
        state = Enums.MenuState.DISABLED;
    }

    // Toggle active state
    public void toggleActive() {
        if(isActive()){
            setInactive();
        } else {
            setActive();
        }
    }
}

Button.java

The Button class extends Entity, it adds an OnClickListener, a hitbox, an icon texture and a selector entity. The OnClickListener defines
what to do when the button is clicked, the hitbox allows us to test if the mouse position when a click occurs should interact with
button, the selector is a texture that is drawn when the button is being hovered over and the icon is a texture to help identify what
the button does.

package uk.co.carelesslabs.ui;

import uk.co.carelesslabs.Enums;
import uk.co.carelesslabs.entity.Entity;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Rectangle;

public class Button extends Entity {
    public OnClickListener listener;
    public Rectangle hitbox;
    public Texture icon;
    public Entity selector;

    public Button(float x, float y, float width, float height, Texture texture, Entity selector) {
        super();
        this.texture = texture;
        this.selector = selector;
        this.pos.x = x;
        this.pos.y = y;
        this.width = width;
        this.height = height;
        hitbox = new Rectangle(pos.x, pos.y, width, height);
    }

    public void setOnClickListener(OnClickListener listener){
        this.listener = listener;
    }

    @Override
    public void draw(SpriteBatch batch){
        if(texture != null) batch.draw(texture, pos.x, pos.y, width, height);
        if(icon != null) batch.draw(icon, pos.x, pos.y, width, height);
        if(isHovered() && selector != null){
            selector.draw(batch);
        }
    }

    // Is button currently being hovered over by the mouse
    private boolean isHovered(){
        return state == Enums.EnityState.HOVERING;
    }

    // Updates the position and size of the hitbox (Rectangle)
    public void updateHitbox() {
        hitbox.set(pos.x, pos.y, width, height);
    }
}

OnClickListener.java

We can create an instance of this interface for each button and use it to create an over ride of the onClick function, within this function
we can write custom code that will run when onClick is called.

package uk.co.carelesslabs.ui;

public interface OnClickListener {
    public void onClick(Button b);
}

An example of setting the onClick function for a button

// Setting the onClick function for a button called button
// We can put any code here
btn.setOnClickListener(
        new OnClickListener(){
            @Override
            public void onClick(Button b) {
                // Declare code to run here
                System.out.println("This button was clicked.");
            }
        });

SquareMenu.java

The SquareMenu is the bottom left square shapped main menu, it has 3 icons at the moment, one for inventory/resources, a build menu and also an icon for settings.

This class extends menu, we set the position and background texture using super (calls the constructor for the extended class ‘Meny’), 4 buttons are added in a 2×2 grid.
The icons and onClick for each button is set, currently we set up only 3 of the buttons.

A new type of menu ‘Build’ is also added, this menu will be hidden by default and only shown when the build icon/button is clicked.

package uk.co.carelesslabs.ui;

import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Vector2;
import uk.co.carelesslabs.Media;
import uk.co.carelesslabs.gameclass;

public class SquareMenu extends Menu {
    public BuildMenu build;

    public SquareMenu(final gameclass game){
        super(0, 0, 2, Media.squareMenu);

        int scale = 2;
        addButtons(3, 2, 2, Media.pinkButton, Media.selector, scale);

        Button btn = buttons.get(0);
        btn.setOnClickListener(
                new OnClickListener(){
                    @Override
                    public void onClick(Button b) {

                    }
                });

        btn = buttons.get(1);
        btn.icon = Media.iconSettings;
        btn.setOnClickListener(
                new OnClickListener(){
                    @Override
                    public void onClick(Button b) {
                        System.out.println("Settings.");
                    }
                });

        btn = buttons.get(2);
        btn.icon = Media.iconResources;
        btn.setOnClickListener(
                new OnClickListener(){
                    @Override
                    public void onClick(Button b) {
                        game.control.inventory = true;
                    }
                });

        btn = buttons.get(3);
        btn.icon = Media.iconBuild;
        buttons.get(3).setOnClickListener(
                new OnClickListener(){
                    @Override
                    public void onClick(Button b) {
                        build.toggleActive();
                    }
                });

        // BUILDING
        build = new BuildMenu(pos.x + width, 0, 2, Media.mainBack);
    }

    // Draw the extended menu and also the build menu.
    @Override
    public void draw(SpriteBatch batch){
        super.draw(batch);
        build.draw(batch);
    }

    // Check if the menu / build menu buttons are being hovered over.
    @Override
    public void checkHover(Vector2 pos) {
        super.checkHover(pos);
        build.checkHover(pos);
    }

}

BuildMenu.java

This class is another extended Menu class which is shown when the build menu button has been pressed. It has 14 columns and 2 rows which will be used
to add building options in another tutorial. Clicking these will allow items to be built/placed onto the map.

This class also adds another button which will call toggleActive(), basically this is a close menu button. The main build menu button will also close
menu when it is clicked and the build menu is currently active.

package uk.co.carelesslabs.ui;

import uk.co.carelesslabs.Media;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class BuildMenu extends Menu {

    public BuildMenu(float x, int y, int scale, Texture mainBack){
        super(x, y, 2, Media.mainBack);
        addButtons(3, 14, 2, Media.pinkButton, Media.selector, 2);
        setInactive();

        // Add a close button
        Button close = new Button(0, 0, Media.close_menu.getWidth() * scale, Media.close_menu.getHeight() * scale, Media.close_menu, null);
        close.pos.x = x + width - (Media.close_menu.getWidth() * scale) - (6 * scale);
        close.pos.y = height - (Media.close_menu.getHeight() * scale) - (6 * scale);
        close.updateHitbox();
        close.setOnClickListener(
                new OnClickListener(){
                    @Override
                    public void onClick(Button b) {
                        toggleActive();
                    }
                });
        buttons.add(close);
    }

    // Only draw when the menu is active.
    public void draw(SpriteBatch batch){
        if(isActive()){
            super.draw(batch);
        }
    }
}

Enum.java

New ENUM list for menu/button states.

...
public enum MenuState {
    ACTIVE,
    DISABLED,
    HOVEROVER,
    CLICKED
}

Control.java

New Vector for the mouse position which is updated on mouseMoved.

...
public Vector2  mousePos = new Vector2();

...
@Override
public boolean mouseMoved(int screenX, int screenY) {
    mousePos.set(screenX, screenHeight - screenY);
    return false;
}

The menus are not yet complete but this gives us a decent foundation for completing / progressing our HUD.

Making a LibGDX Roguelike Survival Game Part 10 – Adding a bird with simple AI #gamedev

[ Full source code ]

In this tutorial we will add an animated bird that will fly randomly around the island, sometimes landing, feeding and walking about. The bird extends the Entity class so can be interacted with by default and added to the Inventory, later we can look at more creative ways to interact with the Bird.  I have not used a state machine for the bird AI, but you can read up on an example of one here.

Images

core/assets/entities/bird

There are 4 images needed for the bird entity, flying, walking, feeding and a shadow. We only require the animation in one direction; the images can be flipped horizontally for the opposite, the shadow is used to give the illusion that the bird is airborne.

Media.java

The class that loads all our image assets requires Texture, TextureRegion[] and Animation variables so that the bird animations can be setup, add the class variables:

public static Texture birdWalk, birdFly, birdPeck, birdShadow;

// Texture Regions
public static TextureRegion[] birdWalkFrames, birdFlyFrames, birdPeckFrames;    

// Animations
public static Animation<TextureRegion> birdWalkAnim, birdPeckAnim, birdFlyAnim;

Loading the assets, first the textures are loaded, they are used to create a TextureRegion[]; an array of frames. We split the texture up into an array using the dimensions of the animation frames. It is possible to have more than one row of images but for simplicity we are using textures with just one so set the TextureRegion to row 0. Once we have an array of frames we can use the TextureRegion to create a new Animation, we will set the frame time to .1 seconds.

Screen Shot 2017-09-04 at 23.21.43

  public static void load_assets(){
        ...
        // Textures
        birdPeck = new Texture("entities/bird/bird_peck.png");
        birdWalk = new Texture("entities/bird/bird_walk.png");
        birdFly  = new Texture("entities/bird/bird_fly.png");
        birdShadow = new Texture("entities/bird/bird_shadow.png");

        // Texture Regions
        birdWalkFrames = TextureRegion.split(birdWalk, 10, 9)[0];
        birdPeckFrames = TextureRegion.split(birdPeck, 10, 9)[0];
        birdFlyFrames = TextureRegion.split(birdFly, 10, 9)[0];

        // Animations
        birdWalkAnim = new Animation<TextureRegion>(.1f, birdWalkFrames);
        birdPeckAnim = new Animation<TextureRegion>(.1f, birdPeckFrames);
        birdFlyAnim = new Animation<TextureRegion>(.1f, birdFlyFrames);
    }

Enums.java

Add a new EntityType of BIRD and add new EnityState to hold all of the possible states for the Bird Entity, we will add the state to the Entity class.

public enum EntityType {
  HERO,
  TREE,
  BIRD
}

public enum EnityState {
  NONE,
  IDLE,
  FEEDING,
  WALKING,
  FLYING,
  HOVERING,
  LANDING
}

Entity.java

There are several changes to the base Entity class, these are added to enable the Bird class to function but will be useful for other AI types. The shadow will need to be drawn so we add an extra draw call to the draw method, checking first if the shadow is present. A new tick method is added that accepts a chunk, some Entities will need to check the tiles in the current chunk.

// Class Vars
public Texture shadow;
public EnityState state; // For logic and selecting image to draw
public Boolean ticks; // .tick will only be called if true
public float time; // Store the time up for the Entity
public Vector3 destVec; // Destination vector for movement
public Tile currentTile; // Tile the Entity occupies
public float coolDown; // For logic

public void draw(SpriteBatch batch){
  if(shadow != null) batch.draw(shadow, pos.x, pos.y, width, height);
  if(texture != null) batch.draw(texture, pos.x, pos.y, width, height);
}

public void tick(float delta){
  time += delta;
}		    

public void tick(float delta, Chunk chunk){

}

Chunk.java

Later when we want to set the currentTile of the Bird we will use the Body position which is a Vector2, we will need to add a new method that returns a tile given a coordinate that has a X and a Y value. We can use the currentTile type to help us make logic decisions, for example we wouldn’t let a bird land on water.

public Tile getTile(Vector2 vector2) {
      ArrayList<Tile> chunk_row;
      int row = (int) ((vector2.y*tileSize/2) / numberRows);
      int col = (int) ((vector2.x*tileSize/2) / numberCols);
      if(tiles.size() > row && row >= 0){
          chunk_row = tiles.get(row);

          if(chunk_row != null && chunk_row.size() > col && col >= 0){
              return chunk_row.get(col);
          }
      }
      return null;
  }

Bird.java

I created this diagram after completing the code but this is where I should have started, when ever you design the logic behind an entity you will save yourself time by planning it out first. I used draw.io to produce this, its an online free and easy to use diagram tool.

bird_logic

It is also useful in my opinion to make the main method for your Entity as simple as possible and be readable, the complexity is hidden within methods. This becomes the following code:

Screen Shot 2017-09-05 at 01.40.08
Hovering Phase

Check if the Entity is hovering state there is a chance to change the state to LANDING, randomBoolean return true if a random value between 0 and 1 is less than the specified value. By setting the value to .2f there is a 20% chance the bird state changes, this is called each tick (60 times per second). This results in the hovering time being somewhat random.

private boolean isHovering(){
    return state == Enums.EnityState.HOVERING;
}
private void setLanding() {
    if(MathUtils.randomBoolean(.2f)){
        state = Enums.EnityState.LANDING;
    }
}

Landing Phase

Once set to LANDING the Entity will have the position Z value decreased until it is less than or equal to zero. On hitting zero the hit-boxes are made active and the state is set to NONE.

private boolean isLanding(){
    return state == Enums.EnityState.LANDING;
}

private void land() {
    if (isAirBorn()) pos.z -= 0.5;
    if(pos.z && <= 0){
        // Landed
        pos.z = 0;
        state = Enums.EnityState.NONE;
        toggleHitboxes(true);
    }
}

public boolean isAirBorn(){
 return pos.z > 0;
}

private void toggleHitboxes(boolean b) {
  body.setActive(b);
  sensor.setActive(b);
}

Move or Hover Phase

If the Entity is flying and has no destination then there is a 85% chance one is set, if the current tile occupied is water then a new destination is always set. Setting destination loops through all of the tiles and checks that the tile is grass, a random number 0 to 100 is equal to 100 and that the tile is not current one. When all these checks are true the destination tile is set and a new destination vector set. The destination vector was originally used to move the entity if you check the commits to this tutorial, it is now only used to check if the Entity is moving in a negative or positive direction to set if the texture is flipped or not.

The max height of the Entity is set to a value of 10 to 20 each time a new destination is set, this added some variety to its movement.

private boolean needsDestination() {
    return destVec == null && isFlying();
}

private void newDestinationOrHover(float delta, Chunk chunk) {
    // 85% chance a new destination is set, unless over water then always
    // get a new destination
    if(MathUtils.randomBoolean(.85f) || currentTile.isWater()){
        setDestination(delta, chunk);
        maxHeight = setHeight();
    } else {
        state = Enums.EnityState.HOVERING;
    }
}

private void setDestination(float delta, Chunk chunk){
    for(ArrayList<Tile> row : chunk.tiles){
        if(destTile != null) break;

        for(Tile tile : row){
            if (tile.isGrass() && MathUtils.random(100) > 99 && tile != currentTile){
                destTile = tile;
                getVector(destTile.pos);
                break;
            }
        }
    }
}

private float setHeight() {
    return MathUtils.random(10) + 10;
}

private boolean isFlying() {
 return state == Enums.EnityState.FLYING;
}

// Enitty.java
public void getVector(Vector3 dest){
    float dx = dest.x - pos.x;
    float dy = dest.y - pos.y;
    double h = Math.sqrt(dx * dx + dy * dy);
    float dn = (float)(h / 1.4142135623730951);

    destVec = new Vector3(dx / dn, dy / dn, 0);
}

Moving Phase

When the destination vector is not NULL (Could also check the destination tile) we move the Entity toward it until it has reached that position. We move the Body of the entity, as it is flying we assume it will not collide with anything so can move the body directly rather than applying for to it. After it moves we update the sensor and the vector3 position.

body.setTransform is used to move the entity, this accepts a Vector2 and an angle which will be 0. The new Vector2 is that of the body moving incrementally closer to the destination tile.

Using interpolate Interpolates between this vector and the given target vector by alpha (within range [0,1]) using the given Interpolation method. the result is stored in this vector.”  we can get the new Vector and move the body. There are many options for the movement type available check them out here: https://github.com/libgdx/libgdx/wiki/Interpolation

To check if the Entity is at the destination the current and destination tile positions. When within a distance of the destination the destination vector and tile are set to NULL.

private boolean hasDestination() {
    return destVec != null;
}

private void moveToDestination(float delta) {
    // https://github.com/libgdx/libgdx/wiki/Interpolation
    body.setTransform(body.getPosition().interpolate(new Vector2(destTile.pos.x + width, destTile.pos.y + height), delta * speed / 4, Interpolation.circle), 0);

    updatePositions();
}

private void updatePositions() {
    sensor.setTransform(body.getPosition(),0);
    pos.x = body.getPosition().x - width/2;
    pos.y = body.getPosition().y - height/4;
}

private void clearDestination() {
    if(isAtDestination()){
        destVec = null;
        destTile = null;
    }
}

private boolean isAtDestination() {
    return currentTile.pos.epsilonEquals(destTile.pos, 20);
}

Landed Phase

When the Z position of the Entity is zero we look to set a new state, this can be feeding, walking or flying. There is a 20% chance of flying, a 5% chance of feeding giving the flying was not set and a 3% change of walking given feeding state was not set. Most often after landing a bird will just fly away, some times it will feed and on the rarely it will walk. When walking is set than a one second cool down is set to ensure the walk lasts at least that amount of time.  Walking moves the bird in the direction it is currently set to face.

public boolean isNotAirBorn(){
   return pos.z == 0;
}

private void setNewState(float delta) {
    if(coolDown > 0){
        coolDown -= delta;
        if(isWalking()){
            walk(delta);
        }
    } else {
        if(MathUtils.randomBoolean(.2f)){
            state = Enums.EnityState.FLYING;
        } else if(MathUtils.randomBoolean(.5f)) {
            state = Enums.EnityState.FEEDING;
            coolDown = .5f;
        } else if(MathUtils.randomBoolean(.3f)) {
            state = Enums.EnityState.WALKING;
            coolDown = 1f;
        }
    }
} 

private boolean isWalking(){
    return state == Enums.EnityState.WALKING;
}

private void walk(float delta) {
  if(currentTile.isPassable()){
    if(tRegion.isFlipX()){
            body.setTransform(body.getPosition().x - speed / 4 * delta, body.getPosition().y,0);
        } else {
            body.setTransform(body.getPosition().x + speed / 4 * delta, body.getPosition().y,0);
        }
        updatePositions();
  }
}

gameclass.java

We add an instance to the gameclass on create() before calling the populateEntityMap, this will ensure the hit-boxes collisions are handled. The Update() method is updated to set the current tile of the entity, call the new tick method that accepts the delta time and also the Island current chunk. Add the same creation of the Bird Entity after if(control.reset){ to ensure a new bird is added when the Island is re-generated.

// New Class variable
// TIME
float time; 
...
// Create() Method
// Bird
island.entities.add(new Bird(new Vector3(10,10,0), box2D, Enums.EnityState.FLYING));

// HashMap of Entities for collisions
box2D.populateEntityMap(island.entities); 
...
// Update() Method
// Tick all entities
for(Entity e: island.entities){
    e.tick(Gdx.graphics.getDeltaTime());
    e.currentTile = island.chunk.getTile(e.body.getPosition());
    e.tick(Gdx.graphics.getDeltaTime(), island.chunk);
}

...
// Last line in Update() Method
time += Gdx.graphics.getDeltaTime();

We now have a Bird that will randomly fly around the Island, it is possible to collect the Bird by interacting with it.

bird_walking

Making a LibGDX Roguelike Survival Game Part 9 – Screen Shake & Inventory #gamedev

In the last tutorial trees could be removed from the map by interacting (pressing ‘E’) on them, as the trees are being cut down lets add in a small screen shake. On a tree being cut down we will add it the the Hero inventory (we will need to create a basic inventory).

[ Full source code for this tutorial ]

shakes

Rumble.java
Rumble allows us to return a Vector allowing us to move the camera relevant to a shake size for a given time in seconds. The variables and methods in this class are all static, so we will be able to use the class without creating an instance of it. Rumble.rumble(1,10) will set the power to 1 and the length of the shake to 10 seconds, the current time is reset to 0.

We have to call the tick method to update the x and y values of the Vector3 to new random values taking into account the size of the shake and update the current time. When current time is greater than time (shake was lasted 10 seconds) we reset the time to 0.

package uk.co.carelesslabs;

import java.util.Random;
import com.badlogic.gdx.math.Vector3;

public class Rumble {
    private static float time = 0;
    private static float currentTime = 0;
    private static float power = 0;
    private static float currentPower = 0;
    private static Random random;
    private static Vector3 pos = new Vector3();

    public static void rumble(float rumblePower, float rumbleLength) {
        random = new Random();
        power = rumblePower;
        time = rumbleLength;
        currentTime = 0;
    }

    public static Vector3 tick(float delta) {
        if (currentTime <= time) {
            currentPower = power * ((time - currentTime) / time);

            pos.x = (random.nextFloat() - 0.5f) * 2 * currentPower;
            pos.y = (random.nextFloat() - 0.5f) * 2 * currentPower;

            currentTime += delta;
        } else {
            time = 0;
        }
        return pos;
    }

    public static float getRumbleTimeLeft() {
        return time;
    }

    public static Vector3 getPos() {
        return pos;
    }
}

Entity.java
The interact method is changed to accept Entity as a parameter:

public void interact(Entity entity){}

Hero.java
When the Hero Entity interacts with another Entity we pass itself, this will give us access to the Hero in the Tree interact method:

// If interact key pressed and interactEntities present interact with first in list.
if(control.interact && interactEntities.size() > 0){
  interactEntities.get(0).interact(this);
}

Tree.java
When a tree is removed we can use Rumble to change the power and time, 1 is a small shake and .2 of a second is a short period:

@Override
public void interact(Entity entity){
    remove = true;
    Rumble.rumble(1, .2f);
}

gameclass.java
We update the render section of gameclass where previously there was only “camera.position.lerp(hero.pos, .2f);”, we now check if there is a shake/rumble to process, if there is then the camera is taken over by the rumble. When the rumble is complete the camera lerps back the the hero.

// Hero Position
if (Rumble.getRumbleTimeLeft() > 0){
    Rumble.tick(Gdx.graphics.getDeltaTime());
    camera.translate(Rumble.getPos());
 } else {
     camera.position.lerp(hero.pos, .2f);
}

Before we look at how we implement a basic inventory lets try out a larger shake just for fun:

Rumble.rumble(3, 5) :big_shake

Control.java
Setup the “I” key to turn on inventory, we can use this to print out the current inventory when ever I is pressed:

public boolean inventory;
... 
// Key up 
case Keys.I:
    inventory = true;
    break;

Inventory.java
This is just a basic class to hold an array of inventory entities and will change in future tutorials. To future proof the idea of the inventory it is better to create a new class rather than testing some inline solution. The class uses a HashMap but any array type would be fine at the moment, we need methods to initialise the array, add to it, return its size and print to console our current inventory:

package uk.co.carelesslabs.entity;

import java.util.HashMap;

public class Inventory {
    HashMap&amp;amp;lt;Integer, Entity&amp;amp;gt; entities;

    public Inventory(){
        reset();
    }

    public int getInventorySize(){
        return entities.size();
    }

    public void addEntity(Entity entity) {
        entities.put(getInventorySize(), entity);
    }

    public HashMap<Integer, Entity> getInventory(){
        return entities;
    }

    public void print() {
        System.out.println("*** Inventory ***");
        for(int i = 0 ; i < entities.size(); i++){
            Entity e = entities.get(i);
            System.out.println("* ["+i+"] " + e.type.toString());
        }    
        System.out.println("*****************");
    }

    public void reset() {
        entities = new HashMap<Integer, Entity>();
    }
}

Entity.java
Add a new Inventory variable to the Entity class:

public Inventory inventory;

Hero.java
Inventory is initialised within Hero and not in the base class, not every Entity will require one:

public Hero(Vector3 pos, Box2DWorld box2d){
   type = EntityType.HERO;
   width = 8;
   height = 8;
   texture = Media.hero;
   speed = 30;
   inventory = new Inventory(); // Init Inventory
   reset(box2d, pos);
}

Tree.java
When an Entity interacts with a Tree we check if that entity has an inventory, at present only our Hero has an inventory, given this is true we add the Tree to the inventory array of entities, flag the tree for removal from the map and trigger some screen shake, more logic will be applied here later:

@Override
public void interact(Entity entity){
    if(entity.inventory != null){
        entity.inventory.addEntity(this);
        remove = true;
        Rumble.rumble(1, .2f);
    }
}

gameclass.java
Checking if inventory key has been pressed we can call the method/function that prints out the entities in the current inventory array and then set inventory boolean to false:

// Render method
...
if(control.inventory){
    hero.inventory.print();
    control.inventory = false;
}

Running the application and hitting down some trees our inventory will contain some entities, pressing “I” will print out the current inventory to the console:

*** Inventory ***
* [0] TREE
* [1] TREE
* [2] TREE
*****************

 

 

 

 

 

 

 

 

 

 

 

Making a LibGDX Roguelike Survival Game Part 8 – Collision Listeners & Interacting #gamedev

When our Hero collides with certain bodies within our Island we want to be able to interact with them in some way. Most of the bodies exist only to stop us walking through objects, some are set as sensors, we can walk through them but our game is aware of when we come into contact with them and when we lose that contact.  Here is a gif (quite long) explaining how we can handle those collisions. We will go into more detail of the code but hopefully this helps explain the process:

[ Full source code for this tutorial ]

collision_anim

Box2D currently stops the player passing through the boundaries on the Island and walking over trees, to add interactions to objects we can implement listeners to handle collisions.

Before we look at using the Box2D callbacks for collisions we need to add some variables and methods to the Entity class. We have already added a body which acts as a collision hitbox, we add a new body called sensor, this will be a body which can be passed over.

Entity.java

public class Entity implements Comparable<Entity> {
    public int hashcode; // to be explained.
    public Body sensor; // A trigger hitbox
    public boolean remove;

When a collision occurs on the body of an Entity we will call the collision method, the first param of entity is the object that was collided with and begin is true if the hit-boxes started to overlap or false is the collision has ended (They no longer overlap).

The interact method will be overrode to handle the player using an object in some way.

The removeBodies method will be used to remove an entities bodies from the world, cutting down a tree for example would remove it from the Island, we would no longer need the bodies for collisions, leaving them in place would leave behind invisible blocks.

public void collision(Entity entity, boolean begin){}

public void interact(){}
public void removeBodies(Box2DWorld box2D) {
    if(sensor != null) box2D.world.destroyBody(sensor);
    if(body != null) box2D.world.destroyBody(body);
}

Control.java

Add a new class boolean called interact, this is set to true on key up of ‘E’, this is the ‘use’ key.  Once an interaction is processed it will set this value back to false.

// ACTIONS
public boolean interact;
...
public boolean keyUp(int keycode) {
  switch (keycode) {
    case Keys.E:
      interact = true;
      break;

Box2DHelper.java

A new method called createSensor which is almost identical to the createBody method creates hit boxes that are used like triggers. Our Hero will be able to pass over these hitboxes; the key line of code is fixtureDef.isSensor = true;  :

public static Body createSensor(World world, float width, float height, float xOffset, float yOffset, Vector3 pos, BodyDef.BodyType type) {
        Body body;
        BodyDef bodyDef = new BodyDef();
        bodyDef.position.x = pos.x + xOffset;
        bodyDef.position.y = pos.y + yOffset;
        bodyDef.angle = 0;
        bodyDef.fixedRotation = true;
        bodyDef.type = type;
        body = world.createBody(bodyDef);

        FixtureDef fixtureDef = new FixtureDef();
        PolygonShape boxShape = new PolygonShape();
        boxShape.setAsBox(width / 2, height / 2);

        fixtureDef.shape=boxShape;
        fixtureDef.isSensor = true;

        body.createFixture(fixtureDef);
        boxShape.dispose();

        return body;
    }

Box2DWorld.java

There are significant changes to the Box2DWorld class, first we add a HashMap to store the Island entities, you may wonder why we store two entity arrays (two arrays with the same entities), one for drawing/logic and this new array for dealing with collisions. A HashMap stores key and value, we set the key to the body hashcode and the value as the Entity. When a collision (contact) occurs we get access to the two fixtures present in the collision, these fixtures are the bodies (body and sensor) that are defined within Entity, by using the hashcode for the key we can select the correct Entity from the array without having to loop.

Given the Hero body has a code of 001 and the Tree sensor has a code of 333, when a contact between the two occur we get fixtureA and fixtureB from the contact, lets say A is the body of the Hero and B is the sensor of the Tree. We can look up the two entities by using the hash codes of the fixtures (bodies).

public class Box2DWorld {
  public World world;
  private Box2DDebugRenderer debugRenderer;
  // New array to hold entities
  private HashMap<Integer, Entity> entityMap;

  public Box2DWorld(){
    world = new World(new Vector2(.0f, .0f), true);
    debugRenderer = new Box2DDebugRenderer();
    // Init the new entity array
    entityMap = new HashMap<Integer, Entity>();

    // Setup the world contacts listeners
    world.setContactListener(new ContactListener() {
      @Override
      public void beginContact(Contact contact) {
        Fixture fixtureA = contact.getFixtureA();
        Fixture fixtureB = contact.getFixtureB();

        process_collisions(fixtureA, fixtureB, true);
      }

      @Override
      public void endContact(Contact contact) {
        Fixture fixtureA = contact.getFixtureA();
        Fixture fixtureB = contact.getFixtureB();

        process_collisions(fixtureA, fixtureB, false);
      }

      @Override
      public void preSolve(Contact contact, Manifold oldManifold) {}

      @Override
      public void postSolve(Contact contact, ContactImpulse impulse) {}
    });
  }

  public void tick(OrthographicCamera camera, Control control){
  if (control.debug) debugRenderer.render(world, camera.combined);
    world.step(Gdx.app.getGraphics().getDeltaTime(), 6, 2);
    world.clearForces();
  }

  // Method to clear down all bodies
  public void clearAllBodies() {
    Array<Body> bodies = new Array<Body>;();
    world.getBodies(bodies);
      for(Body b: bodies){
      world.destroyBody(b);
    }

    entityMap.clear();
  }

  // check if the two bodies colliding exist in our array of entities
  // check that only one entity is a sensor
  // When entityA is a sensor then entityB is the player as its the only
  // moving object.
  // We want to call the Entity method collision for the hero:
  // ... Hero.collision(Tree, true)
  private void process_collisions(Fixture aFixture, Fixture bFixture, boolean begin) {
    Entity entityA = entityMap.get(aFixture.hashCode());
    Entity entityB = entityMap.get(bFixture.hashCode());

    if(entityA != null && entityB != null){
      if(aFixture.isSensor() && !bFixture.isSensor()){
        entityB.collision(entityA, begin);
      } else if(bFixture.isSensor() && !aFixture.isSensor()){
        entityA.collision(entityB, begin);
      }
    }
  }

  // Pass in Island entities and copy them into a new
  // array that has a key (the hashcode of the entity).
  public void populateEntityMap(ArrayList<Entity> entities){
    entityMap.clear();
    for(Entity e: entities){
      entityMap.put(e.hashcode, e);
    }
  }

  // When a tree/hero is added to the island we
  // track it in the entity array
  public void addEntityToMap(Entity entity){
    entityMap.put(entity.hashcode, entity);
  }

  // removes an entity from the array
  public void removeEntityToMap(Entity entity){
    entityMap.remove(entity.hashcode, entity);
  }
}

Hero.java

A new ArrayList of entities is added to keep track of the entities currently in contact with the hero hitbox (Body), the player may be overlapping more than one tree for example, but we will only allow interactions with one at a time.

When collisions or contacts occur with other entities they will be added to this array, when those contacts are broken (collision is no longer true) the entities will be removed from this array. When a collision occurs with the hero body then we use the hashcode of the body which we get via the contact > fixture to select the hero from the hashmap and call the collision method.

When the hero update is called we pass in control and check if interact is true, if it is and the interactEntity array has items then we call the interact method on that entity:

// Class variable. Array of entities currently overlapping/coliding with.
ArrayList<Entity> interactEntities;

body = Box2DHelper.createBody(box2d.world, width/2, height/2, width/4, 0, pos, BodyType.DynamicBody);
// Set hashcode to that of the bodies fixture
// Our body has a single fixture
hashcode = body.getFixtureList().get(0).hashCode();
// init the Entity Array
interactEntities = new ArrayList<Entity>();

public void update(Control control) {
        dirX = 0;
        dirY = 0;

        if (control.down)  dirY = -1;
        if (control.up)    dirY = 1;
        if (control.left)  dirX = -1;
        if (control.right) dirX = 1;    

        body.setLinearVelocity(dirX * speed, dirY * speed);
        pos.x = body.getPosition().x - width/2;
        pos.y = body.getPosition().y - height/4;

        // If interact key pressed and interactEntities present interact with first in list.
        if(control.interact && interactEntities.size() > 0){
        	interactEntities.get(0).interact();
        }

        // Reset interact
        control.interact = false;
    }

    @Override
    public void collision(Entity entity, boolean begin)
    	if(begin){
    	    // Hero entered hitbox
    	    interactEntities.add(entity);
    	} else {
    	    // Hero Left hitbox
    	    interactEntities.remove(entity);
    	}
    }

Tree.java
The sensor for the tree is setup to be bigger than the body, the body will only stop the player walking through it making it a solid object. The sensor will trigger collision callbacks, by setting the entity hashcode to that of the sensor (Body) it will be found in the hashmap of entities and we can trigger interactions with it.

On interacting with a tree we set remove to true:

public Tree(Vector3 pos, Box2DWorld box2d){
    ...
    body = Box2DHelper.createBody(box2d.world, width/2, height/2, width/4, 0, pos, BodyDef.BodyType.StaticBody);
    sensor = Box2DHelper.createSensor(box2d.world, width, height*.85f, width/2, height/3, pos, BodyDef.BodyType.DynamicBody);
    hashcode = sensor.getFixtureList().get(0).hashCode();
}

@Override
public void interact(){
    remove = true;
}

gameclass.java
The create method passes the island entities in the the box2D class instance to setup the hashmap of entities, these entities are copied, the point toward the same on entity so changing an entity in the arraylist or the hashmap array has the same outcome.

After reseting the map we have to re-populate the hashmap, the last line in the render loop is clearing out any entities that have remove set to true.

@Override
public void create() {
    ....
    // HashMap of Entities for collisions
    box2D.populateEntityMap(island.entities);
}

@Override
public void render () {
    Gdx.gl.glClearColor(0, 0, 0, 0);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

    // GAME LOGIC
    if(control.reset){
        island.reset(box2D);
        hero.reset(box2D,island.getCentrePosition());
        island.entities.add(hero);
        // re-populate hashmap
        box2D.populateEntityMap(island.entities);
        control.reset = false;
    }

    ...
    island.clearRemovedEntities(box2D);
}

Island.java
The new method clearRemoverEntities uses an iterator to loop through the objects, this is a safe way to loop and change an array without causing errors:

public void clearRemovedEntities(Box2DWorld box2D) {
    Iterator<Entity> it = entities.iterator();
    while(it.hasNext()) {
        Entity e = it.next();
        if(e.remove){
            e.removeBodies(box2D);
            box2D.removeEntityToMap(e);

            it.remove();
        }
    }
}

So now we have Box2D listeners calling our collision methods everytime a contact takes place between two bodies. We filter out which contacts we are interesting in and can pass that interaction occurance to an entity to be dealt with.

At the moment we simply remove a tree when the Hero interacts with it, we will later add HP and allow many hits before it is removed also add items to the hero when it is removed as we should get resources for downing a tree.

 

Making a LibGDX Roguelike Survival Game Part 7 – Adding Trees, Entity Sorting #gamedev

** Noticed a typo in my entity package, this has been renamed 
evoscape/core/src/uk/co/carelesslans/entity/Entity.java **

[ Full source code for this tutorial ]

EvoScape is starting to look a little like a game but we yet to have any items to interact with, if we are going to have crafting etc then we need resources, to get started we will add some trees for wood. Before we look at how we handle collisions we need to populate the island with some trees.

Screen Shot 2017-08-06 at 14.24.27

The tree images needs to be saved to assets/entities/tree.png

Media.java
Add new a new class variable for the Tree texture, load it in the load_assets() method and ensure it is disposed in the dispose method:

public static Texture tree;
...
tree = new Texture("entities/tree.png");
...
tree.dispose();

Enums.java
Our EntityType enum needs a new type of “TREE” added for the Tree class, at the moment we do not need to use the type but it will come in useful when we want to save our game etc:

public enum EntityType {
 HERO,
 TREE
}

Tree.java
The Tree class extends Entity, it initialises entity its public construcor then sets the type, size, position, texture and creates a new Box2D body so it has a collision box the hero cannot walk through.

The hitbox is half the width and height, here you can see the createBody method accepts another 2 parameters, these are xOffset and yOffset. These new values allow us to have better positioned hitboxes. We want the Tree hitbox to be just the size of the trunk area:

public class Tree extends Entity{
    public Tree(Vector3 pos, Box2DWorld box2d){
        super();
        type = EntityType.TREE;
        width = 8;
        height = 8;
        this.pos = pos;
        texture = Media.tree;
        body = Box2DHelper.createBody(box2d.world, width/2, height/2, width/4, 0, pos, BodyDef.BodyType.StaticBody);
    }
}
tree_hitbox.png

The Tree hitbox.

Box2DHelper.java
The updated class adds xOffset onto the X value and yOffset onto the Y value of the entities position in the createBody method:

public static Body createBody(World world, float width, float height, float xOffset, float yOffset, Vector3 pos, BodyDef.BodyType type) {
       Body body;
       BodyDef bodyDef = new BodyDef();
       // Updated postion code.
       bodyDef.position.set( (pos.x + width/2) + xOffset, (pos.y + height/2) + yOffset);
       bodyDef.angle = 0;
       bodyDef.fixedRotation = true;
       bodyDef.type = type;
       body = world.createBody(bodyDef);

       FixtureDef fixtureDef = new FixtureDef();
       PolygonShape boxShape = new PolygonShape();
       boxShape.setAsBox(width / 2, height / 2);

       fixtureDef.shape = boxShape;
       fixtureDef.restitution = 0.4f;

       body.createFixture(fixtureDef);
       boxShape.dispose();

       return body;
   }

Island.java

Before Trees can be added to the Island we need an ArrayList to store them in, the array will hold a type of Entity, any class that extends Entity can be added to this list:

// Class variable
public ArrayList<Entity> entities = new ArrayList<Entity>();

Next we create a new method that will loop through all of our tiles and randomly add trees, we will look if the tile is grass before adding one also we will roll a random number and allow there to be a 10% chance of a tree being placed:

private void addEntities(Box2DWorld box2D) {
    // Loop all tiles and add random trees
    for(ArrayList<Tile>; row : chunk.tiles){
        for(Tile tile : row){
            if (tile.isGrass()){
                if(MathUtils.random(100) > 90){
                    entities.add(new Tree(tile.pos, box2D));
                }
            }
        }
    }
}
...
// As previously shown the Tree public constuctor which is called to create
// a new tree
public Tree(Vector3 pos, Box2DWorld box2d){

Calling this method and populating Trees will work but running the game they will not be drawn, we add to our render loop after the tiles have been drawn, looping and drawing the entities. The draw method within the Entity class is called unless the Tree class overrides this method:

// Draw all entities
for(Entity e: island.entities){
    e.draw(batch);
}

Running this code will show our randomly sized map and now a number of trees, this looks great until you behind a tree and find you are rendered on top of it. To overcome this we need to add the hero to the array of entities and sort it before rendering.

Screen Shot 2017-08-05 at 23.57.32

No “Z Sorting”

Entity.java
To sort the array of Entity we implement Comparable and compare the class to itself. With that in place we add a compareTo method that will compare the Y values:

// implement comparable
public class Entity implements Comparable<Entity> {
...

// New method
public int compareTo(Entity e) {
    float tempY =  e.pos.y;
    float compareY = pos.y;

    return (tempY < compareY ) ? -1: (tempY > compareY) ? 1:0 ;
}

gameclass.java
After the Hero instance is created add it to the list of Island entities so that it can be sorted and drawn in order with the array.

// Island
island = new Island(box2D); 

// Hero
hero = new Hero(island.centreTile.pos, box2D);
island.entities.add(hero); // Add hero to entity array
...

// Before drawing the entities sort the array
Collections.sort(island.entities);

Running the game we can now move behind trees:

zSort.gif

The hero is now drawn in the correct order (based on its Y value)

Adding a reset option that re-generates the Island

Control.java
Add a new boolean to the class called reset, this will be set to true on pressing ‘R’:

// New class variable
public boolean reset;

// public boolean keyUp(int keycode) {
// new key
case Keys.R:
    reset = true;
    break;

Box2DWorld.java
To reset everything we will need to clear down all of the bodies we have added to the world, a new method clearAllBodies handles this:

// New method to remove Box2D bodies from the World
public void clearAllBodies() {
    Array<Body> bodies = new Array<Body>();
    world.getBodies(bodies);
    for(Body b: bodies){
        world.destroyBody(b);
        }
    }
 }

gameclass.java
Within our game logic (Render loop) we check this reset variable and when its true call reset on hero and island, re-add hero to entities and set reset to false:

// GAME LOGIC
if(control.reset){
    island.reset(box2D);
    hero.reset(box2D,island.getCentrePosition());
    island.entities.add(hero);
    control.reset = false;
}

Island.java
Create a new reset method and call this from the public constructor, this saves re-writing the same calls twice:

public Island(Box2DWorld box2D){
    reset(box2D);
}

public void reset(Box2DWorld box2D) {
   entities.clear();
   box2D.clearAllBodies();
   setupTiles();
   codeTiles();
   generateHitboxes(box2D);
   addEntities(box2D);
}

Hero.java
Add reset method and update the public constructor, when we reset the hero we re-position it to the centre and re-create its hitbox:

public Hero(Vector3 pos, Box2DWorld box2d){
    type = EntityType.HERO;
    width = 8;
    height = 8;
    texture = Media.hero;
    speed = 30;
    reset(box2d, pos);
}

public void reset(Box2DWorld box2d, Vector3 pos) {
    this.pos.set(pos);
    body = Box2DHelper.createBody(box2d.world, width/2, height/2, width/4, 0, pos, BodyType.DynamicBody);
}

Now when we run the game and press ‘R’ the hero is placed back to the centre of the map and we generate a new island with different number of trees.

random_island

Next we will implement collision listeners so we can act on being in contact with a tree, exciting stuff!