Swift Regex Deep Dive
iOS MacOur introductory guide to Swift Regex. Learn regular expressions in Swift including RegexBuilder examples and strongly-typed captures.
In this series’ previous posts, I discussed how to use the game controllers and then built the skeleton of a Robotron:2084 game. In this third post, I will cover how to turn our boring boxes into fun animated sprites. The full source is available on Github for your use.
It’s fairly easy to search for Robotron sprites and early on I began doing this. It didn’t take long before I wanted to customize things and realized that my existing tools were not up to the task. After some searching around, I found an amazing tool called Aseprite that has a wonderfully retro interface of its own. Just get a load of that!
It took me about half a day to learn the interface and draw myself a little cowboy sprite walking:
When saving the animation in PNG format, Aseprite helpfully numbered all the files dude-01.png
, dude-02.png
and so forth. It was a fun break from coding, so I designed a whole slew of sprites:
I imported all these images into my asset catalog and got to work.
I opened up my Movable
class and added some logic to rotate through whatever textures were supplied.
var spriteStep = Int(arc4random_uniform(4))
var spriteTextures : [SKTexture] = []
func nextSprite() {
guard spriteTextures.count >= 2 else {
return
}
texture = spriteTextures[spriteStep]
if let sz = texture?.size() {
self.size = CGSize(width: sz.width*3, height: sz.height*3)
}
spriteStep = (spriteStep + 1) % spriteTextures.count
}
And up in my move()
method, when I update the position, I’ll swap the sprite.
lastWalkVector = vec
position = pos
nextSprite()
I knew that I was going to want to select a set of textures based on the direction the character was facing, so I’d have a dictionary where the key was the direction, and the value was an array of textures. The right and left sprite sets have four distinct steps, but the back and front sprite sets only have three, repeating the middle one. By using string interpolation and well-chosen arrays, it made generating the sets straightforward.
static let textures : [String : [SKTexture]] = {
var ret : [String : [SKTexture]] = [:]
for set in ["back", "front"] {
var arr : [SKTexture] = []
for frame in [ 1, 2, 3, 2 ] {
arr.append(SKTexture(imageNamed: "dude-(set)-(frame)"))
}
ret[set] = arr
}
for set in ["right", "left"] {
var arr : [SKTexture] = []
for frame in [ 1, 2, 3, 4 ] {
arr.append(SKTexture(imageNamed: "dude-(set)-(frame)"))
}
ret[set] = arr
}
return ret
}()
func didChangeDirection(_ direction: Movable.WalkDirection) {
let set = direction.spriteSet()
spriteTextures = Player.textures[set]!
}
What’s that spriteSet()
call on WalkDirection
?
func spriteSet() -> String {
switch self {
case .north: return "back"
case .south: return "front"
case .east: return "right"
case .west: return "left"
}
}
And voila! Our player has an animated sprite.
The enemy can follow simpler sprite logic, since there’s no “left”, “right”, or “back” variations – just the one face. However, the civilian is more complex since there are three types.
enum CivilianType : String {
case lady = "lady"
case man = "man"
case boy = "boy"
var pointValue : Int {
switch(self) {
case .lady: return 300
case .man: return 200
case .boy: return 100
}
}
static var allTypes : [CivilianType] = [.lady, .man, .boy]
static var allTypeNames : [String] = {
return allTypes.map() { return $0.rawValue }
}()
}
Our civilian class then gets a type
attribute:
var type : CivilianType = {
switch (arc4random_uniform(3)) {
case 0: return .lady
case 1: return .man
default: return .boy
}
}()
Simple enough, eh? Now, just as the Player had dude-(direction)-(step)
logic to find the textures, now we’ll want (type)-(direction)-(step)
for each type of civilian.
lazy var textureBanks : [String : [SKTexture]] = {
return textureDictionary[self.type.rawValue]!
}()
override func didChangeDirection(_ direction: Movable.WalkDirection) {
let set = direction.spriteSet()
let textureBank : Array<SKTexture> = textureBanks[set]!
self.spriteTextures = textureBank
}
static let textureDictionary : [String:[String:[SKTexture]]] = {
var dict : [String:[String:[SKTexture]]] = [:]
for type in allTypeNames {
var ret : [String : [SKTexture]] = [:]
for set in ["back", "front"] {
var arr : [SKTexture] = []
for frame in [ 1, 2, 3, 2 ] {
arr.append(SKTexture(imageNamed: "(type)-(set)-(frame)"))
}
ret[set] = arr
}
for set in ["right", "left"] {
var arr : [SKTexture] = []
let list = [1,2,3,4]
for frame in list {
arr.append(SKTexture(imageNamed: "(type)-(set)-(frame)"))
}
ret[set] = arr
}
dict[type] = ret
}
return dict
}()
And all our sprites are animated now.
So at this point I have a level with my characters running around and looking great. This is really fun, but it’s time to get to the “shoot” part of “shoot-em-up game.” Check out the next installment for details on how to do it!
Our introductory guide to Swift Regex. Learn regular expressions in Swift including RegexBuilder examples and strongly-typed captures.
The Combine framework in Swift is a powerful declarative API for the asynchronous processing of values over time. It takes full advantage of Swift...
SwiftUI has changed a great many things about how developers create applications for iOS, and not just in the way we lay out our...