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 3 of 6 of this article. Click here to view the first page.

Continuous Movement

Actually, the pig stops moving after it reaches the last waypoint. For Hogville, it makes more sense and will be more challenging if the pigs keep moving. Otherwise, you could simply draw a path to the pigs straight to their pens and the game would be over!

Let's correct this. Open Pig.m and add the following code at the end of move::

else {
  newPosition = CGPointMake(currentPosition.x + _velocity.x * [dt doubleValue],
                            currentPosition.y + _velocity.y * [dt doubleValue]);
}

This code simply continues to move the pig with its most recently calculated velocity when it runs out of waypoints. Remember, velocity includes both direction and speed.

Now find this line inside move::

self.position = newPosition;

Move the above line to the end of the method so that after the if-else statements.

Build and run, and watch your pig moving and moving and moving... until you can’t see it anymore. This little piggy went off screen!

Moving_Offscreen

What you need is a method to check if the pig is about to leave the screen and if so, switch its direction.

Also in Pig.m, add this method:

- (CGPoint)checkBoundaries:(CGPoint)point {
  //1
  CGPoint newVelocity = _velocity;
  CGPoint newPosition = point;
    
  //2
  CGPoint bottomLeft = CGPointZero;
  CGPoint topRight = CGPointMake(self.scene.size.width, self.scene.size.height);
    
  //3
  if (newPosition.x <= bottomLeft.x) {
    newPosition.x = bottomLeft.x;
    newVelocity.x = -newVelocity.x;
  } 
  else if (newPosition.x >= topRight.x) {
    newPosition.x = topRight.x;
    newVelocity.x = -newVelocity.x;
  }
    
  if (newPosition.y <= bottomLeft.y) {
    newPosition.y = bottomLeft.y;
    newVelocity.y = -newVelocity.y;
  }
  else if (newPosition.y >= topRight.y) {
    newPosition.y = topRight.y;
    newVelocity.y = -newVelocity.y;
  }
    
  //4
  _velocity = newVelocity;

  return newPosition;
}

This looks more complicated than it is, so let’s look more closely:

  1. First, you assign the current velocity and point to local variables.
  2. Here you define the important points on the screen. You can use bottomLeft to check if the pig is moving off screen from the left or bottom sides and topRight to check the top and right sides of the screen. You perform these checks inside the following if statements, one for each side of the screen.
  3. The first if statement checks the x value of newPosition. If this value is zero or less, the pig is leaving the screen from the left side. To avoid this, you set the pig's x-position to the left boundary—zero—and reverse the x-component of the velocity so the pig starts moving in the opposite direction. The other if statements do the same for the remaining three bounds of the screen.
  4. At the end, you change _velocity to whatever value you calculated and then return newPosition.

To make use of this new method, change the last line in move: from this:

self.position = newPosition;

To this:

self.position = [self checkBoundaries:newPosition];

Build and run your project again and watch the pig bounce off of the screen’s borders. You’ve got yourself a pig pen!

Bouncing_Pig

Rotating the Sprite

Before adding the actual gameplay, there are some minor improvements you should make. First, the pig doesn't rotate at all, which looks a bit weird. Like most animals, pigs normally walk facing forward rather than backward or to the side!

Rotate the pig so that it faces the direction it’s moving by adding the following line to the end of move: in Pig.m:

self.zRotation = atan2f(_velocity.y, _velocity.x) + M_PI_2;

atan2f returns the angle between the x-axis and the given point. You add M_PI_2 because the pig image you use faces down and the value you receive from atan2f assumes the image faces to the right. Therefore, by adding M_PI_2, you rotate the pig by 90° counterclockwise.

A rotating pig

Note: If this is too much math for you, check out our open source SKTUtils library. The library includes a ton of helper functions that abstracts common math like this; for example it includes a CGPointToAngle method that would be helpful here.

Note: If this is too much math for you, check out our open source SKTUtils library. The library includes a ton of helper functions that abstracts common math like this; for example it includes a CGPointToAngle method that would be helpful here.

Animating the Sprite

Now that the pig faces the direction it's moving, it's time to add a pretty animation. Add the following new instance variable in Pig.m's @implementation section:

SKAction *_moveAnimation;

Next, go to initWithImageNamed: and add the following lines directly after the line that creates _wayPoints:

SKTexture *texture1 = [SKTexture textureWithImageNamed:@"pig_1"];
SKTexture *texture2 = [SKTexture textureWithImageNamed:@"pig_2"];
SKTexture *texture3 = [SKTexture textureWithImageNamed:@"pig_3"];
_moveAnimation = [SKAction animateWithTextures:@[texture1, texture2, texture3] timePerFrame:0.1];

This creates three textures, each of which represents a frame of the animation. The last line creates an SKAction that animates the pig’s movement using the three textures and stores the action in _moveAnimation. The game will show each frame for 0.1 seconds, resulting in a nice walking animation for the pig.

The last step is to run the animation. In Pig.m, go to move: and add the following lines at the beginning of the method:

if(![self actionForKey:@"moveAction"]) {
  [self runAction:_moveAnimation withKey:@"moveAction"];
}

First, you check if an animation named "moveAction" is already running and add it if there is no such animation. This ensures that the game adds the animation only once. Without this check, you wouldn't see the animation because it would start from the beginning every time you called move:.

Build and run, and watch your pig animate around the screen as you direct it.

Move_Animation1

Your Gameplay Strategy

Let’s pause to think about how the gameplay will work and what you need to achieve it.

The basic idea behind Hogville is that pigs will appear at random positions on the left side of the screen and from there, move in random directions. The time between the appearances of each new pig will decrease until it reaches a threshold defined by you.

The player must herd all of these pigs to a trough of food where they can eat and after that, bring them to a barn where they can sleep. If any two pigs collide, the game is over.

Here are the needed steps:

  1. Add sprites for the food trough and barn.
  2. Spawn pigs over time.
  3. Add collision handling.
  4. Add logic that controls when a pig needs food or is ready to sleep.
  5. Handle win/lose conditions.

You will go step by step through this list to finish your game. Don't panic—this tutorial is here to guide you!