Игра движения на основе плитки в Java 2D

Недавно я начал свой первый «настоящий» проект. Моя цель — написать первое поколение покемонов на Java. Мой код хорошо ориентирован на видео на YouTube (https://www.youtube.com/watch?v=dEKs-3GhVKQ&list=PLah6faXAgguMnTBs3JnEJY0shAc18XYQZ&index=1). Я многому научился, работая с учебником, и до сих пор все понял. Теперь я нахожусь в точке, чтобы интегрировать свое движение на основе плитки, и я действительно не уверен, правильно ли я сделал это. Проблема в том, что если я визуализирую игрока только по плиткам (или перемещаю игрока только по плиткам), он перескакивает с одной плитки на другую. Это больше не гладкая анимация ходьбы. Я решил эту проблему, установив таймер на нажатые клавиши. Если u нажимает клавишу, для определения нажатия клавиши устанавливается значение true в течение определенного времени. Проблема в том, что я не могу хорошо контролировать скорость своего плеера. Теперь вопрос, можно ли лучше решить эту проблему, может быть, с помощью твининга? Или есть другой способ?

Я новичок в программировании игр, поэтому любые советы или помощь будут очень любезны.

Вот весь проект:
https://github.com/komiza24/Pokemom

Здесь установлен таймер на время нажатия клавиши: https://github.com/komiza24/Pokemom/blob/main/src/input/KeyManager.java

public class KeyManager implements KeyListener {

    private boolean[] keys;
    public boolean up, down, left, right;
    private long lastTime, timer;
    private float timeToWaitUntilTick;

    public KeyManager() {
        keys = new boolean[256];
        timer = 0;
        lastTime = System.currentTimeMillis();
        timeToWaitUntilTick = 520.00f ;  
                

    }

    public void tick() {
        
        timer += System.currentTimeMillis() - lastTime;
        lastTime = System.currentTimeMillis();
        
        
         if (timer > timeToWaitUntilTick) {


         up = keys[KeyEvent.VK_W];
        down = keys[KeyEvent.VK_S];
        left = keys[KeyEvent.VK_A];
        right = keys[KeyEvent.VK_D];
        
        
       timer = 0 ;  
       
        }
        
    
    }

    @Override
    public void keyPressed(KeyEvent e) {
        keys[e.getKeyCode()] = true;
    }

    @Override
    public void keyReleased(KeyEvent e) {
        keys[e.getKeyCode()] = false;
    }

Здесь установлена ​​скорость существа:
https://github.com/komiza24/Pokemom/blob/main/src/entities/creatures/Creatures.java

public abstract class Creatures extends Entity {

    public static final int DEFAULT_HEALTH = 10;
  //  public static final float DEFAULT_SPPED = 0.125f;   // größe des Spieler damit er sich nur in Tiles bewegt (1 tile ist eig 8x8 aber spieler 16x16 )  WER FÜR TILEBASIERTES MOVEMENT
    public static final float DEFAULT_SPPED = 2 ;
    public static final int DEFAULT_CREATURE_WIDTH = 64,
            DEFAULT_CREATURE_HEIGHT = 64;
    protected int health;
    protected float speed;
    protected float xMove, yMove;

    public Creatures(Handler handler, float x, float y, int width, int height) {
        super(handler, x, y, width, height);
        health = DEFAULT_HEALTH;
        speed = DEFAULT_SPPED;
        xMove = 0;
        yMove = 0;
    }

    public void move() {
        if (!checkEntitiyCollision(xMove, 0f)) 
        moveX();

         if (!checkEntitiyCollision(0f, yMove)) 
        moveY();

    }

    public void moveX() {

        if (xMove > 0) {  //right

            int tx = (int) (x /** Tile.TILEWIDTH*/ + xMove + bounds.x + bounds.width) / Tile.TILEWIDTH;

            if (!collisionWithTile(tx, (int) (y /** Tile.TILEHEIGHT */+ bounds.y) / Tile.TILEHEIGHT)
                    && // oben rechts
                    !collisionWithTile(tx, (int) (y /** Tile.TILEHEIGHT*/ + bounds.y - 1 + bounds.height) / Tile.TILEHEIGHT)) {  // unten rechts
                x += xMove;
            }

        } else if (xMove < 0) { // left

            int tx = (int) (x /** Tile.TILEWIDTH*/ + xMove + bounds.x) / Tile.TILEWIDTH;

            if (!collisionWithTile(tx, (int) (y /** Tile.TILEHEIGHT*/ + bounds.y) / Tile.TILEHEIGHT)
                    && // oben links
                    !collisionWithTile(tx, (int) (y /** Tile.TILEHEIGHT*/ + bounds.y - 1 + bounds.height) / Tile.TILEHEIGHT)) {  // unten links
                x += xMove;
            }

        }

    }

    public void moveY() {

        if (yMove < 0) {//up

            int ty = (int) (y /** Tile.TILEHEIGHT*/ + yMove + bounds.y) / Tile.TILEHEIGHT;

            if (!collisionWithTile((int) (x /** Tile.TILEWIDTH */+ bounds.x) / Tile.TILEWIDTH, ty)
                    &&// oben links
                    !collisionWithTile((int) (x /** Tile.TILEWIDTH*/ + bounds.x - 1 + bounds.width) / Tile.TILEWIDTH, ty)) {   // oben rechts
                y += yMove;

            }

        } else if (yMove > 0) { //down

            int ty = (int) (y /** Tile.TILEHEIGHT*/ + yMove + bounds.y + bounds.height) / Tile.TILEHEIGHT;

            if (!collisionWithTile((int) (x /** Tile.TILEWIDTH*/ + bounds.x) / Tile.TILEWIDTH, ty)
                    &&// oben links
                    !collisionWithTile((int) (x /** Tile.TILEWIDTH*/ + bounds.x - 1 + bounds.width) / Tile.TILEWIDTH, ty)) {   // oben rechts
                y += yMove;

            }

        }

    }

    protected boolean collisionWithTile(int x, int y) {

        return handler.getWorld().getTile(x, y).isSolid();
    }

    public int getHealth() {
        return health;
    }

    public float getSpeed() {
        return speed;
    }

    public void setHealth(int health) {
        this.health = health;
    }

    public void setSpeed(float speed) {
        this.speed = speed;
    }

    public void setxMove(float xMove) {
        this.xMove = xMove;
    }

    public void setyMove(float yMove) {
        this.yMove = yMove;
    }

    public float getxMove() {
        return xMove;
    }

    public float getyMove() {
        return yMove;
    }

}

И, наконец, здесь игрок перемещается:
https://github.com/komiza24/Pokemom/blob/main/src/entities/creatures/Player.java

public class Player extends Creatures {

    //animations
    private Animation animDown, animUp, animRight, animLeft;
    private int direktion;

    public Player(Handler handler, float x, float y) {
        super(handler, x, y, Creatures.DEFAULT_CREATURE_WIDTH, Creatures.DEFAULT_CREATURE_HEIGHT);
        
      
        

        // animations                       Time to wait = time to wait until get next input my keymanager so läuft die animation genau 1 mal pro Tile(Feld 16x16 micht 8x8 ! )
        animDown = new Animation(175, Assets.player_down);
        animUp = new Animation(175, Assets.player_up);
        animRight = new Animation(175, Assets.player_right);
        animLeft = new Animation(175, Assets.player_left);

    }

    @Override
    public void tick() {

        //animations
        animRight.tick();
        animLeft.tick();
        animDown.tick();
        animUp.tick();

        getInput();

        //movement
        move();

        handler.getGameCamera().centerOnEntity(this);
    }

    private void getInput() {
        xMove = 0;
        yMove = 0;

        if (handler.getKeyManager().up && !handler.getKeyManager().down && !handler.getKeyManager().left && !handler.getKeyManager().right) {

            yMove = -speed;
            direktion = 1;
        }
        if (handler.getKeyManager().down && !handler.getKeyManager().up && !handler.getKeyManager().left && !handler.getKeyManager().right) {
            yMove = +speed;
            direktion = 4;
        }
        if (handler.getKeyManager().left && !handler.getKeyManager().right && !handler.getKeyManager().down && !handler.getKeyManager().up) {

            xMove = -speed;
            direktion = 3;

        }
        if (handler.getKeyManager().right && !handler.getKeyManager().left && !handler.getKeyManager().down && !handler.getKeyManager().up) {

            xMove = +speed;
            direktion = 2;

        }

    }

    @Override
    public void render(Graphics g) {

        g.drawImage(getCurrentAnimationFrame(), (int) (x - handler.getGameCamera().getxOffset()), (int) (y  - handler.getGameCamera().getyOffset()), width, height, null);
        
        
        // HitBox Rectagle  :D 
        
        
        g.setColor(Color.RED);
        g.fillRect((int) (x + bounds.x - handler.getGameCamera().getxOffset()),
                (int) (y  + bounds.y - handler.getGameCamera().getyOffset()),
                bounds.width, bounds.height);
            
    }

    private BufferedImage getCurrentAnimationFrame() {

        if (xMove < 0) {
            return animLeft.getCurrentFrame();
        } else if (xMove > 0) {
            return animRight.getCurrentFrame();
        } else if (yMove < 0) {
            return animUp.getCurrentFrame();
        } else if (yMove > 0) {
            return animDown.getCurrentFrame();
        } else {

            
            
            
            
           
            switch (direktion) {
                case 1:
                    return Assets.player_default_up;
                case 2:
                    return Assets.player_default_right;
                case 3:
                    return Assets.player_default_left;
                default:
                    return Assets.player_default_down;
            }
           
        }

    }

}

1 ответ
1

При тестировании вашего кода я обнаружил, что фигура движется по сетке, которой на самом деле не существует. Таким образом, я обнаружил, что натыкаюсь на знаки, хотя моя фигура в основном была выше знака. Я также заметил, что если вы быстро нажимаете клавиши WASD, фигура не перемещается.

Для решения этих проблем необходимо учитывать несколько моментов:

  • Вы сделали свое движение таким образом, чтобы оно основывалось на скорости и времени, а не на времени и расстоянии. Если вы используете время и расстояние для расчета скорости, это поможет вашей фигуре остановиться в нужном месте. Я бы пошел еще дальше, установив конечное положение. Таким образом, у вас действительно есть сетка.
  • Попробуйте это: вместо того, чтобы изменять логические значения нажатия клавиш только через определенные интервалы, проверяйте логические значения нажатия клавиш через определенные интервалы. Если обнаружено нажатие клавиши для определенного направления, выполните движение, анимацию и все такое. Как только это будет сделано, снова проверьте нажатие клавиши.

Вот кое-что, что я собрал вместе — я рекомендую вам попробовать реализовать мои предложения, прежде чем смотреть на это. Здесь также есть несколько вспомогательных методов, которые я создал, поэтому он не будет работать, если вы скопируете пасту.

package entities.creatures;
import gfx.Animation;
import gfx.Assets;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;

import pokemon.Handler;
import tiles.Tile;

public class Player extends Creatures {
    //animations
    private final Animation animDown;
    private final Animation animUp;
    private final Animation animRight;
    private final Animation animLeft;

    private int standing_direction;
    private boolean moving;
    private long animation_start_time;
    private float animation_duration = 260f;

    public Player(Handler handler, float x, float y) {
        super(handler, x, y, Creatures.DEFAULT_CREATURE_WIDTH, Creatures.DEFAULT_CREATURE_HEIGHT);

        // 63 cause sliding from right to a wall wasn't functioning
        bounds.x = 0;
        bounds.y = 0;
        bounds.width = 63;
        bounds.height = 63;

        // animations Time to wait = time to wait until get next input my keymanager so läuft die animation genau 1 mal pro Tile(Feld 16x16 micht 8x8 ! )
        animDown = new Animation(175, Assets.player_down);
        animUp = new Animation(175, Assets.player_up);
        animRight = new Animation(175, Assets.player_right);
        animLeft = new Animation(175, Assets.player_left);
    }

    @Override
    public void tick() {
        //animations
        animRight.tick();
        animLeft.tick();
        animDown.tick();
        animUp.tick();

        if (!moving) {
            setDirection();
        }

        move();

        handler.getGameCamera().centerOnEntity(this);
    }

    private void setDirection() {
        if (handler.getKeyManager().isOnlyUpPressed()) {
            yMove = -speed;
            standing_direction = 1;
            startAnimation();
        }
        if (handler.getKeyManager().isOnlyDownPressed()) {
            yMove = speed;
            standing_direction = 4;
            startAnimation();
        }
        if (handler.getKeyManager().isOnlyLeftPressed()) {
            xMove = -speed;
            standing_direction = 3;
            startAnimation();
        }
        if (handler.getKeyManager().isOnlyRightPressed()) {
            xMove = speed;
            standing_direction = 2;
            startAnimation();
        }
    }

    private void startAnimation() {
        moving = true;
        animation_start_time = System.currentTimeMillis();
    }

    private void stopAnimationIfRequired() {
        if (System.currentTimeMillis() - animation_start_time > animation_duration) {
            moving = false;
            xMove = 0;
            yMove = 0;

            setDirection();
        }
    }

    @Override
    public void render(Graphics g) {
        if (moving) {
            stopAnimationIfRequired();
        }

        g.drawImage(
                getCurrentAnimationFrame(),
                (int) (x - handler.getGameCamera().getxOffset()),
                (int) (y - handler.getGameCamera().getyOffset()),
                width,
                height,
                null);
    }

    private BufferedImage getCurrentAnimationFrame() {
        if (xMove < 0) {
            return animLeft.getCurrentFrame();
        } else if (xMove > 0) {
            return animRight.getCurrentFrame();
        } else if (yMove < 0) {
            return animUp.getCurrentFrame();
        } else if (yMove > 0) {
            return animDown.getCurrentFrame();
        } else {
            switch (standing_direction) {
                case 1:
                    return Assets.player_default_up;
                case 2:
                    return Assets.player_default_right;
                case 3:
                    return Assets.player_default_left;
                default:
                    return Assets.player_default_down;
            }
        }
    }
 }

  • Я действительно очень благодарен за ваш ответ, прямо сейчас у меня нет времени, чтобы прочитать все это, потому что я пишу задание по математике завтра утром. Завтра посмотрю выше всего, что ты написал в точности! Спасибо !

    — G0ngB0ng

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *