© Radoslava Leseva Adams and Hristo Lesev 2016

Radoslava Leseva Adams and Hristo Lesev, Migrating to Swift from Flash and ActionScript, 10.1007/978-1-4842-1666-8_16

16. Using the High-End Graphics APIs

Radoslava Leseva Adams and Hristo Lesev2

(1)London, UK

(2)Kazanlak, Bulgaria

The ability to play games was an amazing addition to the world of mobile devices. It unleashed developers’ imaginations and allowed users to solve puzzles, create and defend kingdoms, or relieve stress by throwing some really angry birds around.

When developing a game, it is always more fun to focus on the gameplay and the game mechanics, rather than worry about things like rendering speed, user interface (UI) elements, or layout. In recognition of that, Apple introduced two frameworks: SpriteKit, which focuses on building 2D games, and SceneKit, which helps create and manage 3D content.

In this chapter you will do the following:

  • Learn about the SpriteKit and the SceneKit frameworks.

  • Learn how to create a game scene, animate objects on the screen, and work with cameras, geometry, materials, and lights.

  • See how to use Xcode as a game scene editor.

  • Build a 2D app with SpriteKit in which a spaceship chases your finger on the screen.

  • Build an app with SceneKit and learn how to let the user move objects in 3D.

When you are done, you will have two mobile app projects with basic elements of iOS game development in 2D and 3D.

Creating 2D Games with SpriteKit

SpriteKit is Apple’s framework, which helps developers create 2D games. You can think of it as a fully equipped game engine, as it comes with

  • a built-in scene hierarchy manager;

  • an animation engine;

  • a sound engine;

  • a physics engine;

  • a collision detector; and

  • graphics support for lighting and shader effects.1

Not only that, but Xcode also has a dedicated SpriteKit editor, which helps you visually build your game.

The framework makes use of the graphics processing unit (GPU) , in order to achieve fast scene rendering. As you could guess by its name, the SpriteKit framework is intended to render sprites on the screen. As a Flash developer, you are already familiar with sprites—they are textured images. Before we set up a SpriteKit app, let us briefly go over the framework’s architecture.

Learning the Structure of the SpriteKit

SpriteKit provides a custom view component of type SKView, in which the game content is rendered. SKViewis a subclass of UIView—the base class for every UI element on iOS. This means that we can transparently include an SKView and any other element from SpriteKit in the UI of any application for iOS.

Game scenes are presented inside SKView. Every game in SpriteKit is composed of one or more scenes of type SKScene. A scene is the canvas, where all graphic elements of your game are hosted and rendered. You can have multiple scenes for different areas of the game. For example, you could use one scene for the main menu, another scene for the game levels, and a third scene for the scores and ranking. Dividing a game into scenes helps you manage your code and assets by providing a scene container for the different functional areas in your game.

The building blocks of a scene are called nodes. A node is represented by the SKNode base class and serves as a container for the game objects and their properties. Nodes can be composed in a parent-child hierarchy.2 Instead of directly with SKNode we work with its subclasses. For example, to show an image we need an instance of the SKSpriteNode class; to show text we use the SKLabelNode class. There are nodes for displaying shapes, playing audio, simulating a camera, emitting particles, and many more. Nodes are responsible for storing the visual representation, position, and rotation of the game objects in the scene. You can further define a node by attaching objects to it.

One of the objects you can attach to a node is a physics body. It is represented by the SKPhysycsBody class and describes a node’s physical characteristics like mass, shape, and friction. SpriteKit’s physics simulator, SKPhysicsWorld, uses physics bodies to calculate the position and rotation of nodes. This simulator is quite powerful and lets you define gravity fields, apply forces to nodes, calculate collision, and connect bodies with joints.

We use actions to manipulate the state of a node. An action is represented by the SKAction class : it modifies the properties of the game objects, in order to create an animation. Each action has a duration interval, measured in seconds. The most common use for an action is to change a node’s position. Several actions can be composed in sequences or groups to create complex animations.

To display a scene on the screen SpriteKit uses a rendering loop very similar to the one used by the ActionScript virtual machine. It iterates through all nodes in a scene at the beginning of each frame, evaluates any actions associated with them, simulates physics, and then renders the scene on the screen.

