使用Java创建贪吃蛇游戏

4
我决定使用Java重新创建贪吃蛇游戏,但我有些卡住了。目前,我有一个正方形,用户可以使用箭头键在屏幕上移动它。当你按下左键时,正方形开始使用计时器向左移动。你不需要按住键或者不断地按它,只要按下其他设置的键(右、上、下)中的任意一个,它就会改变方向。我的目标是使用ArrayList来保存组成贪吃蛇的正方形。目前,我已经创建了一个只有一个Snake对象的ArrayList,如果我将第二个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));
}
}

顺便提一下,当我在我的Mac上将这段完全相同的代码放入Eclipse时,我的框架背景是黑色的,但是在Windows上它是浅灰色的... 有人知道为什么吗?谢谢。


2
我相当确定每个对象叠加一个JPanel不是一个好主意... - user406009
3个回答

5
您的paintComponent(...)只绘制一个矩形。如果您想绘制多个矩形或椭圆形等,请为您的类提供List<Point>List<Rectangle2D>,并在您的Swing Timer中,从列表中删除尾部并添加新头部。然后让paintComponent()使用for循环遍历列表,绘制列表中所有保留的矩形。
此外,您可能希望使用键绑定而不是KeyListener来获取用户的按键操作,因为这样在其他组件夺取焦点时会更有效。

5
要回答你的附言,需要设置背景颜色或在所有其他内容后面添加一个有绘制颜色的JPanel。
在项目中前进时,请考虑将其变为MVC框架的练习。现在发生的情况是,您的模型和视图链接在同一个类中-这使得难以跟踪一切的逻辑。
您可以做的第一件事是将Snake与任何与视图相关的内容分开-使用ArrayList segmentLocations之类的东西,表示您定义的棋盘上每个部分位置的(x,y)-您可以使用绝对坐标,也可以在您的View中更改为绝对坐标的任意网格(这更符合MVC关系)。 Snake还应该具有表示蛇面朝方向的字段-我会使用枚举Direction {N,S,E,W},但您有其他选择,因为您还可以使用表示方向的整数,或者多种其他方式。
然后,您的Snake还应该具有更新自身的方法-move(),基于当前方向移动所有片段的位置,并导致其他所有片段跟随其前面的运动(如果您考虑它几分钟,这很容易)。
您的视图可以是一个JFrame,其中包含由JPanels组成的GridLayout,这些JPanel轮询您的Snake实例,查看是否在该位置具有片段,如果是,则绘制它,或者有多种其他选项。
您的控制器将是KeyAdapter,当按下箭头键时向Snake发送方向更新的内容。
小提示,为了使您的生活更轻松:当您添加新段时,请将其放置在Snake的最后一段的位置。下次移动时,新段将位于相同的位置,并且其余部分应相应地移动。

4

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

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;
    }
}

考虑一个线段队列,
Queue<Segment> snake = new LinkedList<Segment>();

然后每个迭代都很简单
snake.remove();
snake.add(new Segment(...));

paintComponent() 包含以下循环

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

非常感谢,这确实有助于澄清事情。 - corecase
这段代码中,Segment类中的实例变量x、y、d不是必须要声明为public才能正常工作吗?因为paintComponent方法要访问这些变量。 - corecase
如果您不喜欢公共数据成员,只需为每个变量编写一个get方法(getX()等)。 - Michael
是的,原始代码是 private static Segment。请参考 Bloch 的 Effective Java, 2nd ed,第13条:“最小化类和成员的可访问性”。 - trashgod

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接