Home Release Notes Developer Musings About the Studio

<< Back to Developer Musings

The Problem with XScale (January 31, 2015)

SpriteKit has a lot of functionality that made my iOS port go a lot faster than I would have expected.

In the Android version of Officer Bumble, flipping the direction of a sprite involved passing the inverse texture atlas coordinates to my game engine, and treating that sprite as a separate entity from the original facing sprite. I wasn’t wasting any texture memory, but the whole solution felt sloppy. Looking back, I should have included a "direction" property on my sprite class to handle this automatically, but that's an improvement for another day.

In SpriteKit, SKSpriteNode classes do support this kind of functionality via setttng an XScale property of the node to -1. This actually works great until you have to deal with collision detection.

When you set an SKSpriteNode's XScale property to -1, the collision bounding box goes haywire and adjusts itself to take up the entire width of the screen! You can visually see this simply by turning ShowPhysics on. As you can imagine, this is a deal breaker for the game and nearly caused me to have to build my own collision engine detection extension into SpriteKit - not an attractive prospect! Fortunately, I was able to come up with an acceptable work-around: simply create an invisible SKSpriteNode object, set it’s collision boundaries, and anchor it to your base sprite. Then you just adjust your physics code to detect whenever this inner sprite is collided with. This worked great because I never need to set the XScale property of this collision node since it’s always invisible and the boundaries don’t change when the sprite direction is flipped.

Here’s a little code to illustrate what I’m talking about:

private func GetStandingCollisionBody() -> SKPhysicsBody {
// The bounding box that surrounds Bumble.
let boundingBox = CGSize(width: size.width * BUMBLE_BOUNDING_BOX_PERCENT, height: size.height * 0.80)
let center = CGPoint(x: 0, y: -(size.height / 10.0))
var body:SKPhysicsBody = SKPhysicsBody(rectangleOfSize: boundingBox, center: center)
body.dynamic = true
body.affectedByGravity = false
body.allowsRotation = false
body.categoryBitMask = BODY_TYPE.BUMBLE.rawValue //was toRaw() in Xcode 6
body.collisionBitMask = 0 //if you don't want this body to actually collide with anything
body.contactTestBitMask = BODY_TYPE.BOWLING_BALL.rawValue | BODY_TYPE.PIE.rawValue | BODY_TYPE.CHICKENATOR_HIGH.rawValue | BODY_TYPE.CHICKENATOR_LOW.rawValue | BODY_TYPE.ESCALATOR.rawValue // was toRaw() in Xcode 6

return body
}

private func SetupCollisionBodies() {
self.standingPhysicsBody = GetStandingCollisionBody()
self.duckingPhysicsBody = GetDuckingCollisionBody()
self.jumpingPhysicsBody = GetJumpingCollisionBody()
}

func Run() {
BeginAccelerate()
currentState = .RUNNING
self.removeActionForKey("stateAnimation")
self.runAction(textureManager.bumbleRunningAction, withKey: "stateAnimation")
self.collisionNode!.physicsBody = standingPhysicsBody!
}

And there you have it! I have a private collision node object that’s always attached to Bumble (or whatever object supports collisions). I change this physics body based on the state of the object (obviously the jumping collision area is different than the jumping one). Now it doesn’t matter which way Bumble is facing, his collision area will always remain the same.

This was one of the more annoying things to work around in SpriteKit - only because it cost me a full night of work and about 3 cups of coffee at Starbucks.






© 2015 Rat River Game Studios, All rights reserved