Moving bad guys

Hi,

Going to be working on adding ‘bad guys’ to my game now. I want them to move like in ‘terraria’. I know I will need do some pathfinding for the bad guys who want to
get to the player, but what about those that just seem to randomly bounce about?

Any advice is appreciated.

Thanks

AI is a tricky thing to perfect, but it’s exactly like writing code. Go through what you want the AI to do, step by step. It’s a state-based machine.

For example the slimes in Terraria really only jump towards you. The AI is simply after X ticks, jump towards target entity, which in this case is the player.

Zombies are kind of similar, except I think they attempt to lock onto your X coordinate and only try to jump if you’re too high. They don’t have much fancy pathfinding, not to mention that would be pretty hard with the way terrain works in that game.

I suggest to copy some basic enemies from games you like such as the slimes or zombies, then work your way up to more advanced AIs.

Cheers as always Chrislo27,

At the moment I’m trying to implement a bad guy class, I’ve got the following as a start, but not sure if this is any good:


public interface IBadGuy {
	void move(int x, int y);
	void attack();
	void decreaseHealth(int amount);
	void spawn(int x, int y);
	void load(String textureFile, int cols, int rows);
}


public abstract class BadGuy implements IBadGuy, Screen {

	protected int x, y;
	protected byte health;
	
	protected Texture walkSheet;
	protected Animation walkAnimation;
	protected TextureRegion[] walkFrames;
	protected TextureRegion currentFrame;
	protected SpriteBatch batch;

	@Override
	public void load(String textureFile, int cols, int rows) {
		
		// This is where you should set up your texture region, walkframes, walk animation and sprite batch
		   
		walkSheet = new Texture(Gdx.files.internal(textureFile));
		   
		TextureRegion[][] tmp = TextureRegion.split(walkSheet, walkSheet.getWidth()/cols, walkSheet.getHeight()/rows);              // #10
		walkFrames = new TextureRegion[cols * rows];
	         
	}

	
	@Override
	public void move(int x, int y) {
	}

	@Override
	public void attack() {
	}

	@Override
	public void decreaseHealth(int amount) {
	}

	@Override
	public void spawn(int x, int y) {
	}

	@Override
	public void show() {
		
	}

	@Override
	public void render(float delta) {
		System.out.println("Render in BadGuy");
	}

	@Override
	public void resize(int width, int height) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void pause() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void resume() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void hide() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void dispose() {
		// TODO Auto-generated method stub
		
	}
}


I would then have some bad guy manager class which would call the relevant interface methods…

Having a bit of a mare with animation, just trying to see which is best way to do this.
Would you have all your animations for various baddies in one sprite sheet? If so, how do you tell libgdx that first 4 sprites are badguy#1 animation, next four are badguy#2 animation etc…
Looking at my code below, I guess you have to set the stateTime to start of animation frame?

My code below is what I’m using for animation but it goes through the whole sprite sheet:


	@Override
	public void load(String textureFile, int cols, int rows) {

		walkSheet = new Texture(Gdx.files.internal(textureFile));

		TextureRegion[][] tmp = TextureRegion.split(walkSheet, walkSheet.getWidth() / cols, walkSheet.getHeight() / rows); 
		walkFrames = new TextureRegion[cols * rows];

		int index = 0;
		
		for (int i = 0; i < rows; i++) {
			for (int j = 0; j < cols; j++) {
				walkFrames[index++] = tmp[i][j];
			}
		}
		walkAnimation = new Animation(0.5f, walkFrames); 
		batch = new SpriteBatch(); 
		
		stateTime = 0f;

	}



	@Override
	public void render() {
		stateTime += Gdx.graphics.getDeltaTime();
		currentFrame = walkAnimation.getKeyFrame(stateTime, true);
		batch.begin();
		batch.draw(currentFrame, x, y, 16, 16);
		batch.end();
	}


stateTime is the variable which I guess needs checking?

Thanks

Got animation class written.

One issue, when placing one of the bad guys at a given position, how best as to when it moves off the screen, then I scroll the screen that we then see it?

For instance, bad guy moving right and I scroll the screen to the left, I take it you need to move the bad guy with the screen?

Thanks

If you have a camera class, you can just update the bad guy position relative to the camera’s. So update the badguy with whatever he’s supposed to do, then you can do something like badguy.x = badguy.x - camera.dx, where dx is the amount the screen scrolls when the player moves.

