How to Make a Line Drawing Game with Sprite Kit

Learn how to make a line drawing game like Flight Control and Harbor Master in this Sprite Kit tutorial! By Jean-Pierre Distler.

Leave a rating/review
Save for later
Share
You are currently viewing page 6 of 6 of this article. Click here to view the first page.

Game Over: When Pigs Collide

When two pigs collide, the game will show a “Game Over!” message and then give the player the chance to start a new round.

Open MyScene.m and add a BOOL instance variable to the @implementation section to indicate when the game is over:

BOOL _gameOver;

You don't want the pigs moving or spawning when the game is over, so wrap the current contents of update: inside an if statement, as follows:

if(!_gameOver) {
  // existing code here
}

Also, add the following code at the beginning of spawnAnimal to make sure new pigs don’t appear after the player has lost:

if(_gameOver) {
  return;
}

Now add this method, which you'll call when two pigs collide:

- (void)handleAnimalCollision {
  _gameOver = YES;

  SKLabelNode *gameOverLabel = [SKLabelNode labelNodeWithFontNamed:@"Thonburi-Bold"];
  gameOverLabel.text = @"Game Over!";
  gameOverLabel.name = @"label";
  gameOverLabel.fontSize = 35.0f;
  gameOverLabel.position = CGPointMake(self.size.width / 2.0f, self.size.height / 2.0f + 20.0f);
  gameOverLabel.zPosition = 5;
    
  SKLabelNode *tapLabel = [SKLabelNode labelNodeWithFontNamed:@"Thonburi-Bold"];
  tapLabel.text = @"Tap to restart.";
  tapLabel.name = @"label";
  tapLabel.fontSize = 25.0f;
  tapLabel.position = CGPointMake(self.size.width / 2.0f, self.size.height / 2.0f - 20.0f);
  tapLabel.zPosition = 5;
  [self addChild:gameOverLabel];
  [self addChild:tapLabel];
}

Here you first set _gameOver to YES, which ends the game. You add two labels centered on the screen telling the user that the game is over and that they can tap the screen to restart. You set each label's zPosition to five to ensure they are in front of all other nodes.

Now call this new method in didBeganContact:. Replace the NSLog statement that logs “Animal collision detected” with the following line:

[self handleAnimalCollision];

You told the player that they can tap the screen to restart, but right now that isn't true. Add the following method to MyScene.m to handle restarting:

- (void)restartGame {
  [self enumerateChildNodesWithName:@"line" usingBlock:^(SKNode *node, BOOL *stop) {
    [node removeFromParent];
  }];
    
  [self enumerateChildNodesWithName:@"pig" usingBlock:^(SKNode *node, BOOL *stop) {
    [node removeFromParent];
  }];
    
  [self enumerateChildNodesWithName:@"label" usingBlock:^(SKNode *node, BOOL *stop) {
    [node removeFromParent];
  }];
    
  _currentSpawnTime = 5.0f;
  _gameOver = NO;
  [self spawnAnimal];
}

restartGame removes all lines, pigs and labels from the scene, sets the spawn time back to five seconds, sets _gameOver to NO and begins spawning animals.

You need to call this method if the user taps the screen while the game is over, so add the following lines to the top of touchesBegan:withEvent::

if(_gameOver) {
  [self restartGame];
}

All right, then! Build and run once more, and enjoy the line drawing game you created. See if you’re a natural-born swineherd.

Game_Over

Adding Polish

You may have noticed some places where the game behaves strangely. For example, look at the behavior of spawning pigs—they appear to walk in place! You can fix this easily by calling moveRandom on the pigs when you spawn them.

You'll need to access this method from your scene, so add the following declaration to the interface in Pig.h:

- (void)moveRandom;

Then, inside MyScene.m, call this method in spawnAnimal, just after the line that adds the pig to the scene:

[pig moveRandom];

Build and run, and now the pigs move right away:

EagerPigs

Now let’s take a closer look at the way your game currently handles touches. Run the app and add a path to a pig, but before the pig arrives at the endpoint, try to change the path by drawing a new one.

As you can see, the pig initially ignores your second command and continues along the first path. Then, after reaching the end point, it moves back to the place where you tried to change direction and follows the second path. Every new path is just tacked onto the old one!

It would be much better if the game replaced the old path with a new path. To make this happen, first open Pig.h and declare a new method:

- (void)clearWayPoints;

Next, open Pig.m and implement this method as follows:

- (void)clearWayPoints {
  [_wayPoints removeAllObjects];
}

This method simply removes all objects from _wayPoints.

Now go to MyScene.m and add the following line to touchesBegan:withEvent:, just above the line that calls addPointToMove::

[(Pig *)node clearWayPoints];

You call clearWayPoints every time the player touches a pig.

Build and run, and now the pigs listen to your every command!

ListeningPigs

Challenge: Add a HUD to the scene showing the player how many pigs they have removed. See if you can accomplish this by yourself before checking the solution below.
[spoiler title="Tips"]

  • Add an instance variable named _animalCount inside MyScene. You'll want to increment it every time you remove a pig, but because the logic to remove the pig is in Pig.m, you should add a new public method to MyScene.h, perhaps named pigRemoved, and call it from checkForHome in Pig.m.
  • Add an SKLabelNode to your scene. Be sure to set its zPosition to a value above the other nodes. Take a look at the other labels you added for hints. For easy access to the label, you should hold a reference to it—maybe in an instance variable named _hud?
  • Update the text of your _hud whenever you update _animalCount.
  • Don't forget to reset the counter when you restart the game.

[/spoiler]

Where to Go From Here?

Here is the final project with all the code from the above tutorial.

There is still a lot of room for improvement in your game. But that’s OK—it gives you plenty of opportunities to practice your skills, old and new!

  • You could reduce the area searched when removing waypoints, which would make the pig look like it was on top of the line rather than having the line always just out of the pig's reach.
  • If you move your finger slowly while creating a line, it currently adds far too many points, producing an unpleasant visual experience. You could apply a smoothing algorithm to the points, such as the basic one described here, to reduce redundant points and improve the quality of both the line rendering and the pig's motion.
  • It's currently possible for a new pig to spawn with no warning right on top of an existing pig, ending the game immediately. How would you fix this problem?
  • You could add an indicator to warn the player of incoming pigs.
  • You could add different levels that you load from a plist, as well as different animals.
  • You could use the additional trough images provided with the starter project to make the trough appear empty after a certain amount of feeding, and require the player to refill the trough by tapping on it.
  • To tell you the truth, I really don't know why the game should end when two animals collide. It would be funnier if the animals began running in random directions for a short time after a collision. Because everything must come to an end (this tutorial, too) you could add a counter that ends the game after five collisions.

Whether or not you implement any of these suggestions, I'm sure you have your own excellent ideas, and now you know more of what you need to bring them to the App Store. Make sure to drop me a note when your game is available.