I have a spriteKit project where I have many characters across several scenes.
As a beginner I just built each one individually for each scene - which makes for a ton of extra code.
I know I could clean this up with a "Build character class" or something like that...
I am just not sure where to begin.
Here is code from two of the characters in one scene...but imagine 5-10 characters per scene?
Also is there a way a property list could be useful for storing these type of properties?
//BEAR
func buildBear() {
let bearAnimatedAtlas = SKTextureAtlas(named: "Bear")
var bearFrames: [SKTexture] = []
let numImages = bearAnimatedAtlas.textureNames.count
for i in 1...numImages {
let bearTextureName = "bear\(i)"
bearFrames.append(bearAnimatedAtlas.textureNamed(bearTextureName))
}
animatedBear = bearFrames
let firstFrameTexture = animatedBear[0]
bear = SKSpriteNode(texture: firstFrameTexture)
bear.size.height = 370
bear.size.width = 370
bear.position = CGPoint(x: 295, y: 25)
bear.zPosition = 1
bear.name = "bear"
isUserInteractionEnabled = true
addChild(bear)
}
//CAT
func buildCat() {
let catAnimatedAtlas = SKTextureAtlas(named: "Cat")
var catFrames: [SKTexture] = []
let numImages = catAnimatedAtlas.textureNames.count
for i in 1...numImages {
let catTextureName = "cat\(i)"
catFrames.append(catAnimatedAtlas.textureNamed(catTextureName))
}
animatedCat = catFrames
let firstFrameTexture = animatedCat[0]
cat = SKSpriteNode(texture: firstFrameTexture)
cat.size.height = 240
cat.size.width = 240
cat.position = CGPoint(x: 134, y: -38)
cat.zPosition = 2
cat.name = "cat"
isUserInteractionEnabled = true
addChild(cat)
}
How could I clean up something like this - I need different position/size per scene but I imagine I could just override that per scene?
I know I know how to do this! - just not where to start?
Gimme a nudge please!
One of the confusing things about the existence of so many languages is that they each have their own jargon, and their own conventions. The root of your problem, however, has nothing to do with Swift or Sprite Kit. When I read your question, I see code that could use some Abstract Data Types. In Java, you would create an Interface, in C++ you would create a "pure virtual" class. Well a rose by any other name still gets the job done. I recommend creating a Protocol, perhaps called Spritable
, to define the types of objects that you intend to build into sprites. It would probably be as simple as this:
protocol Spritable {
var species: String { get }
var height: Int { get }
var width: Int { get }
}
The only other thing that differs between your two functions appears to be the starting position. Since this is not inherent in the meaning of a Spritable
object, I would package that data separately. A tuple should do the job. With these revisions, your two functions can be merged into one:
func buildSprite(of creature: Spritable, at position: (x: Int, y: Int, z: Int)) {
let spriteAnimatedAtlas = SKTextureAtlas(named: creature.species)
var spriteFrames: [SKTexture] = []
let numImages = spriteAnimatedAtlas.textureNames.count
for i in 1...numImages {
let spriteTextureName = "\(creature.species.lowercased())\(i)"
spriteFrames.append(spriteAnimatedAtlas.textureNamed(spriteTextureName))
}
animatedSprite = spriteFrames
let firstFrameTexture = animatedSprite[0]
sprite = SKSpriteNode(texture: firstFrameTexture)
sprite.size.height = creature.height
sprite.size.width = creature.width
sprite.position = CGPoint(x: position.x, y: position.y)
sprite.zPosition = position.z
sprite.name = creature.species
isUserInteractionEnabled = true
addChild(sprite)
}
To build a bear, aside from a workshop, you will need to define a struct that implements Spritable
:
struct Bear: Spritable {
var species: String { return "Bear" }
var height: Int
var width: Int
init(height: Int, width: Int) {
self.height = height
self.width = width
}
}
Then here would be your function call:
buildSprite(of: Bear(height: 370, width: 370), at: (295, 25, 1))
This is a pretty simple example, and could be solved in a few simpler ways than this. However, I find that the larger a project gets, the greater the benefits of organizing code around Abstract Data Types become, so it's worth taking that approach even in a simple case like this.