Nuclear Throne style weapon movement #LibGDX #Gamedev

I have published 11 tutorials on making a game from scratch, while the game was intended to be a rougue-like it has evolved away from the genre. I aim to add interesting features which might help others. While this may never be a fun to play or complete game it hopefully will aid others in producing their own games.

If you are interested in checking out the changes to the Chunk management and also the loading and saving methods then check out this pull request (I will create posts about these changes in the future.):

https://github.com/tyler6699/evoscape/pull/7

This tutorial will add a weapon to the Hero that points toward the mouse cursor position. The gun points right or left and swaps hands depending on the mouse position.

gun_anim

Full source code on Github

Entity Class
New variables are added to Entity that will allow rotation and horizontal flipping of
a texture, this means we only need one image for the weapon, also we will add an active flag and an array of guns called weapons. The active flag can be used later to determine if the gun or weapon has been drawn. The array will allow multiple weapons in a future tutorial.

The drawRotated method uses spriteBatch draw that will allow the angle and rotation point of our entity.

// New Variables
public float angle;
public Boolean flipX = false;
public Boolean flipY = false;
public boolean active;
public ArrayList weapons;

// New draw function which we will over ride
public void drawRotated(SpriteBatch batch){
  if(texture != null) batch.draw(texture, pos.x, pos.y, 0, 0, width, height,1, 1, angle, 0, 0, (int)width, (int)height, flipX, flipY);
}

All of the available parameters for the draw method we are calling.

x the x-coordinate in screen space
y the y-coordinate in screen space
originX the x-coordinate of the scaling and rotation origin relative to the screen space coordinates
originY the y-coordinate of the scaling and rotation origin relative to the screen space coordinates
width the width in pixels
height the height in pixels
scaleX the scale of the rectangle around originX/originY in x
scaleY the scale of the rectangle around originX/originY in y
rotation the angle of counter clockwise rotation of the rectangle around originX/originY
srcX the x-coordinate in texel space
srcY the y-coordinate in texel space
srcWidth the source with in texels
srcHeight the source height in texels
flipX whether to flip the sprite horizontally
flipY whether to flip the sprite vertically

Gun Class

At the moment the gun class is a basic entity that only draws to the screen, I will look at shooting in the next tutorial. What we care about is the position of the mouse retaliative to the hero, is it to the left or right side and at what angle.

The Gun class has x and y offsets for the origin and also the x position, these values allow us to move the gun into the hand of the hero. The gun can be set to the Hero’s x position by default and moved left or right accordingly, knowing the size of the gun and hero we can work out how far to move it so it appears in the correct position.

package uk.co.carelesslabs.weapons;

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

public class Gun extends Entity {
	float originXOffset; // OriginX Offset
	float originYOffset; // OriginY Offset
	float xPos;          // X offset for gun position
	float xMinPos;       // X Position offset facing left
	float xMaxPos;       // X Position offset facing right

	public Gun(float originXOffset, float xMinRight, float xMaxRight){
		texture = Media.gun;
		width = texture.getWidth();
		height = texture.getHeight();
		active = true;
		originYOffset = height/2;
		this.originXOffset = originXOffset;
		this.xMinPos = xMinRight;
		this.xMaxPos = xMaxRight;
	}

    public void drawRotated(SpriteBatch batch){
    	if(angle > 90 && angle < 270){ // 6 to 12 Clockwise or LEFT
    		xPos = xMinPos;
    		flipY = true;
    	} else { // 12 to 6 clockwise or RIGHT
    		xPos = xMaxPos;
    		flipY = false;
    	}
        // When the gun is to the right of the hero we move
        // the it by xMaxPos (7) and when
        // its to the left we move it byxMinPos (-1)
        if(texture != null) batch.draw(texture, pos.x + xPos, pos.y, originXOffset, originYOffset, width, height, 1, 1, angle, 0, 0, (int)width, (int)height, flipX, flipY);
    }
}

Hero Class

There is a new Vector3 added to the hero class, previously the hero was not aligned to the centre of the screen, this new variable will be updated to the hero position but half of the hero width +/- from the x value so that the hero appears centred.

A new ArrayList of ‘Gun’ called weapons is setup and initiated, we add a Gun to the list, the origin X Offset is set to 1 and the min and max x offsets set the -1 and 7. This sets up the rotation point and the left/right positions relative to the hero.

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

  // Weapon
  weapons = new ArrayList();
  weapons.add(new Gun(1, -1, 7));
}

Draw function

The Hero draw function now loops through the weapon array and will call drawRotated for any active gun.

The cameraPos variable is also updated in this draw method, it is set to the same as the hero position and then the x value has half of the Hero width added on.

@Override
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);
// Loop all weapons and draw the active ones
  for(Gun g : weapons){
      if(g.active){
        g.drawRotated(batch);
      }
   }

...
     // Update Camera Position
     cameraPos.set(pos);
     cameraPos.x += width / 2;
}

Gun position
The gun by default is positioned at Hero.pos.x, it it was not moved using the x offset this is how it would look:
001_gun_hero_pos

Once it is in game and rotating you can see if flips but stay in the right hand of the Hero:gun_no_x_offset

The small yellow cross shows the point at which the entity rotates around, placing it here makes the movement seem more real than if it was centred.

When the gun needs to face right flip is set to false and it is rendered hero x + 7. When the gun is facing left the texture is flipped and moved left by 1. The texture flips from the x point so the flip alone moves the texture left by the width. If you change the origin X value you can see how it affects the rotation (Not all weapons would have the same rotation point)

 

Hero update

When the Hero update is called we should update all active weapons so they move with the Hero, we also update the angle, I found that the mouse angle from the centre of the screen did not match the draw function, taking 90 degrees away made these match.

public void update(Control control) {
  ....
  // Update weapons
  for(Gun g : weapons){
  	if(g.active){
            g.updatePos(pos.x, pos.y);
      	    g.angle = control.angle - 90;
  	}
  }

ENUMS

If you wanted a Hero that faced the mouse (8 directions) we can implement a mouse angle to compass position, while this is not used in the tutorial it may be useful to someone and we can use it in the future. Here are the ENUM values, the order of these is important.

/**
 * Compass directions
 */
public enum Compass {
    S,
    SE,
    E,
    NE,
    N,
    NW,
    W,
    SW
}

Control Class

The new angle and facing variables will be set/updated within the the Control class.  On mouseMoved we have the x and y values for the current mouse position which can be used to calculate the angle between the screen centre and the cursor:
angle = (float) Math.toDegrees(Math.atan2(screenX – (screenWidth/2), screenY – (screenHeight/2)));

if the angle is negative then we add 360:
angle = angle < 0 ? angle += 360: angle;

To turn this angle into a compass direction:
direction = (int) Math.floor((angle / 45) + 0.5) & 7;

This will produce 0 to 7 as a result, we can then pick N ENUM from the list, 0 is S 1 is SE and so on:
facing = Compass.values()[direction];

  // ACTIONS
  public boolean interact;
  public float   angle;
  public int     direction;
  public Compass facing;

  @Override
  public boolean mouseMoved(int screenX, int screenY) {
      float flippedY = screenHeight - screenY;
      mousePos.set(screenX, flippedY);

      // Set angle of mouse
      angle = (float) Math.toDegrees(Math.atan2(screenX - (screenWidth/2), screenY - (screenHeight/2)));
      angle = angle  0){
            Rumble.tick(Gdx.graphics.getDeltaTime());
            camera.translate(Rumble.getPos());
        } else {
            camera.position.lerp(hero.cameraPos, .2f); // use new Vector3 variable<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>
        }

Hopefully there are a few useful snippets of code in the tutorial, remember there are always other ways to achieve a goal and often more efficient methods.

Next time I will add bullets and firing, thanks for reading!

Advertisements

LibGDX Simple Slides

Currently working on a game for Wing Wing Shoot Bag using Java with LibGDX. While trying to learn Pixel/Game art I produced a few animated GIFS, one of which would be perfect for the main screen.

Being a little lazy I thought of loading in the individual images and looping through them rather than create one large image and cut it up programmatically for an animation. This worked, I had created a sort of slide player.

After pressing start within the game menu I wanted the current frame to pause and fade out, then the intro of the game would play. The intro follows the movie to which the game is based, several images would fade in, hold then fade out.

Fading in and out
First off I needed to find a way to fade an image in or out, there is a library available to do this: https://code.google.com/p/java-universal-tween-engine/

I followed the guide and setup a Sprite Accessor class: Class source

Slide Manager
Next I created a slide manager class, this would hold an array of slides which could have some simple attributes:

  • sprite;              // The image
  • fade_duration  // Time to fade in or out
  • hold_duration  // Time to hold slide on screen after fade in
  • total_duration  // time for fade in, hold and fade out
  • gap_duration   // time between slides

Classes: Slide ClassSlide Manager

Creating and using the Slide manager
Basically you initiate the new class then add to the array of images with some settings. During the render loop you tell the class to update and it will play your images in order; fading them in and out.

// Declare a new var
SlideManager slide_manager;

// Slides
slide_manager = new SlideManager();

// Add slides that fade in for 4 seconds, holds for 1 seconds
// After fade out leaves a gap of 1 seconds before next slide

slide_manager.add_slide(new Slide(“intro/screens/russian.png”, 4, 1, 1));
slide_manager.add_slide(new Slide(“intro/screens/spy.png”, 4, 1, 1));

// Add a slide with an overlay animation
// Passes animation width, speed and position
slide_manager.add_slide(new Slide(“bck.png”, 2, 0, 1, “frames.png”, 70, 0.1f, 45, 25));

I started playing about with a few variations of slides, would probably move the extras such as animations into an array and create a new animation class to pass with the slide.

This is a render loop for the intro screen, when the slides are done it just loads the next screen:

public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
slide_manager.tick(delta);

// DRAW
batch.begin();
if (!slide_manager.finished){
slide_manager.draw(batch);
}
batch.end();

if (slide_manager.finished){
game.setScreen(game.level_one);
}
}

