Simplify snake drawing and movement calculation

This commit is contained in:
Kai S. K. Engelbart 2020-07-01 16:18:59 +02:00
parent 5651ea76b0
commit a8908f7e13
No known key found for this signature in database
GPG Key ID: 0A48559CA32CB48F
4 changed files with 53 additions and 62 deletions

View File

@ -28,7 +28,7 @@ public class FoodFactory {
* @author Leon Hofmeister * @author Leon Hofmeister
* @since Snake 1.0 * @since Snake 1.0
*/ */
public static enum Food { public enum Food {
/** /**
* Use if white food is wanted. * Use if white food is wanted.

View File

@ -1,6 +1,8 @@
package dev.lh; package dev.lh;
import java.awt.*; import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -31,30 +33,31 @@ public class Snake implements Updateable {
/** /**
* Use if the snake should head left. * Use if the snake should head left.
*/ */
Left, LEFT,
/** /**
* Use if the snake should head right. * Use if the snake should head right.
*/ */
Right, RIGHT,
/** /**
* Use if the snake should head up. * Use if the snake should head up.
*/ */
Up, UP,
/** /**
* Use if the snake should head down. * Use if the snake should head down.
*/ */
Down; DOWN;
} }
private static FoodFactory foodFactory = FoodFactory.getInstance(); private static FoodFactory foodFactory = FoodFactory.getInstance();
private static Endscreen endscreen; private static Endscreen endscreen;
private Direction direction; private Direction direction = Direction.RIGHT;
private int length; private int length;
private List<Point> tiles = new ArrayList<>(); private List<Rectangle> tiles = new ArrayList<>();
private final int snakeSize = 10;
private static final int TILE_SIZE = 10;
/** /**
* Constructs a new Snake with the given length in tiles. * Constructs a new Snake with the given length in tiles.
@ -63,21 +66,21 @@ public class Snake implements Updateable {
* @since Snake 1.0 * @since Snake 1.0
*/ */
public Snake(int length) { public Snake(int length) {
this.length = length; this.length = length;
direction = Direction.Right;
// adding the initial tiles of the snake // Add initial tiles
for (int i = 0; i < length; i++) for (int i = 0; i < length; i++)
tiles.add(new Point(320 - snakeSize * i, 240)); tiles.add(new Rectangle(320 - TILE_SIZE * i, 240, TILE_SIZE, TILE_SIZE));
} }
/** /**
* Adds the given length to the current snake object * Increases the given length to the current snake object.
* *
* @param additional the number of tiles to add * @param additional the number of tiles to add
* @since Snake 1.0 * @since Snake 1.0
*/ */
public void addLength(int additional) { public void addLength(int additional) {
Point last = tiles.get(tiles.size() - 1); Rectangle last = tiles.get(tiles.size() - 1);
for (int i = 0; i < additional; i++) for (int i = 0; i < additional; i++)
tiles.add(last); tiles.add(last);
length += additional; length += additional;
@ -88,17 +91,10 @@ public class Snake implements Updateable {
* @since Snake 1.1 * @since Snake 1.1
*/ */
private boolean checkSelfCollision() { private boolean checkSelfCollision() {
Point headIndex = tiles.get(0); return tiles.stream().skip(1).anyMatch(tiles.get(0)::contains);
Rectangle head = new Rectangle(headIndex.x, headIndex.y, snakeSize, snakeSize);
for (int index = 1; index < tiles.size(); index++) {
Point bodyIndex = tiles.get(index);
if (head.contains(new Rectangle(bodyIndex.x, bodyIndex.y, snakeSize, snakeSize))) return true;
}
return false;
} }
/** /**
*
* @since Snake 1.1 * @since Snake 1.1
*/ */
private void gameOver() { private void gameOver() {
@ -117,45 +113,41 @@ public class Snake implements Updateable {
public void nextFrame() { public void nextFrame() {
int velX = 0, velY = 0; int velX = 0, velY = 0;
switch (direction) { switch (direction) {
case Up: case UP:
velY = -snakeSize; velY = -TILE_SIZE;
break; break;
case Down: case DOWN:
velY = snakeSize; velY = TILE_SIZE;
break; break;
case Left: case LEFT:
velX = -snakeSize; velX = -TILE_SIZE;
break; break;
case Right: case RIGHT:
velX = snakeSize; velX = TILE_SIZE;
break; break;
} }
Point next = (Point) tiles.get(0).clone(), cur; // Add a new tile at the front
tiles.get(0).x += velX; tiles.add(
tiles.get(0).y += velY; 0,
new Rectangle(tiles.get(0).x + velX, tiles.get(0).y + velY, TILE_SIZE, TILE_SIZE)
for (int i = 1; i < length; i++) { );
cur = tiles.get(i); // Remove the last tile
tiles.set(i, (Point) next.clone()); tiles.remove(tiles.size() - 1);
next = cur; // Case if snake is outside of the screen or touches itself
}
// case if snake is outside of the screen or touches itself
if (checkSelfCollision()) { if (checkSelfCollision()) {
gameOver(); gameOver();
System.out.println("Snake collided with itself."); System.out.println("Snake collided with itself.");
return; return;
} }
// TODO: the game bounds checking below appears to work on Windows, however // TODO: Test on Linux
// throws a NullPointerException on Linux/UNIX systems
if (!Main.getGame().getBounds().contains(tiles.get(0))) { if (!Main.getGame().getBounds().contains(tiles.get(0))) {
gameOver(); gameOver();
System.out.println("Snake went out of bounds."); System.out.println("Snake went out of bounds.");
return; return;
} }
// TODO: Move to Food class
// case if snake eats food // Case if snake eats food
if (foodFactory.checkCollision(new Rectangle(tiles.get(0).x, tiles.get(0).y, snakeSize, snakeSize))) { if (foodFactory.checkCollision(tiles.get(0))) {
addLength(foodFactory.getAdditionalLength()); addLength(foodFactory.getAdditionalLength());
GameWindow game = Main.getGame(); GameWindow game = Main.getGame();
game.newFood(); game.newFood();
@ -163,10 +155,9 @@ public class Snake implements Updateable {
} }
@Override @Override
public void render(Graphics g) { public void render(Graphics2D g) {
g.setColor(Color.green); g.setColor(Color.green);
for (int i = 0; i < length; i++) tiles.forEach(g::fill);
g.fillRect(tiles.get(i).x, tiles.get(i).y, snakeSize, snakeSize);
} }
/** /**

View File

@ -1,6 +1,6 @@
package dev.lh; package dev.lh;
import java.awt.Graphics; import java.awt.Graphics2D;
/** /**
* This interface contains everything that needs to be updated regularly. * This interface contains everything that needs to be updated regularly.
@ -25,8 +25,8 @@ public interface Updateable {
/** /**
* Renders the object. * Renders the object.
* *
* @param g the {@link Graphics} object that is used to render this object * @param g the graphics object that is used to render this object
* @since Snake 1.0 * @since Snake 1.0
*/ */
void render(Graphics g); void render(Graphics2D g);
} }

View File

@ -52,8 +52,8 @@ public class GameWindow extends JFrame {
protected void paintComponent(Graphics g) { protected void paintComponent(Graphics g) {
super.paintComponent(g); super.paintComponent(g);
g.setColor(Color.black); g.setColor(Color.black);
g.fillRect(0, 0, super.getWidth(), super.getHeight()); g.fillRect(0, 0, getWidth(), getHeight());
s.render(g); s.render((Graphics2D) g);
foodFactory.paintFood(g); foodFactory.paintFood(g);
} }
}); });
@ -66,19 +66,19 @@ public class GameWindow extends JFrame {
switch (e.getKeyCode()) { switch (e.getKeyCode()) {
case KeyEvent.VK_W: case KeyEvent.VK_W:
case KeyEvent.VK_UP: case KeyEvent.VK_UP:
if (!s.getRichtung().equals(Direction.Down)) s.setDirection(Direction.Up); if (!s.getRichtung().equals(Direction.DOWN)) s.setDirection(Direction.UP);
break; break;
case KeyEvent.VK_A: case KeyEvent.VK_A:
case KeyEvent.VK_LEFT: case KeyEvent.VK_LEFT:
if (!s.getRichtung().equals(Direction.Right)) s.setDirection(Direction.Left); if (!s.getRichtung().equals(Direction.RIGHT)) s.setDirection(Direction.LEFT);
break; break;
case KeyEvent.VK_S: case KeyEvent.VK_S:
case KeyEvent.VK_DOWN: case KeyEvent.VK_DOWN:
if (!s.getRichtung().equals(Direction.Up)) s.setDirection(Direction.Down); if (!s.getRichtung().equals(Direction.UP)) s.setDirection(Direction.DOWN);
break; break;
case KeyEvent.VK_D: case KeyEvent.VK_D:
case KeyEvent.VK_RIGHT: case KeyEvent.VK_RIGHT:
if (!s.getRichtung().equals(Direction.Left)) s.setDirection(Direction.Right); if (!s.getRichtung().equals(Direction.LEFT)) s.setDirection(Direction.RIGHT);
break; break;
} }
} }