3
votes

I am working in a RPG 2d in libgdx/java using Box2d.

I have the main character and other 3 following. I would like them to follow the same path of the main character in line like a snake(same as Master System / Phantasy Star and other old RPG) to avoid messing up when found a narrow entrance.

enter image description here

In my PlayScreen class I call all characters individually.

    private static Alis _player;
    private static Myau _follower;
    private static Odin _follower1;
    private static Noah _follower2;

on the Constructor PlayScreen

_player = new Alis(world, this, mapManager);
_follower = new Myau(world, this, mapManager);
_follower1 = new Odin(world, this, mapManager);
_follower2 = new Noah(world, this, mapManager);

HandleInput(float dt) method I only move the main Character

if(Gdx.input.isKeyPressed(Input.Keys.RIGHT) && _player.b2body.getLinearVelocity().x <= 0.5f) {
_player.b2body.applyLinearImpulse(new Vector2(0.05f, 0), _player.b2body.getWorldCenter(), true);

}else if(Gdx.input.isKeyPressed(Input.Keys.LEFT) && _player.b2body.getLinearVelocity().x >= -0.5f) {
_player.b2body.applyLinearImpulse(new Vector2(-0.05f, 0), _player.b2body.getWorldCenter(), true);

}else if(Gdx.input.isKeyPressed(Input.Keys.RIGHT) == Gdx.input.isKeyPressed(Input.Keys.LEFT)){
_player.b2body.setLinearVelocity(0f, _player.b2body.getLinearVelocity().y);
}

if(Gdx.input.isKeyPressed(Input.Keys.UP) && _player.b2body.getLinearVelocity().y <= 0.5f) {
_player.b2body.applyLinearImpulse(new Vector2(0, 0.05f), _player.b2body.getWorldCenter(), true);
} else if(Gdx.input.isKeyPressed(Input.Keys.DOWN) && _player.b2body.getLinearVelocity().y >= -0.5f) {
_player.b2body.applyLinearImpulse(new Vector2(0, -0.05f), _player.b2body.getWorldCenter(), true);
} else if(Gdx.input.isKeyPressed(Input.Keys.UP) == Gdx.input.isKeyPressed(Input.Keys.DOWN)){          _player.b2body.setLinearVelocity(_player.b2body.getLinearVelocity().x, 0f);
}

update(float dt) method

handleInput(dt);
_player.update(dt);
_follower.update(dt, _player.b2body.getPosition());
_follower1.update(dt, _follower.b2body.getPosition());
_follower2.update(dt, _follower1.b2body.getPosition());

render(float delta) method

game.batch.begin();
_follower2.draw(game.batch);
_follower1.draw(game.batch);
_follower.draw(game.batch);
_player.draw(game.batch);
game.batch.end();

dispose() method

_player.dispose();
_follower.dispose();
_follower1.dispose();
_follower2.dispose();

Now, inside my characters class, on update(float dt, Vector2 position) method

if ((position.x - b2body.getPosition().x) > (15 /PhantasyStar.PPM)) {
            b2body.setLinearVelocity(new Vector2(0.5f, 0));

} else if ((position.x - b2body.getPosition().x) < ( - 15 /PhantasyStar.PPM)) {
            b2body.setLinearVelocity(new Vector2(-0.5f, 0));

} else if ((position.y - b2body.getPosition().y) > (15 / PhantasyStar.PPM)) {
            b2body.setLinearVelocity(new Vector2(0, 0.5f));

} else if ((position.y - b2body.getPosition().y) < (-15 / PhantasyStar.PPM)) {
            b2body.setLinearVelocity(new Vector2(0, -0.5f));

} else if (((position.y - b2body.getPosition().y) <= (15 / PhantasyStar.PPM) && (position.y - b2body.getPosition().y) >= (-15 / PhantasyStar.PPM)) || ((position.x - b2body.getPosition().x) <= (15 /PhantasyStar.PPM) && (position.x - b2body.getPosition().x) >= (-15 /PhantasyStar.PPM)) ) {
            b2body.setLinearVelocity(new Vector2(0, 0));
         }

setPosition(b2body.getPosition().x - getWidth() / 2, b2body.getPosition().y - getHeight() / 4);

setRegion(getFrame(dt));

This code gave me a good result, for example, if I walk only on x-axis or only y-axis, the players walk exactly how I need with a correct distance from each other and coming back correctly too, but if I start to move up and down they start to follow each other but every time I found an obstacle, some of the followers stuck. So I want them to follow the exactly path the main character leads.

I am 100% what I did is not correct, but I tried to find some literature for this and I didn't find. I believe if I found a way to get the last position of the main player and make it a target for the second.

I tried to collect after update the player position to be the last position and used on the next update in the follower, however, even I ask the follower to follow that position, it is coming too close, so I assume it is not the correct position to collect, or maybe it is a different way to do it. Should I need to create an Array to collect all the positions and pass this info to the other characters with delay?

It is somebody with more experience in game development to help me?

1
And for future reference, the rpg tag is for the RPG language, not for Role Playing Games.Federico klez Culloca
Thanks Federico, I am going to include my code.EL.ALEX78UK
As for me, I voted to close a lot earlier than your edit. Now I voted to reopen. If another two users do as well, your question will be reopened.Federico klez Culloca
Looks like people don't think this needs to be reopened. If you reopen it and someone notice this one exists, your new question will be closed again. You could try to delete this one and create a new one, but I can't guarantee your success.Federico klez Culloca
Why close so prematurely? Not even German bureaucracy is so strict...Beko

1 Answers

0
votes

On the PlayScreen Class create 2 Arrays variables. These two Arrays will collect the main player positions and states.

public class PlayScreen implements Screen {
private Array<Vector2> _playerPath;
private Array<Alis.State> _playerState;
}

On the PlayScreen Constructor, I initialized the Arrays, so after the .get and .set will work.

public PlayScreen(PhantasyStar game, MapManager mapManager){

//starting Array for player path and state
_playerPath = new Array<Vector2>(100);
_playerState = new Array<Alis.State>(100);
for(int k=0;k<100;k++) {
_playerPath.add(new Vector2(0f, 0f));
_playerState.add(Alis.State.STANDING_DOWN);
}
//Create Player
_player = new Alis(world, this, mapManager);
_follower = new Myau(world, this, mapManager);
_follower1 = new Odin(world, this, mapManager);
_follower2 = new Noah(world, this, mapManager);
}

Still on the PlayScreen Class, update method. Every position and every state are updated inside the Arrays, creating a path for the followers. I updated the followers update method with a delayed of 20 frames each.

public void update(float dt){

//start ***Items for the following path***

 for (int i = 99; i > 0; i--) {
    _playerPath.get(i).set(_playerPath.get(i - 1));
    _playerState.set(i,_playerState.get(i - 1));
 }
    _playerPath.get(0).set(_player.b2body.getPosition());
    _playerState.set(0, _player.currentState);

handleInput(dt);

world.step(1/60f, 6, 2);

_player.update(dt);

_follower.update(dt, _playerPath.get(20), _playerState.get(20));

_follower1.update(dt, _playerPath.get(40), _playerState.get(40));

_follower2.update(dt, _playerPath.get(60), _playerState.get(60));
// end ***Items for the following path ***


        gamecam.position.x = _player.b2body.getPosition().x;
        gamecam.position.y = _player.b2body.getPosition().y;

//removing the blank spaces in the borders stopping the camera
xOffset = (gamecam.position.x - PhantasyStar.V_WIDTH / PhantasyStar.PPM / 2);
xOffsetRight = (mapManager.getMapWidth() / PhantasyStar.PPM) - gamecam.position.x - (PhantasyStar.V_WIDTH / PhantasyStar.PPM / 2);

yOffset = (gamecam.position.y - (PhantasyStar.V_HEIGHT / PhantasyStar.PPM / 2));
yOffsetUp = (mapManager.getMapHeight() / PhantasyStar.PPM) - gamecam.position.y - (PhantasyStar.V_HEIGHT / PhantasyStar.PPM / 2);

if(xOffset < 0)
gamecam.position.x = PhantasyStar.V_WIDTH / PhantasyStar.PPM / 2;
if(xOffsetRight < 0)
gamecam.position.x = (mapManager.getMapWidth() / PhantasyStar.PPM) - (PhantasyStar.V_WIDTH / PhantasyStar.PPM / 2);

if(yOffset < 0)
gamecam.position.y = PhantasyStar.V_HEIGHT / PhantasyStar.PPM / 2;
if(yOffsetUp < 0)
gamecam.position.y = (mapManager.getMapHeight() / PhantasyStar.PPM) - (PhantasyStar.V_HEIGHT / PhantasyStar.PPM / 2);       
// end removing blank spaces

//portal contact
 if(isCollided) {
   updateMap();
 }

 gamecam.update();
 renderer.setView(gamecam);
 }

The next code is the Follower Class, using the position and state from the Arrays.

public TextureRegion getFrame(float dt, Alis.State state) {

        currentState = getState(state);

        TextureRegion region;

        switch (currentState) {
            case RUN_UP:
                region = myauRun_Up.getKeyFrame(stateTimer, true);
                break;
            case RUN_DOWN:
                region = myauRun_Down.getKeyFrame(stateTimer, true);
                break;
            case RUN_RIGHT:
                region = myauRun_Right.getKeyFrame(stateTimer, true);
                break;
            case RUN_LEFT:
                region = myauRun_Left.getKeyFrame(stateTimer, true);
                break;
            case STANDING_UP:
                region = myauStand_Up;
                break;
            case STANDING_RIGHT:
                region = myauStand_Right;
                break;
            case STANDING_LEFT:
                region = myauStand_Left;
                break;

            case STANDING_DOWN:
            default:
                region = myauStand_Down;
                break;

        }
        stateTimer = currentState == previousState ? stateTimer + dt : 0;
        previousState = currentState;
        return region;
    }

    public State getState(Alis.State state) {

        if (state == Alis.State.RUN_UP)
            return State.RUN_UP;
        else if (state == Alis.State.RUN_DOWN)
            return State.RUN_DOWN;
        else if (state == Alis.State.RUN_RIGHT)
            return State.RUN_RIGHT;
        else if (state == Alis.State.RUN_LEFT)
            return State.RUN_LEFT;
        else if (state == Alis.State.STANDING_UP)
            return State.STANDING_UP;
        else if (state == Alis.State.STANDING_LEFT)
            return State.STANDING_LEFT;
        else if (state == Alis.State.STANDING_DOWN)
            return State.STANDING_DOWN;
        else
            return State.STANDING_DOWN;

    }

    public void update(float dt, Vector2 position, Alis.State state) {

        setPosition(position.x - getWidth() / 2, position.y - getHeight() / 4);

        setRegion(getFrame(dt, state));

    }

I hope this can help others