This is a very quick prototype which only allows simple fading in and out and overlaying of one animation. It requires a library to be imported even though setting the colour(Alpha) of a sprite over time is very simple I could go on to use many more of the libs features.

This class will allows me to quickly create simple comic book style cuts scenes between levels while prototyping.

 

Simple Screen Shake

Lots of games add excitement by using screen shake, when your character gets hit or on certain events a little rumble really lets the player know something has occurred. While prototyping some features of Endure I revisited some old code to add a small rumble when the fishing line breaks:

Small Screen Shake

Large Screen Shake (Over the top)

Code (Simple Rumble Class)

Pastie Source Code

 

public class Rumble {

  public float time;
  Random random;
  float x, y;
  float current_time;
  float power;
  float current_power;

  public Rumble(){
    time = 0;
    current_time = 0;
    power = 0;
    current_power = 0;
  }
  
  // Call this function with the force of the shake 
  // and how long it should last      
  public void rumble(float power, float time) {
    random = new Random();
    this.power = power;
    this.time = time;
    this.current_time = 0;
  }
        
  public void tick(float delta, GameController gc, Hero hero){
      // GameController contains the camera
      // Hero is the character centre screen
    
    if(current_time <= time) {
      current_power = power * ((time - current_time) / time);
      // generate random new x and y values taking into account
      // how much force was passed in
      x = (random.nextFloat() - 0.5f) * 2 * current_power;
      y = (random.nextFloat() - 0.5f) * 2 * current_power;
      
      // Set the camera to this new x/y position           
      gc.camera.translate(-x, -y);
      current_time += delta;
    } else {
      // When the shaking is over move the camera back to the hero position
      gc.camera.position.x = hero.x;
      gc.camera.position.y = hero.y;
    }
  }      
}

This is all the code to achieve a basic screen shake. The timer for the affect works its way down to zero then stops. For large shakes once the rumble is over it will snap back to the character and look very jumpy but this is just a prototype.

To use the class create a new instance of it then conditionally produce a new RUMBLE! Below you see on the line snapping a small shake takes place by changing the power and time parameters, then the main render loop just needs to call the tick function:

public Rumble  rumble;
this.rumble = new Rumble();

if (line.tension > line.max_tension){ // LINE SNAP
  gc.rumble.rumble(.2f, .1f); 
  state = STATE.IDLE;
}

if (gameController.rumble.time > 0){
  gameController.rumble.tick(delta, gameController, hero);
}

So there you go a nice feature to add to any game that could do with some atmosphere or excitement, could be progressed much further and also be used in loads of ways, great for explosions, thunder storms etc.

Thanks for reading !_!

LibGDX Lighting – Day / Night cycle

Recently researched the use of a Shader in libGDX to create a simple day/night cycle. Found some basic tutorials to create a fire at night affect including: Lightmap Shader This basically adds a tint to the background and overlays an image to create a spotlight.

Initiating vars etc:

Vector3 bright = new Vector3(6.3f, 6.3f, 6.7f);
// Load shaders from text files
vertexShader = Gdx.files.internal("data/shaders/vertexShader.glsl").readString();
defaultPixelShader = Gdx.files.internal("data/shaders/defaultPixelShader.glsl").readString();
finalPixelShader =  Gdx.files.internal("data/shaders/pixelShader.glsl").readString();

ShaderProgram.pedantic = false;
defaultShader = new ShaderProgram(Art.vertexShader, Art.defaultPixelShader);
finalShader = new ShaderProgram(Art.vertexShader, Art.finalPixelShader);currentShader = finalShader;
ambientColor = bright;
 
finalShader.begin();
finalShader.setUniformi("u_lightmap", 1);
finalShader.setUniformf("ambientColor", ambientColor.x, ambientColor.y, ambientColor.z, ambientIntensity);
finalShader.end();
// Image for spot light 
light = new Texture(Gdx.files.internal("data/shaders/light.png"));
fbo = new FrameBuffer(Format.RGBA8888, 1024, 788, false);

finalShader.begin();
finalShader.setUniformf("resolution", 1024, 788);
finalShader.end();

Render Loop
After setting up the variables and shader files in the render loop I adjusted the ambientColor over time to give affect of night day.

// Adjust ambientColor to give appearance of night/day  
finalShader.begin();
finalShader.setUniformi("u_lightmap", 1);
finalShader.setUniformf("ambientColor", ambientColor.x, ambientColor.y, ambientColor.z, ambientIntensity);
finalShader.end();

// I added logic to only show spotlight during night
if(show_light){
  fbo.begin();
  fbo.getColorBufferTexture().bind(1);
  light_batch.setShader(defaultShader);
  light_batch.begin();
  light.bind(0);
  light_batch.draw(light, hero.x-l_off+.4f,hero.y-l_off+.2f, light_size,light_size);
  light_batch.end();
  fbo.end();
}
       
// Draw your map here
light_batch.setShader(currentShader);
light_batch.begin();
MapGenerator.draw(gameController.camera, light_batch);
light_batch.end(); 

Night

 Evening
This solution limited me to using one spot light, also my knowledge of OpenGL Shader language is zero. At this point I went back to the LibGDX documentation and came across Box2dLights, a library that uses collision data from Box2D to create lights/shadows.
// BOX2DLIGHTS
private PointLight spriteLight;
private RayHandler rayHandler;

// Create a new Box2D World, this is required. 
World world = new World(new Vector2(), true); 
RayHandler.useDiffuseLight(true);
  
// Setup the new RayHandler, it will use the same camera as the main game
rayHandler = new RayHandler(world);
rayHandler.setCulling(true);
rayHandler.setCombinedMatrix(gameController.camera.combined);
rayHandler.setAmbientLight(1);
 
// Light to follow the hero
spriteLight = new PointLight(rayHandler, 50);
spriteLight.setPosition(hero.x,hero.y);
spriteLight.setDistance(5);
spriteLight.setColor(3, 12, 33, 0.3f);
// Keep the unique ID of the light  
hero_light = spriteLight.hashCode();

