GKAgent 不会更新在 ARKit/SCNScene/GamplayKit 游戏中注册为实体的 SCNNode 的位置

GKAgent won't update position of SCNNodes registered as entities on teams in an ARKit/SCNScene/GamplayKit game

我正在使用 GKAgent 移动注册为实体的 SCNNode,分配了一个团队组件和一个移动组件。正在触发 agentWillUpdate 和 agentDidUpdate 方法,但添加到场景的实体的位置拒绝更新它们的位置。有没有办法让 GKAgent 根据以下移动行为更新位置?

亲切的问候,

蒙特勒

MoveComponent.swift

import SceneKit
import GameplayKit

extension float3 {
    var length: Float {
    return sqrt(x*x + y*y + z*z)
  }
}

class MoveComponent: GKAgent3D, GKAgentDelegate {
     let entityManager: EntityManager!

init(maxSpeed: Float, maxAcceleration: Float, radius: Float,       entityManager: EntityManager) {
    self.entityManager = entityManager
    super.init()
    self.delegate = self
    self.maxSpeed = maxSpeed
    self.maxAcceleration = maxAcceleration
    self.radius = radius
    print(self.mass)
    self.mass = 0.01
}


func agentWillUpdate(_ agent: GKAgent) {

   guard let spriteComponent = entity?.component(ofType:    SpriteComponent.self) else
    {
        return
    }


    let pos = spriteComponent.node.presentation.position
    print("Agent Will Update \(spriteComponent.node.name) from  \(pos) to \(self.position)")

    self.position = float3(pos)

}

func agentDidUpdate(_ agent: GKAgent) {
    guard let spriteComponent = entity?.component(ofType: SpriteComponent.self) else {
        return

    }
    spriteComponent.node.position = vec3(self.position)

    print("Agent DID Update to: \(spriteComponent.node.name) to \(position)")


    let xVelocity = self.velocity.x
    let zVelocity = self.velocity.z

    let angle = -Float(atan2(zVelocity, xVelocity)) + Float.pi/2

    spriteComponent.node.rotation = SCNVector4(0,1,0, angle)


}
func closestMoveComponent(_ team: Team) -> GKAgent3D? {
    let moveComponents = entityManager.moveComponentsForTeam(team)
    var closestMoveComponent: GKAgent3D? = nil
    var closestDistance: Float = MAXFLOAT

    for component in moveComponents {
        let distance = (self.position - component.position).length
        if distance < closestDistance {
            closestDistance = distance
            closestMoveComponent = component
        }
    }
    return closestMoveComponent
}


override func update(deltaTime seconds: TimeInterval) {


    // Determine team
    guard let entity = entity,
        let teamComponent = entity.component(ofType: TeamComponent.self) else {
            return
    }

    // Find team castle
    guard let team = entityManager.castleForTeam(teamComponent.team),
        let teamCastleComponent = team.component(ofType: HeroComponent.self),
        let teamMoveComponent = team.component(ofType: MoveComponent.self) else {
            return
    }

    var targetMoveComponent: GKAgent3D

    if teamCastleComponent.attacking {

        // Find closest enemy
        guard let enemyMoveComponent = closestMoveComponent( teamComponent.team.oppositeTeam()) else {
            print("No Opposite enemy components for Agent!!!")
            return
        }
        targetMoveComponent = enemyMoveComponent

        // Override target for ranged attackers
        if let fireComponent = entity.component(ofType: ParticleComponent.self) {
            let newTarget = GKAgent3D()
            let node1Pos = SCNVector3ToGLKVector3(SCNVector3(targetMoveComponent.position))
            let node2Pos = SCNVector3ToGLKVector3(SCNVector3(position))
            let distance = GLKVector3Distance(node1Pos, node2Pos)

            newTarget.position = float3(x: targetMoveComponent.position.x, y: targetMoveComponent.position.y, z: targetMoveComponent.position.z * Float(fireComponent.range))
            //                newTarget.position = float3(targetMoveComponent.position + direction * fireComponent.range)
            targetMoveComponent = newTarget
        }
    } else {

        targetMoveComponent = teamMoveComponent

    }

    // Find allies
    let alliedMoveComponents = entityManager.moveComponentsForTeam(teamComponent.team)

    // Reset behavior
    //        print("Reset move behavior")
    self.behavior = MoveBehavior(targetSpeed: maxSpeed, seek: targetMoveComponent, avoid: alliedMoveComponents)
    print("Agent moving to \(targetMoveComponent), avoiding \(alliedMoveComponents)")
    super.update(deltaTime: seconds)

}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}


 }

