我正在尝试编写一个贪吃蛇游戏,但 KeyListener 不起作用



我曾试图将侦听器添加到Snake类的对象、Window类的对象和我在Snake类别的构造函数中创建的JFrame类别的对象,但它仍然不起作用。С字符必须写出来,蛇必须转动,如果我按任何键,但它没有发生。这会是因为蛇线吗?

public class Main {
public static void main(String[] args) {
Window window = new Window();
System.out.print("k");
}
}
import javax.swing.*;
import java.awt.*;
public class Window  extends JPanel {
private Snake snake; 

public Window() {
super(true);
snake = Snake.getSnake(50, 50);
Thread snakeThread = new Thread(snake);
snakeThread.start();

}
@Override
public void paint(Graphics g) {
super.paint(g);
snake.paint(g);
}
}
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;

public class Snake extends JPanel implements Runnable {
public static int length;
public static ArrayList<Point> parts;
public static Field field;
public static Food food;
public static int speedX=0;
public static int speedY=1;
private static Snake snake = null;
public static final int TIME_DELTA = 1000;
public static  Snake getSnake(int w, int h)
{
if(snake == null) {
snake = new Snake(w, h);
snake.addKeyListener(new Turn(snake));
}
return snake;
}
private Snake(int Width, int Heigth) {
JFrame jf = new JFrame();
jf.setSize(500,500);
jf.add(this); //this - это Jpanel которым расширяется Snake
jf.setVisible(true);

food = new Food();field = Field.getField();
Point start = new Point((int)Width/2, (int)Heigth/2); //размеры поля, а не окна
parts = new ArrayList<>();parts.add(start);
Point p1 = new Point((int)start.getX(), ((int)start.getY())-1);parts.add(p1);
Point p2 = new Point((int)start.getX(), ((int)p1.getY())-1);
parts.add(p2);length = 3;
}

private boolean checkHead()
{
for (int i=1; i<parts.size(); ++i)
{
if(parts.get(parts.size()-1).getLocation() == parts.get(i).getLocation())
return false;
}
if(parts.get(parts.size()-1).getX() <=0 || parts.get(parts.size()-1).getX() >= field.sizeX ||
parts.get(parts.size()-1).getY() <=0 || parts.get(parts.size()-1).getY() >= field.sizeY )
return false;
return true;
}
public static void move()
{
for (Point i: parts)
{
i.y=i.y-1*speedY;
i.x-=1*speedX;
}
}
public static void eat()
{
Point np = new Point ((int)parts.get(length).getX(),(int)parts.get(length).getY()-1 );
parts.add(np);
++length;
food.respawn();
}
public static boolean checkFood()
{
if(parts.get(parts.size()-1).getX() == food.x &&  parts.get(parts.size()-1).getY()==food.y)
return true;
else
return false;
}

@Override
public void paint(Graphics g) {
super.paint(g);
for (Point i: parts) 
g.fillRect((int) i.getX() * 10, (int) i.getY() * 10, 8, 8);
g.setColor(Color.RED);
g.fillRect(food.x * 10, food.y * 10, 8, 8);
g.setColor(Color.BLACK);
}
@Override
public void run() {
while (checkHead()) {
move();
repaint();
if(checkFood())
eat();
try {
Thread.sleep(TIME_DELTA);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public  void turn(char Key)
{
int delta = 0;
for(Point i: parts)
{
switch (Key) {
case 'a':
i.y=parts.get(parts.size()-1).y;
i.x=parts.get(parts.size()-1).x+delta;
break;
case'd':
i.y=parts.get(parts.size()-1).y;
i.x=parts.get(parts.size()-1).x-delta;
break;
case 'w':
i.x=parts.get(parts.size()-1).x;
i.y=parts.get(parts.size()-1).y-delta;
break;
case's':
i.x=parts.get(parts.size()-1).x;
i.y=parts.get(parts.size()-1).y+delta;
break;
}
++delta;
}
repaint();
}

}
import javax.swing.*;
import java.awt.*;
import java.util.Random;
public class Food extends JPanel {
public static  int x;
public static int y;
private static Random random;
public Food()
{
super(true);
random = new Random();

x =  random.nextInt(50);
y = random.nextInt(50);
}
@Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.RED);
g.fillRect(x * 10, y * 10, 8, 8);
g.setColor(Color.BLACK);
}
public  void respawn()
{
x = random.nextInt(40);
y = random.nextInt(40);
repaint();
}
}

听众在这里:

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class Turn implements KeyListener {
private char key = 'O';
private Snake snake;
public Turn(Snake s)
{
this.snake = s;
}

@Override
public void keyTyped(KeyEvent e) {
System.out.println("0");//when I press a button, "0" must be written, but it doesn't
if(e.getKeyChar() == 'A')
{
System.out.println("a");//this character too
if(snake.speedX==0)
{
snake.speedX=-1;//speedX was not changed
snake.speedY=0;
key='a';
}
}
else if (e.getKeyChar() == 'W')
{
System.out.println("w");
if(snake.speedY==0)
{
snake.speedY=-1;
snake.speedX=0;
key='w';
}
}
else if (e.getKeyChar() == 'S')
{
System.out.println("s");
if(snake.speedY==0)
{
snake.speedY=1;
snake.speedX=0;
key='s';
}
}
else if (e.getKeyChar() == 'D')
{
System.out.println("d");
if(snake.speedX==0)
{
snake.speedX=1;
snake.speedY=0;
key='d';
}
}
if(key!='O')
snake.turn(key);
}
@Override
public void keyPressed(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}
}
  1. 永远不要直接在组件上调用paint(…(。如果需要绘制组件,可以对该组件调用repaint((。然后,Swing将绘制(…(该组件,并在添加到面板的所有子组件上调用paint(…(。

  2. 因此,这意味着您需要将Snake组件添加到父游戏面板中。这也意味着没有理由覆盖"窗口"面板的paint((方法。

  3. 不要使用静态变量和方法。这表明设计不好。从变量和方法中删除static关键字。

  4. 对于动画,您应该使用Swing Timer,而不是Thread。Swing组件的更新应该在事件调度线程(EDT(上完成。计时器将执行EDT上的代码。

  5. 不要使用KeyListener。Swing设计用于键绑定。有关更多信息和示例,请参见:使用键盘运动。

  6. 自定义绘画是通过覆盖paintComponent(...)而不是绘画(…(来完成的。

  7. main((方法的要点是创建框架并将游戏面板添加到框架中。游戏面板不应该创建框架。

我同意camickr的回答。代码中有很多错误,从零开始考虑这些问题将有助于避免您面临的许多问题。

然而,您的实际问题是这条线路private static Snake snake = null;和这种方法:

public static  Snake getSnake(int w, int h)
{
if(snake == null) {
snake = new Snake(w, h);
snake.addKeyListener(new Turn(snake));
}
return snake;
}

你永远不应该在这样的类中创建一个自引用。Snake类已经是Snake对象,因此在类中使用private static Snake snake = null;只需在Snake中创建一个对全新且不同的Snake的新引用。相反,您需要使用this关键字来引用Snake对象,例如,this.addKeyListener(new Turn(this));而不是snake.addKeyListener(new Turn(snake));

删除/移除getSnake方法,您正在尝试混合静态和非静态对象,但这是不需要的,我们可以将该方法的keylistener部分移动到我们的构造函数中(见下文(。请注意,如果要更新蛇的宽度和高度,则应使用不同的方法分别更新partsp1p2

修复Snake类:

固定的Snake类可能看起来像这样:

public class Snake extends JPanel implements Runnable {
//None of the variables or methods should be static
//remove the static keyword from everywhere in the Snake class
public int length;
public ArrayList<Point> parts;
public Field field;
public Food food;
public int speedX=0;
public int speedY=1;
public final int TIME_DELTA = 1000;
//The constructor needs to be a public method (Not private)
public Snake(int Width, int Heigth) {
//Delete all the JFrame code,
//it should be done in the main method
//Add the key ilstener here (moved from the getSnake method)
this.addKeyListener(new Turn(this));

//Create the snake
food = new Food();
field = Field.getField();
Point start = new Point((int)Width/2, (int)Heigth/2); //размеры поля, а не окна
parts = new ArrayList<>();
parts.add(start);
Point p1 = new Point((int)start.getX(), ((int)start.getY())-1);parts.add(p1);
Point p2 = new Point((int)start.getX(), ((int)p1.getY())-1);
parts.add(p2);
length = 3;
}
//Rest of the code removed for clarity
...
}

修复主方法并删除Window类:

要使用这个更新的Snake类,你可以完全删除Window类,并在你的主类中使用类似的东西:

public class Main {
public static void main(String[] args) {
//Create JFrame
JFrame jf = new JFrame();
jf.setSize(500,500);
//Create Snake
Snake snake = new Snake(50, 50);
//Add Snake to JFrame and set the frame visible
jf.add(snake);
jf.setVisible(true);

//Finally start the Snake thread
Thread snakeThread = new Thread(snake);
snakeThread.start();
}
}

仍然有很多事情需要修复,但这应该可以解决关键侦听器无法工作的问题。


其他修复程序:

一个更好的解决方案是使用密钥绑定,而不是密钥侦听器。我们可以在您的主要方法中创建这样的密钥绑定:

//Key binding to trigger when KeyEvent.VK_W ('w') is pressed
snake.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), "NORTH");
snake.getActionMap().put("NORTH", new AbstractAction()
{
@Override
public void actionPerformed(ActionEvent e)
{
//Your code here to change the snake direction
//we can call the turn method from inside this snake object/class
snake.turn('w');
}
});

您应该在Snake类中重写protected void paintComponent(Graphics g)而不是public void paint(Graphics g),如下所示:

@Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (Point i: parts) 
g2d.fillRect((int) i.getX() * 10, (int) i.getY() * 10, 8, 8);
g2d.setColor(Color.RED);
g2d.fillRect(food.x * 10, food.y * 10, 8, 8);
g2d.setColor(Color.BLACK);
}

最后,与其让Snake类实现Runniable,不如在Snake类中创建一个简单的独立摆动计时器:

void startTimer(){
//Start timer to update snake location every 100ms
Timer timer = new Timer(100, new ActionListener(){
@Override
public void actionPerformed(ActionEvent ae){
//Your code here to update and repaint the snake location
snake.updateLocation();
}
});
timer.start();
}

在您的主要方法中,您可以简单地使用snake.startTimer();而不是Thread snakeThread = new Thread(snake);snakeThread.start();

相关内容

  • 没有找到相关文章

最新更新