Java 蛇与自身碰撞的问题(蛇游戏)
我的蛇游戏有个问题,比如说,当你向左快速向上和向右快速按时,它会自动运行,因为我只告诉游戏,如果它向左,我不能向右按,因此,如果我在向右按之前向右按,它允许我向上按,这会使蛇进入自身Java 蛇与自身碰撞的问题(蛇游戏),java,swing,timer,keypress,paintcomponent,Java,Swing,Timer,Keypress,Paintcomponent,我的蛇游戏有个问题,比如说,当你向左快速向上和向右快速按时,它会自动运行,因为我只告诉游戏,如果它向左,我不能向右按,因此,如果我在向右按之前向右按,它允许我向上按,这会使蛇进入自身 因此,当你运行程序时,只需按“下一步”和“空格”,游戏就应该开始了。当你向左移动时,只需快速按“上一步”和“右一步”,然后自己查看。不幸的是,我不知道如何解决这个问题,因为我们只学了6个月的Java,我们只真正学到了基础知识,如if等。如果您有任何问题,我会很快回答。这不是正确的解决方案吗 如果有人转向太快,比如说
因此,当你运行程序时,只需按“下一步”和“空格”,游戏就应该开始了。当你向左移动时,只需快速按“上一步”和“右一步”,然后自己查看。不幸的是,我不知道如何解决这个问题,因为我们只学了6个月的Java,我们只真正学到了基础知识,如if等。如果您有任何问题,我会很快回答。这不是正确的解决方案吗 如果有人转向太快,比如说向左行驶时,“向上”和“向右”的速度太快,则snake仍在同一个y轴上,因为它还没有随着向上移动而改变,并且按下“向右”键,snake试图在x轴上移动,而y轴值仍然相同 我在你的程序中捕捉到了x和y值,在第一次关机时正常运行时它们看起来是这样的。启动后,我让蛇一直跑到右边的墙上,然后在它撞到墙上之前向下转弯:
x[0],y[0] | x[1],y[1]
720 , 0 | 680, 0 <-- coordinates before turn
720 , 40 | 720, 0 <-- coordinates after turn
x[0],y[0]| x[1],y[1]
720,0 | 680,0我对您的代码做了一些更改,现在它可以像您预期的那样正常工作
基本上,问题是如果在计时器滴答作响之前按两个键足够快,并且调用snakeMove
,则会覆盖direction
变量,因此会“错过”一个键
想象一下这些步骤发生了:
方向
为V,蛇向左转
计时器滴答作响,调用snakeMove
,该方法计算direction
,该方向为V,因此snake继续向左移动
在计时器再次滴答作响之前,我同时按向上+向右键。因此,在计时器再次计时之前,会发生两个事件:
第一个键被处理,因此方向
被设置为设置方向==“U”
第二个键被处理,因此direction
被设置为rightdirection==“H”
现在,只有计时器再次滴答作响并调用snakeMove
。该方法在switch语句中计算direction
,并且direction==“H”
,因此我们“错过”了direction==“U”
,因为它在计时器勾选之前被方法中的第二次按键覆盖
为了克服这个问题,正如我在前面的问题中建议的那样,使用FIFO(先进先出)列表来正确处理所有键,这样它们就不会“丢失”
这可以使用具有我们需要的pop()
函数的
因此,在您的代码中,我将全局变量direction
重命名为currentDirection
:
private String currentDirection;
并删除了static
修饰符,因为这是不需要的,我还删除了snakeMove
上的static
修饰符,因为这也是不需要的,并阻止我们访问实例变量,即currentDirection
。我还将作用域更改为private
,正如您在代码片段中显示的那样,不需要将其设置为public
,但这些更改只是为了提高代码的正确性。然后我创建了一个全局变量:
private LinkedList<String> directions = new LinkedList<>();
这就解决了上述问题
以下是实现了这些更改的代码:
import javax.sound.sampled.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.util.LinkedList;
public class FirstGraphic extends JPanel implements ActionListener, KeyListener {
//Storlek på fönstret
static int WIDTH = 800, HEIGHT = 840;
Timer tmMove = new Timer(150, this);
private JFrame window;
static int bodySize = 40, xNormalFruit = 0, yNormalFruit = 0, gameSquares = (WIDTH * HEIGHT) / bodySize,
snakeParts = 7, score = 0, restartButtonWIDTH = 190, restartButtonHEIGHT = 50;
static int x[] = new int[gameSquares];
static int y[] = new int[gameSquares];
private String currentDirection;
boolean gameRunning = false, gameStarted = false, instructions = false, isDead = false;
public static JButton restartButton = new JButton("STARTA OM"), toInstructionsButton = new JButton("Nästa");
private LinkedList<String> directions = new LinkedList<>();
public static void main(String[] args) {
JFrame window = new JFrame("Snake Game");
FirstGraphic content = new FirstGraphic(window);
window.setContentPane(content);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(false);
window.pack();
restartButton.setBounds((WIDTH / 2) - (restartButtonWIDTH) / 2, (HEIGHT - 40) / 2 + 100, restartButtonWIDTH, restartButtonHEIGHT);
restartButton.setBackground(new Color(48, 165, 55));
restartButton.setFont(new Font("Arial", Font.BOLD, 20));
window.setLocationRelativeTo(null);
window.setVisible(true);
content.setUp();
}
public FirstGraphic(JFrame window) {
super();
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setFocusable(true);
requestFocus();
this.window = window;
}
public void setUp() {
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
restartButton.addActionListener(this);
directions.add("H");
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (gameRunning) {
g.setColor(new Color(0, 0, 0));
g.fillRect(0, 0, WIDTH, (HEIGHT - 40));
g.setColor(new Color(63, 116, 41, 255));
g.fillRect(0, HEIGHT - 40, WIDTH, (2));
g.setColor(new Color(0, 0, 0, 240));
g.fillRect(0, HEIGHT - 38, WIDTH, 38);
g.setColor(new Color(0, 0, 0));
}
draw(g);
}
public void draw(Graphics g) {
if (gameRunning) {
g.setColor(new Color(35, 179, 52, 223));
g.fillOval(xNormalFruit, yNormalFruit, bodySize, bodySize);
g.setColor(new Color(44, 141, 23, 255));
g.setFont(new Font("Arial", Font.BOLD, 18));
for (int i = 0; i < snakeParts; i++) {
if (i == 0) {
g.setColor(Color.RED);
g.fillOval(x[i], y[i], bodySize, bodySize);
} else {
g.setColor(Color.PINK);
g.fillOval(x[i], y[i], bodySize, bodySize);
}
}
} else if (!gameRunning && gameStarted) {
gameOver(g);
} else if (!instructions) {
startScene(g);
} else {
instructions(g);
}
}
public void startScene(Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(0, 0, WIDTH, HEIGHT);
g.setColor(Color.WHITE);
g.setFont(new Font("Arial", Font.BOLD, 85));
g.drawString("Ormen Olle's", 150, 170);
g.drawString("Äventyr", 235, 254);
window.add(toInstructionsButton);
toInstructionsButton.setBounds(240, 660, 300, 100);
toInstructionsButton.setBackground(new Color(48, 165, 55));
toInstructionsButton.setForeground(Color.BLACK);
toInstructionsButton.setFont(new Font("Arial", Font.BOLD, 60));
toInstructionsButton.addActionListener(this);
}
public void instructions(Graphics g) {
g.setFont(new Font("Arial", Font.BOLD, 85));
g.setColor(new Color(14, 69, 114));
g.drawString("PRESS SPACE", 210, 720);
}
public void gameOver(Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(0, 0, WIDTH, HEIGHT);
g.setColor(Color.red);
g.setFont(new Font("Arial", Font.BOLD, 65));
FontMetrics metrics = getFontMetrics(g.getFont());
g.drawString("Du dog!", (WIDTH - metrics.stringWidth("Du dog!")) / 2, (HEIGHT - 40) / 2);
g.setColor(new Color(44, 141, 23, 255));
g.setFont(new Font("Arial", Font.BOLD, 20));
FontMetrics metrics2 = getFontMetrics(g.getFont());
g.drawString("SCORE: " + score, (WIDTH - metrics2.stringWidth("SCORE: " + score)) / 2, 50);
window.add(restartButton);
}
public void checkFruit() {
if ((x[0] == xNormalFruit) && (y[0] == yNormalFruit)) {
snakeParts++;
score++;
newFruit();
}
for (int v = 1; v < snakeParts; v++) {
if ((x[v] == xNormalFruit) && y[v] == yNormalFruit) {
newFruit();
}
}
}
public void checkCollisions() {
for (int i = snakeParts; i > 0; i--) {
if ((x[0] == x[i]) && (y[0] == y[i])) {
gameRunning = false;
isDead = true;
}
}
if (x[0] < 0) {
gameRunning = false;
isDead = true;
}
if (x[0] == WIDTH) {
gameRunning = false;
isDead = true;
}
if (y[0] < 0) {
gameRunning = false;
isDead = true;
}
if (y[0] > (HEIGHT - 40) - bodySize) {
gameRunning = false;
isDead = true;
}
if (!gameRunning) {
tmMove.stop();
}
}
public void snakeMove() {
for (int i = snakeParts; i > 0; i--) {
x[i] = x[i - 1];
y[i] = y[i - 1];
}
if (!directions.isEmpty()) {
currentDirection = directions.pop();
}
switch (currentDirection) {
case "H":
x[0] = x[0] + bodySize;
break;
case "V":
x[0] = x[0] - bodySize;
break;
case "U":
y[0] = y[0] - bodySize;
break;
case "N":
y[0] = y[0] + bodySize;
break;
}
}
public static void newFruit() {
xNormalFruit = (rollDice(WIDTH / bodySize) * bodySize) - bodySize;
yNormalFruit = (rollDice((HEIGHT - 40) / bodySize) * bodySize) - bodySize;
}
public static int rollDice(int numberOfSides) {
//Kastar en tärning med ett specifikt antal sidor.
return (int) (Math.random() * numberOfSides + 1);
}
@Override
public void actionPerformed(ActionEvent actionEvent) {
if (actionEvent.getSource() == restartButton && isDead) {
isDead = false;
for (int i = 0; i < snakeParts; i++) {
if (i == 0) {
x[i] = 0;
y[i] = 0;
} else {
x[i] = 0 - bodySize;
y[i] = 0;
}
}
gameRunning = true;
tmMove.start();
//direction = "H";
directions.clear();
directions.add("H");
window.remove(restartButton);
score = 0;
snakeParts = 7;
newFruit();
repaint();
}
if (actionEvent.getSource() == toInstructionsButton && !instructions) {
instructions = true;
window.remove(toInstructionsButton);
repaint();
}
if (actionEvent.getSource() == tmMove) {
if (gameRunning) {
snakeMove();
checkFruit();
checkCollisions();
} else {
repaint();
}
repaint();
}
}
@Override
public void keyTyped(KeyEvent ke) {
}
@Override
public void keyPressed(KeyEvent ke) {
if (ke.getKeyCode() == KeyEvent.VK_SPACE && !gameRunning && instructions) {
snakeMove();
checkFruit();
checkCollisions();
newFruit();
gameRunning = true;
instructions = false;
}
if (ke.getKeyCode() == KeyEvent.VK_SPACE && gameRunning) {
if (gameStarted) {
gameStarted = false;
tmMove.stop();
} else {
tmMove.start();
gameStarted = true;
}
}
if (gameStarted) {
switch (ke.getKeyCode()) {
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_D:
directions.add("H");
break;
case KeyEvent.VK_LEFT:
case KeyEvent.VK_A:
directions.add("V");
break;
case KeyEvent.VK_UP:
case KeyEvent.VK_W:
directions.add("U");
break;
case KeyEvent.VK_DOWN:
case KeyEvent.VK_S:
directions.add("N");
break;
case KeyEvent.VK_E:
tmMove.setDelay(200);
break;
case KeyEvent.VK_M:
tmMove.setDelay(150);
break;
case KeyEvent.VK_H:
tmMove.setDelay(100);
break;
}
}
}
@Override
public void keyReleased(KeyEvent ke) {
}
}
导入javax.sound.sampled.*;
导入javax.swing.*;
导入java.awt.*;
导入java.awt.event.ActionEvent;
导入java.awt.event.ActionListener;
导入java.awt.event.KeyEvent;
导入java.awt.event.KeyListener;
导入java.io.File;
导入java.util.LinkedList;
公共类FirstGraphic扩展JPanel实现ActionListener、KeyListener{
//斯托莱克·潘斯特雷特
静态整数宽度=800,高度=840;
定时器tmMove=新定时器(150,此);
私有JFrame窗口;
静态int bodySize=40,xNormalFruit=0,ynnormalfruit=0,gameSquares=(宽度*高度)/体型,
蛇形部分=7,分数=0,重启按钮宽度=190,重启按钮宽度=50;
静态整数x[]=新整数[gameSquares];
静态整数y[]=新整数[gameSquares];
私有字符串方向;
布尔gameRunning=false,gameStarted=false,instructions=false,isDead=false;
公共静态JButton restartButton=newjbutton(“startaom”),toInstructionsButton=newjbutton(“Nästa”);
私有LinkedList方向=新建LinkedList();
公共静态void main(字符串[]args){
JFrame窗口=新JFrame(“蛇游戏”);
FirstGraphic内容=新的FirstGraphic(窗口);
setContentPane(内容);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setresizeable(false);
window.pack();
restartButton.setBounds((宽度/2)-(restartButtonWIDTH)/2,(高度-40)/2+100,restartButtonWIDTH,restartButtonHEIGHT);
restartButton.setBackground(新颜色(48165,55));
setFont(新字体(“Arial”,Font.BOLD,20));
window.setLocationRelativeTo(空);
window.setVisible(true);
content.setUp();
}
公共FirstGraphic(JFrame窗口){
超级();
setPreferredSize(新尺寸(宽度、高度));
设置聚焦(真);
requestFocus();
this.window=窗口;
}
公共作废设置(){
addKeyListener(此);
设置聚焦(真);
setFocusTraversalKeysEnabled(false);
restartButton.addActionListener(此);
指示。添加(“H”);
}
@凌驾
公共组件(图形g){
超级组件(g);
如果(游戏运行){
g、 setColor(新颜色(0,0,0));
g、 fillRect(0,0,宽度,(高度-40));
g、 setColor(新颜色(63116、41255));
public void snakeMove() {
...
if (!directions.isEmpty()) { // if its not empty we have a new key(s) to process
// proccess the keys pressed from the oldest to the newest and set the new direction
currentDirection = directions.pop(); // takes the first oldest key from the queue
}
switch (currentDirection) {
...
}
}
import javax.sound.sampled.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.util.LinkedList;
public class FirstGraphic extends JPanel implements ActionListener, KeyListener {
//Storlek på fönstret
static int WIDTH = 800, HEIGHT = 840;
Timer tmMove = new Timer(150, this);
private JFrame window;
static int bodySize = 40, xNormalFruit = 0, yNormalFruit = 0, gameSquares = (WIDTH * HEIGHT) / bodySize,
snakeParts = 7, score = 0, restartButtonWIDTH = 190, restartButtonHEIGHT = 50;
static int x[] = new int[gameSquares];
static int y[] = new int[gameSquares];
private String currentDirection;
boolean gameRunning = false, gameStarted = false, instructions = false, isDead = false;
public static JButton restartButton = new JButton("STARTA OM"), toInstructionsButton = new JButton("Nästa");
private LinkedList<String> directions = new LinkedList<>();
public static void main(String[] args) {
JFrame window = new JFrame("Snake Game");
FirstGraphic content = new FirstGraphic(window);
window.setContentPane(content);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(false);
window.pack();
restartButton.setBounds((WIDTH / 2) - (restartButtonWIDTH) / 2, (HEIGHT - 40) / 2 + 100, restartButtonWIDTH, restartButtonHEIGHT);
restartButton.setBackground(new Color(48, 165, 55));
restartButton.setFont(new Font("Arial", Font.BOLD, 20));
window.setLocationRelativeTo(null);
window.setVisible(true);
content.setUp();
}
public FirstGraphic(JFrame window) {
super();
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setFocusable(true);
requestFocus();
this.window = window;
}
public void setUp() {
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
restartButton.addActionListener(this);
directions.add("H");
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (gameRunning) {
g.setColor(new Color(0, 0, 0));
g.fillRect(0, 0, WIDTH, (HEIGHT - 40));
g.setColor(new Color(63, 116, 41, 255));
g.fillRect(0, HEIGHT - 40, WIDTH, (2));
g.setColor(new Color(0, 0, 0, 240));
g.fillRect(0, HEIGHT - 38, WIDTH, 38);
g.setColor(new Color(0, 0, 0));
}
draw(g);
}
public void draw(Graphics g) {
if (gameRunning) {
g.setColor(new Color(35, 179, 52, 223));
g.fillOval(xNormalFruit, yNormalFruit, bodySize, bodySize);
g.setColor(new Color(44, 141, 23, 255));
g.setFont(new Font("Arial", Font.BOLD, 18));
for (int i = 0; i < snakeParts; i++) {
if (i == 0) {
g.setColor(Color.RED);
g.fillOval(x[i], y[i], bodySize, bodySize);
} else {
g.setColor(Color.PINK);
g.fillOval(x[i], y[i], bodySize, bodySize);
}
}
} else if (!gameRunning && gameStarted) {
gameOver(g);
} else if (!instructions) {
startScene(g);
} else {
instructions(g);
}
}
public void startScene(Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(0, 0, WIDTH, HEIGHT);
g.setColor(Color.WHITE);
g.setFont(new Font("Arial", Font.BOLD, 85));
g.drawString("Ormen Olle's", 150, 170);
g.drawString("Äventyr", 235, 254);
window.add(toInstructionsButton);
toInstructionsButton.setBounds(240, 660, 300, 100);
toInstructionsButton.setBackground(new Color(48, 165, 55));
toInstructionsButton.setForeground(Color.BLACK);
toInstructionsButton.setFont(new Font("Arial", Font.BOLD, 60));
toInstructionsButton.addActionListener(this);
}
public void instructions(Graphics g) {
g.setFont(new Font("Arial", Font.BOLD, 85));
g.setColor(new Color(14, 69, 114));
g.drawString("PRESS SPACE", 210, 720);
}
public void gameOver(Graphics g) {
g.setColor(Color.BLACK);
g.fillRect(0, 0, WIDTH, HEIGHT);
g.setColor(Color.red);
g.setFont(new Font("Arial", Font.BOLD, 65));
FontMetrics metrics = getFontMetrics(g.getFont());
g.drawString("Du dog!", (WIDTH - metrics.stringWidth("Du dog!")) / 2, (HEIGHT - 40) / 2);
g.setColor(new Color(44, 141, 23, 255));
g.setFont(new Font("Arial", Font.BOLD, 20));
FontMetrics metrics2 = getFontMetrics(g.getFont());
g.drawString("SCORE: " + score, (WIDTH - metrics2.stringWidth("SCORE: " + score)) / 2, 50);
window.add(restartButton);
}
public void checkFruit() {
if ((x[0] == xNormalFruit) && (y[0] == yNormalFruit)) {
snakeParts++;
score++;
newFruit();
}
for (int v = 1; v < snakeParts; v++) {
if ((x[v] == xNormalFruit) && y[v] == yNormalFruit) {
newFruit();
}
}
}
public void checkCollisions() {
for (int i = snakeParts; i > 0; i--) {
if ((x[0] == x[i]) && (y[0] == y[i])) {
gameRunning = false;
isDead = true;
}
}
if (x[0] < 0) {
gameRunning = false;
isDead = true;
}
if (x[0] == WIDTH) {
gameRunning = false;
isDead = true;
}
if (y[0] < 0) {
gameRunning = false;
isDead = true;
}
if (y[0] > (HEIGHT - 40) - bodySize) {
gameRunning = false;
isDead = true;
}
if (!gameRunning) {
tmMove.stop();
}
}
public void snakeMove() {
for (int i = snakeParts; i > 0; i--) {
x[i] = x[i - 1];
y[i] = y[i - 1];
}
if (!directions.isEmpty()) {
currentDirection = directions.pop();
}
switch (currentDirection) {
case "H":
x[0] = x[0] + bodySize;
break;
case "V":
x[0] = x[0] - bodySize;
break;
case "U":
y[0] = y[0] - bodySize;
break;
case "N":
y[0] = y[0] + bodySize;
break;
}
}
public static void newFruit() {
xNormalFruit = (rollDice(WIDTH / bodySize) * bodySize) - bodySize;
yNormalFruit = (rollDice((HEIGHT - 40) / bodySize) * bodySize) - bodySize;
}
public static int rollDice(int numberOfSides) {
//Kastar en tärning med ett specifikt antal sidor.
return (int) (Math.random() * numberOfSides + 1);
}
@Override
public void actionPerformed(ActionEvent actionEvent) {
if (actionEvent.getSource() == restartButton && isDead) {
isDead = false;
for (int i = 0; i < snakeParts; i++) {
if (i == 0) {
x[i] = 0;
y[i] = 0;
} else {
x[i] = 0 - bodySize;
y[i] = 0;
}
}
gameRunning = true;
tmMove.start();
//direction = "H";
directions.clear();
directions.add("H");
window.remove(restartButton);
score = 0;
snakeParts = 7;
newFruit();
repaint();
}
if (actionEvent.getSource() == toInstructionsButton && !instructions) {
instructions = true;
window.remove(toInstructionsButton);
repaint();
}
if (actionEvent.getSource() == tmMove) {
if (gameRunning) {
snakeMove();
checkFruit();
checkCollisions();
} else {
repaint();
}
repaint();
}
}
@Override
public void keyTyped(KeyEvent ke) {
}
@Override
public void keyPressed(KeyEvent ke) {
if (ke.getKeyCode() == KeyEvent.VK_SPACE && !gameRunning && instructions) {
snakeMove();
checkFruit();
checkCollisions();
newFruit();
gameRunning = true;
instructions = false;
}
if (ke.getKeyCode() == KeyEvent.VK_SPACE && gameRunning) {
if (gameStarted) {
gameStarted = false;
tmMove.stop();
} else {
tmMove.start();
gameStarted = true;
}
}
if (gameStarted) {
switch (ke.getKeyCode()) {
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_D:
directions.add("H");
break;
case KeyEvent.VK_LEFT:
case KeyEvent.VK_A:
directions.add("V");
break;
case KeyEvent.VK_UP:
case KeyEvent.VK_W:
directions.add("U");
break;
case KeyEvent.VK_DOWN:
case KeyEvent.VK_S:
directions.add("N");
break;
case KeyEvent.VK_E:
tmMove.setDelay(200);
break;
case KeyEvent.VK_M:
tmMove.setDelay(150);
break;
case KeyEvent.VK_H:
tmMove.setDelay(100);
break;
}
}
}
@Override
public void keyReleased(KeyEvent ke) {
}
}