Java Snake Game: Apple showing while snake is invisible

北战南征 提交于 2021-02-11 14:51:04

问题


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 BodyParts 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!