GameplayKit > Make agent stop moving after goal to follow path

551 views Asked by At

I am making an agent follow a GKPath by adding to its behavior the goal toFollow: GKPath. Everything works fine, however the agent continues moving in a 8-like shape when reaching the target point on the path. How do I prevent the agent to wander when reaching the target point on a GKPath?

A solution I have thought off would be to set another goal toReachTargetSpeed: 0.0 when close to approaching the target point. However, I am not sure what would be the best practice, since there is no built in way to see when the goal is "completed".

  1. Do I use an IF statement to check the distance to target at each update:? What about performance?
  2. Do I use some kind of a GKRule?

Your suggestions would be very much appreciated. Thanks.

1

There are 1 answers

0
Filip Juncu On BEST ANSWER

I went with the first option, i.e. to track the distance to target in the movementComponent update function:

override func update(deltaTime seconds: TimeInterval) {
    guard let entity = self.entity as? BaseEntity else { return }
    if entity.moving {
        let targetPoint = CGPoint(x: 100, y: 100)
        let distanceToTarget = distanceFrom(point: point)
        if distanceToTarget < GameConfiguration.Movement.targetProximity {
            stopMovement(afterTraversing: distanceToTarget)
        }
    }
}

func distanceFrom(point: CGPoint) -> Float {
    guard let entity = self.entity as? BaseEntity else { return CGFloat.greatestFiniteMagnitude }
    let dx = point.x - entity.visualComponent.node.position.x
    let dy = point.y - entity.visualComponent.node.position.y
    return hypotf(Float(dx), Float(dy))
}

When the agent is close enough to the target, run the stopMovement function. In turn, this will calculate the time needed to traverse the distance left to target (having a constant speed), and will put a timer as to when to force stop the agent:

func stopMovement(afterTraversing distance: Float) {
    guard let entity = self.entity as? BaseEntity else { return }
    guard (entity.moving) else { return }
    guard let behavior = entity.agent.behavior as? MovingBehavior else { return }
    let timeToTarget = TimeInterval(distance / entity.agent.maxSpeed)
    Timer.scheduledTimer(withTimeInterval: timeToTarget, repeats: false, block: {_ in
        behavior.stopMoving()
        entity.agent.maxSpeed = 0.0
        entity.moving = false
    })
}

class MovingBehavior: GKBehavior {

    var reachMaxSpeed: GKGoal
    var followPathGoal: GKGoal
    var stopMovingGoal = GKGoal(toReachTargetSpeed: 0.0)

    init(path: GKPath, target: GKGraphNode, maxSpeed: Float) {
        reachMaxSpeed = GKGoal(toReachTargetSpeed: maxSpeed)
        followPathGoal = GKGoal(toFollow: path, maxPredictionTime: 1.0, forward: true)
        super.init()
        setWeight(1.0, for: reachMaxSpeed)
        setWeight(0.8, for: followPathGoal)
        setWeight(0.0, for: stopMovingGoal)
    }

    func stopMoving() {
        setWeight(0.0, for: reachMaxSpeed)
        setWeight(0.0, for: followPathGoal)
        setWeight(1.0, for: stopMovingGoal)
    }
}