Creating screen boundaries using GKObstacle

I need to restrain my agents from going off the screen. I tried the following:


let screenWidth = UIScreen.mainScreen().bounds.width
let screenHeight = UIScreen.mainScreen().bounds.height

// bottom
let obstacle1 = GKPolygonObstacle(points: UnsafeMutablePointer([float2(0, 0), float2(Float(screenWidth), 0)]), count: 2)
// left
let obstacle2 = GKPolygonObstacle(points: UnsafeMutablePointer([float2(0, 0), float2(0, Float(screenHeight))]), count: 2)
// right
let obstacle3 = GKPolygonObstacle(points: UnsafeMutablePointer([float2(Float(screenWidth), 0), float2(Float(screenWidth), Float(screenHeight))]), count: 2)
// top
let obstacle4 = GKPolygonObstacle(points: UnsafeMutablePointer([float2(0, Float(screenHeight)), float2(Float(screenWidth), Float(screenHeight))]), count: 2)

let obstacles = [obstacle1, obstacle2, obstacle3, obstacle4]

// set the goal for the behaviour of the agent
agent.behavior?.setWeight(1000000, forGoal: GKGoal(toAvoidObstacles: obstacles, maxPredictionTime: 1))


This, however, does not work. The agent has other working goals so it is set up properly. What am I doing wrong?

Goals are not a guaranteed thing. I would expect your agents to try to avoid the screen edges, but they will probably overshoot. If you want a guarantee that they don't go off-screen, perhaps you need to add a SpriteKit edge-loop physics body, or just force-set the agents' position to keep them on-screen after they update.


But if an agent isn't even trying to avoid the obstacles, perhaps it would work if the obstacles had some thickness to them rather than simply being lines. I know that goals don't do well with single-element GKPaths.

In the Agents, Goals and Behaviors sample app provided by Apple, the agent completely avoids the obstacles, so it is possible. To add some thickness, I have tried this, yet still it doesn't work:


// bottom
let obstacle1 = GKPolygonObstacle(points: UnsafeMutablePointer([float2(0, 0), float2(Float(screenWidth), 0), float2(Float(screenWidth), 10), float2(0, 10)]), count: 4)
// left
let obstacle2 = GKPolygonObstacle(points: UnsafeMutablePointer([float2(0, 0), float2(0, Float(screenHeight)), float2(10, Float(screenHeight)), float2(10, 0)]), count: 4)
// right
let obstacle3 = GKPolygonObstacle(points: UnsafeMutablePointer([float2(Float(screenWidth), 0), float2(Float(screenWidth), Float(screenHeight)), float2(Float(screenWidth)-10, Float(screenHeight)), float2(Float(screenWidth)-10, 0)]), count: 4)
// top
let obstacle4 = GKPolygonObstacle(points: UnsafeMutablePointer([float2(0, Float(screenHeight)), float2(Float(screenWidth), Float(screenHeight)), float2(Float(screenWidth), Float(screenHeight)-10), float2(0, Float(screenHeight)-10)]), count: 4)

Hey DevDude, Did you figure this out? I have the same issue. 😕

Having a similar issue with GKObstacle and GKGoal avoid obstacles... I'm trying to use it for the canonical use: to prevent my agents (enemies) from crossing internal obstacles (walls, pits, and such).

Was having a similar issue with agent+rendercomponent butting heads against world boundary in SKScene...


In simple behaviours with GKGoal 's setting 'toAvoidObstacleswith' maxPredictionTime for value of >10 seems to solve for me.


I experimented with a few other things like increasing the weight of my avoidObstaclesGoal vs maxSpeed vs wander etc. but always reverting to low value for maxPredictionTime caused problem to reoccour.

I've tried everything and my Agents still seemed to go through my obstacles.


Then I came across a blog that had this:

func agentDidUpdate(agent: GKAgent) {
     if let agent2d = agent as? GKAgent2D {
          node.position = CGPoint(x: CGFloat(agent2d.position.x), y: CGFloat(agent2d.position.y))
     }
}
func agentWillUpdate(agent: GKAgent) {
     if let agent2d = agent as? GKAgent2D {
          agent2d.position = float2(Float(node.position.x), Float(node.position.y))
     }
}

Where it updated the Agent's position (I also added zRotation) in agentWillUpdate(), and then everything seemed to magically work! Without the need for incredibly high weights and predictionTime values. The behaviors are still relatively stupid about actually avoiding things, but at least it doesn't go through my obstacles anymore.

Source of code

Add a seekGoal(self) behaviour to your wander, and you should find the entity does not leave the scene. Well, it worked for me...

The solution in 2024, I believe, is to use a GKBehavior with a GKGoal(toStayOn:maxPredictionTime:)

The following code will keep an agent within the bounds of a scene, where wh is the width/height of a square-shaped scene.

Depending on your use case, you might need to adjust the weight and/or maxPredictionTime.

let agent = GKAgent2D()
let wh: Float = 2000.0
let bounds = [
      SIMD2(x: -(wh*0.5), y: -(wh*0.5)),
      SIMD2(x: -(wh*0.5), y: (wh*0.5)),
      SIMD2(x: (wh*0.5), y: (wh*0.5)),
      SIMD2(x: (wh*0.5), y: -(wh*0.5))
]
let keepInBoundsGoal = GKGoal(toStayOn: GKPath(points: bounds, radius: wh*0.5, cyclical: true), maxPredictionTime: 160.0)
let behavior = GKBehavior(goals: [keepInBoundsGoal])

behavior.setWeight(30.0, for: keepInBoundsGoal)

agent.behavior = behavior
Creating screen boundaries using GKObstacle
 
 
Q