Slide 1

Slide 1 text

Short intro Game Development in iOS Swift 3.0 / Xcode 8

Slide 2

Slide 2 text

Different iOS Approaches • SpriteKit • Makes it easier to build 2D Games • Uses graphics hardware on both Macs and iOS • SceneKit • Tools for creating 3D games • Physics Engine, Particle Generator • gravity, collisions, motion... • Possible to combine with SpriteKit • Metal • SpriteKit and SceneKit is for "casual games" • API that brings app code close to GPU to maximize performance • For games that require lot of processing power • Like OpenGL but should be easier and you get more performance on iOS devices

Slide 3

Slide 3 text

SpriteKit – pros & cons • Pros • Built-in to iOS – no need for external libraries • Built-in tool for texture atlases and particles • Really easy to implement stuff, also features things like video- sprites and applying image effects and masks • Cons • Locked to iOS • Still rather new framework – some features may be missing compared to older frameworks • If you develop cross-platform, Unity is good choice • If you are a beginner in game development and/or solely focused on iOS, SpriteKit is a good choice

Slide 4

Slide 4 text

Getting Started • UI iOS app • Storyboard holds view controller with one view (UIView) • The view is added to Window so that it's visible on the screen • SpriteKit • Exactly the same, but change UIView to SKView!

Slide 5

Slide 5 text

SKView • SKView object is a view that displays SpriteKit – content. Content is provided by an SKScene object • SKView inherites UIView • SKScene represents a "level" or "screen" in the game • Handles the per-frame logic • Create your own class that extends SKScene • Composed with SKNodes (Base class): SKSpriteNode, SKLabelNode, SKShapeNode, SKLightNode, SKCameraNode, SKAudioNode

Slide 6

Slide 6 text