MoveBehavior.swift

import GameplayKit
import SceneKit

class MoveBehavior: GKBehavior {

  init(targetSpeed: Float, seek: GKAgent3D, avoid: [GKAgent3D]) {
    super.init()

if targetSpeed > 0 {
    print("Running Move Behavior")
  setWeight(0.1, for: GKGoal(toReachTargetSpeed: targetSpeed))
  setWeight(0.5, for: GKGoal(toSeekAgent: seek))
  setWeight(1.0, for: GKGoal(toAvoid: avoid, maxPredictionTime: 1.0))
    }
  }
}

EntityManager.swift

import Foundation
import Foundation
import ARKit
import SceneKit
import GameplayKit

class EntityManager {
    var toRemove = Set<GKEntity>()
let costQuirk = 20
lazy var componentSystems: [GKComponentSystem] = {
    let moveSystem = GKComponentSystem(componentClass: MoveComponent.self)
    let meleeSystem = GKComponentSystem(componentClass: MeleeComponent.self)
    let firingSystem = GKComponentSystem(componentClass: ParticleComponent.self)
    let castleSystem = GKComponentSystem(componentClass: HeroComponent.self)
    let heroSystem = GKComponentSystem(componentClass: HeroComponent.self)
    let aiSystem = GKComponentSystem(componentClass: AiComponent.self)
    let nodeComponent = GKComponentSystem(componentClass: NodeComponent.self)
    return [moveSystem, meleeSystem, firingSystem, castleSystem, aiSystem,heroSystem,nodeComponent]
}()

// 1
var entities: Set<GKEntity>

let scene: SCNScene

// 2
init(scene: SCNScene) {
    self.scene = scene
    self.entities = Set<GKEntity>()
}

// 3
func add(_ entity: GKEntity) {

    if let spriteNode = entity.component(ofType: SpriteComponent.self)?.node {
        scene.rootNode.addChildNode(spriteNode)
        let speed = Int.random(in: 7 ... 10)
         let smoke = SCNParticleSystem(named: "art.scnassets/Models/spawnSmoke.scnp", inDirectory: nil)
        let smokeNode = SCNNode()
        spriteNode.addChildNode(smokeNode)
        print("Monster Added")
    }
    for componentSystem in componentSystems {
        componentSystem.addComponent(foundIn: entity)
    }
      entities.insert(entity)
}

// 4
func remove(_ entity: GKEntity) {
    if let spriteNode = entity.component(ofType: SpriteComponent.self)?.node {
        let confetti = SCNParticleSystem(named: "Media.scnassets/Fire.scnp", inDirectory: nil)
        confetti?.loops = false
        confetti?.particleLifeSpan = 0.05
        confetti?.particleSize -= 1
        confetti?.particleIntensity -= 0.5
        print("Making Explosion")
        if let geometry = spriteNode.geometry {
        confetti?.emitterShape = geometry
        }
        spriteNode.addParticleSystem(confetti!)
        print("Exploding Node!")
        DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(3))  {
        spriteNode.removeFromParentNode()
        spriteNode.removeAllActions()
        self.entities.remove(entity)
        self.toRemove.insert(entity)

    }

    }
        entities.remove(entity)
}
func update(_ deltaTime: CFTimeInterval) {
    // 1
    for system in componentSystems {
        system.update(deltaTime: deltaTime)
    }

    // 2
    for currentRemove in toRemove {
        for componentSystem in componentSystems {
            componentSystem.removeComponent(foundIn: currentRemove)
        }
    }
    toRemove.removeAll()

}
func castle(for team: Team) -> GKEntity? {
    for entity in self.entities {
        if let teamComponent = entity.component(ofType: TeamComponent.self),
            let _ = entity.component(ofType: HeroComponent.self) {
            if teamComponent.team == team {
                return entity
            }
        }
    }
    return nil
}