This is how SpriteKit works in a nutshell. Now we will explore the framework by making our first SpriteKit application.

Setting Up a SpriteKit App

The app we are about to develop will display a spaceship sprite on the screen. The spaceship will follow the user’s finger—we will animate it to move to the coordinates of each screen touch.

Instead of starting with an empty project we will create the app using Xcode’s SpriteKit project template and modify its code.

In Xcode create a Game application project (FileNewProject…, then iOSApplicationGame) as shown in Figure 16-1.

A371202_1_En_16_Fig1_HTML.jpg
Figure 16-1. Creating a Game project in Xcode

Set the project’s name to SpriteKitDemo and set Game Technology to SpriteKit (Figure 16-2).

A371202_1_En_16_Fig2_HTML.jpg
Figure 16-2. Setting up the project to use the SpriteKit

Well, that was easy. Now you have a fully configured project, which uses the SpriteKit framework and probably looks familiar, as we used the same template in Chapter 4 . The project comes set up with a default scene and an image of a spaceship. Run it in a simulator or on a device and you will initially see a gray screen with a “Hello, World!” label. When you tap the screen, a rotating spaceship will appear where you tapped (Figure 16-3).

A371202_1_En_16_Fig3_HTML.jpg
Figure 16-3. Running the SpriteKitDemo project for the first time

Try tapping the screen a few times to see how many sprites your device can handle before the frame rate deteriorates. Try not to get carried away, though, as it is time to focus on the interesting part of the project: the source code.

Dissecting the Project

If you look at the Project navigator, you will notice that the structure closely resembles that of a regular iOS project. However, there are some new additions.

Let us have a look at the new files in the project tree:

  • GameViewController.swift. This file contains the definition of GameViewController—a UIViewController subclass, which is responsible for creating a scene and presenting it on an SKView. The view itself can be found and configured in the Main.storyboard file.

  • GameScene.swift. This file defines the game loop logic for the scene. It does gesture handling and takes care of the creation of nodes and scene updates. Nodes are the building blocks of a scene.

  • GameScene.sks. This is SpriteKit’s equivalent of a storyboard. When you open this file, you will see a visual editor, which helps you build a scene by dragging and dropping game nodes on it. This is the scene’s resource archive.

Now we are going to look inside these files and see how the game is built.

Open GameViewController.swift and locate the viewDidLoad method (Listing 16-1). Inside it, first an instance of the GameScene class is initialized using the GameScene resource archive. Then a constant is created, named skView, to keep a reference to the view controller’s view (SKView). Setting the showsFPS and showsNodeCount properties to true instructs the SpriteKit to display the corresponding debug information.

The scene is shown on the screen by calling SKView’s presentScene method. When you have more than one scene in your game, you could use animation for the transition between scenes.

Listing 16-1. Scene-Creating Routines
override func viewDidLoad() {
    super.viewDidLoad()


    if let scene = GameScene(fileNamed:"GameScene") {
        // Configure the view.
        let skView = self.view as! SKView
        skView.showsFPS = true
        skView.showsNodeCount = true


        /* Sprite Kit applies additional optimizations to improve rendering performance */
        skView.ignoresSiblingOrder = true


        /* Set the scale mode to scale to fit the window */
        scene.scaleMode = .AspectFill


        skView.presentScene(scene)
    }
}

Let us now open the GameScene.swiftfile. It contains the implementation of the GameScene class, which is responsible for controlling the objects in the current scene. The class inherits SKScene and overrides three of its methods:

  • The didMoveToView method is called after the scene is presented on the screen by a SKView instance. This method is used to create the scene’s contents.

  • The touchesBegan method is called when the user taps inside the view. Here we will add the logic that will make the spaceship follow users’ taps on the screen.

  • The third method, update, is called by the game loop in the beginning of every frame and is used as an entry point for any necessary updates to the scene.

We will replace the default implementation of these methods with our own, so delete the bodies of all three. The GameScene class should now look like that in Listing 16-2.

Listing 16-2. Removing the Default Implementation of the GameScene Class
class GameScene: SKScene {

    override func didMoveToView(view: SKView) {
    }


    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    }


    override func update(currentTime: CFTimeInterval) {
    }
}