// this lights the map (Sun)  
spriteLight = new PointLight(rayHandler, 50);
spriteLight.setPosition(hero.x-20,hero.y-20);
spriteLight.setDistance(250);
spriteLight.setColor(3, 12, 33, 0.5f);
 
// Test shadow being cast by solid object 
PolygonShape tileShape = new PolygonShape();
tileShape.setAsBox(.5f, .5f);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = tileShape;
fixtureDef.filter.groupIndex = 0;
BodyDef tileBodyDef = new BodyDef();
float bodyX = hero.x-3;
float bodyY = hero.y-4f;
tileBodyDef.position.set(bodyX, bodyY); 
tileBodyDef.type = BodyType.StaticBody;
Body tileBody = world.createBody(tileBodyDef);
tileBody.createFixture(fixtureDef);

// RENDER LOOP CODE
rayHandler.setCombinedMatrix(gameController.camera.combined, gameController.camera.position.x, gameController.camera.position.y,gameController.camera.viewportWidth, gameController.camera.viewportHeight);
rayHandler.updateAndRender();

Here I set up some basic lights, a small one for the hero and a large light to act as the sun, I added one solid object to show a shadow being cast. Would need to add bodies for each object in the game and update there positions. Here the alpha is set to low on the sun light to give impression of night:

Zoomed out, earlier in the day:
Multiple Lights

Accessing and looping through all lights is simple, we stored the hashcode of the hero light so on each render loop update its position, else change the alpha value; increase/decrease this value to simulate night to day and vice versa.

for (Light light : rayHandler.lightList) {
 if (light.hashCode() == hero_light){
  light.setPosition(hero.x,hero.y); 
 }else{
  light.setColor(1, 1, 1, ambientIntensity);
 }
}

2D Projectile Motion

Recently added trees to the game, when one of these objects is broken it should break down into wood and leaf blocks. I would like these the tree to fall apart and fall to the ground and maybe even bounce. So I need a function where I can apply some force in a direction to any block and make it move/fly/

After some research found a wiki page on projectile motion:

Wiki: Projectile Motion

Here is a simple function that takes a velocity (speed) and an angle and generates all the points through the flight of the object.

Test Function

void projectile(double velocity, double angle, float delta) {    
  double vx, vy, ux, uy, tt, terminal_v;
  double gravity = 9.8;
   
  ux = velocity * Math.cos(angle * Math.PI / 180);
  uy = velocity * Math.sin(angle * Math.PI / 180);  
  terminal_v = uy/gravity;
  
  tt = time_x*time_x;
  vy = uy * time_x - 0.5 * - -gravity * tt;
    
  if (time_x < terminal_v*2){
    Vector2 t = new Vector2((float) vx + hero.x ,(float) vy + hero.y);
    DotArray.add(t);
  }
  
  time_x += delta;
}

ux – the initial velocity accross

uy the initial velocity upwards

terminal_v is the time it takes for the object to stop moving upwards, if you double this you get the total flight time (Given the object takes off from and lands at zero Y).

For each time render is called (usually 60fps) calculate the position of the object at the current flight time and add it to an array.

In the draw part of the code I just draw each co-ordinate held in the array of vectors. This needs work as it only works for positive x (Left to right) and is hard coded to run from the hero current position but this is just a test.

Was unsure how to square a double so just multiples time.

tt = time_x*time_x;

Screen Shot

 

Random Island Generator

After making a few maps with Tiled I realized it was slow and tedious, I thought a procedural island would be doable, I read a few articles on different methods and decided to go with one which was simple enough for me to code.
Step 1
Create a 18×18 array of Tiles, this is a simple class with variables, name, x, y, number, texture, code. The texture for each tile is set to water:
 private void create_water_map(){
  int id = 0;
  SQUARE = 18;

  for (int i = 0; i < LAYERS; i++) {
    for (int y = 0; y < SQUARE; y++) {
      for (int x = 0; x < SQUARE; x++) { 
 Tile tile = new Tile(x << 5, y << 5, x, y, "WATER", Art.water, id);
 id += 1;
 tileArray_1.add(tile);
      }
    }  
   }
 }
