You are now able to control a moving Snake. It also grows as it eats the food but one thing that you should also do is prevent the Snake from moving off the screen.
For this game, you will be detecting if the Snake has collided with the bounds of the play-area. You can obviously built on top of this strategy and put in other collidable objects in the play-area to make the game harder. But to keep things simple, we will only be considering the boundaries of the play-area as collidable.
Let’s begin.
To start off, first you will using the getPlayAreaBorder method, which is part of the starter project, to display the play-area boundaries on the screen.
Head over to the build method and add this method to the Stack.
@override
Widget build(BuildContext context) {
...
return Scaffold(
body: Container(
color: Color(0XFFF5BB00),
child: Stack(
children: [
getPlayAreaBorder(),
...
],
),
),
);
}
As you save, you should see a rectangular border defining the play area for the game. Next, we will be checking if the Snake has collided with any of the boundaries of the play-area.
To detect collision between the Snake and any of the play-area boundaries, we will be implementing the detectCollision method.
bool detectCollision(Offset position) {
}
This is a method that takes a position and checks if the position is within the bounds of the play-area or not. It returns a boolean value i.e. it returns true if the position overlaps or crosses one of the boundaries else it returns false.
bool detectCollision(Offset position) {
if (position.dx >= upperBoundX && direction == Direction.right) {
return true;
} else if (position.dx <= lowerBoundX && direction == Direction.left) {
return true;
} else if (position.dy >= upperBoundY && direction == Direction.down) {
return true;
} else if (position.dy <= lowerBoundY && direction == Direction.up) {
return true;
}
return false;
}
In the above code, we are simply checking if the position’s dx and dy are within the bounds defined by upperBoundX, upperBoundY, lowerBoundX and lowerBoundY. These names are pretty self-explanatory.
Finally, we need to invoke detectCollision everytime the Snake moves. If the method returns false, we immediately stop the game. If the method returns true, we let the game proceed normally.
Later, we will display a game-over dialog that will allow the user to restart the game.
Inside the getNextPosition method, right before we calculate the next position of the Snake, add the following code.
Future<Offset> getNextPosition(Offset position) async {
Offset nextPosition;
// Add this
if (detectCollision(position) == true) {
if (timer != null && timer.isActive) timer.cancel();
return position;
}
...
return nextPosition;
}
In the code above, we are just checking if there is a collision using detectCollision. If yes, we stop the timer so the Snake does not move anymore. Stopping the timer also prevents the Snake from moving, unless you hot-reload the application to do it manually. Returning the original position also prevents the Snake from moving anymore even if you hot-reload.
Save the file, and restart the application.
Now when the Snake moves outside of the play-area, the game willl stop immediately.
Let’s add a game-over dialog that will let the user know that the game is over because of the collision and allow them to restart the game.