使用 Java 创建 Snake

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10964853/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 03:15:04  来源:igfitidea点击:

Creating Snake using Java

javaswinguser-interfacekeylistener

提问by corecase

I decided to re-create Snake using Java, but I'm sort of stuck. At the moment, I have a square that the user can move around the screen using the arrow keys. When you press LEFT once, the square begins to move left using a timer.. You don't need to hold down the key or keep pressing it; it changes direction when you press any of the other keys that are set(Right, Up, Down). My goal is to use an ArrayList to hold the squares that make up the snake. At the moment, I've created an ArrayList with just one Snake object inside, if I add a second Snake object to the list and add it to the frame(with the first), only one Snake object is visible and the keys to move it don't function. I'm looking for some ideas as to how I can progress with this project - please don't give me the full answer, because I'd like to figure out most of it on my own; I just need some direction. Thanks in advance - code is below.

我决定使用 Java 重新创建 Snake,但我有点卡住了。目前,我有一个正方形,用户可以使用箭头键在屏幕上移动。当您按一次 LEFT 时,方块开始使用计时器向左移动。您不需要按住该键或一直按住它;当您按下任何其他设置的键(向右、向上、向下)时,它会改变方向。我的目标是使用 ArrayList 来保存构成蛇的方块。目前,我已经创建了一个 ArrayList,里面只有一个 Snake 对象,如果我将第二个 Snake 对象添加到列表中并将其添加到框架中(第一个),则只有一个 Snake 对象可见并且要移动的键它不起作用。我正在寻找一些关于如何推进这个项目的想法 - 请不要给我完整的答案,因为我' 我想自己解决大部分问题;我只是需要一些指导。提前致谢 - 代码如下。

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Snake extends JPanel implements KeyListener, ActionListener
{
int x = 0, y = 0, velx = 0, vely = 0;
Timer t = new Timer(250, this);


public Snake(int num1, int num2)
{
    t.start();
    addKeyListener(this);
    setFocusable(true);
    setFocusTraversalKeysEnabled(true);
    x = num1;
    y = num2;
}
public void paintComponent(Graphics g)
{   
    super.paintComponent(g);

    g.setColor(Color.blue);
    g.fillRect(x, y, 20, 20);
}
public void actionPerformed(ActionEvent e)
{
    repaint();
    x += velx;
    y += vely;
}
public void up()
{
    vely = -20;
    velx = 0;
}
public void down()
{
    vely = 20;
    velx = 0;
}
public void left()
{
    vely = 0;
    velx = -20;
}
public void right()
{
    vely = 0;
    velx = 20;
}
public void keyPressed(KeyEvent e)
{
    int code = e.getKeyCode();

    if(code == KeyEvent.VK_UP)
        up();
    else if(code == KeyEvent.VK_DOWN)
        down();
    else if(code == KeyEvent.VK_RIGHT)
        right();
    else if(code == KeyEvent.VK_LEFT)
        left();

}
public void keyReleased(KeyEvent e)
{

}
public void keyTyped(KeyEvent e)
{

}
}
//Driver Class
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.util.*;

public class UserClass
{
private static JFrame frame = new JFrame("Snake");
private static ArrayList<Snake> mySnake = new ArrayList<Snake>();

public static void main(String[] args)
{
    frame.setSize(500,500);
    frame.setBackground(Color.black);
    frame.setVisible(true);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    mySnake.add(new Snake(20,20));
    frame.add(mySnake.get(0));
}
}

P.S When I put this same exact code in Eclipse on my Mac, the background of my frame is black, but on Windows it's light gray... Anyone know why? Thanks.

PS 当我在 Mac 上的 Eclipse 中放置完全相同的代码时,框架的背景是黑色的,但在 Windows 上它是浅灰色的......有人知道为什么吗?谢谢。

回答by Hovercraft Full Of Eels

Your paintComponent(...)only draws one rectangle. Instead if you want to draw multiple rectangles or ovals, or whatever, give your class a List<Point>or List<Rectangle2D>, and in your Swing Timer, remove the tail from the list and add a new head. then have paintComponent()use a for loop to iterate through a list, drawing all the rectangles held by the list.

paintComponent(...)只画一个矩形。相反,如果您想绘制多个矩形或椭圆形或其他任何内容,请为您的班级提供List<Point>List<Rectangle2D>,然后在您的 Swing Timer 中,从列表中移除尾部并添加一个新头部。然后paintComponent()使用 for 循环遍历列表,绘制列表中的所有矩形。

Also you will probably want to use key bindingsrather than a KeyListener to get the user's key presses as this will work better when other components steal the focus.

此外,您可能希望使用键绑定而不是 KeyListener 来获取用户的按键,因为当其他组件窃取焦点时,这会更好地工作。

回答by Michael

To answer your postscript, set a background color or make a JPanel behind everything else with a painted color.

要回答您的后记,请设置背景颜色或在其他所有内容后面添加一个 JPanel 并涂上颜色。

To progress with the project, consider making it an exercise in the MVC framework. What is happening now is that your Model and View are linked in the same class - this makes it hard to follow the logic behind everything.

要推进该项目,请考虑将其作为 MVC 框架中的一项练习。现在发生的事情是您的模型和视图链接在同一个类中 - 这使得很难遵循一切背后的逻辑。

What you can do is first separate your Snake from anything to do with the view - make a snake with an ArrayList<Integer[]> segmentLocationsor something, to represent the (x,y) of each of the segment locations on a board that you define - you can use absolute coordinates or you can make an arbitrary grid and change to absolute coordinates in your View (this typifies the MVC relationship better). Snake should also have a field for the direction the snake is facing - I would use an enum Direction {N, S, E, W}, but you have options on that, as you could also have an integer representing direction, or a variety of other ways.

您可以做的是首先将您的 Snake 与与视图有关的任何事情分开 - 用一个ArrayList<Integer[]> segmentLocations或某物制作一条蛇,以表示您定义的板上每个线段位置的 (x,y) - 您可以使用绝对坐标,或者您可以制作任意网格并在您的视图中更改为绝对坐标(这更好地代表了 MVC 关系)。Snake 还应该有一个表示蛇面向的方向的字段 - 我会使用enum Direction {N, S, E, W},但是你可以选择,因为你也可以有一个表示方向的整数,或其他各种方式。

Your Snake would then also have ways to update itself - move(), shifting the location of all of the segments based on the current direction for the initial segment and causing all of the other segments to follow the movement of the one before it (this is pretty easy if you consider it for a couple of minutes).

然后,您的 Snake 也可以自行更新 - move(),根据初始线段的当前方向移动所有线段的位置,并使所有其他线段跟随它之前的线段的移动(这如果您考虑几分钟,这很容易)。

Your view could be a JFrame with a GridLayout consisting of JPanels which poll your Snake instance and see if there is a segment in the location and if so, draw it, or a multitude of other options.

您的视图可以是一个带有 GridLayout 的 JFrame,它由 JPanel 组成,这些 JPanel 轮询您的 Snake 实例并查看该位置是否有一个段,如果有,绘制它,或许多其他选项。

Your controller would be the KeyAdapter that sends the updates in direction to your Snake when the arrow keys are pressed.

您的控制器将是 KeyAdapter,当按下箭头键时,它将向您的 Snake 发送更新。

Small hint, to make your life easier: when you add a new segment, just have it at the location of the last segment of the Snake. The next time it moves, the new segment will be in the same location, and the rest of the Snake should have move accordingly.

小提示,让您的生活更轻松:当您添加新段时,只需将其放在 Snake 的最后一段的位置即可。下次它移动时,新的部分将在同一位置,而 Snake 的其余部分应该相应地移动。

回答by trashgod

Given a class defining a segment's geometry,

给定一个定义线段几何的类,

class Segment {
    private int x, y, d;

    public Segment(int x, int y, int r) {
        this.x = x - r;
        this.y = y - r;
        this.d = 2 * r;
    }
}

Consider a queue of segments,

考虑一个段队列,

Queue<Segment> snake = new LinkedList<Segment>();

Then each iteration is simply

那么每次迭代就是

snake.remove();
snake.add(new Segment(...));

And paintComponent()includes this loop

paintComponent()包括这个循环

@Override
public void paintComponent(Graphics g) {
    ...
    for (Segment s : snake) {
        g.fillXxxx(s.x, s.y, s.d, s.d);
    }
    ...
}