Tue
Apr 7th

Let’s Make a Game 4: Scores and Death

Note: Remember to view this article on Quite Useful’s site in case you can’t read the code examples.

The snake game we’ve been building over the last 3 articles is still pretty basic.  The player can’t restart a game, there’s no score, food doesn’t reappear, and the snake can slither off the screen.

If this is the first part you’ve read, check out Part 1 to see how to get started.

In this part we’re going to:

  • Track the snake’s death
  • Track the player’s score
  • Add a border around the game board
  • Allow the player to restart the game after death
  • Introduce source code version control

Here’s what the results will look like:

The Boundaries

The game currently lays out random bricks and food using [rand(50), rand(50)].  Where did 50 come from?  I made it up for testing!  We need to store the boundary of the board so it can be reused by the collision detection methods and for positioning game items.

GameBoard#initialize will be modified to record this:

  @boundary_x = [1, 58]

@boundary_y = [4, 48]

By the way, the ClassName#method_name notation is commonly used by Ruby programmers to refer to “instance methods” — methods that can be run when a class has been instantiated (created, initialized) with new.

Next we need to make the random coordinate code reusable so bricks and food can use it when setting up the game board.  GameBoard needs this method:

  def random_coordinate

[rand(@boundary_x.last - @boundary_x.first - 1) + @boundary_x.first + 1,

rand(@boundary_y.last - @boundary_y.first - 1) + @boundary_y.first + 1]

end

This will return a coordinate within the boundary.  The boundary is now flexible, and can be resized as required.  These variables can be used to draw bricks to represent the boundary:

I use Brick.new here rather than add_brick because I don’t want the collision detection methods to search for boundary collisions.  This is because they add a lot of tiles to the brick array, and we’d have to check for collisions on them every animation loop.  Instead, the crashed_into_brick? method needs to detect boundary collisions:

return true if @snake.x == @boundary_x.first or @snake.x == @boundary_x.last

return true if @snake.y == @boundary_y.first or @snake.y == @boundary_y.last

Splat!

The GameBoard#random_coordinate method can be passed to all methods that take x and y coordinate parameters.  Even though this method returns an array, it can be passed as two parameters to a method using the splat operator:

add_food *random_coordinate

This method is defined as def add_food(x, y), but the splat operator (*) will allow us to pass the array that random_coordinate returns.

Death

We need to track the snake’s death so the game knows when the game can be restarted without losing the player’s progress.  Simply add an instance variable to the Snake class called dead, and set it to false in the initialize method.  Then, when the snake dies in the animation loop, set it to true.

I’ve changed the keypress detection code to respond to ‘r’ only when the snake is dead.  This allows the game to be restarted:

A setup method must also be added so the game board and snake can be reinitialized.  Shoes lets us use clear to clear all objects, which will both clear the display and free up the memory they were using.

Scores

Adding text in Shoes is easy, just write a paragraph with the para method.  Like most methods in Shoes, para will return an object that can be modified later.  Whenever the snake eats food, the score’s text can be updated like this:

@score.text = "Score: #{@board.score}"

Version Control

The code’s actually getting quite long now.  Adding huge examples in a blog post isn’t a great idea, so I’m going to put the code in a version control system.  If you’re a programmer who’s just following this for fun, you’ll know all about version control so you can skip this and get my code at the end.  Otherwise, it’s worth reading just to get the basics.

I’m going to show you how to use Git for version control.  I already have a folder called snake, with snake.rb inside it.  I’ve got an account on GitHub which lets me share code with people.  GitHub will let you download a zip of my code, but you could also install Git and checkout the code yourself.

Windows: Try tortoisegit

Mac: See GitHub’s installation methods

Linux: Most package managers have git as git-core (ubuntu/debian do)

To get my snake code, you need to clone my repository from the public clone URL:

git clone git://github.com/alexyoung/snake-shoes.git

To get subsequent updates, run git pull.  To see changes, run git log.

The beauty of using git is you could fork my snake code on GitHub, then contact me and I could merge your code with the main branch.  That means anyone could contribute code to the snake project.

Putting it All Together

The version of the code I checked in for this tutorial can be viewed and downloaded here: snake.rb

Alternatively, visit my Snake GitHub page (or follow the clone instructions above).

Comments (View)
Related Posts Widget for Blogs by LinkWithin