SO I'm making a game where the user moves on a horizontal, vertical tiled map. However I dont want the user to be able to press 2 keys and make the player move diagonal. ony left,right,up and down.
I currently have a code that does it but if the user presses another key and then lifts it, the player stops moving due to how I have coded it
#Override
public boolean keyDown(int keycode) {
if (keyNotDown) {
switch (keycode) {
case Keys.RIGHT:
playScreen.setXD(2);
break;
case Keys.LEFT:
playScreen.setXD(-2);
break;
case Keys.DOWN:
playScreen.setYD(-2);
break;
case Keys.UP:
playScreen.setYD(2);
break;
}
keyNotDown = false;
}
return false;
}
#Override
public boolean keyUp(int keycode) {
player.stopMove();
keyNotDown = true;
return false;
}
As you can see if a person presses a key while another one is pressed, it will not work but when they lift that said key, then it'll stop moving
EDIT
When the user presses down a key it sends the xd,yD values to the render method and because render keeps being called, it keeps on moving the player with the xD,yD values. The stop move method resets those values to 0 so it stops moving when the key is not being pressed.
Try this following code and let me know, because I haven't test it.
private KEY_PRESSE keyPressed = KEY_PRESSE.NONE;
#Override
public boolean keyDown(int keycode) {
switch (keycode) {
case Keys.RIGHT:
if (keyPressed.equals(KEY_PRESSE.RIGHT) || keyPressed.equals(KEY_PRESSE.NONE)) {
playScreen.setXD(2);
keyPressed = KEY_PRESSE.RIGHT;
break;
}
case Keys.LEFT:
if (keyPressed.equals(KEY_PRESSE.LEFT) || keyPressed.equals(KEY_PRESSE.NONE)) {
playScreen.setXD(-2);
keyPressed = KEY_PRESSE.LEFT;
break;
}
case Keys.DOWN:
if (keyPressed.equals(KEY_PRESSE.DOWN) || keyPressed.equals(KEY_PRESSE.NONE)) {
playScreen.setYD(-2);
keyPressed = KEY_PRESSE.DOWN;
break;
}
case Keys.UP:
if (keyPressed.equals(KEY_PRESSE.UP) || keyPressed.equals(KEY_PRESSE.NONE)) {
playScreen.setYD(2);
keyPressed = KEY_PRESSE.UP;
break;
}
}
return false;
}
#Override
public boolean keyUp(int keycode) {
player.stopMove();// I don't know exactly but can you remove this ligne, I
// think its useless , when no key is pressed
// the player will stop no?
keyPressed = KEY_PRESSE.NONE;
return false;
}
enum KEY_PRESSE {
RIGHT, LEFT, DOWN, UP, NONE
}
Well normally I don't post a full solution but its fairly simple what you want to do. The solution offers you a different way to implement your design. Since your speed is constant I figure out don't need to set the delta but only change the direction.
You can keep track of the key presses and based on what is pressed you can determine the direction to move your character. So for instance, when you press the up arrow, it sets the direction to up, but when you press left I track it but ignore the button. If you release the up arrow, then your character will move left and at no point will it move in more than 1 direction.
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
public class Move implements ApplicationListener {
private static final Map<Integer, Direction> MOVE_KEYS = new HashMap<Integer, Direction>(){{
this.put(Keys.RIGHT, Direction.RIGHT);
this.put(Keys.LEFT, Direction.LEFT);
this.put(Keys.UP, Direction.UP);
this.put(Keys.DOWN, Direction.DOWN);
}};
private static enum Direction {
RIGHT, LEFT, UP, DOWN;
}
private static class Player {
public float x;
public float y;
public float speed;
public Queue<Direction> dirStack = new LinkedList<Direction>();
public Player(float x, float y, float speed) {
this.x = x;
this.y = y;
this.speed = speed;
}
public void update(float delta) {
if (!dirStack.isEmpty()) {
switch(dirStack.peek()) {
case DOWN:
y -= speed;
break;
case LEFT:
x -= speed;
break;
case RIGHT:
x += speed;
break;
case UP:
y += speed;
break;
}
}
}
public void render(ShapeRenderer render) {
render.begin(ShapeType.FilledRectangle);
render.setColor(1, 0, 0, 1);
render.filledRect(x, y, 20, 20);
render.end();
}
}
private static class MoveController extends InputAdapter {
private final Player player;
public MoveController(Player player) {
this.player = player;
}
#Override public boolean keyDown(int keycode) {
if (MOVE_KEYS.containsKey(keycode)) {
final Direction dir = MOVE_KEYS.get(keycode);
Gdx.app.log("D", dir.toString());
return player.dirStack.add(dir);
}
return false;
}
#Override public boolean keyUp(int keycode) {
if (MOVE_KEYS.containsKey(keycode)) {
final Direction dir = MOVE_KEYS.get(keycode);
Gdx.app.log("U", dir.toString());
return player.dirStack.remove(dir);
}
return false;
}
}
private Camera cam;
private ShapeRenderer render;
private Player player;
#Override public void create() {
Gdx.app.log("CREATE", "App Opening");
this.player = new Player(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2, 5);
Gdx.input.setInputProcessor(new MoveController(player));
this.cam = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
this.render = new ShapeRenderer();
cam.apply(Gdx.gl10);
Gdx.gl10.glClearColor(0f, 0f, 0f, 1);
}
#Override public void render() {
Gdx.gl10.glClear(GL20.GL_COLOR_BUFFER_BIT);
player.update(Gdx.graphics.getDeltaTime());
player.render(render);
}
#Override public void dispose() {
Gdx.app.log("DISPOSE", "App Closing");
}
#Override public void resize(final int width, final int height) {
Gdx.app.log("RESIZE", width + "x" + height);
Gdx.gl10.glViewport(0, 0, width, height);
}
#Override public void pause() {}
#Override public void resume() {}
}
Related
I am making an RPG game, however, I have an error at the moment. The player's character can move in all four cardinal directions, but the player becomes stuck if you move right, up or down.
Also, the error seems to have some logic to it:
if moving down, the character is stuck in a loop moving down
unless the up arrow is pressed, then the player will begin a new infinite loop for up
unless the right arrow is pressed, then the player will begin a new infinite loop for right
So right seems to take precedence over up which takes precedence over down.
Strangely, the leftward movement works perfectly. Even when the character is stuck in an infinite loop, the left arrow key will always cause the player to move left in the correct way.
I don't understand where the error is in my code. I don't know why the leftward movement works while the other three directions do not. I don't understand why there seems to be some sort of priority ordering between the other three directions as well.
I have looked through the program and I don't think I treat leftward movement differently to any other direction. However, I might be wrong, considering this error.
Due to the 30k word limit, I do not have enough space to include all the code here. Therefore, I have included a link to my github, so you can see the code there instead.
https://github.com/davey67/bludbourne
I think these classes are probably the most important classes though:
package com.bludbourne.game.desktop;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Application;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import com.bludbourne.game.BludBourne;
public class DesktopLauncher {
public static void main (String[] arg) {
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.title="BludBourne";
config.useGL30=false;
config.width =480;
config.height=480;
Application app = new LwjglApplication(new BludBourne(),config);
Gdx.app=app;
Gdx.app.setLogLevel(Application.LOG_DEBUG);
}
}
/
package com.bludbourne.game;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.bludbourne.game.screens.MainGameScreen;
import com.badlogic.gdx.Game;
public class BludBourne extends Game {
public static final MainGameScreen _mainGameScreen = new MainGameScreen();
#Override
public void create () {
setScreen(_mainGameScreen);
}
#Override
public void dispose () {
_mainGameScreen.dispose();
}
}
/
package com.bludbourne.game;
import java.util.HashMap;
import java.util.Map;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.math.Vector3;
public class PlayerController implements InputProcessor
{
private final static String TAG = PlayerController.class.getSimpleName();
enum Keys{
LEFT,RIGHT,UP,DOWN,QUIT
}
enum Mouse{
SELECT,DOACTION
}
private static Map<Keys,Boolean> keys=new HashMap<PlayerController.Keys,Boolean>();
private static Map<Mouse,Boolean> mouseButtons = new HashMap<PlayerController.Mouse,Boolean>();
private Vector3 lastMouseCoordinates;
static {
keys.put(Keys.LEFT,false);
keys.put(Keys.RIGHT,false);
keys.put(Keys.UP,false);
keys.put(Keys.DOWN, false);
keys.put(Keys.QUIT, false);
}
static {
mouseButtons.put(Mouse.SELECT, false);
mouseButtons.put(Mouse.DOACTION, false);
}
private Entity _player;
public PlayerController(Entity player) {
this.lastMouseCoordinates=new Vector3();
this._player=player;
}
#Override
public boolean keyDown(int keycode)
{
if(keycode ==Input.Keys.LEFT||keycode==Input.Keys.A) {
this.leftPressed();
}
if(keycode ==Input.Keys.RIGHT||keycode==Input.Keys.D) {
this.rightPressed();
}
if(keycode ==Input.Keys.UP||keycode==Input.Keys.W) {
this.upPressed();
}
if(keycode ==Input.Keys.DOWN||keycode==Input.Keys.S) {
this.downPressed();
}
if(keycode==Input.Keys.Q) {
this.quitPressed();
}
return true;
}
#Override
public boolean keyUp(int keycode)
{
if(keycode ==Input.Keys.LEFT||keycode==Input.Keys.A) {
this.leftReleased();
}
if(keycode ==Input.Keys.RIGHT||keycode==Input.Keys.D) {
this.rightReleased();
}
if(keycode ==Input.Keys.UP||keycode==Input.Keys.W) {
this.upReleased();
}
if(keycode ==Input.Keys.DOWN||keycode==Input.Keys.S) {
this.downReleased();
}
if(keycode==Input.Keys.Q) {
this.quitReleased();
}
return true;
}
#Override
public boolean keyTyped(char character)
{
// TODO Auto-generated method stub
return false;
}
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button)
{
if(button==Input.Buttons.LEFT||button==Input.Buttons.RIGHT) {
this.setClickedMouseCoordinates(screenX,screenY);
}
if(button==Input.Buttons.LEFT) {
this.selectMouseButtonPressed(screenX,screenY);
}
if(button==Input.Buttons.RIGHT) {
this.doActionMouseButtonPressed(screenX,screenY);
}
return true;
}
#Override
public boolean touchUp(int screenX, int screenY, int pointer, int button)
{
if(button==Input.Buttons.LEFT) {
this.selectMouseButtonReleased(screenX,screenY);
}
if(button==Input.Buttons.RIGHT) {
this.doActionMouseButtonReleased(screenX,screenY);
}
return true;
}
#Override
public boolean touchDragged(int screenX, int screenY, int pointer)
{
// TODO Auto-generated method stub
return false;
}
#Override
public boolean mouseMoved(int screenX, int screenY)
{
// TODO Auto-generated method stub
return false;
}
#Override
public boolean scrolled(int amount)
{
// TODO Auto-generated method stub
return false;
}
public void dispose() {
}
public void leftPressed() {
keys.put(Keys.LEFT,true);
}
public void rightPressed() {
keys.put(Keys.RIGHT,true);
}
public void upPressed() {
keys.put(Keys.UP,true);
}
public void downPressed() {
keys.put(Keys.DOWN,true);
}
public void quitPressed() {
keys.put(Keys.QUIT, true);
}
public void setClickedMouseCoordinates(int x,int y) {
lastMouseCoordinates.set(x,y,0);
}
public void selectMouseButtonPressed(int x,int y) {
mouseButtons.put(Mouse.SELECT,true);
}
public void doActionMouseButtonPressed(int x,int y) {
mouseButtons.put(Mouse.DOACTION, true);
}
public void leftReleased() {
keys.put(Keys.LEFT,false);
}
public void rightReleased() {
keys.put(Keys.RIGHT,true);
}
public void upReleased() {
keys.put(Keys.UP,true);
}
public void downReleased() {
keys.put(Keys.DOWN,true);
}
public void quitReleased() {
keys.put(Keys.QUIT, true);
}
public void selectMouseButtonReleased(int x,int y) {
mouseButtons.put(Mouse.SELECT, false);
}
public void doActionMouseButtonReleased(int x ,int y) {
mouseButtons.put(Mouse.DOACTION, false);
}
public void update(float delta) {
processInput(delta);
}
public static void hide() {
keys.put(Keys.LEFT, false);
keys.put(Keys.RIGHT, false);
keys.put(Keys.UP, false);
keys.put(Keys.DOWN, false);
keys.put(Keys.QUIT, false);
}
private void processInput(float delta) {
if(keys.get(Keys.LEFT)) {
_player.calculateNextPosition(Entity.Direction.LEFT,delta);
_player.setState(Entity.State.WALKING);
_player.setDirection(Entity.Direction.LEFT,delta);
}
else if(keys.get(Keys.RIGHT)) {
_player.calculateNextPosition(Entity.Direction.RIGHT,delta);
_player.setState(Entity.State.WALKING);
_player.setDirection(Entity.Direction.RIGHT,delta);
}
else if(keys.get(Keys.UP)) {
_player.calculateNextPosition(Entity.Direction.UP,delta);
_player.setState(Entity.State.WALKING);
_player.setDirection(Entity.Direction.UP,delta);
}
else if(keys.get(Keys.DOWN)) {
_player.calculateNextPosition(Entity.Direction.DOWN,delta);
_player.setState(Entity.State.WALKING);
_player.setDirection(Entity.Direction.DOWN,delta);
}
else if(keys.get(Keys.QUIT)) {
Gdx.app.exit();
}
else {
_player.setState(Entity.State.IDLE);
}
if(mouseButtons.get(Mouse.SELECT)) {
mouseButtons.put(Mouse.SELECT, false);
}
}
}
/
package com.bludbourne.game;
import java.util.UUID;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
public class Entity
{
private static final String TAG = Entity.class.getSimpleName();
private static final String _defaultSpritePath = "sprites/characters/Warrior.png";
private Vector2 _velocity;
private String _entityID;
private Direction _currentDirection = Direction.LEFT;
private Direction _previousDirection = Direction.UP;
private Animation _walkLeftAnimation;
private Animation _walkRightAnimation;
private Animation _walkUpAnimation;
private Animation _walkDownAnimation;
private Array<TextureRegion> _walkLeftFrames;
private Array<TextureRegion> _walkRightFrames;
private Array<TextureRegion> _walkUpFrames;
private Array<TextureRegion> _walkDownFrames;
protected Vector2 _nextPlayerPosition;
protected Vector2 _currentPlayerPosition;
protected State _state = State.IDLE;
protected float _frameTime = 0f;
protected Sprite _frameSprite = null;
protected TextureRegion _currentFrame = null;
public final int FRAME_WIDTH = 16;
public final int FRAME_HEIGHT = 16;
public static Rectangle boundingBox;
public enum State
{
IDLE, WALKING
}
public enum Direction
{
UP, RIGHT, DOWN, LEFT
}
public Entity() {
initEntity();
}
public void initEntity()
{
this._entityID = UUID.randomUUID().toString();
this._nextPlayerPosition = new Vector2();
this._currentPlayerPosition = new Vector2();
this.boundingBox = new Rectangle();
this._velocity = new Vector2(2f, 2f);
Utility.loadTextureAsset(_defaultSpritePath);
loadDefaultSprite();
loadAllAnimations();
}
public void update(float delta)
{
_frameTime = (_frameTime + delta) % 5;
setBoundingBoxSize(0f, 0.5f);
}
public void init(float startX, float startY)
{
this._currentPlayerPosition.x = startX;
this._currentPlayerPosition.y = startY;
this._nextPlayerPosition.x = startX;
this._nextPlayerPosition.y = startY;
}
public void setBoundingBoxSize(float percentageWidthReduced, float percentageHeightReduced)
{
float width;
float height;
float widthReductionAmount = 1.0f - percentageWidthReduced;
float heightReductionAmount = 1.0f - percentageHeightReduced;
if (widthReductionAmount > 0 && widthReductionAmount < 1)
{
width = FRAME_WIDTH * widthReductionAmount;
}
else
{
width = FRAME_WIDTH;
}
if (heightReductionAmount > 0 && heightReductionAmount < 1)
{
height = FRAME_HEIGHT * heightReductionAmount;
}
else
{
height = FRAME_HEIGHT;
}
if (width == 0 || height == 0)
{
Gdx.app.debug(TAG, "Width and Height are 0!! " + width + ":" + height);
}
float minX;
float minY;
if (MapManager.UNIT_SCALE > 0)
{
minX = _nextPlayerPosition.x / MapManager.UNIT_SCALE;
minY = _nextPlayerPosition.y / MapManager.UNIT_SCALE;
}
else
{
minX = _nextPlayerPosition.x;
minY = _nextPlayerPosition.y;
}
boundingBox.set(minX, minY, width, height);
}
private void loadDefaultSprite()
{
Texture texture = Utility.getTextureAsset(_defaultSpritePath);
TextureRegion[][] textureFrames = TextureRegion.split(texture, FRAME_WIDTH, FRAME_HEIGHT);
_frameSprite = new Sprite(textureFrames[0][0].getTexture(), 0, 0, FRAME_WIDTH, FRAME_HEIGHT);
_currentFrame = textureFrames[0][0];
}
public void loadAllAnimations()
{
Texture texture = Utility.getTextureAsset(_defaultSpritePath);
TextureRegion[][] textureFrames = TextureRegion.split(texture, FRAME_WIDTH, FRAME_HEIGHT);
_walkDownFrames = new Array<TextureRegion>(4);
_walkLeftFrames = new Array<TextureRegion>(4);
_walkRightFrames = new Array<TextureRegion>(4);
_walkUpFrames = new Array<TextureRegion>(4);
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
TextureRegion region=textureFrames[i][j];
if(region==null) {
Gdx.app.debug(TAG, "Got null animation frame "+i+","+j);
}
switch(i) {
case 0:
_walkDownFrames.insert(j,region);
break;
case 1:
_walkLeftFrames.insert(j,region);
break;
case 2:
_walkRightFrames.insert(j,region);
break;
case 3:
_walkUpFrames.insert(j,region);
break;
}
}
}
_walkDownAnimation = new Animation(0.25f,_walkDownFrames,Animation.PlayMode.LOOP);
_walkLeftAnimation = new Animation(0.25f,_walkLeftFrames,Animation.PlayMode.LOOP);
_walkRightAnimation = new Animation(0.25f,_walkRightFrames,Animation.PlayMode.LOOP);
_walkUpAnimation = new Animation(0.25f,_walkUpFrames,Animation.PlayMode.LOOP);
}
public void dispose() {
Utility.unloadAsset(_defaultSpritePath);
}
public void setState(State state) {
this._state = state;
}
public Sprite getFrameSprite() {
return _frameSprite;
}
public TextureRegion getFrame() {
return _currentFrame;
}
public Vector2 getCurrentPosition() {
return _currentPlayerPosition;
}
public void setCurrentPosition(float currentPositionX,float currentPositionY) {
_frameSprite.setX(currentPositionX);
_frameSprite.setY(currentPositionY);
this._currentPlayerPosition.x=currentPositionX;
this._currentPlayerPosition.y=currentPositionY;
}
public void setDirection(Direction direction,float deltaTime) {
this._previousDirection=this._currentDirection;
this._currentDirection=direction;
switch(_currentDirection) {
//not sure about this
case DOWN:
_currentFrame=(TextureRegion) _walkDownAnimation.getKeyFrame(_frameTime);
break;
case LEFT:
_currentFrame=(TextureRegion) _walkLeftAnimation.getKeyFrame(_frameTime);
break;
case RIGHT:
_currentFrame=(TextureRegion) _walkRightAnimation.getKeyFrame(_frameTime);
break;
case UP:
_currentFrame=(TextureRegion) _walkUpAnimation.getKeyFrame(_frameTime);
break;
default:
break;
}
}
public void setNextPositionToCurrent() {
setCurrentPosition(_nextPlayerPosition.x,_nextPlayerPosition.y);
}
public void calculateNextPosition(Direction currentDirection,float deltaTime) {
float testX=_currentPlayerPosition.x;
float testY=_currentPlayerPosition.y;
_velocity.scl(deltaTime);
switch(currentDirection) {
case LEFT:
testX-=_velocity.x;
break;
case RIGHT:
testX+=_velocity.x;
break;
case UP:
testY+=_velocity.y;
break;
case DOWN:
testY-=_velocity.y;
break;
default:
break;
}
_nextPlayerPosition.x=testX;
_nextPlayerPosition.y=testY;
_velocity.scl(1/deltaTime);
}
}
I feel that I am probably missing something obvious, however, I still cannot see the error. Help is much appreciated. Thank you.
My github also contains the sprite sheet and the three maps, all of which would be stored in the asset folder of my project.
May I draw your attention to these four methods in PlayerInput
public void leftReleased() {
keys.put(Keys.LEFT,false);
}
public void rightReleased() {
keys.put(Keys.RIGHT,true);
}
public void upReleased() {
keys.put(Keys.UP,true);
}
public void downReleased() {
keys.put(Keys.DOWN,true);
}
I believe it should be false, not true when the key is released. I've verified that this prevents the infinite loop.
The priority order of right, up, then down is due to the order in which the keys are checked in processInput.
I am trying to code a very simple Pong-game, I think that is the name of this old game where a ball bounces between the player and a bunch of bricks with the ball going up and down. The player can move horizontally at the bottom.
So basically just some moving rectangles, eventually they are supposed to collide and so on.
However, I can allow the player to move the Rectangle with the buttons, but I can't manage to make objects move with the help of the AnimationTimer. (javafx.animation.AnimationTimer)
I already searched a lot but only found examples with mistakes that I managed to avoid.
(eg. like this AnimationTimer & JavaFX: Rectangle won't move horizontally using the setTranslateX() method. How to move Rectangle?).
I tried debugging it to see where the unexpected happens, but I couldn't find it. here is my code:
package bounceBall;
import java.util.ArrayList;
import java.util.List;
import javafx.scene.control.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class BounceBallApp extends Application {
private Pane root = new Pane();
private Sprite player = new Sprite(400, 500, 40, 4, "player", Color.BLUE);
private boolean play;
private Parent createContent() {
this.play = false;
root.setPrefSize(800, 600);
root.getChildren().add(player);
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
update();
}
};
timer.start();
for (int y = 0; y < 10; y++) {
for (int x = 0; x < 40; x++) {
Sprite s = new Sprite(20*x, y*8, 18, 5, "brick", Color.AQUA);
root.getChildren().add(s);
}
}
return root;
}
private List<Sprite> getSprites(){
List<Sprite> list = new ArrayList<Sprite>();
for (Node n : root.getChildren()) {
if (n instanceof Sprite) {
Sprite s = (Sprite)n;
list.add(s);
}
}
return list;
}
private void update() {
getSprites().forEach(b -> {
if (b.type.equals("bullet")) {
b.moveRight(b.getXspeed());
b.moveDown(b.getYspeed());
}
});
}
#Override
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(createContent());
scene.setOnKeyPressed(e -> {
//as soon as any key gets hit the game starts with the shoot method
if (play == false) {
shoot(player);
play = true;
}
switch (e.getCode()) {
case A:{
player.moveLeft(10);
break;
}
case D:{
player.moveRight(10);
break;
}
default:
break;
}
});
primaryStage.setScene(scene);
primaryStage.setTitle("Bounce Ball");
primaryStage.show();
}
/*this method should be called once at the beginning to initiate the ball
the ball appears but it doesn't move, instead remains still
if I try to make the player move with AnimationTimer it doesn't work either, it works just by pressing keys*/
private void shoot(Sprite who) {
Sprite s = new Sprite((int)who.getTranslateX(), (int)who.getTranslateY()-2, 4, 4, "bullet", Color.BLACK);
s.setYspeed(-10);
root.getChildren().add(s);
System.out.println(root.getChildren().size());
}
private static class Sprite extends Rectangle {
final String type;
private int Xspeed, Yspeed;
public Sprite(int x, int y, int w, int h, String type, Color color) {
super(w, h, color);
this.type = type;
setTranslateX(x);
setTranslateY(y);
Xspeed = 0;
Yspeed = 0;
}
public void moveLeft(int d) {
setTranslateX(getTranslateX() - d);
}
public void moveRight(int d) {
setTranslateX(getTranslateX() + d);
}
public void moveUp(int d) {
setTranslateX(getTranslateY() - d);
}
public void moveDown(int d) {
setTranslateX(getTranslateY() + d);
}
public int getXspeed() {
return Xspeed;
}
public int getYspeed() {
return Yspeed;
}
public void setXspeed(int xspeed) {
Xspeed = xspeed;
}
public void setYspeed(int yspeed) {
Yspeed = yspeed;
}
}
public static void main(String[] args) {
launch(args);
}
}
I expect the ball to go straight up at the beginning but it appears at the wrong position (ca. 50px right from where I expect).
Most importantly it doesn't move.
Thanks a lot in advance.
Here is my dilemma. I am having problems animating my lightning attack by using my GamePad class (I made this class for a game controller for android devices).
I found out that if using the Gdx.input.isKeyPressed(keyname), the code produces a lightning attack every frame, which stack on top of each other. So, I then used the Gdx.input.isKeyJustPressed() and it only creates one attack, that is animated, which is perfect.
So when making the GamePad class, I made images with listeners to handle touch events. For each button I have a touchDown and touchUp method. When running my character from left to right, these touch events work fine. However, similar to Gdx.input.isKeyPressed(), the lightning attack is created each frame and the attacks get layered on top of each other. I tried a workaround to fix the creation of an attack each frame, and now the image is just static, and does not animate. This is better than the 60 fps, but doesn't solve my problem.
is there a touchDown event similar to Gdx.input.isKeyJustPressed()? How could I fix the GamePad class to work similarly to the Config class?
GamePad.java
public class GamePad implements Disposable{
private Viewport viewport;
public Stage stage;
boolean leftPressed, rightPressed, pausePressed, aPressed, bReleased, bPressed, bPreviouslyPressed;
private Config config = Config.getInstance();
private Table table;
public GamePad(){
viewport = new FitViewport(EIUGame.V_WIDTH, EIUGame.V_HEIGHT, new OrthographicCamera());
stage = new Stage(viewport);
table = new Table();
table.setFillParent(true);
table.bottom();
bPreviouslyPressed = false;
// "Left" Button
Image leftImg = new Image(new Texture("controller/leftButton.png"));
leftImg.setSize(35, 35);
leftImg.addListener(new InputListener(){
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button){
leftPressed = true;
return true;
}
#Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button){
leftPressed = false;
}
});
// "Right" Button
Image rightImg = new Image(new Texture("controller/rightButton.png"));
rightImg.setSize(35, 35);
rightImg.addListener(new InputListener(){
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button){
rightPressed = true;
return true;
}
#Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button){
rightPressed = false;
}
});
// "Pause" Button
Image pauseImg = new Image(new Texture("controller/pauseButton.png"));
pauseImg.setSize(15, 15);
pauseImg.addListener(new InputListener(){
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button){
pausePressed = true;
return true;
}
#Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button){
pausePressed = false;
}
});
// "A" Button
Image aImg = new Image(new Texture("controller/aButton.png"));
aImg.setSize(35, 35);
aImg.addListener(new InputListener(){
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button){
aPressed = true;
return true;
}
#Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button){
aPressed = false;
}
});
// "B" Button
Image bImg = new Image(new Texture("controller/bButton.png"));
bImg.setSize(35, 35);
bImg.addListener(new InputListener(){
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button){
bPressed = true;
setBReleased(false);
return true;
}
#Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button){
setBReleased(true);
bPreviouslyPressed = false;
bPressed = false;
}
});
table.add(leftImg).size(leftImg.getWidth(), leftImg.getHeight());
table.add().size(5,35);
table.add(rightImg).size(rightImg.getWidth(), rightImg.getHeight());
table.add().size(100, 35);
table.add(pauseImg).size(pauseImg.getWidth(), pauseImg.getHeight()).bottom();
table.add().size(100, 35);
table.add(bImg).size(bImg.getWidth(), bImg.getHeight());
table.add().size(5,35);
table.add(aImg).size(aImg.getWidth(), aImg.getHeight());
stage.addActor(table);
}
// Returns the stage object so that it can be added to a multiplexer
public Stage getStage() {
return stage;
}
public void draw(){
stage.draw();
}
public boolean isLeftPressed(){
return leftPressed;
}
public boolean isRightPressed(){
return rightPressed;
}
public boolean isPausePressed(){
return pausePressed;
}
public boolean isAPressed(){
return aPressed;
}
public boolean isBPressed(){
return bPressed;
}
public boolean isBPreviouslyPressed(){
return bPreviouslyPressed;
}
public boolean isBReleased(){
return bReleased;
}
public void setBReleased(boolean released){
bReleased = released;
}
public void resize(int width, int height){
viewport.update(width, height);
}
public void animateChamp(Champion champ, PauseState pause){
// Move Champion Right
if (isRightPressed() && champ.b2body.getLinearVelocity().x <= 2)
config.runRight(champ);
// Move Champion left
if (isLeftPressed() && champ.b2body.getLinearVelocity().x >= -2)
config.runLeft(champ);
// If A button is pressed and we are not jumping or falling, then Jump.
if (isAPressed() && (champ.getState() != champState.JUMPING && champ.getState() != champState.FALLING)){
config.jump(champ);
aPressed = false;
}
// Toggle Pause Menu
if (isPausePressed())
pause.togglePause();
// Precondition for next else-if statement
if (isBPressed() && champ.b2body.getLinearVelocity().x == 0 && champ.b2body.getLinearVelocity().y == 0){
bPressed = false;
bPreviouslyPressed = true;
}
// If b was pressed down but not released, and champion is not moving, use lightning attack
else if (bPreviouslyPressed && !isBReleased() && champ.b2body.getLinearVelocity().x == 0 && champ.b2body.getLinearVelocity().y == 0){
champ.setMobileTrigger(true); // Sets champion state to attacking region
config.setMLightningActive(true);
config.lightningAttack(champ);
}
// Exit lightning attack if moved
else if (!isBReleased() && (champ.b2body.getLinearVelocity().x != 0 || champ.b2body.getLinearVelocity().y != 0)){
champ.setMobileTrigger(false);
config.setMLightningActive(false);
bReleased = true;
}
// Exit lightning attack if button released
else if (isBReleased() && config.getMLightningActive()){
champ.setMobileTrigger(false); // Does not alter champion state
config.setMLightningActive(false);
bReleased = true;
}
// Attack when moving
else if (isBPressed()){
config.attack(champ);
bPressed = false;
}
}
#Override
public void dispose(){
stage.dispose();
}
}
Config.java
public final class Config {
private static final Config instance = new Config();
private int moveLeft;
private int moveRight;
private int jump;
private int attack;
private String lStr;
private String rStr;
private String jStr;
private String aStr;
private boolean lightningActive = false;
private boolean MlightningActive = false; // Mobile Game
// Default constructor sets the keys to a default schema
private Config() {
moveLeft = Input.Keys.A;
moveRight = Input.Keys.D;
jump = Input.Keys.L;
attack = Input.Keys.J;
lStr = "A";
rStr = "D";
jStr = "L";
aStr = "J";
}
// Return the instance of the class
public static Config getInstance() {
return instance;
}
public void animateChamp(Champion champ){
// Jump!
if(Gdx.input.isKeyJustPressed(jump) && (champ.getState() != champState.JUMPING && champ.getState() != champState.FALLING))
jump(champ);
// Run Right (and make sure character is not moving faster than 2)
if(Gdx.input.isKeyPressed(moveRight) && champ.b2body.getLinearVelocity().x <= 2)
runRight(champ);
// Run Left (and make sure character is not moving faster than 2)
if(Gdx.input.isKeyPressed(moveLeft) && champ.b2body.getLinearVelocity().x >= -2)
runLeft(champ);
// Lightning Attack
if(Gdx.input.isKeyJustPressed(attack) && champ.b2body.getLinearVelocity().x == 0 && champ.b2body.getLinearVelocity().y == 0){
setLightningActive(true);
lightningAttack(champ);
}
else if (getlightningActive() && (champ.b2body.getLinearVelocity().x != 0 || champ.b2body.getLinearVelocity().y != 0 || !Gdx.input.isKeyPressed(attack)))
setLightningActive(false);
else if (Gdx.input.isKeyJustPressed(attack))
attack(champ);
}
public void runRight(Champion champ){
champ.b2body.applyLinearImpulse(new Vector2(0.1f,0), champ.b2body.getWorldCenter(), true);
}
public void runLeft(Champion champ){
champ.b2body.applyLinearImpulse(new Vector2(-0.1f,0), champ.b2body.getWorldCenter(), true);
}
public void jump(Champion champ){
champ.b2body.applyLinearImpulse(new Vector2(0, 4.5f), champ.b2body.getWorldCenter(), true);
}
public void attack(Champion champ){
champ.attack();
}
public void lightningAttack(Champion champ){
champ.lightningAttack();
}
public boolean getlightningActive(){
return lightningActive;
}
public void setLightningActive(boolean value){
lightningActive = value;
}
// For Mobile Version
public boolean getMLightningActive(){
return MlightningActive;
}
// For Mobile Version
public void setMLightningActive(boolean value){
MlightningActive = value;
}
// sets the key to move left
public void setMoveLeft(String n){
moveLeft = Input.Keys.valueOf(n.toUpperCase());
lStr = n;
}
// sets the key to move right
public void setMoveRight(String n) {
moveRight = Input.Keys.valueOf(n.toUpperCase());
rStr = n;
}
// sets the key to jump
public void setJump(String n) {
jump = Input.Keys.valueOf(n.toUpperCase());
jStr = n;
}
// sets the key to attack
public void setAttack(String n) {
attack = Input.Keys.valueOf(n.toUpperCase());
aStr = n;
}
// Returns the string representation of the move left key
public String getMoveLeft(){
return lStr;
}
// Returns the string representation of the move right key
public String getMoveRight() {
return rStr;
}
// Returns the string representation of the jump key
public String getJump() {
return jStr;
}
// Returns the string representation of the attack key
public String getAttack() {
return aStr;
}
// Returns the int representation of the attack key
public int getAttackInt(){
return attack;
}
}
If I might suggest an alternative approach, it sounds like what you really want is to apply a cooldown to your lightning attack- a cooldown keeps it from firing repeatedly if the player holds the button down, and it gives you some control over how fast the player can fire it if they pound the keyboard (you may want to let the player "upgrade" their lightning attack to something faster in the future).
I threw together an example below:
public class Cooldown {
private float cooldownTime = 0;
private float length = 0;
private Action action;
public Cooldown(float length, Action action) {
this.length = length;
this.action = action;
}
public boolean update(float delta) {
// Subtract the delta until we hit 0
this.cooldownTime = this.cooldownTime - delta <= 0 ? 0 : this.cooldownTime - delta;
// The boolean tells you that the cooldown has expired- useful for special effects
return this.cooldownTime == 0;
}
public void execute() {
if(this.cooldownTime > 0) return;
this.cooldownTime = this.length;
this.action.execute();
}
public interface Action {
void execute();
}
}
To use this class, simply add something like the following somewhere under your player's constructor:
this.lighteningAttack = new Cooldown(0.25f, new LightningAttack());
Then in your gameloop somewhere:
this.lighteningAttack.update(deltaTime);
Finally, in your eventlistener:
this.lighteningAttack.execute();
No matter how many times you press the button, it should only execute four times a second.
I have asked a question regarding this same program before. I solved the issues I had then, and I now have rooted out a different problem. When I run the code it shows a small blue box on the screen. It is supposed to move when you press the arrow keys and I have discovered that if you click the character and hold an arrow key for a moment then it will move. What I need is to make it refreshes the screen automatically, I believe I need an update() method for that, but I am not sure. If anybody can help me figure this out or improve my code in some way that would be helpful. I made some changes based off of some comments I received on my previous question.
CharacterTest.java:
import javax.swing.JFrame;
import java.awt.Graphics;
import javax.swing.JPanel;
import java.awt.Color;
import javax.swing.JLabel;
import javax.swing.ImageIcon;
import java.awt.BorderLayout;
import java.awt.Canvas;
//import java.awt.event.KeyEvent;
import java.awt.event.*;
public class CharacterTest extends JFrame{
public CharacterTest()
{
super("Character Test"); //instantiate a window to draw the character in
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //this will stop the program when it closes
setSize(800, 800); //create the window
MCharacter C = new MCharacter(); //call the box so that it can be affected
getContentPane().add(C); //draws the character on the window
setVisible(true); //show the window
while(C.determine(C.getX(), C.getY()) == false) //as long as the character is witin a particular area
{
if(C.getUpKey() == true) //if the up arrow key is pressed
{
C.up(); //set the y variable to a higher position
}
if(C.getDownKey() == true) //if the down arrow key is pressed
{
C.down(); //set the y variable to a lower positon
}
if(C.getRightKey() == true) //if the right arrow key is pressed
{
C.right(); //set the x variable ro a more right position
}
if(C.getLeftKey() == true) //if the left key is pressed
{
C.left(); //set the x variable to a more left position
}
repaint(); //repaint the character at a new position
try {
Thread.sleep(20);
}
catch (InterruptedException ex){
}
}
}
public static void main(String[] args) {
CharacterTest test = new CharacterTest(); //calls the method which creates the screeen, the character, and checks for key srokes
}
}
MCharacter.java:
import javax.swing.JFrame;
import java.awt.Graphics;
import javax.swing.JPanel;
import java.awt.Color;
import javax.swing.JLabel;
import javax.swing.ImageIcon;
import java.awt.BorderLayout;
import java.awt.Canvas;
//import java.awt.event.KeyEvent;
import java.awt.event.*;
public class MCharacter extends Canvas{
//these will be the instance variables for the character's parameters- its size and its location
private int width;
private int height;
private int x;
private int y;
//these will be turned true if the corresponding key is pressed - defined in the private class line 132
private boolean leftKey = false;
private boolean rightKey = false;
private boolean upKey = false;
private boolean downKey = false;
//some constructors that would easily be called
public MCharacter()
{
setBackground(Color.WHITE);
setWidth(10);
setHeight(10);
setX(400);
setY(400);
addKeyListener(new MyKeyListener()); //adds a key listener as the private class below (line 152)
}
public MCharacter(int xPos, int yPos)
{
setBackground(Color.WHITE);
setWidth(10);
setHeight(10);
setX(xPos);
setY(yPos);
addKeyListener(new MyKeyListener()); //adds a key listener as the private class below (line 152)
}
public MCharacter(int wth, int hgt, int xPos, int yPos)
{
setBackground(Color.WHITE);
setWidth(wth);
setHeight(hgt);
setX(xPos);
setY(yPos);
addKeyListener(new MyKeyListener()); //adds a key listener as the private class below (line 152)
}
//setters for each of the variables
public void setWidth(int wth)
{
width = wth;
}
public void setHeight(int hgt)
{
height = hgt;
}
public void setX(int xPos)
{
x = xPos;
}
public void setY(int yPos)
{
y = yPos;
}
//getters for each of the varaibles
public int getWidth()
{
return width;
}
public int getHeight()
{
return height;
}
public int getX()
{
return x;
}
public int getY()
{
return y;
}
//used to determine if the character should move
public void setUpKey(boolean setUp)
{
upKey = setUp;
}
public void setDownKey(boolean setDown)
{
downKey = setDown;
}
public void setRightKey(boolean setRight)
{
rightKey = setRight;
}
public void setLeftKey(boolean setLeft)
{
leftKey = setLeft;
}
public boolean getUpKey()
{
if(upKey == true)
{
return true;
}
return false;
}
public boolean getDownKey()
{
if(downKey == true)
{
return true;
}
return false;
}
public boolean getRightKey()
{
if(rightKey == true)
{
return true;
}
return false;
}
public boolean getLeftKey()
{
if(leftKey == true)
{
return true;
}
return false;
}
//the following class is goign to be used to determine if an arrow key is being pressed
private class MyKeyListener extends KeyAdapter{
public void keyPressed(KeyEvent e){
switch (e.getKeyCode()){
case KeyEvent.VK_LEFT: //if left key is pressed
setLeftKey(true); //set this boolean to true
break;
case KeyEvent.VK_RIGHT: //if right key is pressed
setRightKey(true); //set this boolean to true
break;
case KeyEvent.VK_UP: //if the up key is pressed
setUpKey(true); //set this boolean to true
break;
case KeyEvent.VK_DOWN: //if the down key is pressed
setDownKey(true); //set this boolean to true
break;
}
}
public void keyReleased(KeyEvent e){
switch (e.getKeyCode()){
case KeyEvent.VK_LEFT: //if left key is released
setLeftKey(false); //set this boolean to false
break;
case KeyEvent.VK_RIGHT: //if right key is released
setRightKey(false); //set this boolean to false
break;
case KeyEvent.VK_UP: //if up key is released
setUpKey(false); //set this boolean to false
break;
case KeyEvent.VK_DOWN: //if down key is released
setDownKey(false); //set this boolean to false
break;
}
}
}
//I am going to call the paint method here and create a small box that I will use as the character
public void paint(Graphics window)
{
window.setColor(Color.BLUE); //sets the color of the character
window.fillRect(x, y, width, height); //sets the dimensions of the character
}
//this method will be used to keep checking for a key pressed: while this is false check for a keytyped
public boolean determine(int x, int y)
{
if( x >= 10 && x <= 790 && y >= 10 && y <= 790)
{
return false;
}
return true;
}
//the following methods will be to move the character
public void up()
{
setY(getY()-2);
}
public void down()
{
setY(getY()+2);
}
public void right()
{
setX(getX()+2);
}
public void left()
{
setX(getX()-2);
}
}
To do this smoothly, you will have to move the while-loop out of the CharacterTest constructor and execute this on a separate Thread. Also define your leftKey, rightKey, upKey and downKey fields as volatile to prevent synchronization issues between the AWT/Swing Event Thread and your rendering Thread (since the event thread and your rendering thread may be accessing these fields at the same time).
If you execute the Canvas' repaint() from your own separate Thread, you should also setIgnoreRepaint(true) in MCharacter's constructor to prevent Swing from repainting your Canvas as well.
Also, to make your code more readable, I suggest you define a method in MCharacter called move() and execute the contents of your while loop there (it makes sense to 'move' a character). the run() method with the loop will then look like this:
#Override public void run() {
while (character.determine()) {
character.move();
}
}
Sorry guys, I don't know if the title is the best way to explain it. I'm making a pacman clone in java with libgdx. I've got the map rendered and collision detection on the tiles working. The only problem, the pacman is suppose to keep moving no matter what. When he is going right, and you press down or up which is blocked, he stops. I've tried a bunch of different things and can't seem to get it to work right.
This is my pacman player class
package com.psillidev.pacman1;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.math.Rectangle;
public class player {
private static int height = 16;
private static int width = 16;
private int playerX , playerY;
private int direction;
private Texture playerTexture;
private boolean isAlive;
public player(int startX,int startY) {
playerTexture = new Texture(Gdx.files.internal("data/pacman.png"));
playerX = startX;
playerY = startY;
}
public void move(Map map) {
if (direction == 0) {
if (isNoTile(map,direction,getX(),getY())) {
setY(getY() + (int)(Gdx.graphics.getDeltaTime() + 2));
}
}
if (direction ==1) {
if (isNoTile(map,direction,getX(),getY())) {
setX(getX() + (int)(Gdx.graphics.getDeltaTime() + 2));
}
}
if (direction ==2) {
if (isNoTile(map,direction,getX(),getY())) {
setY(getY() - (int)(Gdx.graphics.getDeltaTime() + 2));
}
}
if (direction ==3) {
if (isNoTile(map,direction,getX(),getY())) {
setX(getX() - (int)(Gdx.graphics.getDeltaTime() + 2));
}
}
}
private boolean isNoTile(Map map, int dir, int translated_x, int translated_y) {
System.out.println("X: " + translated_x + " Y: " + translated_y);
for (Rectangle r : map.getBlockedTiles()) {
switch (dir) {
case 0:
Rectangle up = new Rectangle(translated_x,translated_y + 2 , 16, 16);
if (up.overlaps(r)) {
return false;
}
break;
case 1:
Rectangle right = new Rectangle(translated_x+2,translated_y, 16, 16);
if (right.overlaps(r)) {
return false;
}
break;
case 2:
Rectangle down = new Rectangle(translated_x,translated_y - 2, 16, 16);
if (down.overlaps(r)) {
return false;
}
break;
case 3:
Rectangle left = new Rectangle(translated_x-2,translated_y, 16, 16);
if (left.overlaps(r)) {
return false;
}
break;
default:
break;
}
}
return true;
}
public void render(SpriteBatch batch) {
batch.draw(playerTexture, playerX, playerY);
}
public int getX() {
return playerX;
}
public void setX(int x) {
playerX = x;
}
public int getY() {
return playerY;
}
public void setY(int y) {
playerY = y;
}
public int getDirection() {
return direction;
}
public void setDirection(int dir) {
direction = dir;
}
public Rectangle getRect() {
return new Rectangle(playerX,playerY,width,height);
}
}
I can't answer from the code that you've supplied, but my suggestion is that you need to make an adjustment to your KeyListener class. I'm assuming that you have a KeyListener that is listening for the user pressing an arrow key. When a key is pressed, are you doing something like Pacman.setDirection()?
What you would actually need to do is detect whether the direction is valid before calling setDirection(). So your code of the KeyListener would be something like this...
Determine whether the user has pressed up, down, left, or right
Check the game grid/map to see if the direction is valid based on the current position of the Pacman
If the direction is valid, call setDirection(). If not, don't call anything, and the Pacman will continue in the current direction.
This really shouldn't be handled by the player itself but maybe by a movement manager, makes for easier testing/expansion of the game. In your code you only allow him to move to empty tiles in the direction your input decides, so he automatically stops moving if you press UP when walking to the right if UP is a wall, regardless of what is to the right.
What you can do is on input: If requested to move up, check if up is empty, then move up, if up is not empty then keeping moving in the direction he was previously moving. Same for other directions.
Your setDirection Method is incorrect it needs to be
public void setDirection(int dir) {
// only change direction if we can.
if (isValidDirection(dir)){
direction = dir;
}
}