As the title suggests, in this project we will build a simple snake-style game. This style of game has been around since the mid-1970s under other names like Worm and Surround. I played this thing for many hours on the ancient Sword M5 and the “green screen” Sharp MZ80 back in the 1980s. Snake finally reached global acclaim in the 2000s however, when it was supplied as standard with a whole generation of Nokia mobile phones.

patreon

 

 

About this project

Skill level 1
Time to complete 1 hour

New Concepts:

  1. A deliberately restricted frame rate

Recommended preparation tutorials

Projects that demonstrate these concepts

This game will use a different engine to the other games on this site as it will make a pre-determined number of “moves” each second, rather than playing as many frames of animation as possible and then timing each frame for maximum smoothness. The reason for this is we can recreate a authentic blocky/jumpy animation.

Take a look at this image to see the game at the start.

snake-game-at-start

One dot for the snake and one dot for Bob waiting to be eaten. Before anyone complains that snakes don’t eat Bobs, they don’t eat apples either. As the game continues and many Bobs are eaten the snake grows in length making it more likely that the player will trap or eat himself.

Let’s start coding.

Coding the Snake Activity

As usual, we will start with an Activity that will control a thread in a class that controls the game and handles input from the player. If you want a more in-depth discussion of the interaction between the Activity and the main class then take a look at the Breakout tutorial.

Create a new project in Android Studio, use the Empty Activity template, and call it Snake. Leave the rest of the settings at their defaults. Call the Activity SnakeActivity and amend its code to be the same as this.

 

Here we declare an instance of SnakeEngine called snakeEngine which doesn’t exist yet but it will soon. Now code the onCreate method of the SnakeActivity class to initialize the SnakeEngine object. Obviously, there will be errors in our code but if we code SnakeActivity in full we won’t need to keep coming back to it. Add the following code and we will discuss it.

The onCreate method uses the Display class and an object of the type Point to get the resolution of the device the game is running on. Our SnakeEngine class will need a reference to the Activity and the resolution so we pass them into the SnakeEngine constructor. The last thing we do is use snakeEngine to be the View of the SnakeActivity.

[widgets_on_pages id=”udemy_advert_java_2″]

Now we can code the onPause and onResume methods. Add the following code. Again. for a full explanation read the Breakout tutorial but basically Android will call these onPause and onResume methods then these methods call the relevant methods inside SnakeEngine to start and stop the thread which handles the entire game.

Making the game fullscreen and landscape

We want to use every pixel that the device has to offer so we will make changes to the app’s AndroidManifest.xml configuration file.

  • In the project explorer pane in Android Studio double click on the manifests folder, this will open up the AndroidManifest.xml file in the code editor.
  • In the AndroidManifest.xml file, locate the following line of code: android:name=”.SnakeActivity”>
  • Place the cursor before the closing > shown above. Tap the enter key a couple of times to move the > a couple of lines below the rest of the line shown above.
  • Immediately below ParallaxActivity but BEFORE the newly positioned > type or copy and paste these two lines to make the game run full screen and lock it in the landscape orientation.

Add the sound to the project

Download the sounds by right-clicking on the files listed below. Add them to the Snake project by using your operating system’s file browser go to the app\src\main folder of the project and create a new folder called assets. Add your sound files to this folder. Here are my sound effects. Right-click and select Save link as… to download them.

Note: At the moment my Web host seems to be restricting me from uploading .ogg files. Just look at one of my other projects, download the files from them, and rename them to suit below.  You could also make your own or download the bonus content (above). Sorry will fix this as soon as I can.

snake_crash

eat_bob

We can now get rid of the errors by moving on to the SnakeEngine class.

Coding SnakeEngine

Add a new class called SnakeEngine and amend the code as shown next so we have all the required imports.

When we extend SurfaceView so that the call to setContentView in the SnakeActivity class works and we implement the Runnable interface so we can later pass this class to the Thread constructor to create a Thread instance. Runnable has one method that we must implement and we will override run soon.

The SnakeEngine variables

Add all the member variables after the class declaration then they will be ready for use as we proceed through the rest of the code.

We can now code the constructor.

Coding the SnakeEngine constructor

Add this code next, be sure to add it inside the closing curly brace of the SnakeEngine class.

First, we initialize,  context, screenX and screenY with the values passed in from SnakeActivity. Next, we divide the number of pixels by the final int NUM_BLOCKS_WIDE in order to determine the appropriate number of pixels in the width of blockSize. Now we can use this to work out, based on the number of vertical pixels, how many blocks high the playable area will be.

Next, the sound files are loaded and associated with an appropriately named identifier. They are now ready to play at will with soundPool.playSound.

What follows is we initialize surfaceHolder and paint.

