问题
I am following a the following video to design a snake game: https://www.youtube.com/watch?v=91a7ceECNTc
I am following it step by step, but when I run it, the snake does not show on my screen, just the apple. I think I have something wrong when implementing public void paint(Graphics g);
Can someone help me?
This is the code my Main class
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);
}
}
This is the Panel class:
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
}
}
The Snake's body parts class:
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;
}
}
And the Apple's Class:
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:
Okay, so the issue comes down to some basic maths...
If we take a look at the draw
method for BodyPart
you will find...
g.fillRect(xCoor * width, yCoor * height, width, height);
Okay, pretty basic, but are all these values actually set too?
If we take a look at the tick
method (where BodyPart
s are created), we can find...
b = new BodyPart(xCoor, yCoor, 10);
snake.add(b);
Okay, so the width
and height
is 10
, but what about xCoor
and yCoor
?
They're first initialised as instance fields along with the class...
private int xCoor = 100, yCoor = 100, size = 10;
So, a quick bit of maths tells us the initial location of the BodyPart
is 100 * 10
which equals 1000x1000
.
If we also take a look at ...
public static final int WIDTH = 1000, HEIGHT = 1000; //Dimensions of the panel (Will be set by user input later)
and
setPreferredSize(new Dimension(WIDTH, HEIGHT));
we can see that the BodyPart
is actually been set off screen initially.
So, if we change the initial position to something more like...
private int xCoor = 10, yCoor = 10, size = 10;
you will find your missing snake.
General advice...
You should avoid overriding paint
. It's to high in the paint chain and it's to easy to screw it up. Instead, prefer paintComponent
instead (and make sure you're calling super.paintComponent
). JPanel
will then clear the Graphics
context for you (with the background color of the component).
Swing is not thread safe. You should not be modify the UI or any state the UI relies on from outside the context of the Event Dispatching Thread.
The current "main" loop is in danger of introducing dirty updates which could cause issues later. See Concurrency in Swing. As a "general" preference, you should consider using a Swing Timer instead. It won't block the EDT, but's "ticks" are generated inside the EDT, making it safer to update the UI and/or its state from within.
You should avoid using "magic numbers" when performing your operations...
for (int i = 0; i < WIDTH/10; i++) {
g.drawLine(i*10, 0, i*10, HEIGHT);
}
Here, WIDTH
and HEIGHT
may not represent that actual size of the component. Instead make use of JPanel#getWidth
and JPanel#getHeight
instead.
As a general recommendation, you should avoid using setPreferred/Minimum/MaximumSize
, it's to easy for someone else to change these to a state you don't want. Instead, override getPreferred/Minimum/MaximumSize
instead, this way you maintain control.
来源:https://stackoverflow.com/questions/53095491/java-snake-game-apple-showing-while-snake-is-invisible