In the next section we will add a spaceship sprite to the scene and make it move as we tap the screen.

Moving the Sprite Around

Start by adding a constant called playerSprite of type SKSpriteNode as a member of the GameScene class. Initialize it with the default Spaceship image resource created by Xcode:

let playerSprite = SKSpriteNode(imageNamed:"Spaceship")

This line creates a node and assigns an image to it. The node, however, is not part of the scene yet. To show a node on the screen we need to add it to the scene’s tree by calling addChild. We want this to happen before the scene becomes visible, so we will put the code inside the didMoveToView function.

Let us do a small adjustment before showing the node on the screen. The original spaceship image is too large, so we will scale it down to a third of its original size by changing its xScale and yScale properties. You can see the code in Listing 16-3.

Listing 16-3. Adding a Sprite Node to the Scene
override func didMoveToView(view: SKView) {

    //Scale down the sprite to 30%
    playerSprite.xScale = 0.3
    playerSprite.yScale = 0.3


    //Add the playerSprite to the scene
    self.addChild(playerSprite)
}

Now that we have added the sprite, let us make it move. When the user touches the screen we want the spaceship to move from its current position to the coordinates of the touch event. First we will get the location of the touch event in scene coordinates and then we will create an action to animate the movement of the sprite towards its new position. We will implement the logic inside touchesBegan (Listing 16-4).

The touchesBegan function supports multitouch (i.e., the user tapping the screen with more than one finger). In our case we will take only one of these touch sites into account and ignore the others. To get the touch location in scene coordinates we call SKNode’s helper function named locationInNode.

For the spaceship animation to look realistic we need not only to move the sprite but also to rotate it, so that it faces the direction of the movement. To calculate the angle of rotation we use simple trigonometry: first we construct a vector, which starts at the player’s current position and ends at the destination point by subtracting the two points. Then we compute the angle between the vector and the positive x-axis with the help of the atan2 function. atan2 returns the angle in radians, which is a good thing, because SpriteKit prefers angles in radians too. One last thing: the rotation angle is calculated relative to the x axis, but the spaceship’s nose is facing the y axis. For that we need to offset the angle by ninety degrees, that is, add $$ frac{pi }{2} $$ (approximately 1.56) radians to it.

We create an action for moving and an action for rotating the sprite in order to animate it. To play the actions one after another we create an action sequence in the form of an array. Calling runAction and passing it that array at the end of touchesBegan runs the sequence and creates the animation.

Listing 16-4. Animating the Player Node to Follow Touch Coordinates
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {

    //Get the scene coordinates of the touch event
    let location = touches.first!.locationInNode(self)


    //Calculate the angle, at which we need to rotate the sprite,
    // in order to face the direction of movement:
    let angle = atan2(location.y - playerSprite.position.y,
        location.x - playerSprite.position.x ) - 1.56


    //Create an action to move the sprite to the touch location coordinates
    let moveAction = SKAction.moveTo(location, duration: 0.5)
    //Create an action to rotate the sprite to face the direction of movement
    let rotateAction = SKAction.rotateToAngle(angle, duration: 0.1)


    //Animate the sprite executing actions sequentially
    playerSprite.runAction(SKAction.sequence([rotateAction, moveAction]))
}

Run the application, tap the screen, and you will see the spaceship sprite fly toward the point where you tapped. Touch the screen again and the spaceship will rotate itself and chase your finger (Figure 16-4).

A371202_1_En_16_Fig4_HTML.jpg
Figure 16-4. Running the game

Using the SpriteKit Scene Editor

Xcode comes with an integrated SpriteKit scene editor, which helps you visually compose scenes for your game. It allows you to drag and drop and arrange and edit nodes inside the integrated development environment (IDE) instead of doing it programmatically.

To open the editor select the GameScene.sks file in the Project navigator. You should see an empty scene like the one in Figure 16-5.

A371202_1_En_16_Fig5_HTML.jpg
Figure 16-5. Xcode SpriteKit scene editor interface

Xcode’s Scene editor looks a lot like its Storyboard editor. On the left there is the Attributes inspector, where you set nodes’ parameters and the Object library with nodes you can drag and drop on the scene.

