Initial project setup, sprite creation and movement using SKAction and SKConstraint
Tutorial Overview:
- Part 1: Initial project setup, sprite creation and movement using SKAction and SKConstraint
- Part 2: Adding enemies, bullets and shooting with SKAction and SKConstraint
- Part 3: Adding a HUD with SKLabelNode and SKSpriteNode
- Part 4: Adding basic game logic and collision detection
- Part 5: Adding particles and sound
- Part 6: GameCenter integration
- Part 7: iAd integration
- Part 8: In-App Purchases
1. Create a new universal project (template: game; language: Swift)
2. Remove the GameScene.sks file
I'll not use the internal level editor, so this file is obsolete and can be deleted:
Inside GameController.swift remove this code block:
extension SKNode {
class func unarchiveFromFile(file : NSString) -> SKNode? {if let path = NSBundle.mainBundle().pathForResource(file, ofType: "sks") {
var sceneData = NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe, error: nil)!
var archiver = NSKeyedUnarchiver(forReadingWithData: sceneData)
let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as GameScene
archiver.finishDecoding()return scene
} else {
return nil
}
}
}
Replace the ViewDidLoad method with this code to create a scene in fullscreen mode for iPad and iPhone:
override func viewDidLoad() {
super.viewDidLoad()
// Detect the screensize
var sizeRect = UIScreen.mainScreen().applicationFrame
var width = sizeRect.size.width * UIScreen.mainScreen().scale
var height = sizeRect.size.height * UIScreen.mainScreen().scale
// Scene should be shown in fullscreen mode
let scene = GameScene(size: CGSizeMake(width, height))
// 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)
}
Limit the supported screen orientations to LandscapeLeft:
override func supportedInterfaceOrientations() -> Int {
return Int(UIInterfaceOrientationMask.LandscapeLeft.rawValue)
}
3. Add the space ship:
I'll add a spaceship sprite. The sprite will automatically orient and move to a touch location on the screen:
The automated orienting of the spaceship is implemented with a little trick. I'll add a SKConstraint to an invisible sprite. I case of a touch event, the invisible sprite is moved to the touch location and the spaceship is automatically oriented to this invisible sprite/touch location.
3.1. Open the GameScene.swift file and remove the code for creating a label in didMoveToView:
override func didMoveToView(view: SKView) {
/* Setup your scene here */
}
3.2. Create two global Sprite properties:
var heroSprite = SKSpriteNode(imageNamed:"Spaceship")
var invisibleControllerSprite = SKSpriteNode()
var invisibleControllerSprite = SKSpriteNode()
override func didMoveToView(view: SKView) {
/* Setup your scene here */
}
3.3. Create a sprite inside didMoveToView:
self.backgroundColor = UIColor.blackColor()
self.addChild(heroSprite)
// Create the hero sprite and place it in the middle of the screen
heroSprite.xScale = 0.15
heroSprite.yScale = 0.15
heroSprite.position = CGPointMake(self.frame.width/2, self.frame.height/2)self.addChild(heroSprite)
3.4. Create an invisible sprite to implement the orientation behavior inside didMoveToView:
// Define invisible sprite for rotating and steering behavior without trigonometry
// Define a constraint for the orientation behavior
let rangeForOrientation = SKRange(constantValue: CGFloat(M_2_PI*7))
invisibleControllerSprite.size = CGSizeMake(0, 0)
self.addChild(invisibleControllerSprite)
let rangeForOrientation = SKRange(constantValue: CGFloat(M_2_PI*7))
heroSprite.constraints = [SKConstraint.orientToNode(invisibleControllerSprite, offset: rangeForOrientation)]
3.5. Replace the code inside the touchesBegan method to implement the sprite movement:
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
var yOffset:CGFloat = 1.0
var location = touch.locationInNode(self)
if location.x>heroSprite.position.x {
xOffset = -1.0
}
if location.y>heroSprite.position.y {
yOffset = -1.0
}
invisibleControllerSprite.runAction(actionMoveInvisibleNode)
heroSprite.runAction(actionMove)
}
}
/* Called when a touch begins */
// Determine the new position for the invisible sprite:
// The calculation is needed to ensure the positions of both sprites
// are nearly the same, but different. Otherwise the hero sprite rotates
// back to it's original orientation after reaching the location of
// the invisible sprite
var xOffset:CGFloat = 1.0var yOffset:CGFloat = 1.0
var location = touch.locationInNode(self)
if location.x>heroSprite.position.x {
xOffset = -1.0
}
if location.y>heroSprite.position.y {
yOffset = -1.0
}
// Create an action to move the invisibleControllerSprite.
// This will cause automatic orientation changes for the hero sprite
let actionMoveInvisibleNode = SKAction.moveTo(CGPointMake(location.x - xOffset, location.y - yOffset), duration: 0.2)invisibleControllerSprite.runAction(actionMoveInvisibleNode)
// Create an action to move the hero sprite to the touch location
let actionMove = SKAction.moveTo(location, duration: 1)heroSprite.runAction(actionMove)
}
Cheers,
Stefan
Hi, there seems to be a stray line of code:
ReplyDeletefollowerSprite.constraints = [orientConstraint, distanceConstraint]
Thanks
Hi,
Deleteyes, you are right. I've removed it.
Thanks
Stefan
Hi Stefan, Awesome tutorial you go here, I have used some of your ideas to implement my own space shooter game. I have a question for you regarding the star field emitter. What would I need to do to make the stars generate horizontally instead of vertically? I've tried a few things but haven't been able to figure it out.
ReplyDeleteThanks,
Daniel
Hi Daniel
Deletejust rotate the emitter nodes:
emitterNode.zRotation = CGFloat(M_PI_2)
Cheers,
Stefan
Thank you so much for this Stefan, keep up the great work!
DeleteDaniel
This comment has been removed by the author.
ReplyDelete