GameplayKit:路径被特定障碍物阻挡时终止寻路
解决GameplayKit寻路中区分危险/友好障碍物的问题
你不需要使用GKRule或GKRuleSystem来实现这个需求,核心思路是仅将危险障碍物纳入寻路障碍图,让GameplayKit自动避开它们;友好障碍物因为允许主角接触,不需要加入障碍图(寻路时会忽略其存在)。当所有可行路径都被危险障碍物阻挡时,findPath会返回长度不足2的路径,此时即可判定寻路失败。
具体实现步骤
- 自定义障碍物类型标识
通过扩展GKPolygonObstacle添加关联属性,标记每个障碍物是友好还是危险:
enum ObstacleType { case friendly case dangerous } extension GKPolygonObstacle { private static let typeKey = UnsafeRawPointer(bitPattern: "ObstacleType".hashValue)! var obstacleType: ObstacleType { get { objc_getAssociatedObject(self, Self.typeKey) as? ObstacleType ?? .friendly } set { objc_setAssociatedObject(self, Self.typeKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } }
- 分离存储两类障碍物
创建两个数组分别存储友好和危险障碍物,根据实际场景逻辑设置每个障碍物的类型:
var friendlyObstacles = [GKPolygonObstacle]() var dangerousObstacles = [GKPolygonObstacle]() // 示例:创建危险障碍物(多边形模拟圆形) let pegRadius: Float = 0.15 let numOfPoints = 12 let totalRadians: Float = 2 * .pi let radiansPerPoint: Float = totalRadians / Float(numOfPoints) var polyPoints = [SIMD2<Float>]() let pegPosition = SIMD2(x: 0, y: 0) // 替换为实际位置 for i in 0..<numOfPoints { let angle = radiansPerPoint * Float(i) let x = pegPosition.x + (pegRadius * cos(angle)) let y = pegPosition.y + (pegRadius * sin(angle)) polyPoints.append(SIMD2(x: x, y: y)) } let obstacle = GKPolygonObstacle(points: polyPoints) obstacle.obstacleType = .dangerous // 标记为危险障碍物 dangerousObstacles.append(obstacle) // 如果是友好障碍物,加入friendlyObstacles数组即可,无需加入障碍图
- 仅用危险障碍物构建寻路图
GKObstacleGraph只传入危险障碍物,这样寻路时会自动避开危险区域:
let graph = GKObstacleGraph(obstacles: dangerousObstacles, bufferRadius: 0.05)
- 执行寻路并判断结果
当findPath返回的路径长度小于2时,说明所有可行路径都被危险障碍物阻挡,进入失败分支:
// 获取起点和终点节点 guard let receptors = scnView.scene?.rootNode.childNodes(passingTest: { theNode, stop in return theNode.name == "receptors" }) as? [SCNNodeIdentifiable], receptors.count >= 2 else { return } let receptorIndex: Int = 0 let startPosition = receptors[receptorIndex].presentation.worldPosition let startNode = GKGraphNode2D(point: vector_float2(x: Float(startPosition.x), y: Float(startPosition.y))) let endPosition = receptors[receptorIndex+1].presentation.worldPosition let endNode = GKGraphNode2D(point: vector_float2(x: Float(endPosition.x), y: Float(endPosition.y))) // 连接节点到寻路图 graph.connectUsingObstacles(node: startNode) graph.connectUsingObstacles(node: endNode) // 执行寻路 let path = graph.findPath(from: startNode, to: endNode) if path.count >= 2 { // 找到可行路径(未被危险障碍物完全阻挡) // 处理路径逻辑 } else { // 寻路失败:所有路径被危险障碍物阻挡 // 触发关卡失败逻辑 }
补充:关于GKCircleObstacle的使用
你之前无法使用GKCircleObstacle,可以尝试强制转换为GKPolygonObstacle(因为它是子类):
let circleObstacle = GKCircleObstacle(radius: pegRadius) circleObstacle.position = vector_float2(x: pegPosition.x, y: pegPosition.y) circleObstacle.obstacleType = .dangerous dangerousObstacles.append(circleObstacle as! GKPolygonObstacle)
内容的提问来源于stack exchange,提问作者West1
相关产品推荐
相关产品推荐

