Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From \$16.50/m

# SpriteKit Basics: Actions and Physics

Difficulty:IntermediateLength:LongLanguages:
This post is part of a series called SpriteKit Basics.
SpriteKit Basics: Sprites
SpriteKit Basics: Putting It All Together

In this series, we're learning how to use SpriteKit to build 2D games for iOS. In this post, we'll learn about two important features of SpriteKit: actions and physics.

To follow along with this tutorial, just download the accompanying GitHub repo. It has two folders: one for actions and one for physics. Just open either starter project in Xcode and you're all set.

## Actions

For most games, you'll want nodes to do something like move, scale, or rotate. The `SKAction` class was designed with this purpose in mind. The `SKAction` class has many class methods that you can invoke to move, scale, or rotate a node's properties over a period of time.

You can also play sounds, animate a group of textures, or run custom code using the `SKAction` class. You can run a single action, run two or more actions one after another in a sequence, run two or more actions at the same time together as a group, and even repeat any actions.

### Motion

Let's get a node moving across the screen. Enter the following within Example1.swift.

Here we create an `SKAction` and invoke the class method `moveTo(y:duration:)`, which takes as a parameter the `y` position to move the node to and the `duration` in seconds. To execute the action, you must call a node's `run(_:)` method and pass in the `SKAction`. If you test now, you should see an airplane move up the screen.

There are several varieties of the move methods, including `move(to:duration:)`, which will move the node to a new position on both the x and y axis, and `move(by:duration:)`, which will move a node relative to its current position. I suggest you read through the documentation on `SKAction` to learn about all of the varieties of the move methods.

### Completion Closures

There is another variety of the run method that allows you to call some code in a completion closure. Enter the following code within Example2.swift.

The `run(_:completion:)` method allows you to run a block of code once the action has fully completed executing. Here we execute a simple print statement, but the code could be as complex as you need it to be.

### Sequences of Actions

Sometimes you'll want to run actions one after another, and you can do this with the `sequence(_:)` method. Add the following to Example3.swift.

Here we create two `SKAction`s: one uses the `moveTo(y:duration:)`, and the other uses the `scale(to:duration:)`, which changes the x and y scale of the node. We then invoke the `sequence(_:)` method, which takes as a parameter an array of `SKAction`s to be run one after the other. If you test now, you should see the plane move up the screen, and once it has reached its destination, it will then grow to three times its original size.

### Grouped Actions

At other times, you may wish to run actions together as a group. Add the following code to Example4.swift.

Here we are using the same `moveTo` and `scale` methods as the previous example, but we are also invoking the `group(_:)` method, which takes as a parameter an array of `SKAction`s to be run at the same time. If you were to test now, you would see that the plane moves and scales at the same time.

### Reversing Actions

Some of these actions can be reversed by invoking the `reversed()` method. The best way to figure out which actions support the `reversed()` method is to consult the documentation. One action that is reversible is the `fadeOut(withDuration:)`, which will fade a node to invisibility by changing its alpha value. Let's get the plane to fade out and then fade back in. Add the following to Example5.swift.

Here we create a `SKAction` and invoke the `fadeOut(withDuration:)` method. In the next line of code, we invoke the `reversed()` method, which will cause the action to reverse what it has just done. Test the project, and you will see the plane fade out and then fade back in.

### Repeating Actions

If you ever need to repeat an action a specific number of times, the `repeat(_:count:)` and `repeatForever(_:)` methods have you covered. Let's make the plane repeatedly fade out and then back in forever. Enter the following code in Example6.swift.

Here we invoke the `repeatForever(_:)` method, passing in the `fadePlayerSequence`. If you test, you will see the plane fades out and then back in forever.

### Stopping Actions

Many times you'll need to stop a node from running its actions. You can use the `removeAllActions()` method for this. Let's make the player node stop fading when we touch on the screen. Add the following within Example7.swift.

If you touch on the screen, the player node will have all actions removed and will no longer fade in and out.

### Keeping Track of Actions

Sometimes you need a way to keep track of your actions. For example, if you run two or more actions on a node, you may want a way to identify them. You can do this by registering a key with the node, which is a simple string of text. Enter the following within the Example8.swift.

