- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试制作一个简单的游戏,我需要我的宇宙飞船在其中心旋转(使用 A 和 D 键),并相对于它所面对的方向移动(W 和 S 键)。
我已经基本弄清楚了移动和旋转的数学原理,但我对关键听众有疑问。
当我编译时,按键监听器在前几次按键时工作正常。但如果我按住A(按下时向左旋转)然后切换到D,然后再次切换到A,它就会停止工作。任何其他按键组合也会导致这种情况。
基本上,如果我按太多键最终它会停止工作。如果我走得很慢,当我按下我想要工作的键几次后,它就会再次开始工作。但是,如果我一次按一堆键(快速连续按 A,然后 D,然后 W),那么它完全停止工作,并且所有键都变得无响应。
我认为这不是处理速度问题,因为程序不会崩溃,也不会产生任何内存错误。
我已经阅读了类似的问题,并看到人们建议不要使用 KeyListeners,而使用 KeyBindings,但我尝试了这两种方法并得到了完全相同的问题。
这是我的程序的更新代码(尝试制作 MCVE):我在中间注释掉的部分是键绑定(bind)的代码。
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.geom.AffineTransform;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.math.*;
import java.net.URL;
public class Game extends JPanel implements ActionListener{
private Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
JFrame frame = new JFrame();
private Ship ship = new Ship();
private Timer gameTimer, turnRight, turnLeft, moveForward, moveBackward;
private static final int IFW = JComponent.WHEN_IN_FOCUSED_WINDOW;
public Game(){
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Study Buddy Menu");
frame.setSize(800,700);
frame.setLocation(dim.width/2 - 400, dim.height/2 - 350);
turnLeft = new Timer(20, new ActionListener(){
public void actionPerformed(ActionEvent e) {
ship.setAngle(ship.getAngle() + ship.getTurnSpeed());
}
});
turnRight = new Timer(20, new ActionListener(){
public void actionPerformed(ActionEvent e) {
ship.setAngle(ship.getAngle() - ship.getTurnSpeed());
}
});
moveForward = new Timer(20, new ActionListener(){
public void actionPerformed(ActionEvent e) {
ship.setX(ship.getX() + (float)Math.cos(Math.toRadians(ship.getAngle())));
ship.setY(ship.getY() - (float)Math.sin(Math.toRadians(ship.getAngle())));
System.out.println("UP");
}
});
moveBackward = new Timer(20, new ActionListener(){
public void actionPerformed(ActionEvent e) {
ship.setX(ship.getX() - (float)Math.cos(Math.toRadians(ship.getAngle())));
ship.setY(ship.getY() + (float)Math.sin(Math.toRadians(ship.getAngle())));
System.out.println("DOWN");
}
});
this.setFocusable(true);
this.requestFocus();
/*getInputMap(IFW).put(KeyStroke.getKeyStroke('d'), "right");
getActionMap().put("right", new MoveAction("d"));
getInputMap(IFW).put(KeyStroke.getKeyStroke('a'), "left");
getActionMap().put("left", new MoveAction("a"));
getInputMap(IFW).put(KeyStroke.getKeyStroke('w'), "up");
getActionMap().put("up", new MoveAction("w"));
getInputMap(IFW).put(KeyStroke.getKeyStroke('s'), "down");
getActionMap().put("down", new MoveAction("s"));*/
this.addKeyListener(new KeyAdapter(){
public void keyPressed(KeyEvent e){
//TURN
if(e.getKeyCode() == KeyEvent.VK_D){
turnLeft.start();
turnRight.stop();
}
if(e.getKeyCode() == KeyEvent.VK_A){
turnRight.start();
turnLeft.stop();
}
//MOVE
if(e.getKeyCode() == KeyEvent.VK_W){
moveForward.start();
moveBackward.stop();
}
if(e.getKeyCode() == KeyEvent.VK_S){
moveBackward.start();
moveForward.stop();
}
}
public void keyReleased(KeyEvent e){
turnRight.stop();
turnLeft.stop();
moveForward.stop();
moveBackward.stop();
}
});
frame.add(this);
repaint();
gameTimer = new Timer(20, this);
gameTimer.start();
frame.setVisible(true);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
ship.draw(g);
}
public void actionPerformed(ActionEvent e) {
repaint();
}
private class MoveAction extends AbstractAction {
String d = null;
MoveAction(String direction) {
d = direction;
}
public void actionPerformed(ActionEvent e) {
switch (d){
case "d": turnLeft.start(); turnRight.stop();break;
case "a": turnRight.start(); turnLeft.stop();break;
case "w": moveForward.start(); moveBackward.stop();break;
case "s": moveBackward.start(); moveForward.stop();break;
}
}
}
class main {
public void main(String[] args) {
Game game = new Game();
}
}
class Ship {
private float x, y, radius, speed, angle, turnSpeed;
private Image icon;
public Ship(){
x = 400;
y = 350;
speed = 1;
radius = 20;
angle = 90;
turnSpeed = 5;
try {
icon = ImageIO.read(new URL("/image/L5DGx.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
//GETTERS
public float getX(){
return x;
}
public float getY(){
return y;
}
public float getTurnSpeed(){
return turnSpeed;
}
public float getAngle(){
return angle;
}
public float getRadius(){
return radius;
}
public float getSpeed(){
return speed;
}
public Image getIcon(){
return icon;
}
//SETTERS
public void setX(float X){
x = X;
}
public void setTurnSpeed(float S){
turnSpeed = S;
}
public void setY(float Y){
y = Y;
}
public void setAngle(float A){
angle = A;
}
public void setSpeed(float S){
speed = S;
}
public void setRadius(float R){
radius = R;
}
public void setIcon(Image image){
icon = image;
}
//DRAW
public void draw(Graphics g){
Graphics2D g2d = (Graphics2D) g;
AffineTransform at = new AffineTransform();
at.setToRotation(Math.toRadians(angle-90), x, y);
//at.translate(x, y);
g2d.setTransform(at);
g2d.drawImage(icon,(int)(x - radius), (int)(y - radius),(int)(radius * 2),(int)(radius * 2), null);
g2d.dispose();
}
}
}
最佳答案
我正要 sleep ,所以我不能太深入地研究你的代码,但是一个良好的移动方法是这样的:
isTurningLeft
, isTurningRight
, isMovingForwards
, isMovingBackwards
.同时跟踪每个按钮非常重要,这样您才能掌握所有信息。例如,您可以这样做:
double turnRate = 0;
if (isTurningLeft) {
turnRate -= maxTurnRate;
}
if (isTurningRight) {
turnRate += maxTurnRate;
}
rotationAngle += timeElapsed * turnRate;
这样,如果同时按住两个按钮,转弯速率仅为 0。这将避免很多奇怪的行为,例如粘滞和卡顿。然而,要做到这一点,您需要在一个地方知道两个按钮的状态。
<小时/>编辑
我修改了你的程序作为示例:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.net.URL;
public class KeyListenerGame extends JPanel implements ActionListener{
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new KeyListenerGame();
}
});
}
// private static final int IFW = JComponent.WHEN_IN_FOCUSED_WINDOW;
// private Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
private JFrame frame;
private Ship ship;
private Timer gameTimer;//, turnRight, turnLeft, moveForward, moveBackward;
private KeyBindingController controller;
// This is just an example. I recommend using
// the key bindings instead, even though they
// are a little more complicated to set up.
class KeyListenerController extends KeyAdapter {
boolean isTurningLeft;
boolean isTurningRight;
boolean isMovingForward;
boolean isMovingBackward;
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_A:
isTurningLeft = true;
break;
case KeyEvent.VK_D:
isTurningRight = true;
break;
case KeyEvent.VK_W:
isMovingForward = true;
break;
case KeyEvent.VK_S:
isMovingBackward = true;
break;
}
}
@Override
public void keyReleased(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_A:
isTurningLeft = false;
break;
case KeyEvent.VK_D:
isTurningRight = false;
break;
case KeyEvent.VK_W:
isMovingForward = false;
break;
case KeyEvent.VK_S:
isMovingBackward = false;
break;
}
}
}
// This could be simplified a lot with Java 8 features.
class KeyBindingController {
boolean isTurningLeft;
boolean isTurningRight;
boolean isMovingForward;
boolean isMovingBackward;
JComponent component;
KeyBindingController(JComponent component) {
this.component = component;
// Bind key pressed Actions.
bind(KeyEvent.VK_A, true, new AbstractAction("turnLeft.pressed") {
@Override
public void actionPerformed(ActionEvent e) {
isTurningLeft = true;
}
});
bind(KeyEvent.VK_D, true, new AbstractAction("turnRight.pressed") {
@Override
public void actionPerformed(ActionEvent e) {
isTurningRight = true;
}
});
bind(KeyEvent.VK_W, true, new AbstractAction("moveForward.pressed") {
@Override
public void actionPerformed(ActionEvent e) {
isMovingForward = true;
}
});
bind(KeyEvent.VK_S, true, new AbstractAction("moveBackward.pressed") {
@Override
public void actionPerformed(ActionEvent e) {
isMovingBackward = true;
}
});
// Bind key released Actions.
bind(KeyEvent.VK_A, false, new AbstractAction("turnLeft.released") {
@Override
public void actionPerformed(ActionEvent e) {
isTurningLeft = false;
}
});
bind(KeyEvent.VK_D, false, new AbstractAction("turnRight.released") {
@Override
public void actionPerformed(ActionEvent e) {
isTurningRight = false;
}
});
bind(KeyEvent.VK_W, false, new AbstractAction("moveForward.released") {
@Override
public void actionPerformed(ActionEvent e) {
isMovingForward = false;
}
});
bind(KeyEvent.VK_S, false, new AbstractAction("moveBackward.released") {
@Override
public void actionPerformed(ActionEvent e) {
isMovingBackward = false;
}
});
}
void bind(int keyCode, boolean onKeyPress, Action action) {
KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode, 0, !onKeyPress);
String actionName = (String) action.getValue(Action.NAME);
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
.put(keyStroke, actionName);
component.getActionMap()
.put(actionName, action);
}
}
public KeyListenerGame(){
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Study Buddy Menu");
frame.add(this);
frame.pack();
frame.setLocationRelativeTo(null);
gameTimer = new Timer(20, this);
controller = new KeyBindingController(this);
ship = new Ship(getSize());
gameTimer.start();
frame.setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
double secsElapsed = gameTimer.getDelay() / 1000.0;
double maxSpeed = ship.getSpeed();
double maxTurnSpeed = ship.getTurnSpeed();
double theta = Math.toRadians( ship.getAngle() );
double x = ship.getX();
double y = ship.getY();
double turnSpeed = 0;
if (controller.isTurningLeft) {
turnSpeed -= maxTurnSpeed;
}
if (controller.isTurningRight) {
turnSpeed += maxTurnSpeed;
}
theta += secsElapsed * Math.toRadians(turnSpeed);
double speed = 0;
if (controller.isMovingForward) {
speed += maxSpeed;
}
if (controller.isMovingBackward) {
speed -= maxSpeed;
}
double velX = speed * Math.cos(theta);
double velY = speed * Math.sin(theta);
x += secsElapsed * velX;
y += secsElapsed * velY;
ship.setX( (float) x );
ship.setY( (float) y);
ship.setAngle( (float) Math.toDegrees(theta) );
repaint();
}
@Override
public Dimension getPreferredSize() {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
// Computes a preferred size based on the
// dimensions of the screen size.
int prefWidth = screenSize.width / 2;
// Compute 4:3 aspect ratio.
int prefHeight = prefWidth * 3 / 4;
return new Dimension(prefWidth, prefHeight);
}
@Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
ship.draw(g);
}
class Ship {
private float x, y, radius, speed, angle, turnSpeed;
private Image icon;
public Ship(Dimension gameSize) {
// x = 400;
// y = 350;
// speed = 1;
// radius = 20;
// angle = 90;
// turnSpeed = 5;
x = gameSize.width / 2;
y = gameSize.height / 2;
radius = 20;
angle = -90;
// 1/4 of the game height per second
speed = gameSize.height / 4;
// 180 degrees per second
turnSpeed = 180;
try {
icon = ImageIO.read(new URL("/image/L5DGx.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
//GETTERS
public float getX(){
return x;
}
public float getY(){
return y;
}
public float getTurnSpeed(){
return turnSpeed;
}
public float getAngle(){
return angle;
}
public float getRadius(){
return radius;
}
public float getSpeed(){
return speed;
}
public Image getIcon(){
return icon;
}
//SETTERS
public void setX(float X){
x = X;
}
public void setTurnSpeed(float S){
turnSpeed = S;
}
public void setY(float Y){
y = Y;
}
public void setAngle(float A){
angle = A;
}
public void setSpeed(float S){
speed = S;
}
public void setRadius(float R){
radius = R;
}
public void setIcon(Image image){
icon = image;
}
//DRAW
public void draw(Graphics g){
Graphics2D g2d = (Graphics2D) g.create();
//
// Draw the ship's movement vector.
double theta = Math.toRadians(angle);
double velX = speed * Math.cos(theta);
double velY = speed * Math.sin(theta);
g2d.setColor(java.awt.Color.blue);
g2d.draw(new java.awt.geom.Line2D.Double(x, y, x + velX, y + velY));
//
g2d.rotate(theta, x, y);
int imgX = (int) ( x - radius );
int imgY = (int) ( y - radius );
int imgW = (int) ( 2 * radius );
int imgH = (int) ( 2 * radius );
g2d.drawImage(icon, imgX, imgY, imgW, imgH, null);
//
g2d.dispose();
}
}
}
除了控件之外,我还更改了其他一些内容:
SwingUtilities.invokeLater(...)
来启动 Swing 程序。这是因为 Swing 运行在与 main
不同的线程上。继续运行。 https://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html JFrame
的大小直接,我覆盖了 getPreferredSize
并根据屏幕尺寸计算初始尺寸,然后调用 pack()
关于JFrame
。这比尝试静态设置像素大小要好得多。另外, JFrame
的大小包括它的插图(例如标题栏),所以如果您调用例如jFrame.setSize(800,600)
显示游戏的内容 Pane 实际上比800x600
小一点。 .ship.draw
我创建 g
的副本与 g.create()
,因为g
是Swing个人使用的图形对象。执行诸如在 Graphics
上处理或设置转换之类的操作传入并不是一个好主意。有很多方法可以使键绑定(bind)代码更好,但我只是采用了最明显的方法,这样您就可以清楚地看到我实际上在做什么。您必须为按下和释放事件设置绑定(bind)。不过,您会注意到它有一个模式,因此您可以编写一个小类来执行通用 boolean 逻辑,而不是像我那样进行复制粘贴。
Java 8 lambda 也很好,但我的 Java 8 电脑坏了。不过,使用 Java 8,您最终可能会得到这样的结果:
bind(KeyEvent.VK_A, "moveLeft", b -> isMovingLeft = b);
还有:
void bind(int keyCode, String name, Consumer<Boolean> c) {
Action pressAction = new AbstractAction(name + ".pressed") {
@Override
public void actionPerformed(ActionEvent e) {
c.accept(true);
}
};
Action releaseAction = new AbstractAction(name + ".released") {
@Override
public void actionPerformed(ActionEvent e) {
c.accept(false);
}
};
// Then bind it to the InputMap/ActionMap like I did
// in the MCVE.
}
关于java - KeyListener/KeyBinding 不一致触发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43691987/
我正在用 Java 开发一个非常非常简单的互联网浏览器。我已经把大部分内容都写下来了,但我在获取“按 Enter 键发送 URL”部分时遇到了困难。具体来说,当我使用 keyTyped() 方法时,我
我是一名编码新手,大约 10 年前有一点 C++ 经验,现在正在学习 java(大约 4-5 个月)。我正在进行一个小型协作项目,并且我有一些事情需要解决。 代码如下: import java.awt
import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.ArrayList; public
我正在尝试向我的输入字段添加一个按键监听器,但无法使其工作。我的输入类型是 text 和 id= "code"。这是我尝试过的: document.querySelector('code').addE
我正在尝试用 Java 创建一个自动点击器(我只知道这门语言,而且我刚学过 Threads)。我想让小程序在它自己的窗口中打开(而不是在网页上),并且我希望能够在不选择窗口的情况下使用空格键启动和停止
我是 BlackBerry 应用程序开发的新手。我希望能够在黑莓(在我的情况下为 8900)打开并且在所有屏幕上时能够监听按键事件,这可能吗? 如果是这样,有人将我引向正确的方向会很棒。我已经在看接口
import java.awt.Canvas; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics;
我对 KeyListener 没有太多经验,但我在我的应用程序中使用了一个,它工作得很好,只是我需要等待输入才能继续我的程序。为此,我做了一个 while 循环,循环直到 String temp 不为
我正在编写一个小游戏。到目前为止,我有一个玩家,他可以在 2D 打印的瓷砖世界中跟随玩家移动。这里有一个小preview . 现在我的玩家移动代码如下所示: KeyListener 类 public
我对 Java 还很陌生,到目前为止我完全是自学的。 现在我正在尝试将 KeyListener 添加到我的 JFrame 类中,我不知道我做错了什么,需要一些帮助。欢迎任何其他提示。 我的 JFram
尽管我将框架注册到了 keyListener,但 keyListener 并未触发操作。在其他代码中,实现 KeyListener 接口(interface)、注册 JFrame 并重写适当的方法就是
我一直在研究这个蛇项目,但并不真正理解为什么键监听器实际上没有更改变量 char 键。我还有一些其他的按键监听器示例,它们都工作正常,但由于某种原因我的无法工作。一些帮助将不胜感激。非常感谢您的帮助。
我在使用 KeyListener 时遇到问题。我将 KeyListener 添加到我创建的 JPanel,但 KeyListener 不起作用。 我的主类代码是 package client; imp
这个问题不太可能对任何 future 的访客有帮助;它只与一个较小的地理区域、一个特定的时间点或一个非常狭窄的情况相关,通常不适用于全世界的互联网受众。如需帮助使此问题更广泛适用,visit the
我正在尝试用java进行一个简单的 react 测试。当屏幕变绿时,我按空格键,女巫应该将 boolean 值“单击为 false 并停止测量时间的循环。实际上,关键监听器什么也不做。我是否将 kea
我有一个扩展 JFrame 的 GUI 类,以及一个扩展 KeyAdapter 的单独内部类 KeyPresses。在 GUI 的构造函数中,我将 KeyAdapter 的实例传递给 addKeyLi
我卡住了,当我点击箭头键时,我的图片无法加载。仅显示第一张图片。然后就什么都没有发生了。我希望能够通过单击箭头键来更改文件夹中的图片。这是代码。 import java.awt.Dimension;
我编写了一个简单的 KeyListener 来识别 Arrow_Left/Right 事件。由于某种原因,我的左键触发了 1 个 VK_LEFT 按下和 1 个 VK_RIGHT 事件(右箭头正常工作
这个问题已经有答案了: java keylistener not called (2 个回答) 已关闭 9 年前。 我遇到了 KeyListener 问题。我创建类(class)gra在构造函数中玩俄
我希望我能以正确的方式描述我的问题。我现在正在编写一个简单的双杆射击游戏,因此我实现了一个实现 KeyListener 的 KeyInputHandler 类。但是当玩家同时按下 2 个按钮时我遇到了
我是一名优秀的程序员,十分优秀!