Java Snake Game: Apple показывает, когда змея невидима - PullRequest
0 голосов
/ 01 ноября 2018

Я слежу за следующим видео для разработки игры со змеями: https://www.youtube.com/watch?v=91a7ceECNTc

Я следую за этим шаг за шагом, но когда я запускаю его, змея не появляется на моем экране, только яблоко. Я думаю, что у меня что-то не так при реализации public void paint(Graphics g); Может кто-нибудь мне помочь?

Это код моего Главного класса

import javax.swing.JFrame;
public class Main {

public static void main(String[] args) {
    JFrame frame = new JFrame ();
    GamePanel panel = new GamePanel();

    frame.add(panel);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setTitle("Snake");

    frame.pack();
    frame.setVisible(true);
    frame.setLocationRelativeTo(null);
}
}

Это класс Panel:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Random;

import javax.swing.JPanel;

public class GamePanel extends JPanel implements Runnable, KeyListener{

private static final long serialVersionUID = 1L;
public static final int WIDTH = 1000, HEIGHT = 1000; //Dimensions of the panel (Will be set by user input later)
private Thread thread;
private boolean running;
private boolean right = true, left = false, up = false, down = false;

private BodyPart b;
private ArrayList<BodyPart> snake;
private Apple apple;
private ArrayList<Apple> apples;
private Random r;

private int xCoor = 100, yCoor = 100, size = 10;
private int ticks = 0;

public GamePanel() {
    setFocusable(true);
    setPreferredSize(new Dimension(WIDTH, HEIGHT));
    addKeyListener(this);
    snake = new ArrayList<BodyPart>();
    apples = new ArrayList<Apple>();
    r = new Random();
    start();

}
public void start() {
    running = true;
    thread = new Thread(this);
    thread.start();
}
public void stop() {
    running = false;
    try {
        thread.join();
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

}
public void tick() {
    if (snake.size() == 0) {
        b = new BodyPart(xCoor, yCoor, 10);
        snake.add(b);
    }
    ticks++;
    if (ticks > 250000) {
        if (right) {
            xCoor++;
        }
        if (left) {
            xCoor--;
        }
        if (up) {
            yCoor--;
        }
        if (down) {
            yCoor++;
        }
        ticks = 0;
        b = new BodyPart(xCoor, yCoor, 10);
        snake.add(b);
        if (snake.size() > size) {
            snake.remove(0);
        }
    }
    if (apples.size() == 0) {
        int xCoor = r.nextInt(99);
        int yCoor = r.nextInt(99);
        apple = new Apple(xCoor, yCoor, 10);
        apples.add(apple);
    }
}
public void paint(Graphics g) {

    g.clearRect(0, 0, WIDTH, HEIGHT);
    g.setColor(Color.BLACK);
    g.fillRect(0, 0, WIDTH, HEIGHT);

    for (int i = 0; i < WIDTH/10; i++) {
        g.drawLine(i*10, 0, i*10, HEIGHT);
    }
    for (int i = 0; i < HEIGHT/10; i++) {
        g.drawLine(0, i*10, HEIGHT, i*10);
    }
    for (int i = 0; i < snake.size(); i++) {
        snake.get(i).draw(g);
    }
    for (int i = 0; i < apples.size(); i++) {
        apples.get(i).draw(g);
    }
}

@Override
public void run() {
    while (running) {
        tick();
        repaint();
    }
}
@Override
public void keyTyped(KeyEvent e) {
    int key = e.getKeyCode();
    if (key == KeyEvent.VK_RIGHT && !left) {
        right = true;
        up = false;
        down = false;
    }
    if (key == KeyEvent.VK_LEFT && !right) {
        left = true;
        up = false;
        down = false;
    }
    if (key == KeyEvent.VK_UP && !down) {
        up = true;
        right = false;
        left = false;
    }
    if (key == KeyEvent.VK_DOWN && !up) {
        down = true;
        right = false;
        left = false;
    }

}
@Override
public void keyPressed(KeyEvent e) {
    // TODO Auto-generated method stub

}
@Override
public void keyReleased(KeyEvent e) {
    // TODO Auto-generated method stub

}
}

Класс частей тела Змеи:

import java.awt.Color;
import java.awt.Graphics;

public class BodyPart {
public int xCoor, yCoor, width, height;

public BodyPart(int xCoor, int yCoor, int tileSize) {
    this.xCoor = xCoor;
    this.yCoor = yCoor;
    width = tileSize;
    height = tileSize;
}
public void tick() {

}
public void draw(Graphics g) {
    g.setColor(Color.YELLOW);
    g.fillRect(xCoor * width, yCoor * height, width, height);
}
public int getCoorX() {
    return xCoor;
}
public void setCoorX (int xCoor) {
    this.xCoor = xCoor;
}
public int getCoorY() {
    return yCoor;
}
public void setCoorY(int yCoor) {
    this.yCoor = yCoor;
}
}

И класс Apple:

import java.awt.Color;
import java.awt.Graphics;

public class Apple {
public int xCoor, yCoor, width, height;

public Apple(int xCoor, int yCoor, int tileSize) {
    this.xCoor = xCoor;
    this.yCoor = yCoor;
    width = tileSize;
    height = tileSize;
}
public void tick() {

}
public void draw(Graphics g) {
    g.setColor(Color.RED);
    g.fillRect(xCoor * width, yCoor * height, width, height);
}
public int getxCoor() {
    return xCoor;
}
public void setxCoor(int xCoor) {
    this.xCoor = xCoor;
}
public int getyCoor() {
    return yCoor;
}
public void setyCoor(int yCoor) {
    this.yCoor = yCoor;
}

}

1 Ответ

0 голосов
/ 01 ноября 2018

Хорошо, так что проблема сводится к некоторым основным математикам ...

Если мы посмотрим на метод draw для BodyPart, вы найдете ...

g.fillRect(xCoor * width, yCoor * height, width, height);

Хорошо, довольно просто, но действительно ли все эти значения установлены?

Если мы посмотрим на метод tick (где создаются BodyPart s), мы можем найти ...

b = new BodyPart(xCoor, yCoor, 10);
snake.add(b);

Хорошо, значит width и height - это 10, но как насчет xCoor и yCoor?

Сначала они инициализируются как поля экземпляров вместе с классом ...

private int xCoor = 100, yCoor = 100, size = 10;

Итак, небольшое количество математики говорит нам, что начальное местоположение BodyPart равно 100 * 10, что равно 1000x1000.

Если мы тоже посмотрим на ...

public static final int WIDTH = 1000, HEIGHT = 1000; //Dimensions of the panel (Will be set by user input later)

и

setPreferredSize(new Dimension(WIDTH, HEIGHT));

мы можем видеть, что BodyPart изначально отключен от экрана.

Итак, если мы изменим исходную позицию на что-то более похожее ...

private int xCoor = 10, yCoor = 10, size = 10;

вы найдете пропавшую змею.

Общие советы ...

Вам следует избегать переопределения paint. Это высоко в цепочке краски, и это легко испортить. Вместо этого предпочтите paintComponent вместо этого (и убедитесь, что вы звоните super.paintComponent). JPanel очистит для вас контекст Graphics (с цветом фона компонента).

Swing не безопасен для потоков. Вы не должны изменять пользовательский интерфейс или любое другое состояние, на которое полагается пользовательский интерфейс, вне контекста потока диспетчеризации событий.

Текущий «основной» цикл находится под угрозой введения грязных обновлений, которые могут вызвать проблемы позже. См. Параллельность в Swing . В качестве «общего» предпочтения вы должны рассмотреть возможность использования Swing Timer. Он не будет блокировать EDT, но внутри EDT генерируются «галочки», что делает более безопасным обновление интерфейса и / или его состояния изнутри.

Вы должны избегать использования "магических чисел" при выполнении ваших операций ...

for (int i = 0; i < WIDTH/10; i++) {
    g.drawLine(i*10, 0, i*10, HEIGHT);
}

Здесь WIDTH и HEIGHT могут не соответствовать фактическому размеру компонента. Вместо этого используйте JPanel#getWidth и JPanel#getHeight.

Как общая рекомендация, вам следует избегать использования setPreferred/Minimum/MaximumSize, кому-то еще легко изменить их в состояние, которое вы не хотите. Вместо этого переопределите getPreferred/Minimum/MaximumSize, вместо этого вы сохраните контроль.

...