Вопрос по user-interface, keylistener, swing, java – Создание Snake с использованием Java

4

Я решил воссоздать Snake, используя Java, но я вроде застрял. На данный момент у меня есть квадрат, по которому пользователь может перемещаться по экрану с помощью клавиш со стрелками. Когда вы нажимаете кнопку ВЛЕВО один раз, квадрат начинает двигаться влево с помощью таймера. Вам не нужно удерживать клавишу или продолжать ее нажимать; он меняет направление при нажатии любой из других установленных клавиш (вправо, вверх, вниз). Моя цель - использовать 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. Когда я помещаю этот же точный код в Eclipse на моем Mac, фон моего кадра черный, но в Windows он светло-серый ... Кто-нибудь знает почему? Благодарю.

Я уверен, что наложение одного JPanel на объект не очень хорошая идея ... Lalaland

Ваш Ответ

3   ответа
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);
    }
    ...
}
Разве переменные экземпляра x, y, d в классе Segment должны быть общедоступными, чтобы это работало? Так как paintComponent обращается к этим переменным? corecase
Просто создайте метод get для каждой переменной, если вам не нравится иметь открытые члены данных. (getX () и т. д.)
Да, оригинал былprivate static Segment, Смотрите также Блох,Effective Java, 2nd ed, & quot; Пункт 13: Минимизируйте доступность классов и членов. & quot;
Большое спасибо; это определенно помогло прояснить ситуацию. corecase
5

установите цвет фона или создайте JPanel позади всего остального с нарисованным цветом.

Чтобы продвинуться в проекте, рассмотрите возможность сделать его упражнением в рамках MVC. Сейчас происходит то, что ваша Модель и Представление связаны в одном классе - это усложняет следование логике, стоящей за всем.

Что вы можете сделать, так это сначала отделить вашу Змею от всего, что связано с видом - сделать змею сArrayList<Integer[]> segmentLocations или что-то, чтобы представить (x, y) каждого местоположения сегмента на доске, которую вы определяете - вы можете использовать абсолютные координаты или вы можете создать произвольную сетку и изменить абсолютные координаты в вашем представлении (это типизирует отношение MVC лучше). Змея также должна иметь поле для направления, в котором она находится - я бы использовалenum Direction {N, S, E, W}, но у вас есть варианты для этого, так как у вас также может быть целое число, представляющее направление, или множество других способов.

Ваша Змея также будет иметь возможность обновлять себя - move (), смещая расположение всех сегментов в зависимости от текущего направления для начального сегмента и заставляя все другие сегменты следовать за движением одного перед ним (это это довольно легко, если учесть это на пару минут).

Ваше представление может быть JFrame с GridLayout, состоящим из JPanels, которые опрашивают ваш экземпляр Snake и проверяют наличие сегмента в местоположении и, если да, рисуют его, или множество других опций.

Ваш контроллер будет KeyAdapter, который отправляет обновления в направлении к вашему Snake, когда нажаты клавиши со стрелками.

Небольшая подсказка, чтобы сделать вашу жизнь проще: когда вы добавляете новый сегмент, просто располагайте его в месте последнего сегмента Змеи. В следующий раз, когда он переместится, новый сегмент будет находиться в том же месте, и остальная часть Змеи должна была соответственно двигаться.

Все отличные предложения. +1 голос.
Большое спасибо. corecase
5

paintComponent(...) рисует только один прямоугольник Вместо этого, если вы хотите нарисовать несколько прямоугольников или овалов, или что-то еще, дайте вашему классуList<Point> или жеList<Rectangle2D>и в вашем Swing Timer удалите хвост из списка и добавьте новую голову. тогда естьpaintComponent() используйте цикл for для итерации по списку, рисуя все прямоугольники, содержащиеся в списке.

Также вы, вероятно, захотите использоватьпривязки клавиш вместо KeyListener, чтобы получить нажатие клавиши пользователем, поскольку это будет работать лучше, когда другие компоненты украдут фокус.

Спасибо за ваш вклад. corecase

Похожие вопросы