Here we are invoking the node's `run(_:withKey:)` method, which, as mentioned, takes a simple string of text. Within the `touchesBegan(_:with:)` method, we are invoking  `action(forKey:)` to make sure the node has the key we assigned. If it does, we invoke `.removeAction(forKey:)`, which takes as a parameter the key you have previously set.

### Sound Actions

A lot of times you'll want to play some sound in your game. You can accomplish this using the class method `playSoundFileNamed(_:waitForCompletion:)`. Enter the following within Example9.swift.

The `playSoundFileNamed(_:waitForCompletion:)` takes as parameters the name of the sound file without the extension, and a boolean that determines whether the action will wait until the sound is complete before moving on.

For example, suppose you had two actions in a sequence, with the sound being the first action. If `waitForCompletion` was `true` then the sequence would wait until that sound was finished playing before moving to the next action within the sequence. If you need more control over your sounds, you can use an `SKAudioNode`. We will not be covering the `SKAudioNode` in this series, but is definitely something you should take a look at during your career as a SpriteKit developer.

### Frame Animation

Animating a group of images is something that many games call for. The `animate(with:timePerFrame:)` has you covered in those cases. Enter the following within Example10.swift.

The `animate(with:timePerFrame:)` takes as a parameter an array of `SKTexture`s, and a `timePerFrame` value which will be how long it takes between each texture change. To execute this action, you invoke a node's run method and pass in the `SKAction`.

### Custom Code Actions

The last type of action we will look at is one that lets you run custom code. This could come in handy when you need to do something in the middle of your actions, or just need a way to execute something that the `SKAction` class does not provide for. Enter the following within Example11.swift.

Here we invoke the scene's `run(_:)` method and pass a function `printToConsole()` as a parameter. Remember that scenes are nodes too, so you can invoke the ` run(_:)` method on them as well.

This concludes our study of actions. There is a lot you can do with the `SKAction` class, and I would suggest after reading this tutorial that you further explore the documentation on `SKActions`.

## Physics

SpriteKit offers a robust physics engine out of the box, with little setup required. To get started, you just add a physics body to each of your nodes and you're good to go. The physics engine is built on top of the popular Box2d engine. SpriteKit's API is much easier to use than the original Box2d API, however.

Let's get started by adding a physics body to a node and see what happens. Add the following code to Example1.swift.

Go ahead and test the project now. You will see the plane sitting at the top of the scene. Once you press on the screen, the plane will fall off the screen, and it will keep falling forever. This shows how simple it is to get started using physics—just add a physics body to a node and you are all set.

### The `physicsBody` Shape

The `physicsBody` property is of type `SKPhysicsBody`, which is going to be a rough outline of your node's shape... or a very precise outline of your node's shape, depending on which constructor you use to initialize this property.

Here we have used the `init(circleOfRadius:)` initializer, which takes as a parameter the radius of the circle. There are several other initializers, including one for a rectangle or a polygon from a CGPath. You can even use the node's own texture, which would make the `physicsBody` a near exact representation of the node.

To see what I mean, update the GameViewController.swift file with the following code. I have commented the line to be added.

Now the node's `physicsBody` will be outlined in green. In collision detection, the shape of `physicsBody` is what is evaluated. This example would have the circle around the plane guiding the collision detection, meaning that if a bullet, for example, were to hit the outer edge of the circle, then that would count as a collision.

Now add the following to Example2.swift.

Here we are using the sprite's texture. If you test the project now, you should see the outline has changed to a near exact representation of the sprite's texture.

### Gravity

We set `physicsBody`'s `affectedByGravity` property to `false` in the previous examples. As soon as you add a physics body to a node, the physics engine will take over. The result is that the plane falls immediately when the project is run!

You can also set the gravity on a per node basis, as we have here, or you can turn off gravity altogether. Add the following to Example3.swift.

We can set the gravity using the `physicsWorld` `gravity` property. The `gravity` property is of type `CGVector`. We set both the `dx` and `dy` components to 0, and then when the screen is touched we set the `dy` property to -9.8. The components are measured in meters, and the default is (0, -9.8), which represents Earth’s gravity.

