This article is part of my personal wiki where I write personal notes while I am learning new technologies. You are welcome to use it for your own learning!
SpriteKit is the latest 2D gaming framework targeting iOS and OS X released with the announce of iOS 7. It provides all the necessary tools to build 2D games: graphics rendering, animations infrastructure, hardware acceleration, physics engine, particle generator and sound playback.
As with any gaming framework, the fundamentals of the development of a game consist of managing the game loop, an infinite loop that continuously listens for user input, updates the state of the game and draws the different pieces in the screen.
The game loop in SpriteKit follows these steps:
- Update the status of all entities within the game scene
- Evaluate Actions done by every entity
- Simulate Physics
- Draw/Render the game
- Back to 1
In each of these steps the entities (
SKNodes) that are part of a scene (
SKScene) will be updated, animated and drawn in the screen (
SKView). In the next sections, I will briefly describe how the different parts of SpriteKit. (I sound like a robot when I am writing this wiki articles XDDD)
Building The Scene
A scene (
SKScene) is what we could call as the basic unit of playability within a game:
- It is used to provide content to be rendered by an
- It is composed by a tree of nodes (
SKNodes) that represent the different entities within the scene (the player, monsters, score, etc).
- When presented by a view, it is in charge of handling the game loop for all it subnodes. In summary:
It Provides a Coordinate System To Its Children
When a node is placed within a node tree, its
position property places it within the coordinate system provided by its parent. Since the Scene works as the root node, all its children will use the coordinate system defined by the scene.
SpriteKit uses the standard conventions used in gaming for coordinate systems and its values are measured in units:
- Positive X to the right, positive Y to the top,
- An angle of 0 radians is specified by the x axis and positive angles are determined in the counterclockwise direction.
Creating a Scene
The first time a scene is initialized, its
size property is configured via its constructor. This size represents the size of the visible portion of the scene in points.
If the scene doesn’t match the view, you can specify how the scene will be scaled to match the view.
Nodes in SpriteKit
Nodes represent the most basic building components in SpriteKit, basically, everything within SpriteKit is node, even the scenes are nodes.
Notes can draw content, but that is not required of them.
SKSpriteNode, for instance, will draw a sprite, but the
SKNode will not. You can identify whether a node will draw content by reading its
frame property. This property represents the visible area of the parent’s coordinate system that the node draws into, and will be different than zero for drawing nodes.
If a node has descendants that draw content, it is possible for a node’s subtree to provide content even thought that the parent doesn’t. You can access the calculated frame by calling the method
Physics in SpriteKit
Physics in SpriteKit are performed by adding physic bodies to a scene. A physics body is a simulated physical object connected to a node within the scene that adds physical properties to this node such as mass, density or velocity. These characteristics are going to define how a body is affected by the physical world: how it moves, how it responds to collisions, etc.
Each time a scene computes a new frame of animation, it simulates the effects of forces and collisions on physical bodies conected to the node tree. It computes a final position, orientation and velocity for each physics body and the scene updates the position and rotation of each corresponding node.
Physic bodies are represented by
SKPhysicsBody and assigned to nodes via the
physicsBody property. There are three kinds of bodies:
dynamic volumesimulates a physical object with volume and mass that can be affected by forces and collisions in the system. Use dynamic volumes when you need to represent items in the scene that move around and collide with each other.
static volumeis similar to a dynamic volume but its velocity is ignored and it is unaffected by forces or collisions (other bodies can still collide with a static volume because indeed it has volume). Use static volumes for elements within the scene that take up space but do not move, such as walls.
Note that you can this dynamicity can be enabled/disabled for the same physics body.
edgeis a static volume-less body. They are never moved by the simulation and their mass doesn’t matter. They are used to represent the negative space within a scene (transparencies) or an uncrossable, invisible thin boundary, e.g. the boundaries of your scene. The main difference between an edge and a volume, is that the former allows movement inside its own boundaries. Also if edges are moved, they don’t interact with other edges, only with volumes.
SpriteKit provides some basic standard shapes (circles, squares, etc) as well as arbitrary shapes based on paths.
Using a Physics Shape That Matches A Sprite
Since within a game, we care a lot about collisions, it is very important that a physic body shape matches that of the sprite it is associated with.
The selection of the physics body shape is a tradeoff between precision and performance, where more complex shapes require moer work to be simulated. It is recommended:
- For volumes:
- use a circle as it is the most efficient shape
- a path-based polygon is the least efficient
- For edges,:
- use lines and rectangles
- edge loops and chains are the least efficient
Note that edges are naturally more expensive to compute than volumes, since they have to account for interactions that can occur both in the inside and in the outside of the edge.
Creating Physical Bodies
You can create a physical body by using the methods provided by the
For instance, you can create an edge loop around your scene by using this snippet:
1 2 3 4 5
And a circular volume for a sprite:
1 2 3
Configuring Physical Bodies
SKPhysicsBody class defines the following properties that determine how the physics body is simulated.
Some properties determine how it reacts to forces or collisions in the screen:
massproperty determines how forces affect the body, as well as how much momemtum the body has when involved in a collision
frictionproperty determines the roughness of the body surface. It is used to calculate the frictional force that a body applies to other bodies moving along its surface.
angularDampingproperties are used to calculate friction on the body as it moves through the world (through air, water, etc).
restitutionproperty determines how much energy a body maintains during a collision (a.k.a. bounciness).
Other properties are used to determine how the simulation is performed on the body itself:
dynamic property determines whether the body is simulated by the physics subsystem.
affectedByGravity property determines whether the simulation exerts a gravitational force on the body.
allowsRotation property determine whether forces can impart angular velocity on the body.
It is recommended that you set the mass on every volume in your scene so that it reacts properly to forces applied to it. A body
density are interrelated. Whenever you change the
density the other value is recalculated (
mass = density x area).
Configuring the Physics World
All bodies within a scene are part of the physics world, which is represented by the
SKPhysicsWorld attached to the scene. It defines two important characteristics:
gravitythat applies an acceleration to volumes
speedor rate at which the simulation runs
Making Bodies Move
By default, only gravity is applied to volumes in the scene. In most cases, you will need to change the speed of the bodies by setting the
angularVelocity properties. As with many other properties, you normally se these values when the body is first created and let the physics simulation do the necessary adjustments.
1 2 3 4
It is also common to adjust the velocity of an object based on the forces applied to it. The default collection of forces applied to a body include:
- The gravitational force applied by the world
- The damping forces
- The frictinal forces
Additionally, you can apply your own forces and impulses:
- A force is applied for a length of time based on the amount of simulation time that passes between when you apply the force and when the next frame of simulation is processed. They are usually continuous effects.
- An impulse makes an instantaneous change to the body’s velocity that is independent of the amount of simulation time that has passed. They are usually immediate effects.
You can apply a force/impulse to a body in these ways: * As a linear force that only affects its linear velocity. * As a angular force that only affects its angular velocity * As a force applied to a point on the body. The physics engine calculates the changes in velocity itself.
Below you can see an example on how to simulate a spaceship thrust:
1 2 3 4 5 6 7 8 9
Collisions and Contacts
SpriteKit supports two kinds of interactions when two bodies collide:
- A contact is used to know that two bodies are touching each other. You use contacts when you need to perform gameplay changes when collisions occur.
- A collision is used to prevent objects from interpenetrating each other. It is automatically calculated by SpriteKit.
Since collisions are expensive, Sprite Kit uses two mechanisms to limit the number of interactions in each frame: * Edge-based bodies never interact with other edges. * Every body is categorized. Categories are defined by your app (each scene can have up to 32). when you configure a body, you dfine which category it belongs to and which categories of bodies it wants to interact with. Contacts and collisions are specified separatedly.
Relationships between categories don’t need be symmetrical, in fact, it is probably more performant to have one way relationships.
When you have decided on the categories of you game, you need to implement them, together with the interactions using 32-bit masks. Whenever a potential interaction occurs, the category mask of each body is tested against the contact and collision masks of the other body. Sprite Kit performs these test by performing and AND operation between the two masks.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
* The contacts delegate is passed an
SKPhysicsContact object that declares which bodies were involved in the collision.
* The bodies in the contact can appear in any order.
* When your game needs to work with contacts, you need to determine the outcome based on both bodies in the collision. Consider using the Double Dispatch Pattern.
node property can access the node that a body is attached to. You can use it to access information about the colliding nodes.
Finally, you can specify high precision collision for small and fast moving objects by using the
usePreciseCollisionDecision property. Bear in mind that this technique will be expensive.