Step 2
Take the centre tile and spiral out, for the first 3 or 4 passes set the Tile to grass, then set the tile to grass if random number < value, the chance gets less as the number of the cycle goes up.
private void create_mini_island(){
  int number_of_cycles = 7;
  int move_amount = 0;
  int random_cycle_no = 3;
  int tile_count = SQUARE * SQUARE;
  int tile_no = tile_count/2 + (SQUARE/2);
  int prev_tile_no = tile_no;
  
  Tile current_tile = tileArray_1.get(tile_no);
  current_tile.texture = Art.grass;
  current_tile.name = "GRASS";
  
  for(int cycle = 1; cycle <= number_of_cycles;cycle++){ 
    int rnd = 100 - (cycle*12);
  
    for(int d = 0; d < 4; d++){
      if(d == 0 || d == 2){
        move_amount += 1;
      }
      for (int m = 1; m <= move_amount; m ++ ){
        if (d == DOWN){
   tile_no -= SQUARE;     
 }else if (d == LEFT){
         tile_no -= 1;             
 }else if (d == UP){
   tile_no += SQUARE;       
 }else if (d == RIGHT){
   tile_no += 1;
 }
 
        process_direction(tile_no,prev_tile_no, tile_count, "GRASS", current_tile, Art.grass, rnd, cycle, random_cycle_no);    
        prev_tile_no = tile_no; 
      }
    }
  }
}
Step 3
The map resembles a basic island but is way too small, now loop through the array and split each tile up into many more and set the border to water, this will help make the island less block like later on.

 

 


 

 

private void enlarge_mini_island(){
  int x,y,start_x,start_y;
  int count = -1;
  int t = -1;
  int split_by = 8;
  land_keep_percent = (int) (split_by * 0.8);
  
  for (Tile tile : tileArray_1) {
    count ++;
    for (int h = 0; h < split_by; h++){
      if (count == t){
 for (int w = 0; w < split_by; w++){
   int row = tile.number / SQUARE;
   int column = tile.number % SQUARE;
   // replace hard coded new width and height 144 and 1008
   int id = w + ((tile.number)*split_by) + (h*144) + (row * 1008);
   
          if (count == t){System.out.println(id + " tile: " + count + " w:" + w + " h:" + h + " row: " + row );}
     start_x = (column*split_by) + w;
     x = (start_x << 5);
     start_y = (row * split_by) + h;
     y = (start_y << 5);
     Tile new_tile;
     // make centre tiles water
     if (w < 1 || w > land_keep_percent || h < 1 || h > land_keep_percent){
       new_tile = new Tile(x, y, row, column, "WATER", Art.water, id);
     } else {
       new_tile = new Tile(x, y, row, column, tile.name, tile.texture, id);
     }
            
            tileArray_2.add(new_tile);
     if (w == 1 && h == 1 && new_tile.name.equals("GRASS")){
       new_tile.marker = true;
       tile_connector.add(new_tile);
            }
        }
      }
    }
  Collections.sort(tileArray_2);  
}

Step 4
 For each tile added to the connector array check down to see if there is land, if yes then turn the tiles below into grass, repeat this for left also which now gives us this:

 

 

Step 5
Smoothing out the land to make the grass areas less box like, loop through all the tiles, if the tile is water and touching at least x number of grass tiles then turn it into grass.

 

Repeat

 

 Step 6
Loop through the array of tiles again making water tiles touching grass randomly into grass.

 

Step 7
Now that the island is finished loop through the array once more, every tile that is water and touching grass becomes sand. Run through this again randomly this time so some shores are thicker.

 

Step 8
Next for each tile that is sand and touching grass calculate which sand to grass tile it should be.
Check the 3 tiles above, to the left and right and the 3 tiles below use this to create a code. If the water tile code is 001 01 001 then it has 3 sand tiles to the right so show the correct tile.
The sand to grass tiles:

 

 Repeat this process for water touching sand.

 

 

The next stages include creating rivers, placing rocks etc and identifying areas to place trees and so on. I have included some code snippets just to give an idea how how parts of this work, its quite simple and takes only a moment to run through.