Let us add a label node to the scene. Find a label in the Object library and drag it toward the yellow rectangle shown in Figure 16-6. With the label node selected go to the Attributes inspector and set Text to This game is awesome!

A371202_1_En_16_Fig6_HTML.jpg
Figure 16-6. Adding a label node to the scene

Run the game and you will see the label in the middle of the screen (Figure 16-7).

A371202_1_En_16_Fig7_HTML.jpg
Figure 16-7. The scene with the newly added label

Note that, although the scene editor can save us a lot of time composing content, we still need to initialize sprite instances and program the gameplay by hand.

Developing 3D Apps with SceneKit

The SceneKit framework is similar to SpriteKit but deals with three-dimensional space. SceneKit is a bit more general and can be used not just for games but also for business-oriented apps that need to show 3D content. For example, you could use it to build an e-book reader, which shows an embedded 3D view alongside the text.

Before SceneKit was released, iOS developers had to use low-level graphic application programming interfaces (APIs) like OpenGL to render 3D objects in their apps. OpenGL is a powerful graphic library, but it forces you to think in terms of triangles, vertex buffers, and color buffers, in order to show something on the screen. SceneKit hides these technical details behind a higher-level framework and allows you to work with objects like geometry, lights, materials, shaders, and cameras.

Learning the Structure of SceneKit

SceneKit renders its content in a SCNView, which is a standard UI element. All content that will be rendered in SCNView is stored in a scene, represented by the SCNScene class.

A scene is structured as a hierarchical tree of nodes, much like a scene in SpriteKit. All nodes in the tree are linked together with parent-child relationships. This tree of nodes is also known as a scene graph. A node is represented by the SCNNode class and has properties describing its position, rotation, and scale relative to its parent node. Basically, nodes are used as placeholders for objects to keep the scene structured. To visualize an object we need to attach it to a node. We can use the following types of objects :

  • Geometry. Represents a 3D object. SceneKit comes with built-in geometry objects like boxes, spheres, cones, and planes. You can also create geometry programmatically or load it from a file. SceneKit supports COLLADA files, an industry-standard file format, which can be exported from many 3D modeling programs.

  • Camera. Represents the viewer of the scene.

  • Light. Represents objects that can cast light onto the scene. Lights are parameterized and can simulate a variety of light sources from a light bulb to the sun.

  • Physics body. You can simulate physical effects such as gravity or collisions by creating a physical body and attaching it to a node.

  • Material. Defines how geometry will be visualized. You can specify how a surface reacts to light and what texture or color to be used to imitate a real-world material.

You can animate all objects on the screen by either changing their properties or manipulating their geometry. SpriteKit allows you to import a rigged 3 model with skeletal animation that can be used to change a model’s geometry. Skeletal animation is commonly used for animating characters. The rigged objects have a skinner object,4 which is used to control individual bones in the skeletal hierarchy.

Another way to create animation is to use actions like we did in the SpriteKit framework tutorial.

Speaking of SpriteKit, there is another similarity between the two frameworks: the game loop. SceneKit goes through the same steps in the game loop to prepare and render the scene graph on the screen. First the scene graph is updated, next all actions and animations are applied, then physics is simulated, and finally the scene is rendered on the screen.

This is the end of our brief overview of SceneKit’s structure. Now is time to use it in an app.

Setting Up the App

Let us create a project using Xcode’s SceneKit project template. We will go through the project’s code and see how a 3D scene is created and populated with objects first.

In Xcode create a Game application project (FileNewProject…, then iOSApplicationGame), name it SceneKitDemo and set Game Technology to SceneKit (Figure 16-8).

A371202_1_En_16_Fig8_HTML.jpg
Figure 16-8. Creating a SceneKit project

When you click Next, Xcode will generate a simple project with one view and a SCNView inside. The default scene has one geometry node, which contains a 3D model of a spaceship, two light nodes, and a camera node. If you run the project, you will see a rotating spaceship on a black background (Figure 16-9).

A371202_1_En_16_Fig9_HTML.jpg
Figure 16-9. Running the SceneKitDemo project

Let us examine the source files that Xcode has generated for us.

Examining the SceneKit Project’s Structure

The project strongly resembles a typical iOS app project structure. There is a storyboard with one view inside, a view controller, and an assets group.

The only difference with the iOS app templates we have been using so far in the book is that here the root view element in the storyboard is of type SCNView. This makes the game cover the whole available screen space. If your application only needs to render 3D scenes in a smaller portion of the screen, SCNView can be used in a layout with other UI elements and be put in a container, for example. You can easily show a scene view in a table or in another container.

In the Project navigator there is a group called art.scnassets. Inside you will find a file called ship.scn and an image: texture.png. The PNG image is used as a texture that will cover the spaceship geometry model. Here textures are used as skins on top of 3D models. This allows us to show visual detail without necessarily creating complex 3D geometry. For example, instead of constructing every element of the spaceship’s cockpit in 3D and thus adding more triangles to its geometry, we show these elements in a 2D texture.

The ship.scn file contains a ready-made scene graph. Click it to open Xcode’s visual 3D scene editor. In it you can compose scenes by dragging and dropping nodes, geometry, lights, cameras, and many more scene elements.

And now let us look at the most interesting part of this project: the GameViewController.swift file. Inside is the source code responsible for scene creation, animation, and the rest of the game’s logic. Open the file and search for the viewDidLoad() function ; it contains the code for setting up the scene.

Adding a Scene

The default SceneKit workflow starts with creating a scene, configuring it, and assigning it to a SceneKit View to be rendered on the screen (Listing 16-5).

You can create an empty scene or to load one from an .scn file by calling the SCNScene(named:) class initializer. To visualize a scene, you need to assign it to the scene property of a SceneKit view instance. You can create multiple scenes and choose which one is visible at any given time by assigning it to the SceneKit view.

Listing 16-5. Adding a Scene
// create a new scene
let scene = SCNScene(named: "art.scnassets/ship.scn")!


// retrieve the SCNView
let scnView = self.view as! SCNView


// assign the scene to the view
scnView.scene = scene

Adding a Camera

As a next step we will add a camera to the scene. You can think of the camera as the eye of the observer of the scene. What the camera sees is what the user will see. To move the user’s view point in the scene we change camera’s position, angle, field of view, and other parameters. Listing 16-6 shows you how to create and configure a camera.

First we create an SCNNode instance, which will “host” the camera in the node tree. Next, we initialize a SCNCamera instance and attach it to the node by assigning it to its camera property. Then we add the camera node as a child node of the scene graph. The final step is to set the camera’s position in 3D space with the help of a class, which represents three-dimensional vectors: SCNVector3. With this we move the camera by 15 units5 backward from the center of the scene.

Listing 16-6. Adding a Camera
// create and add a camera to the scene
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
scene.rootNode.addChildNode(cameraNode)


// position the camera
cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)

Adding Light

If you go inside the viewDidLoad() function, comment these two lines of code,

scene.rootNode.addChildNode(lightNode)
scene.rootNode.addChildNode( ambientLightNode)

and run the app, you will notice that the spaceship does not look very three-dimensional but, rather, like a collection of flat-colored objects. To add depth to the scene, we need light.

We can choose between four types of light sources in SceneKit:

  • Omni. Also known as point light, it emits light equally in all directions. It can be used to simulate a light bulb.

  • Ambient. Ambient light illuminates all objects in the scene from all directions. It is used to approximate the light bouncing between objects the way it happens in nature, and prevent our scene from getting too dark.

  • Directional. Directional light simulates a distant light source, which emits light in a given direction. It is often used to simulate the sun.

  • Spot. This one represents a cone-shaped light source like the floodlights used in stadiums and theaters.

You can see how an omni light source is added to the scene in Listing 16-7. First a container node for the light object is created. Then an instance of the SCNLight class is assigned to the light property of the node. The SCNLight class represents a general light object; setting its type property to SNLightTypeOmni makes it an omni light. We set the position of the light as a SCNVector3.

Listing 16-7. Adding an Omni Light
// create and add a light to the scene
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light!.type = SCNLightTypeOmni
lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
scene.rootNode.addChildNode(lightNode)

This omni light will be the main light source in the scene, but if we use it as the only light source, most of the scene will appear dark. A directional light source illuminates only surfaces that face it and leaves the rest of the surfaces in shadow.

In order to fill the scene with light, we will add an ambient light source too—you can see this done in Listing 16-8. This looks similar to how we created the omni light, but without setting a position for it. Ambient sources cast light from all directions in the scene.

Listing 16-8. Adding an Ambient Light
// create and add an ambient light to the scene
let ambientLightNode = SCNNode()
ambientLightNode.light = SCNLight()
ambientLightNode.light!.type = SCNLightTypeAmbient
ambientLightNode.light!.color = UIColor.darkGrayColor()
scene.rootNode.addChildNode(ambientLightNode)

Animating the Spaceship

Next, we will animate the spaceship model to rotate infinitely around the y axis, as if on a turntable.

In order to assign animation to the spaceship node, we will locate it by its name in the scene graph by calling childNodeWithName(name:recursively:). This function iterates through all child nodes of a given node and looks for a name match. Setting the recursively parameter to true will make it iterate through the subtrees coming out of the node’s children too.

Once we have located the spaceship node, we will attach an SCNAction instance to it to do the rotation and repeat it forever (Listing 16-9).

Listing 16-9. Animating the Spaceship
// retrieve the ship node
let ship = scene.rootNode.childNodeWithName("ship", recursively: true)!


// animate the 3d object
ship.runAction(SCNAction.repeatActionForever(SCNAction.rotateByX(0, y: 2, z: 0, duration: 1)))

Selecting an Object

Making the 3D scene interactive involves letting the user select an object by tapping it. In general this requires quite a lot of calculations, in order to determine which of the objects in the scene lies underneath a certain point in the 2D space, which is available to the user for tapping. SceneKit makes our job easier by handling a lot of these calculations. With the next few listings we will implement object selection (picking) in our demo project.

We start by adding a tap gesture recognizer handler to the SceneKit view (Listing 16-10).

Listing 16-10. Add a Handler for the Tap Gesture
// add a tap gesture recognizer
let tapGesture = UITapGestureRecognizer(target: self, action: "handleTap:")
scnView.addGestureRecognizer(tapGesture)

The actual object picking happens in the handleTap(:) function, shown in Listing 16-11.

Listing 16-11. Implementing Object Picking and Highlighting
func handleTap(gestureRecognize: UIGestureRecognizer) {
    // retrieve the SCNView
    let scnView = self.view as! SCNView


    // check what nodes are tapped
    let p = gestureRecognize.locationInView(scnView)
    let hitResults = scnView.hitTest(p, options: nil)
    // check that we clicked on at least one object
    if hitResults.count > 0 {
        // retrieved the first clicked object
        let result: AnyObject! = hitResults[0]


        // get its material
        let material = result.node!.geometry!.firstMaterial!


        // highlight it
        SCNTransaction.begin()
        SCNTransaction.setAnimationDuration(0.5)


        // on completion - unhighlight
        SCNTransaction.setCompletionBlock {
            SCNTransaction.begin()
            SCNTransaction.setAnimationDuration(0.5)


            material.emission.contents = UIColor.blackColor()

            SCNTransaction.commit()
        }


        material.emission.contents = UIColor.redColor()

        SCNTransaction.commit()
    }
}

On the first line of code in this function we get a reference to the scene view that generated the touch event. Then we use it to get the coordinates of the point of the touch event with the locationInView function. Calling the view’s hitTest method gives us the objects, which fall under the finger. hitTest returns an array of SCNHitTestResult objects that match the hit test. An instance of SCNHitTestResult contains the scene graph node of a matching object, as well as the 3D coordinates (in world space) of the touch event’s projection onto the object.

When we get the array of matching objects, we first check if it contains anything (i.e., if there were objects the tap gesture managed to hit). If there were any, we take the first one and highlight it with red by modifying the emission property of its material and creating a little animation that will make it glow in red. Note the line where the material of the object is obtained:

let material = result.node!.geometry!.firstMaterial!

This line relies on several assumptions to be true:

  • We have hit a node in the scene tree.

  • The node has a geometry mesh attached.

  • The geometry has at least one material.

