Today you will begin putting the pieces together for a brand new game – see what we did there? To many, Swiftris resembles not only in name but in nearly every other respect a game written in the early 1980s that to this day continues to be played all around the world. Rest assured, Bloc is absolutely certain that any semblance to said game is merely coincidental.
In all seriousness, this is a Tetris clone written in Swift for the iOS platform. This Bloc Book is meant for educational purposes only and we do not recommend releasing your version of Swiftris to the App Store. If you do release Swiftris anyway, hope that you never cross paths with Alexey Pajitnov. As you can see, he’s a very dangerous man.
Before we start playing with blocks, you should know the tools we’ll be using: Swift, SpriteKit and Xcode.
Swift
Swift is Apple’s latest programming language. In time it will replace Objective-C as the primary language in which iPhone and Macintosh applications are written. Swiftris is written entirely in Swift and this book will present a wide variety of the language’s capabilities.
If you are not a programmer, do not worry. Regardless of skill level, you will have your very own copy of Swiftris after completing this guide. However, this book does not intend to teach you the language in its entirety. Several aspects of it will be covered in brief and supplemented by external resources.
SpriteKit
SpriteKit is a set of APIs provided by the iOS SDK (software development kit) which allow native 2D game development from within Xcode. Swiftris is powered by SpriteKit and therefore no additional libraries or 3rd party tools will be required to build this great game.
Xcode
As of the writing of this Bloc Book, Xcode 6 Beta 5 is the latest version required for Swift compilation. To download Xcode beta versions, you’ll need to register as an Apple Developer.
Once you’ve registered, download Xcode.
While it’s not required for this book, you may want to consider signing up for the iOS Developer Program. We think the $99 annual fee is wholly worthwhile: it provides access to yet-to-be public software updates and allows you to publish apps to your iPhone and the App Store.
Once you have Xcode downloaded and installed, you’re ready to move to the next chapter.
We’ll need to create a new project to build Swiftris in. An Xcode project organizes everything your app needs into one convenient place. Let’s begin by creating a brand new game project in Xcode by doing either of these two things:
- Click Create a new Xcode project from the Welcome screen:
Or
- Select File > New > Project… from the file menu:
When the new project window appears, choose Game from under the iOS > Application category and press Next.
The next window beckons you to customize options for your project. Fill out the fields described in the table below:
Option | Value |
---|---|
Product Name | Swiftris |
Organization Name | Bloc |
Organization Identifier | Bloc.io |
Language | Swift |
Game Technology | SpriteKit |
Devices | iPhone |
Press Next and Xcode will ask where to place your new project. Choose a magical, wonderful directory, make sure Create Git repository is checked, and then click Create.
After saving it should open Xcode to your brand new Swiftris project, specifically to the project properties screen. On this screen, check off the Portrait option under Device Orientation:. This file is automatically saved so you won’t have to do anything further.
Result
Run the default game project by pressing ⌘ + R on your keyboard or by clicking the little play button in the top left corner. If a simulator isn’t present, Xcode will download one for you before launching the app.
Congratulations, you’re infinitely closer to a completed Swiftris game than you were 10 minutes ago. That’s a big deal.
Don’t get me wrong, Spin-The-Bottle: Space Edition was a great game. However, you didn’t start this Bloc Book to make that. At least I hope not, if so, please stop now because your quest is over. For those of you still interested in building Swiftris, we must unceremoniously delete every unnecessary file provided to us by Xcode.
Open Project Navigator by either clicking the designated icon or pressing ⌘ + 1:
Right-click GameScene.sks
and choose the Delete option:
When asked to confirm, make sure to choose Move to trash:
To get rid of the aimless space ship once and for all, click the Images.xcassets
folder and highlight the Spaceship
entry, press the delete key to delete that sucker.
Trimming The Fat
Having slaughtered those files which are of no use, we must now purge our project of any and all code which we do not require. There’s no need to have lingering source code designed to support inept space pilots. Delete all of the lines marked in red within their corresponding files:
import SpriteKit
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
/* Setup your scene here */
let myLabel = SKLabelNode(fontNamed:"Chalkduster")
myLabel.text = "Hello, World!";
myLabel.fontSize = 65;
myLabel.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame));
self.addChild(myLabel)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
let sprite = SKSpriteNode(imageNamed:"Spaceship")
sprite.xScale = 0.5
sprite.yScale = 0.5
sprite.position = location
let action = SKAction.rotateByAngle(CGFloat(M_PI), duration:1)
sprite.runAction(SKAction.repeatActionForever(action))
self.addChild(sprite)
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
That was a lot, but there’s more:
import UIKit
import SpriteKit
extension SKNode {
class func unarchiveFromFile(file : NSString) -> SKNode? {
let path = NSBundle.mainBundle().pathForResource(file, ofType: "sks")
var sceneData = NSData.dataWithContentsOfFile(path, options: .DataReadingMappedIfSafe, error: nil)
var archiver = NSKeyedUnarchiver(forReadingWithData: sceneData)
archiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKScene")
let scene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as GameScene
archiver.finishDecoding()
return scene
}
}
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene.unarchiveFromFile("GameScene") as? 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)
}
}
override func shouldAutorotate() -> Bool {
return true
}
override func supportedInterfaceOrientations() -> Int {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return Int(UIInterfaceOrientationMask.AllButUpsideDown.toRaw())
} else {
return Int(UIInterfaceOrientationMask.All.toRaw())
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Release any cached data, images, etc that aren't in use.
}
override func prefersStatusBarHidden() -> Bool {
return true
}
}
The Sights And Sounds Of Swiftris
In order to experience Swiftris in all its visual and auditory glory, we’re going to need images and sounds, respectively. Download the necessary assets to your Desktop
or Downloads
folder, anywhere other than the Swiftris project directory. Unzip the archive and perform a drag-and-drop of the Sounds
folder into the Project Navigator immediately above the Supporting Files
directory. The following window should appear:
Make sure to check the Copy items if necessary option. This will place a copy of the directory and all of the sound files within it into your Swiftris project and directory. Click Finish. Repeat this task with theSprites.atlas
folder. Next, select all of the images found within the Images
directory and drag them into the Supporting Files
folder found in Project Navigator. Once again, make sure that the Copy items if needed checkbox is checked. Finally, click on Images.xcassets
to open the window and highlight AppIcon
. Drag and drop the appropriate icon
file from the downloaded “Blocs” folder into its respective slot: 29pt, 40pt and 60pt.
All this dragging and dropping has my clickin’ hand beat, let’s just code already…
Start At The Back
Let’s put those new background images to work. We’ll begin by establishing GameScene
inside of GameViewController
. GameScene
will be responsible for displaying everything for Swiftris – it will render the tetrominos on screen, the background, and the game board. Furthermore, GameScene
will be responsible for playing the sounds and keeping track of the time.
GameViewController
, on the other hand, will be responsible for handling user input and communicating between GameScene
and a game logic class you’ll write soon.
If you’re working with Swift for the first time, we highly encourage you to type each line by hand in order to get a feel for the language in your fingers… it sounds dirty but it’s good for you.
required init(coder aDecoder: NSCoder!) {
fatalError("NSCoder not supported")
}
override init(size: CGSize) {
super.init(size: size)
anchorPoint = CGPoint(x: 0, y: 1.0)
let background = SKSpriteNode(imageNamed: "background")
background.position = CGPoint(x: 0, y: 0)
background.anchorPoint = CGPoint(x: 0, y: 1.0)
addChild(background)
}
SpriteKit is based on OpenGL and therefore its coordinate system is opposite to iOS’ native cocoa coordinates. 0, 0
in SpriteKit is the bottom-left corner. Swiftris will be drawn from the top down so therefore we anchor our game in the top-left corner of the screen: 0, 1.0
. We then create an SKSpriteNode
capable of representing our background image and we add it to the scene.
background
is the variable’s name, its type is inferred to be that ofSKSpriteNode
and the keywordlet
indicates that it can not be re-assigned.let
is akin to Java’sfinal
.
var scene: GameScene!
override func viewDidLoad() {
super.viewDidLoad()
// Configure the view.
let skView = view as SKView
skView.multipleTouchEnabled = false
// Create and configure the scene.
scene = GameScene(size: skView.bounds.size)
scene.scaleMode = .AspectFill
// Present the scene.
skView.presentScene(scene)
}
In GameViewController
we’ve added a member variable, scene
. Its declaration: var scene: GameScene!
lets us know that it is a variable, its name is scene
, its type is GameScene
and it is a non-optional value which will eventually be instantiated. Swift typically enforces instantiation either in-line where you declare the variable or during the initializer, init…
. In order to circumvent this requirement we’ve added an !
after the type.
In viewDidLoad()
we assign scene
as promised, using the initializer we had just written moments ago. We tell it to fill the screen and then ask our view to present that scene to the user. Run Swiftris and you should see a super cool background appear. Not titillating enough for you? Read on to continue the fun.
https://www.bloc.io/tutorials/swiftris-build-your-first-ios-game-with-swift#!/chapters/678