### Edge Loops

As it stands now, any nodes added to the scene will just fall off the screen forever. We can add an edge loop around the scene using the `init(edgeLoopFrom:)` method. Add the following to Example4.swift.

Here we have added a physics body to the scene itself. The `init(edgeLoopFrom:)` takes as a parameter a `CGRect` that defines its edges. If you test now, you will see that the plane still falls; however, it interacts with this edge loop and no longer falls out of the scene. It also bounces and even turns a little on its side. This is the power of the physics engine—you get all this functionality out of the box for free. Writing something like this on your own would be quite complex.

### Bounciness

We have seen that the plane bounces and turns on its side. You can control the bounciness and whether the physics body allows rotation. Enter the following into Example5.swift.

If you test now, you'll see that `player` is very bouncy and takes a few seconds to settle down. You will also notice that it no longer rotates. The `restitution` property takes a number from 0.0 (less bouncy) to 1.0 (very bouncy), and the `allowsRotation` property is a simple boolean.

### Friction

In the real world, when two objects move against each other, there is a bit of friction between them. You can change the amount of friction a physics body has—this equates to the “roughness” of the body. This property must be between 0.0 and 1.0. The default is 0.2. Add the following to Example6.swift.

Here we create a rectangular Sprite and set the `friction` property on its `physicsBody` to 0.0. If you test now, you will see the plane very quickly glides down the rotated rectangle. Now change the friction property to 1.0 and test again. You will see the plane does not glide down the rectangle quite as fast. This is because of the friction. If you wanted it to move even more slowly, you could apply more friction to the `player`'s `physicsBody` (remember the default is 0.2).

### Density and Mass

There are a couple of other properties that you can change on the physics body, such as density and mass. The `density` and `mass` properties are interrelated, and when you change one, the other is automatically recalculated. When you first create a physics body, the body's `area` property is calculated and never changes afterward (it is read only). The density and mass are based on the formula `mass = density * area`.

When you have more than one node in a scene, the density and mass would affect the simulation of how the nodes bounce off each other and interact. Think of a basketball and a bowling ball—they're roughly the same size, but a bowling ball is much denser. When they collide, the basketball will change direction and velocity much more than the bowling ball.

### Force and Impulse

You can apply forces and impulses to move the physics body. An impulse is applied immediately and only one time. A force, on the other hand, is usually applied for a continuous effect. The force is applied from the time you add the force until the next frame of the simulation is processed. To apply a continuous force, you would need to apply it on each frame. Add the following to Example7.swift.

Run the project and wait till the player comes to rest on the bottom of the screen, and then tap on the player. You will see the player fly up the screen and eventually come to rest again at the bottom. We apply an impulse using the method `applyImpulse(_:)`, which takes as a parameter a `CGVector` and is measured in Newton-seconds.

Why not try the opposite and add a force to the player node? Remember you will need to add the force continuously for it to have the desired effect. One good place to do that is in the scene's `update(_:)` method. Also, you may want to try increasing the restitution property on the player to see how it affects the simulation.

### Collision Detection

The physics engine has a robust collision and contact detection system. By default, any two nodes with physics bodies can collide. You have seen this in previous examples—no special code was required to tell the objects to interact. However, you can change this behaviour by setting a "category" on the physics body. This category can then be used to determine what nodes will collide with each other and also can be used to inform you when certain nodes are making contact.

The difference between a contact and a collision is that a contact is used to tell when two physics bodies are touching each other. A collision, on the other hand, prevents two physics bodies from crossing into each other's space—when the physics engine detects a collision, it will apply opposing impulses to bring the objects apart again. We have seen collisions in action with the player and the edge loop and the player and the rectangle from the previous examples.

#### Types of `physicsBodies`

Before we move on to setting up our Categories for the physics bodies, we should talk about the types of `physicsBodies`. There are three:

1. A dynamic volume simulates objects with volume and mass. These objects are affected by forces and collisions in the physics world (e.g. the airplane in previous examples).
2. A static volume is not affected by forces and collisions. However, because it does have volume itself, other bodies can bounce off and interact with it. You set the physics body's `isDynamic` property to false to create a static volume. These volumes are never moved by the physics engine. We saw this in action earlier with example six, where the airplane interacted with the rectangle, but the rectangle was not affected by the plane or by gravity. To see what I mean, go back to example six and remove the line of code which sets `rectangle.physicsBody?.isDynamic = false`.
3. The third type of physics body is an edge, which is a static, volume-less body. We have seen this type of body in action with the edge loop we created around the scene in all the previous examples. Edges interact with other volume-based bodies, but never with another edge.

The categories use a 32-bit integer with 32 individual flags that can be either on or off. This also means you can only have a maximum of 32 categories. This should not present a problem for most games, but it is something to keep in mind.

#### Creating Categories

Create a new Swift file by going to File > New > File and making sure Swift File is highlighted.

Enter PhysicsCategories as the name and press Create.

Enter the following into the file you've just created.

We use a structure `PhysicsCategories` to create categories for `Player`, `EdgeLoop`, and `RedBall`. We are using bit shifting to turn the bits on.

Now enter the following in Example8.swift.

Here we create the `player` as usual, and create two variables `dx` and `dy`, which will be used as the components of a `CGVector` when we apply an impulse to the `player`.

Inside `didMove(to:)`, we set up the player and add the `categoryBitMask`, `contactBitMask`, and `collisionBitMask`. The `categoryBitMask` should make sense—this is the player, so we set it to `PhysicsCategories.Player`. We are interested in when the player makes contact with the `redBall`, so we set the `contactBitMask` to `PhysicsCategories.RedBall`. Lastly, we want it to collide with and be affected by physics with the edge loop, so we set its `collisionBitMask` to `PhysicsCategories.EdgeLoop`. Finally, we apply an impulse to get it moving.

On the `redBall`, we just set its `categoryBitMask`. With the `edgeLoop`, we set its `categoryBitMask`, and because we are interested in when the `player` makes contact with it, we set its `contactBitMask`.

When setting up the `contactBitMask` and `collisionBitMask`, only one of the bodies needs to reference the other. In other words, you do not need to set up both bodies as contacting or colliding with the other.

For the `edgeLoop`, we set it to contact with the player. However, we could have instead set up the player to interact with the `edgeLoop` by using the bitwise or (`|`) operator. Using this operator, you can set up multiple contact or collision bit masks. For example:

To be able to respond when two bodies make contact, you have to implement the `SKPhysicsContactDelegate` protocol. You may have noticed this in the example code.

To respond to contact events, you can implement the `didBegin(_:)` and `didEnd(_:)` methods. They will be called when the two objects have begun making contact and when they have ended contact respectively. We'll stick with the `didBegin(_:)` method for this tutorial.

Here is the code once again for the `didBegin(_:)` method.

First, we set up two variables `firstBody` and `secondBody`. The two bodies in the contact parameter are not passed in a guaranteed order, so we'll use an if statement to determine which body has a lower `contactBitMask` and set that to `firstBody`.

We can now check and see which physics bodies are making contact. We check to see which physics bodies we are dealing with by anding (`&&`) the bodies' `categoryBitMask` with the `PhysicsCategory`s we set up previously, and if the result is non-zero we know we have the right body.

Finally, we print which bodies are making contact. If it was the player and `edgeLoop`, we also invert the `dx` and `dy` properties and apply an impulse to the player. This keeps the player constantly moving.

This concludes our study of SpriteKit's physics engine. There is a lot that was not covered such as SKPhysicsJoint, for example. The physics engine is very robust, and I highly suggest you read through all the various aspects of it, starting with SKPhysicBody.

## Conclusion

In this post we learned about actions and physics—two very important parts of the SpriteKit framework. We looked at a lot of examples, but there is still a lot you can do with actions and physics, and the documentation is a great place to learn.

In the next and final part of this series, we'll put together everything we have learned by making a simple game. Thanks for reading, and I will see you there!

In the meantime, check out some of our comprehensive courses on Swift and SpriteKit development!