Я пишу небольшую игру на Java, и, как и в большинстве игр, мне нужно слушать вход пользователя. Я решил использовать java.awt.event.KeyListener для обработки входных данных.
Вот мой основной метод:
public static void main(String[] args){
LevelViewer l = new LevelViewer(LevelFileIO.load());
List<Entity> entities = l.getEntities();
List<GameObject> gameObjects = l.getGameObjects();
Pacman player = null;
for (Entity e : entities){
if (e instanceof Pacman){
player = (Pacman)e;
break;
}
}
JFrame frame = new JFrame();
frame.add(l);
frame.setResizable(false);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setFocusable(true);
frame.requestFocus();
class Listener implements KeyListener{
Pacman player;
Listener(Pacman player){
this.player = player;
}
@Override
public void keyPressed(KeyEvent e) {
switch(e.getKeyCode()){
case KeyEvent.VK_W:
player.up();
break;
case KeyEvent.VK_D:
player.right();
break;
case KeyEvent.VK_S:
player.down();
break;
case KeyEvent.VK_A:
player.left();
break;
}
}
@Override
public void keyReleased(KeyEvent e) {}
@Override
public void keyTyped(KeyEvent e) {}
}
frame.addKeyListener(new Listener(player));
Timer timer = new Timer();
timer.schedule(new UpdateTimer(
entities.toArray(new Entity[entities.size()]),
gameObjects.toArray(new GameObject[gameObjects.size()]),
l), 0, 17);
}
Это все довольно просто, когда я читаю ключевые события, я вызываю методы в проигрывателе, которые просто меняют его скорость.
Пример:
public void right(){
this.setVelocity(new Vector2(speed, 0));
this.setSprite(right);
}
Таймер в конце основного метода просто вызывает это:
public UpdateTimer(Entity[] toUpdate, GameObject[] gameObjects, LevelViewer toRedraw){
this.toUpdate = toUpdate;
this.gameObjects = gameObjects;
this.toRedraw = toRedraw;
}
@Override
public void run() {
toRedraw.repaint();
if (timeAtLastUpdateCall == 0){
timeAtLastUpdateCall = System.currentTimeMillis();
for (Entity e : toUpdate){
e.start();
}
return;
}
long newTime = System.currentTimeMillis();
double deltaT = ((double)(newTime - timeAtLastUpdateCall)/1000.0);
timeAtLastUpdateCall = newTime;
for (Entity e : toUpdate){
for (GameObject g : gameObjects){
if (g.touching(e)){
e.setVelocity(e.getVelocity().multiply(-1));
e.free(g);
}
}
e.update(deltaT);
e.getSprite().nextFrame();
}
}
Вся эта функция запуска выполняет некоторую проверку столкновений, а затем вызывает метод обновления в каждом объекте. В случае с игроком он просто вычисляет, сколько он должен двигаться с момента последнего вызова обновления:
public void update(double deltaT){
this.setPosition(this.getPosition().add(velocity.multiply(deltaT)));
}
Теперь проблема в том, что когда я нажимаю клавишу, которая меняет скорость игроков, изменение обычно происходит мгновенно, но иногда может наблюдаться заметное отставание (около четверти секунды, намного больше 17 мс).
Это проблема с Java KeyListener или с моей реализацией?
Я хотел бы исправить это, чтобы иметь гладкий пользовательский ввод.
Я не думаю, что это проблема с KeyListener
. Я не разработчик игры, но некоторые вещи, которые нужно учитывать:
TimerTask
? Если это когда-либо более 17 мс, это может быть проблемой, особенно когда вы сравниваете расписание с графикомAtFixedRate