Недавно я начал свой первый «настоящий» проект. Моя цель — написать первое поколение покемонов на 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 ответ
При тестировании вашего кода я обнаружил, что фигура движется по сетке, которой на самом деле не существует. Таким образом, я обнаружил, что натыкаюсь на знаки, хотя моя фигура в основном была выше знака. Я также заметил, что если вы быстро нажимаете клавиши 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