Configure View Controller import UIKit import SpriteKit class ViewController: UIViewController { var spriteView : SKView! override func viewDidLoad() { super.viewDidLoad() // In Storyboard, change view controller's view type // from UIView to SKView! self.spriteView = self.view as? SKView; self.spriteView.showsDrawCount = true; self.spriteView.showsNodeCount = true; self.spriteView.showsFPS = true; }

Slide 7

Slide 7 text

Open a Scene import UIKit import SpriteKit class ViewController: UIViewController { var spriteView : SKView! override func viewWillAppear(_ animated: Bool) { // Create StartScene - class let startScene = StartScene(size: self.spriteView.bounds.size); // Debugging to see what are the points // See: http://www.paintcodeapp.com/news/iphone-6-screens-demystified NSLog("Width = \(self.spriteView.bounds.width)"); NSLog("Height = \(self.spriteView.bounds.height)"); spriteView.presentScene(startScene); } }

Slide 8

Slide 8 text

SKScene • SKScene is a "screen" in your game • Start screen, game screen, highscore screen • SKScene contains nodes, where the root node is SKScene itself • Different nodes available • Usually you subclass SKScene • Create initial content (nodes), implement the game logic, implement responder methods for touch events

Slide 9

Slide 9 text

Scene class import Foundation import SpriteKit class StartScene : SKScene { let TITLE = "Game Title" // Called immediately after a scene is presented with a view override func didMove(to view: SKView) { self.backgroundColor = SKColor.blue // See doc: // Fill, AspectFill, AspectFit, ResizeFill // AspectFit => letterboxing // self.scaleMode = SKSceneScaleMode.aspectFit self.addChild(self.createTitle()); } func createTitle() -> SKLabelNode { .. } }

Slide 10

Slide 10 text

Scene class import Foundation import SpriteKit class StartScene : SKScene { let TITLE = "Game Title" override func didMove(to view: SKView) { ... } func createTitle() -> SKLabelNode { let helloNode = SKLabelNode(fontNamed: "Chalkduster") // To be able to reference to the node later helloNode.name = self.TITLE helloNode.text = self.TITLE helloNode.fontSize = 50; // Center of the screen helloNode.position = CGPoint(x: self.frame.midX, y: self.frame.midY) return helloNode; }

Slide 11

Slide 11 text

class StartScene : SKScene { let TITLE = "Game Title" override func didMove(to view: SKView) { ... } func createTitle() -> SKLabelNode { ... } override func touchesBegan(_ touches: Set, with event: UIEvent?) { let helloNode = self.childNode(withName: self.TITLE) // Unwrap optional from helloNode if let hello = helloNode { // Prevent multiple actions hello.name = ""; // Create actions let zoom = SKAction.scale(to: 2.0, duration: 0.5) let fadeAway = SKAction.fadeOut(withDuration: 0.5) let remove = SKAction.removeFromParent() // Create group of actions running in parallel let parallel = SKAction.group([zoom, fadeAway]) // Create group of actions running in sequence let sequence = SKAction.sequence([parallel, remove]); // Run the action // hello.run(sequence) hello.run(sequence, completion: { let gameScene = GameScene(size: self.size); let transition = SKTransition.doorsOpenVertical(withDuration: 0.5) self.view?.presentScene(gameScene, transition: transition) }) } } }

Slide 12

Slide 12 text

import Foundation import SpriteKit class GameScene : SKScene { // Called immediately after a scene is presented with a view override func didMove(to view: SKView) { self.backgroundColor = SKColor.white self.addChild(self.createAlien()); self.addChild(self.createShootButton()); let borderBody = SKPhysicsBody(edgeLoopFrom: self.frame) self.physicsBody = borderBody } ... }

Slide 13

Slide 13 text

Creating Sprites func createAlien() -> SKSpriteNode { // Drage alien.png to your project alienSprite = SKSpriteNode(imageNamed: "alien") // Position the alien in the middle of the screen alienSprite.position = CGPoint(x: self.frame.midX, y: self.frame.midY) // Scale the texture down alienSprite.setScale(0.5) // Add physics! The physic boundaries are exactly the same than in the texture alienSprite.physicsBody = SKPhysicsBody(texture: alienSprite.texture!, size: alienSprite.size) // The mass in kg alienSprite.physicsBody!.mass = 0.2 // How bouncy alien alienSprite.physicsBody!.restitution = 0.5 // Frictional force when in contact alienSprite.physicsBody!.friction = 0.5 return alienSprite }

Slide 14

Slide 14 text

SKPhysicsBody • Volume-based body • Rectangle, Radius, Texture.. • Edge-based body • Unaffected by forces or impulses, no mass or volume • Boundaries to the game (SKScene) // Boundaries let borderBody = SKPhysicsBody(edgeLoopFromRect: self.frame) self.physicsBody = borderBody

Slide 15

Slide 15 text

Fire Ball func fireBall() -> Void { let ballSprite = SKSpriteNode(imageNamed: "ball") ballSprite.position.y = self.alienSprite.position.y + (alienSprite.frame.height / 2) + ballSprite.frame.height / 2 + 10 ballSprite.position.x = self.alienSprite.position.x ballSprite.physicsBody = SKPhysicsBody(texture: ballSprite.texture!, size: ballSprite.size) ballSprite.physicsBody!.mass = 0.1 ballSprite.physicsBody!.friction = 0.5 ballSprite.physicsBody!.restitution = 0.8 self.insertChild(ballSprite, at: 1) ballSprite.physicsBody?.applyImpulse(CGVector(dx: 0, dy: 80)) }

Slide 16

Slide 16 text

Button func createShootButton() -> SKSpriteNode { let shootSprite = SKSpriteNode(imageNamed: "shoot") shootSprite.position = CGPoint(x: self.frame.width - shootSprite.frame.width/2, y: shootSprite.frame.height/2) shootSprite.name = "fireButton" return shootSprite }

Slide 17

Slide 17 text

Button override func touchesBegan(_ touches: Set, with event: UIEvent?) { let touch = touches.first // Location in this node's coordinate system let location = touch?.location(in: self) let node = self.atPoint(location!) if(node.name == "fireButton") { fireBall() } else { jump() } }

Slide 18

Slide 18 text

Collision vs Contacts • Collisions include all automatic physical interactions between bodies • By default, everything collides with each other • You can customize this: CollisionBitMask • Contacts • Contact event happens when two bodies touch. It's possible to implement custom logic when bodies in contact • By default, no events are send on collide • You can customize this

Slide 19

Slide 19 text

CategoryBitMasks // Categories are defined as 32 - bit masks // When performing tests it uses AND between these let ALIEN_CATEGORY : UInt32 = 1 << 0 // 0000 0001 // 1 let EDGE_CATEGORY : UInt32 = 1 << 1 // 0000 0010 // 2 let BALL_CATEGORY : UInt32 = 1 << 2 // 0000 0100 // 4

Slide 20

Slide 20 text

Adding CollisionBitMasks // Category bitmap mask for ball ballSprite.physicsBody!.categoryBitMask = BALL_CATEGORY // => Ball will collide with Alien and other balls ballSprite.physicsBody!.collisionBitMask = ALIEN_CATEGORY | BALL_CATEGORY /* BALL_CATEGORY = 0000 0100 ALIEN_CATEGORY = 0000 0001 collision mask = BALL_CATEGORY | ALIEN_CATEGORY = 0000 0101 AND EDGE_CATEGORY 0000 0010 0000 0000 => NO COLLISION! */

Slide 21

Slide 21 text

Notifying about Collisions // Notify if contact has happened for custom code // // This body's contactTestBitMask is compared to others body's // category mask by performing a logical AND operation // // NOTIFY WHEN HITTING ALIEN ballSprite.physicsBody!.contactTestBitMask = ALIEN_CATEGORY

Slide 22

Slide 22 text

ContactDelegate class GameScene : SKScene, SKPhysicsContactDelegate { // Called immediately after a scene is presented with a view override func didMoveToView(view: SKView) { self.physicsWorld.contactDelegate = self; } func didBegin(_ contact: SKPhysicsContact) { let bodyABitMask = contact.bodyA.categoryBitMask let bodyBBitMask = contact.bodyB.categoryBitMask // Do something } func didEnd(_ contact: SKPhysicsContact) { } }