[widgets_on_pages id=”udemy_advert_java_3″]

After this, we initialized the two int arrays. snakeXs will hold the horizontal coordinate of each segment of the snake and snakeYs will hold each vertical coordinate.

The last part of the code we call the newGame method which unsurprisingly starts the game. We will code newGame shortly.

Making the thread run the game loop

All in the run method, including method calls from the run method, works in a separate thread to the Android UI. This will allow our game to run smoothly at the same time as listening for player input. Add the run method as well as pause and resume and then we will talk about them.

The pause and resume methods are called by SnakeActivity when Android or the player causes the app to call onPause or onResume. The resume method creates a new instance of Thread when required and pause stops it when required. Now our instance of Thread will play nicely with Android.

Everything in, and called by the run method, will now happen in a separate thread.

The run method calls update and then draw. The whole thing is wrapped in a while loop that repeats continuously if isPlaying is set to true and the thread is running.

These calls are also contained within if(updateRequired()). Only if this is true are the update and draw methods called. The updateRequired method can, therefore, control the frame rate of the game ensuring the blocky/authentic motion.

Some more methods

As we saw, the newGame method is called by the constructor it is also called when the snake crashes and a new game is required. Add the newGame method.

In the newGame method, the snake is prepared. The length is set to just one block then the head of the snake is set to the center of the screen. The first position of each of the arrays holds the head. It is only the head that we will use when we code the collision detection. Next, Bob is prepared for a terrible demise by calling spawnBob and score is initialized to .

The final bit of code in the newGame method sets nextFrameTime to whatever the current time is. This will cause the update and draw methods run.

Spawning and eating Bob

The spawnBob method uses two random int values within the ranges of zero and NUM_BLOCKS_WIDE, zero and numBlocksHigh, then initializes the horizontal and vertical location of the mouse.

Optimization tip: Instantiating a new instance of Random is slow and could be done in the constructor then just reused each time spawnBob is called. In this context, however it will not affect the smooth running of the game.

The eatBob method is simple too.

The snake’s length is increased by one block, a new mouse is spawned, 1 is added to the score and a sound effect is played.

Here is the code for the eatBob method to add after the spawnBob method.

The moveSnake method is quite long but doesn’t involve anything too tricky. Add the code and then we can go through it.

The for loop starts at the last block of the snake in snakeXs and snakeYs and advances it into the location previously occupied by the block ahead of it. When the for loop is complete the last position is in the place the block ahead used to be in and the block that was just behind the head is where the head used to be.

Therefore, as long as we handle the head properly all the other blocks will be correctly positioned too.

To move the head we switch based on the current value of heading and add or subtract 1 from either the heads vertical or horizontal position.

In the detectDeath method, we do collision detection. Notice in the code that follows we check for two things. Has the snake’s head bumped into the edge of the screen and has the snake’s head bumped into a block of the snake’s body?

If either of the collision possibilities happens then detectDeath returns true to the update method which takes further action.

Coding the update method

This method does three things:

  1. It checks if the head has touched/eaten a mouse. If it has then the eatBob method handles things.
  2. It calls the moveSnake method which was coded previously.
  3. It calls the detectDeath method and if it returns true a sound is played and the game begins again.

All this happens ten times per second because of the way updateRequired will work. We will code updateRequired in a minute. Add the code for the update method.

Drawing the game

Add all the code for the draw method and then we will go through it.

First, we lock the surface which is required by Android. If this works, we clear the screen with drawColor and then change the color of all future objects we will draw by calling setColor. We do this once for the snake and once for Bob. Now we draw the text for the score.

We use a for loop to draw a block/square to represent each block of the snake. The code positions the blocks to screen coordinates by using their grid positions(contained in the array) multiplied by blockSize which was determined in the constructor based on screen resolution.

Now we can draw a single block to represent Bob.

Coding updateRequired

We are almost done!

The updateRequired method will let us know if the nextFrameTime variable has been exceeded by the actual current time. If it has then a new time is retrieved and put back in nextFrameTime. The method then returns true allowing draw and update to execute. If not, false is returned and the next frame is delayed until it is time.

You can now add the updateRequired method.

Handling screen touches (player input)

The final code handles the player removing their finger. Holding won’t work. The onTouchEvent method uses motionEvent.getAction to detect MotionEvent.ACTION_UP. This notifies us the player’s finger has left the screen. We then use motionEvent.getX() to determine if that action was on the left or the right of the screen.

If they tap on the left side of the screen then the snake moves to the next direction in the enumeration going anti-clockwise if they tap on the right then it’s clockwise.

It’s meant to be awkward, it is authentic to the original. Add this code to handle touches on the screen.

You can now play the game!

patreon