A real camera class would translate the graphics for you (such as libgdx’s) allowing you to render at real coordinates and have the camera do the math for you.

Hi,

I guess, I draw the bad guys in the same batch.begin as the map sprites?

Thanks

Yes, just had them being drawn to wrong spritebatch :slight_smile: Using libgdx camera, so all ok now.

Any advice on writing a sprite class that basically moves a sprite along a platform and when reaches block in way, either jumps up or just goes back or something…

I guess also, just update the bad guy sprites when camera can see them or have around 100 pixels out of camera view left/right/up/down to make it more realistic?

You could do something like this:

//start badguy moving
dx = 2;

if badguy hits block, either doJumpAction(), or change dx to -2 so he goes the other way.
x += dx

As for the 2nd question, that’s what I do.

Thanks, I thought something like that.

Also thought that when player is in line of sight with bad guy the bad guy comes towards them.

Thought about using a simple state machine, so each bad guy has a move method, I have this as a first go:


	public void move(float x, float y) {
		switch(currentState)
		{
		case IDLE:
			if(Math.abs(this.x - camera.position.x ) < 100)
				currentState = State.CHASE;
                        doIdleMovement();
			break;
		case CHASE: 
			if(Math.abs(this.x - camera.position.x ) > 100)
				currentState = State.IDLE;
                        chasePlayer();
			break;
		}
	}


So, when bad guy is within certain range of player their state changes to chase - in which case, they will move towards the player quickly.
When state is idle, the bad guy will I guess just steer to left/right.

My issue though would be, if player not on same horizontal as bad guy, how would you handle the bad guy to chase the player then?

One way you can do this is to implement a pathfinding algorithm like A*.

One way to avoid this need for certain powerful bosses is to give them the power to teleport around the map, bypassing all pathfinding problems :slight_smile:

Thanks ags1,

I’ve done A* pathfinding before which is a way to do this, may leave that until later though.

Not sure what Terraria uses, but looks simple. Slimes move left/right then jump when near player - guess in the jump method need to check if collided with a block
else they would be jumping through them! Zombies seems to fall down holes after you - maybe this is A*?

You could take the player position and bad guy position then use the distance formula

Why would you do any collision checking in your jump method? All your collision should be handled in one place like an update method for each entity.

If I remember correctly, slimes jump in that game regardless of any walls or ceilings (their AI is “dumb”) and I think zombies do use A* to try to find you. You would definitely need to put a cap on how far it can pathfind though because it can’t possibly search the entire map quick enough. Usually you can try pathfinding to the player and if it fails to find a path it would just attempt to go to the player’s X coordinate and jump constantly if it’s too low. You’ll also have to have your A* pathfinding to handle gravity (jump when at a wall, so to speak).

Hi,

Sorry, collision checks are now in my update method of the entities since last post to this thread.

A* - not so sure how to go about this when the world is dynamic, for instance, working out the best path and player has now put blocks in the way etc since that path was worked out, guess the path-finding would have to be called on each update for the area the particular entity is in within field of view of the player?

Slime AI is as follows:

Slimes exhibit the following behavior in Terraria:

  • Chase the player by jumping towards them
  • Float on water
  • Jump up to higher Wood Platforms, but can not drop down them
  • Take damage from Lava, except the Lava Slime

Many thanks for your advice,

Steve

To manage dynamic terrain with A*, you could try the following:

  1. If gidrah has no current path, plot a new path with A*. May be spread over several frames while gidrah is standing still, pondering which way to go; or if you’ve got enough CPU, do the entire pathfind in one frame. Presumably the path will be in the form of tile-to-tile movements.
  2. Advance gidrah along current path a tile at a time. If something transient or dynamic blocks it during a move, return to previous tile if possible, and discard the current path and go to 1.
  3. Perhaps periodically scan each tile remaining on its path for new fixed obstructions - at the end of each tile movement. If any path element becomes newly impassable, discard the current path and go to 1.
  4. Perhaps check that the objective location has changed every tile too. If it has, discard the current path and go to 1. You might want to change the frequency of the objective check depending on distance-to-target; if it’s a long way away, there’s no point in checking, etc.

Cas :slight_smile:

Thanks Cas,

Got plenty of CPU, but does everybody else ;D

Maybe look at Breadth-first search as all tiles same size?!

Cheers

It depends on whether you have variably weighted costs for moving from one tile to the next. Djikstra’s algorithm might be more straightforward.

Cas :slight_smile: