Search code examples
swiftgameplay-kit

GameplayKit GKGoal: can't get wandering to work


Trying to learn how to use GameplayKit, and in particular, agents & behaviors. Trying to boil down all the tutorials and examples out there into a small, simple piece of code that I can use as a starting point for my own app. Unfortunately, what I've come up with doesn't work properly, and I can't figure out why. It's just a simple sprite with a simple GKGoal(toWander:). Rather than wandering, it just moves in a straight line to the right, forever. It also starts out impossibly slowly and speeds up impossibly slowly, despite my setting the max speed & acceleration to ridiculously high values. I can't figure out the fundamental difference between my simple code and all the complex examples out there. Here's the code, minus required init?(coder aDecoder: NSCoder):

class GremlinAgent: GKAgent2D {
    override init() {
        super.init()

        maxAcceleration = 100000
        maxSpeed = 1000000
        radius = 20
    }

    override func update(deltaTime seconds: TimeInterval) {
        super.update(deltaTime: seconds)
        let goal = GKGoal(toWander: 100)
        behavior = GKBehavior(goal: goal, weight: 1)
    }
}

class Gremlin: GKEntity {
    let sprite: SKShapeNode

    init(scene: GameScene) {
        sprite = SKShapeNode(circleOfRadius: 20)
        sprite.fillColor = .blue

        scene.addChild(sprite)

        super.init()

        let agent = GremlinAgent()
        addComponent(agent)

        let node = GKSKNodeComponent(node: sprite)
        addComponent(node)
        agent.delegate = node
    }
}

And in GameScene.swift, didMove(to view:):

let gremlin = Gremlin(scene: self)
entities.append(gremlin)

Can anyone help me out?


Solution

  • As has been pointed out elsewhere, you have to set the weight for the goal very high. Try 100, or even 1000, and notice the difference in behavior. But even with these large weights, there's still a problem in your example: the maxSpeed value. You can't set it so high, or your sprite will just fly off in a straight line. Set it to a value closer to the speed you set in the GKGoal object.

    Also notice that the wandering will always start off in the direction your sprite is pointing, so if you don't want it to always start off moving to the right, set zRotation to some random value.

    Finally, don't create a new behavior in every call to update(). For wandering, you can just set it once, say, in init().

    Here's some code that works:

    class GremlinAgent: GKAgent2D {
        override init() {
            super.init()
    
            maxAcceleration = 100000
            maxSpeed = 100
    
            let goal = GKGoal(toWander: 100)
            behavior = GKBehavior(goal: goal, weight: 1000)
        }
    }
    
    class Gremlin: GKEntity {
        let sprite: SKShapeNode
    
        init(scene: GameScene) {
            sprite = SKShapeNode(circleOfRadius: 20)
            sprite.fillColor = .blue
            sprite.zRotation = CGFloat(GKRandomDistribution(lowestValue: 0, highestValue: 360).nextInt())
    
            scene.addChild(sprite)
    
            super.init()
    
            let agent = GremlinAgent()
            addComponent(agent)
    
            let node = GKSKNodeComponent(node: sprite)
            addComponent(node)
            agent.delegate = node
        }
    }