GKObstacleGraph не находит путь при появлении препятствия

Я работаю над проверкой концепции поиска путей Gameplaykit и не могу заставить GKObstacleGraph правильно находить пути.

В следующем фрагменте кода (он должен работать на игровой площадке Xcode 7.2) path2 всегда является пустым массивом, если при создании графа имеется препятствие. Если я создаю объект obGraph с пустым массивом препятствий, findPathFromNode возвращает правильный путь.

Создаваемое препятствие должно быть простым многоугольником в форме буквы U с конечной точкой внутри буквы U.

import UIKit

import GameplayKit

let pts = [vector_float2(2,2),
    vector_float2(3,2),
    vector_float2(3,6),
    vector_float2(7,6),
    vector_float2(7,2),
    vector_float2(8,3),
    vector_float2(8,7),
    vector_float2(2,7),
    vector_float2(2,2)]
let obstacle1 = GKPolygonObstacle(points: UnsafeMutablePointer(pts) ,
    count: pts.count)

let obGraph = GKObstacleGraph(obstacles: [obstacle1], bufferRadius: 0)

let startPt = GKGraphNode2D(point: vector_float2(5,9))
let endPt = GKGraphNode2D(point: vector_float2(5,5))
let pt3 = GKGraphNode2D(point: vector_float2(0,0))
let pt4 = GKGraphNode2D(point: vector_float2(0,9))
let pt5 = GKGraphNode2D(point: vector_float2(5,0))
let pt6 = GKGraphNode2D(point: vector_float2(10,0))

obGraph.connectNodeUsingObstacles(startPt)
obGraph.connectNodeUsingObstacles(endPt)
obGraph.connectNodeUsingObstacles(pt3)
obGraph.connectNodeUsingObstacles(pt4)
obGraph.connectNodeUsingObstacles(pt5)
obGraph.connectNodeUsingObstacles(pt6)
startPt.connectedNodes
endPt.connectedNodes
pt3.connectedNodes

let path2 = obGraph.findPathFromNode(startPt, toNode: endPt)
print(path2)

person Jack Cox    schedule 20.01.2016    source источник
comment
Лучше всего думать о GameplayKit как о жесте намерения, а не как о готовом готовом продукте.   -  person Confused    schedule 20.01.2016
comment
Ну, это не очень обнадеживает @Confused   -  person Jack Cox    schedule 21.01.2016
comment
Я также должен отметить, что почти никто не использует GameplayKit. Так что ваши шансы получить компетентный ответ... невелики.   -  person Confused    schedule 21.01.2016
comment
Не уверен, что вы когда-либо находили решение, но у меня были аналогичные трудности с моим приложением здесь: stackoverflow.com/questions/35705597/   -  person Will Von Ullrich    schedule 01.03.2016


Ответы (2)


У меня была та же проблема, что и у Джека. Я начал с примера кода Уилла и перевел его на Swift 5.0 в Xcode 10.3. Я добавил его в шаблон проекта Xcode Game. У меня все еще был тот же результат: пустой массив из findPath(from:to:).

Поиграв с кодом, я понял, что все, что связано с физикой, повлияет на траекторию. Единственный способ показать код, который будет работать для всех, — включить создание SKScene и всех SKNode экземпляров. Обратите внимание, что я установил gravity в 0 в SKPhysicsWorld и не добавляю SKPhysicsBody ни к чему.

Запустите это на игровой площадке. Вы активируете анимацию, нажав в любом месте сцены.

import PlaygroundSupport
import SpriteKit
import GameKit

class GameScene: SKScene {

    let nodeToMove:SKShapeNode = {
        let n = SKShapeNode(circleOfRadius: 10)
        n.lineWidth = 2
        n.strokeColor = UIColor.orange
        n.position = CGPoint(x: -200, y: 150)
        return n
    }()

    override func sceneDidLoad() {
        addChild(nodeToMove)

        let nodeToFind = SKShapeNode(circleOfRadius: 5)
        nodeToFind.lineWidth = 2
        nodeToFind.strokeColor = UIColor.red
        addChild(nodeToFind)
        nodeToFind.position = CGPoint(x: 200, y: -150)

        let nodeToAvoid = SKShapeNode(rectOf: CGSize(width: 100, height: 100))
        nodeToAvoid.lineWidth = 4
        nodeToAvoid.strokeColor = UIColor.blue
        addChild(nodeToAvoid)
        nodeToAvoid.position = CGPoint.zero

        let polygonObstacles = SKNode.obstacles(fromNodeBounds: [nodeToAvoid])
        let graph = GKObstacleGraph(obstacles: polygonObstacles, bufferRadius: 10.0)
        let end = GKGraphNode2D(point: vector2(Float(nodeToMove.position.x), Float(nodeToMove.position.y)))
        let start = GKGraphNode2D(point: vector2(Float(nodeToFind.position.x), Float(nodeToFind.position.y)))
        graph.connectUsingObstacles(node: end)
        graph.connectUsingObstacles(node: start)

        graphNodes = graph.findPath(from: end, to: start) as!  [GKGraphNode2D]
        print("graphNodes = \(graphNodes)")
    }

    var graphNodes = [GKGraphNode2D]()

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        touches.first.flatMap {_ in
            let newActions: [SKAction] = graphNodes.map { n in
                return SKAction.move(to: CGPoint(x: CGFloat(n.position.x), y: CGFloat(n.position.y)), duration: 2)
            }
            nodeToMove.run(SKAction.sequence(newActions))
        }
    }
}

let sceneView = SKView(frame: CGRect(x:0 , y:0, width: 640, height: 480))
let scene = GameScene(size: CGSize(width: 640, height: 480))
scene.scaleMode = .aspectFill
scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
scene.backgroundColor = UIColor.purple
scene.physicsWorld.gravity = CGVector(dx: 0.0, dy: 0.0)
sceneView.presentScene(scene)

PlaygroundSupport.PlaygroundPage.current.liveView = sceneView

Вывод в консоли такой:

graphNodes = [GKGraphNode2D: {-200.00, 150.00}, GKGraphNode2D: {62.85, 62.85}, GKGraphNode2D: {200.00, -150.00}]

Начальное состояние:

Сцена перед началом анимации

Конечное состояние:

Сцена после завершения анимации

Предупреждение. Я не знаю, почему между нажатием пользователя и началом анимации проходит целая секунда. Настройка производительности — отдельная тема.

person Jeff    schedule 18.08.2019

(заранее извините за obj-c не быстрый)

У меня сложилось впечатление, что добавление каждой точки вершины в качестве соединения не обязательно, достаточно будет просто сообщить GKObstacleGraph о GKPolygonObstacle, чтобы избежать сгенерированной формы полигиона. Я использовал следующее и получил 3 узла для создания пути вокруг моего препятствия (с буфером 10,0f), показанного здесь:

- (void)findPathWithNode:(SKNode *)nodeToFindPath {

    NSMutableArray *obstaclesToAvoid = [NSMutableArray array];

    for (SKNode *objectInScene in chapterScene.children) {
        if ([objectInScene.name isEqualToString:@"innerMapBoundary"]) {
            [obstaclesToAvoid addObject:objectInScene];
        }
    }


    /* FYI: The objectInScene is just a black SKSpriteNode with a 
       square physics body 100 x 100 rotated at 45°
    */

    NSArray *obstacles = [SKNode obstaclesFromNodePhysicsBodies:[NSArray arrayWithArray:obstaclesToAvoid]];
    GKObstacleGraph *graph = [GKObstacleGraph graphWithObstacles:obstacles bufferRadius:10.0f];


    GKGraphNode2D *end = [GKGraphNode2D nodeWithPoint:vector2((float)character.position.x, (float)character.position.y)];
    GKGraphNode2D *start = [GKGraphNode2D nodeWithPoint:vector2((float)nodeToFindPath.position.x, (float)nodeToFindPath.position.y)];


    [graph connectNodeUsingObstacles:end];
    [graph connectNodeUsingObstacles:start];


    NSArray *pathPointsFound = [graph findPathFromNode:enemy toNode:target];
    NSLog(@"Path: %@", pathPointsFound);


    GKPath *pathFound;


    // Make sure that there were at least 2 points found before creating the path
    if (pathPointsFound.count > 1) {
        for (GKGraphNode2D *nodeFound in pathPointsFound) {

            // This is just to create a visual for the points found

            vector_float2 v = (vector_float2){(float)nodeFound.position.x, (float)nodeFound.position.y};
            CGPoint p = CGPointMake(v.x, v.y);
            SKShapeNode *shapetoadd = [SKShapeNode shapeNodeWithCircleOfRadius:4];
            shapetoadd.name = @"shapeadded";
            shapetoadd.fillColor = [UIColor redColor];
            shapetoadd.position = p;
            [chapterScene addChild:shapetoadd];
        }

        pathFound = [GKPath pathWithGraphNodes:pathPointsFound radius:10.0];
    }

}

Надеюсь, это укажет вам правильное направление!

person Will Von Ullrich    schedule 29.02.2016
comment
Если путь не найден, как я могу найти путь к ближайшему допустимому местоположению? - person ColdSteel; 17.09.2016
comment
Не совсем уверен... кажется, что если gamekit не находит путь, это не очень хорошо. Я думаю, вы могли бы попробовать повторить свое текущее местоположение на определенном расстоянии d, пока не найдете правильный путь? Конечно, это было бы чрезвычайно дорого с точки зрения времени вычислений/памяти, но не уверен, что Apple предлагает способ сделать это. - person Will Von Ullrich; 20.09.2016
comment
Да, Apple не предоставила достаточно инструментов для ее решения... Я задал его как отдельный вопрос на SO stackoverflow.com/questions/39546759/ С небольшим изменением математики из этого ответа мне удалось получить путь - person ColdSteel; 20.09.2016
comment
Рад, что смог помочь! - person Will Von Ullrich; 21.09.2016