func spawnQuirk(team: Team , positon: SCNVector3) {
    // 1
    guard let teamEntity = castle(for: team) else {return}


    // 2
    if  let teamCastleComponent = teamEntity.component(ofType: HeroComponent.self) {
    if teamCastleComponent.coins < costQuirk , team == .team1{
        return
    }
                teamCastleComponent.coins -= costQuirk
    }



    // 3
    guard let floor = GAMEFLOOR as? SCNNode else {return}
    let monster = Quirk(team: team, entityManager: self, floor:floor)
    if let spriteComponent = monster.component(ofType: SpriteComponent.self) {
        let randomX = Float.random(in: -10.0 ... -0.0)
        let randomZ = Float.random(in: -10.0 ... -1.0)
        spriteComponent.node.position = positon
        let teamRing = SCNParticleSystem(named: "Media.scnassets/teamRing.scnp", inDirectory: nil)

        switch team {
        case .team1 :
            teamRing?.particleColor = .blue
        case .team2:
            teamRing?.particleColor = .red


        }
        spriteComponent.node.addParticleSystem(teamRing!)
    }


    add(monster)

}
func spawnZap(_ team: Team, positon: SCNVector3) {
    guard let teamEntity = castle(for: team) else {return}

    if  let teamCastleComponent = teamEntity.component(ofType: HeroComponent.self) {
        if teamCastleComponent.coins < costZap , team == .team1{
            return
        }

    teamCastleComponent.coins -= costZap

    let monster = Zap(team: team, entityManager: self)
if let spriteComponent = monster.component(ofType: SpriteComponent.self) {
    let randomX = Float.random(in: -10.0 ... -0.0)
    let randomZ = Float.random(in: -10.0 ... -1.0)
    spriteComponent.node.position = positon

    }
        add(monster)
}
}

func spawnMunch(_ team: Team, positon: SCNVector3) {
    guard let teamEntity = castleForTeam(team),
        let teamCastleComponent = teamEntity.component(ofType: HeroComponent.self),
        let teamSpriteComponent = teamEntity.component(ofType: SpriteComponent.self) else {
            return
    }

    if teamCastleComponent.coins < costMunch {
        return
    }

    teamCastleComponent.coins -= costMunch
    guard let floor = GAMEFLOOR as? SCNNode else {return}

    let monster = Munch(team: team, entityManager: self, floor:floor)
          if let spriteComponent = monster.component(ofType: SpriteComponent.self) {
    let randomX = Float.random(in: -10.0 ... -0.0)
    let randomZ = Float.random(in: -10.0 ... -1.0)
    spriteComponent.node.position = positon

}
        add(monster)
}
func entitiesForTeam(_ team: Team) -> [GKEntity] {

    return entities.compactMap{ entity in
        if let teamComponent = entity.component(ofType: TeamComponent.self) {
            if teamComponent.team == team {
                return entity
            }
        }
        return nil
    }

}

func moveComponentsForTeam(_ team: Team) -> [MoveComponent] {
    let entities = entitiesForTeam(team)
    var moveComponents = [MoveComponent]()
    for entity in entities {
        if let moveComponent = entity.component(ofType: MoveComponent.self) {
            moveComponents.append(moveComponent)
        }
    }
    return moveComponents
}

func castleForTeam(_ team: Team) -> GKEntity? {
    for entity in entities {
        if let teamComponent = entity.component(ofType: TeamComponent.self),
            let _ = entity.component(ofType: HeroComponent.self) {
            if teamComponent.team == team {
                return entity
            }
        }
    }
    return nil
}
func entities(for team: Team) -> [GKEntity] {
    return entities.compactMap{ entity in
        if let teamComponent = entity.component(ofType: TeamComponent.self) {
            if teamComponent.team == team {
                return entity
            }
        }
        return nil
    }
}

func moveComponents(for team: Team) -> [MoveComponent] {
    let entitiesToMove = entities(for: team)
    var moveComponents = [MoveComponent]()
    for entity in entitiesToMove {
        if let moveComponent = entity.component(ofType:    MoveComponent.self) {
            moveComponents.append(moveComponent)
        }
    }
    return moveComponents
}
}

TL;DR

我宁愿使用 GKSCNNodeComponent 作为代理的委托,它会自动处理相互位置更新。

如果一个组件被添加到组件系统,更新实体将不会影响它。

也许更新组件系统的顺序很重要。