This line of code can be dangerous and can cause a runtime error if we execute it on an arbitrary scene. We know it will work in our current project, because the spaceship model satisfies all of these assumptions.

To animate the glow effect an SCNTransaction is used. This class controls in a transactional manner all properties of the scene that can be animated. We first create a transaction to be executed for half a second and slowly change the emission color of the object’s material to red. When the first transaction is over, inside the setCompletionBlock closure we create another transaction to unhighlight the object by changing the emission color to black. If you run the project and tap the spaceship, you will see how the model flashes in red for a fraction of a second (Figure 16-10).

A371202_1_En_16_Fig10_HTML.jpg
Figure 16-10. Picking a 3D model from the scene

The SCNTransaction class can also be used as a mechanism to wrap multiple modifications of the scene in one atomic transaction.

We touched on using materials in this section. In the next one we will add a bit more detail about how SceneKit represent materials.

Applying Materials

The material of an object defines what this object will look like when rendered on the screen. In physics an object’s material describes how the object surface reflects incoming light. In programming terms a material is usually a function, which computes the color of every visible point of the object.

Materials in SceneKit have many properties. You can imitate plastic, wood, aluminum, and many other real-world materials by combining and configuring these properties. The most important ones are

  • Diffuse. This property defines the base color of the object. A texture is often used as a diffuse color source. To simulate a wood surface, for example, you could use a brownish diffuse texture with rings and knots.

  • Specular. This property defines the specular reflection of light for the material. If we added a finishing layer to the wood surface material, we would use its specular property to set how shiny it would be.

  • Emission. This property defines how much light is emitted from an object’s surface. Note that this does not make the object a light source, however, and the emitted light will only be visible to the observer (i.e., it will not illuminate and be reflected by other objects in the scene).

  • Normal. A normal is a line or a vector, which is perpendicular to a given surface and thus defines the surface’s orientation. Normals are used for lighting calculations both for diffuse and specular light. When we add a material to a surface, we can define different normals in various points of the surface (using a normal map6) and thus cause light to be reflected differently at each point, even if the whole surface is flat. This trick allows imitating unevenness and adding detail without having to change and complicate the geometry of the surface.

  • Ambient. Defines how this material should appear in the presence of an ambient light.

Materials in SceneKit are represented by the SCNMaterial class and their properties—by the SCNMaterialProperty class. SCNMaterialProperty has a property named contents, which can contain information of different types: color, texture, a SCNScene instance, and so on.

To apply a material to an object you add it to the object’s geometry. The SCNGeometry class has a property named materials, which is an array of SCNMaterial elements. The fact that the materials property is an array means that a geometry object can have multiple materials applied. If a geometry is composed of multiple elements, traditionally triangles, you can assign a separate material to each element. In other words, we could have a cube geometry, each side of it rendered using a different material.

The SCNMaterial class is designed to help you imitate the appearance of a vast majority of real-world materials. In addition, SceneKit allows you to write your own shaders. For that you can use the OpenGL Shading Language (GLSL) .

The SceneKit and SpriteKit frameworks are so powerful that they would each merit a separate book. The scope of a single chapter is too narrow to cover topics like adding physics, enhancing the built-in renderer, using external assets, scene-lighting techniques, and many more. As you are reaching the end of this chapter, however, ideally you have the foundation, on which you can build your 2D and 3D programming techniques for iOS with Swift.

Summary

In this chapter you saw how to use the SpriteKit framework to build 2D games and the SceneKit framework to present 3D scenes in your app.

This chapter marks the end of Part III. In Part IV we go deeper with Swift and look at the language in detail.

Footnotes

1 A shader is a small program which is executed by the GPU. Shaders are used mainly for color calculation, geometry altering, and tessellation. For example, we can define a new material by writing a shader that computes how light is reflected from a given point on a surface and thus defines what color it should be.

2 In fact, SKScene is a subclass of SKNode, so any SKNode instance you add directly to it becomes a child of this root node.

3 Rigging is the process of adding a skeleton to a 3D model, which helps with the animation of movement.

4 A skinner object helps with animating 3D objects by providing access to their skeleton.

5 SceneKit uses meters as a default unit measurement.

6 A normal map is a texture. Instead of color information, each of its pixels contains data about the normal at a given point.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset