Hi,
It would be easier (for me, anyway) to give an accurate answer if I saw your attempt.
What I have put together might be overkill, but this is it:
The base is an abstract class Game from which I instantiate my application. It contains a AWT.Frame which I pass an instance of KeyListener on initialisation. This base class also contains a variable to hold the key state for keyboard input:
int[] keys = new int[255];
private class ApplicationInputAdapter implements KeyListener {
public void keyPressed(KeyEvent e) { keys[e.getKeyCode()] = AbstractInput.DOWN; }
public void keyReleased(KeyEvent e) { keys[e.getKeyCode()] = AbstractInput.UP; }
}
Then, I have an abstract class AbstractInput to which I will delegate input key (map will assign an input to an awt.Event.KeyEvent constant in a derived class).
public abstract class AbstractInput {
static final int UP = 0;
static final int DOWN = 1;
private final Game source;
protected AbstractInput(Game source) { this.source = source; }
protected int[] buttons;
public boolean button(int button) {
try { return (DOWN == source.keys[buttons[button]]); }
catch (Exception ex) { return false; }
}
public final void map(int key, int button) {
try { buttons[button] = key; }
catch (Exception ex) { }
}
}
To capture input for each game I derive a concrete Input class from AbstractInput:
public class Input extends Game.AbstractInput {
public static final int BUTTON_LEFT = 0;
public static final int BUTTON_RIGHT = 1;
public static final int BUTTON_A = 2;
public Input(Game source) { super(source); buttons = new int[3]; }
}
Input is then polled in my game loop in an update function, by passing in to p1’s input function:
@Override protected void gameUpdate() {
long dt = getEllapsedTicks();
p1.input(in, dt);
p1.update(dt);
}
The variable p1 is an implementation of class derived from an abstract Actor class and the actor’s input function delegates input polling and handling to the player’s current state like this:
public void input(Game.AbstractInput in, long dt) { getState().input(in, dt); }
The function getState() gets the actor’s current state and delegates (again) input to it:
public void input(Game.AbstractInput in, long dt) {
if (in.button(Input.BUTTON_LEFT)) {
actor.setState(Player.WALK);
actor.direction.setDirection(Direction.LEFT);
} else if (in.button(Input.BUTTON_RIGHT)) {
actor.setState(Player.WALK);
actor.direction.setDirection(Direction.RIGHT);
} else if (in.button(Input.BUTTON_A)) {
actor.setState(Player.JUMP);
actor.speed.setY(0.75f);
}
}
States are derived from an abstract State class for each of the actors possible states. So, when the jump key is pressed, the jump state is set as the Actor’s current state, which will handle input in the game’s loop next iteration and update the actor as explained next.
After input is handled, the actors update function is called, which in turn calls the current state’s update function:
public class PlayerStateJump extends State {
private static final float JUMP_ACC = 0.007f;
private float jumpSpeed = 0.0f;
@Override public void update(long dt) {
// Keep moving in x-direction with set speed
if (actor.direction.checkDirection(Direction.LEFT)) {
actor.position.setX(actor.position.getX() - actor.speed.getX() * dt);
} else {
actor.position.setX(actor.position.getX() + actor.speed.getX() * dt);
}
jumpSpeed = actor.speed.getY();
jumpSpeed -= JUMP_ACC * dt;
actor.speed.setY(jumpSpeed);
actor.position.setY(actor.position.getY() - actor.speed.getY() * dt);
if (jumpSpeed <= 0.0f) actor.setState(Player.FALL);
}
}
So, say my character is in state “idle”, that state polls if the jump button is “DOWN”; if so, the actors current state is set to an already declared and instantiated PlayerStateJump state. While in this state, at each iteration of my game loop, the jump state’s update function (see above) is called, which controls the jump height and direction. After that I have a simple collision detection, that takes action according to the player’s state.
if (p1.getState().checkState(Player.FALL)) {
if (onGround) {
p1.setState(Player.IDLE);
p1.speed.setY(0.0f);
} else {
applyGravity(p1, dt);
}
} else if (p1.getState().checkState(Player.JUMP)) {
if (hitCeiling) {
p1.setState(Player.FALL);
}
} else {
if (!onGround) {
p1.setState(Player.FALL);
applyGravity(p1, dt);
} else {
}
}
I short:
KeyPress/Release->Array stores key state “UP/DOWN”
In the game update function an instance of a class derived from AbstractInput class (in) is passed to the input functon of an instance of a class derived from Actor (p1)
The p1’s input function passes “in” to the input function of an instance of a class derived from PlayerState class (state)
In the input function of state input is finally polled (via AbstractInput.Button()) and handled (e.g. state change of the Actor derived class from “IDLE” to “JUMP” when the jump button is pressed)
We return to the game’s update function, where the actor is updated, collision check is performed and handled
I hope this helps pointing out to me where I need to make the necessary changes.