Note: I have completely rewritten and improved this game. Most of my traffic comes from these posts, so I've not deleted them. Please use the new version which can be found here.
Now that we have the world and the engine in place, we need to get working on the player. In the case of the Snake Game, the player assumes the role of the Snake. A majority of the game's logic goes into updating the snake, as it has the many more physical properties and updates than anything else in the game. With that said, let's get started, shall we?
Let's begin by creating a new class named Snake. The first thing we need to do is come up with a way of storing a variable number of tiles that make up the snake's body. Thankfully, this can be easily done with a LinkedList. We should also pass the instance of GameBoard to the snake class, as we'll need to reference it later.
/*
* The GameBoard instance.
*/
private GameBoard board;
/*
* Create the list that contains the points of the snake.
*/
private LinkedList points;
/*
* Creates a new Snake instance.
* @param board The GameBoard instance.
*/
public Snake(GameBoard board) {
this.board = board;
this.points = new LinkedList();
}
Let's quickly create an instance of Snake in the Engine's constructor before we move on.
private Snake snake;
private Engine(Canvas canvas) {
//Other code...
snake = new Snake(board);
}
Now, we need a way to determine which direction the snake is moving. We'll handle this with an enum called Direction, as we did with TileType.
public static enum Direction {
/*
* Snake is moving up.
*/
UP,
/*
* Snake is moving down.
*/
DOWN,
/*
* Snake is moving left.
*/
LEFT,
/*
* Snake is moving right.
*/
RIGHT,
/*
* Snake is stationary.
*/
NONE
}
Now, we need to store the current direction that the snake is moving.
/*
* The current direction that the snake is moving.
*/
private Direction currentDirection;
/*
* The last direction pressed on the keyboard, this will be explained in the next lesson.
*/
private Direction temporaryDirection;
Now that we have all of our variables setup, we will create a method that is called whenever a new game starts, in order to reset the snake's properties to their default values.
public void resetSnake() {
this.currentDirection = Direction.NONE;
this.temporaryDirection = Direction.NONE;
Point head = new Point(GameBoard.MAP_SIZE / 2, GameBoard.MAP_SIZE / 2);
points.clear();
points.add(head);
board.setTile(head.x, head.y, TileType.SNAKE);
}
All this method does is set both direction variables to stationary, so the snake doesn't move until we want it to. Afterwards, it clears the list of body points, then adds a new head in the center of the board. Finally, it sets the tile on the board at the location of the head to a SNAKE tile.
Finally, we're on to the meat of the class, the update method. Here is where we move update the snake's position, and determine whether or not we ate a fruit. The method is relatively long, so I'll just let the comments explain everything.
public TileType updateSnake() {
/*
* Polls the snake's direction from the keyboard input, explained later.
*/
this.currentDirection = temporaryDirection;
/*
* Gets the location of the head of the snake.
*/
Point head = points.getFirst();
/*
* Gets the current direction of the snake, and returns false if the direction leads
* us into a wall. Otherwise, it pushes a new point onto the first position of the
* list (the head) in the new position of the head.
*/
switch(currentDirection) {
case UP:
if(head.y <= 0) {
return null;
}
points.push(new Point(head.x, head.y - 1));
break;
case DOWN:
if(head.y >= GameBoard.MAP_SIZE - 1) {
return null;
}
points.push(new Point(head.x, head.y + 1));
break;
case LEFT:
if(head.x <= 0) {
return null;
}
points.push(new Point(head.x - 1, head.y));
break;
case RIGHT:
if(head.x >= GameBoard.MAP_SIZE - 1) {
return null;
}
points.push(new Point(head.x + 1, head.y));
break;
case NONE:
return TileType.EMPTY;
}
/*
* Updates the head variable with the position of the new head.
*/
head = points.getFirst();
/*
* Gets the type of tile that was at the head location before we updated.
* If the tile type was not a fruit, we remove the last point from the snake
* and update the board to reflect this.
*/
TileType oldType = board.getTile(head.x, head.y);
if(!oldType.equals(TileType.FRUIT)) {
Point last = points.removeLast();
board.setTile(last.x, last.y, TileType.EMPTY);
oldType = board.getTile(head.x, head.y);
}
/*
* Set the tile at the head location to a snake tile.
* /
board.setTile(head.x, head.y, TileType.SNAKE);
/*
* Return the tile type.
*/
return oldType;
}
Finally, we need to add a call to the updateSnake method into the engine instance's update method.
public void update() {
snake.updateSnake();
}
Alright, now we're finally getting somewhere. We'll be returning to this class throughout the rest of this tutorial series, namely when we start adding input, so be ready for some major editing to this class.
snake = new Snake(board); can't resolve a variable :(
ReplyDelete