Preferred Area
Once the Denizens populate the beach and dot the landscape, they'll undoubtedly need something to do. It would seem that the most obvious behavior a beachgoer might exhibit is that of moving about; they'd wander here and there, up and down the shoreline, and in some further, more exciting, but as-of-yet undetermined way, interact with the environment.
At its most basic, the interaction model of "meandering about" would be: within some discrete area, pick a destination, move there (at some speed) and upon arrival, generate a new destination. Repeat as necessary.
The Model.
Denizen
's will need to have some ways to check and set their destination(s).
class Denizen: SKNode {
var currentPosition: CGPoint!
var targetPosition: CGPoint!
They'll also need some visual representation of themselves and some knowledge of the update loop.
var sprite: SKSpriteNode!
var lastUpdateTime: NSDate!
A new targetPosition
can be composed of random X and Y coordinates within whatever range we'd like.
func randomInRange(min: CGFloat, max: CGFloat) -> CGFloat {
return round(CGFloat(arc4random()) / CGFloat(UINT32_MAX) * abs(min - max) + min)
}
And then the targetPosition
updated:
let newX = randomInRange(100, max: 500)
let newY = randomInRange(200, max: 300)
targetPosition = CGPoint(x: newX, y: newY)
self.runAction(SKAction.moveTo(targetPosition, duration: 0.5))
But...
They should also know where in the scene they're allowed to set as a destination. If the range of X and Y values were simply the size of the scene, there would be denizens floating in the sky.
var preferredArea: CGRect!
The Areas.
We'll need to section off the scene into CGRect
's which can then be used by the the instances of Denizen
to find a new targetPosition
.
struct PreferredArea {
enum Name {
case NearShore
case Garden
case ByTheBar
}
var x: CGFloat
var y: CGFloat
var width: CGFloat
var height: CGFloat
init(area: Name) {
switch area {
case .NearShore:
self.x = 100
self.y = 100
self.width = 400
self.height = 100
case .Garden:
self.x = 200
self.y = 300
self.width = 100
self.height = 300
case .ByTheBar:
self.x = 30
self.y = 50
self.width = 300
self.height = 600
}
}
These are arbitrary values and names, but hopefully, one gets the idea.
The Class.
Now the Denizen
's .preferredArea
can be set when initializing a new instance.
init(pref: PreferredArea) {
self.preferredArea = CGRect(x: pref.x, y: pref.y, width: pref.width, height: pref.height)
self.sprite = SKSpriteNode(color: UIColor.redColor(), size: CGSize(width: 50, height: 50))
super.init()
self.addChild(sprite)
}
When a new destination is needed, the calculations to acquire one look like this: (This is extremely verbose for explanatory purposes)
private func newTargetPosition() -> CGPoint {
let x = preferredArea.minX
let width: CGFloat = preferredArea.width
let y = preferredArea.minY
let height: CGFloat = preferredArea.height
let maxX = x + width
let maxY = y + height
let newX = randomInRange(x, max: maxX)
let newY = randomInRange(y, max: maxY)
return CGPoint(x: newX, y: newY)
}
func update() {
let now = NSDate()
if lastUpdateTime == nil {
lastUpdateTime = now
}
let timeSinceUpdate = now.timeIntervalSinceDate(lastUpdateTime)
if timeSinceUpdate >= 1.0 {
// get a new place to walk to based off of the preferred area.
currentPosition = self.position
if currentPosition == targetPosition {
targetPosition = newTargetPosition()
self.runAction(SKAction.moveTo(targetPosition, duration: 3.5))
println(targetPosition)
}
lastUpdateTime = now
}
}
Overflow.
The white box below has the dimensions of the .preferredArea
which was used in the instantiation of the Denizen
.
Since the red square (the SKSpriteNode
which represents the Denizen at this moment) has the default .anchorPoint
of CGPoint(x: 0.5, y: 0.5)
, there will exist the possibility that some new targetPosition
would be created with X and Y values which place it outside of the PreferredArea
.
So, we'll have to keep that in mind when creating the individual preferred areas' dimensions, the size of the sprite which will eventually inhabit them must be accounted for.
self.sprite = SKSpriteNode(color: UIColor.redColor(), size: CGSize(width: 50, height: 50))
As the red square is 50x50 pts and the anchorPoint
is at the center, the overflow would be 25 pts on all sides. Here it is with the 25 pts overlaid in yellow.
Animation.
Instead of using a red square, here's Albatross from Rolling Thunder.