我正在尝试SCNNode
在场景中移动一个,受限于GKGridGraph
. 沿着 PacMan 的思路思考,但在 3D 中。
我有一个ControlComponent
处理我的 SCNNode 的移动。逻辑应该是这样的......
- 设置
queuedDirection
属性。 - 如果
isMoving
标志为假,则调用 move() 方法 使用 GKGridGraph 评估下一步行动
direction
3.1 如果实体可以使用属性移动到GKGridGraphNode ,nextNode = nodeInDirection(direction)
queuedDirection
3.2 如果实体可以使用属性移动到GKGridGraphNodenextNode = nodeInDirection(queuedDirection)
3.3 如果实体不能移动到任一方向的节点,将
isMoving
标志设置为false并返回。- 创建
moveTo
动作 - 创建一个
runBlock
调用该move()
方法的 - 将
moveTo
和runBlock
动作作为一个序列应用到 SCNNode
我已经粘贴在下面的完整课程中。但我会先解释我遇到的问题。上述逻辑有效,但只是间歇性的。有时动画几乎立即停止工作,有时它会运行长达一分钟。但是在某些时候,由于某种原因,它只是停止工作 -setDirection()
将触发,move()
将触发,SCNNode 将在指定方向上移动一次空间,然后move()
停止调用该方法。
我不是 100% 相信我目前的方法是正确的,所以我很高兴听到是否有更惯用的 SceneKit/GameplayKit 方法来做到这一点。
这是完整的课程,但我认为重要的是setDirection()
andmove()
方法。
import GameplayKit
import SceneKit
enum BRDirection {
case Up, Down, Left, Right, None
}
class ControlComponent: GKComponent {
var level: BRLevel!
var direction: BRDirection = .None
var queuedDirection: BRDirection?
var isMoving: Bool = false
var speed: NSTimeInterval = 0.5
//----------------------------------------------------------------------------------------
init(level: BRLevel) {
self.level = level
super.init()
}
//----------------------------------------------------------------------------------------
func setDirection( nextDirection: BRDirection) {
self.queuedDirection = nextDirection;
if !self.isMoving {
self.move()
}
}
//----------------------------------------------------------------------------------------
func move() {
let spriteNode: SCNNode = (self.entity?.componentForClass(NodeComponent.self)!.node)!
var nextNode = nodeInDirection( direction )
if let _ = self.queuedDirection {
let attemptedNextNode = nodeInDirection(self.queuedDirection! )
if let _ = attemptedNextNode {
nextNode = attemptedNextNode
self.direction = self.queuedDirection!
self.queuedDirection = nil
}
}
// Bail if we don't have a valid next node
guard let _ = nextNode else {
self.direction = .None
self.queuedDirection = nil
self.isMoving = false
return
}
// Set flag
self.isMoving = true;
// convert graphNode coordinates to Scene coordinates
let xPos: Float = Float(nextNode!.gridPosition.x) + 0.5
let zPos: Float = Float(nextNode!.gridPosition.y) + 0.5
let nextPosition: SCNVector3 = SCNVector3Make(xPos, 0, zPos)
// Configure actions
let moveTo = SCNAction.moveTo(nextPosition, duration: speed)
let repeatAction = SCNAction.runBlock( { _ in self.move() } )
let sequence = SCNAction.sequence([ moveTo, repeatAction ])
spriteNode.runAction( sequence )
}
//----------------------------------------------------------------------------------------
func getCurrentGridGraphNode() -> GKGridGraphNode {
// Acces the node in the scene and gett he grid positions
let spriteNode: SCNNode = (self.entity?.componentForClass(NodeComponent.self)!.node)!
// Account for visual offset
let currentGridPosition: vector_int2 = vector_int2(
Int32( floor(spriteNode.position.x) ),
Int32( floor(spriteNode.position.z) )
)
// return unwrapped node
return level.gridGraph.nodeAtGridPosition(currentGridPosition)!
}
//----------------------------------------------------------------------------------------
func nodeInDirection( nextDirection:BRDirection? ) -> GKGridGraphNode? {
guard let _ = nextDirection else { return nil }
let currentGridGraphNode = self.getCurrentGridGraphNode()
return self.nodeInDirection(nextDirection!, fromNode: currentGridGraphNode)
}
//----------------------------------------------------------------------------------------
func nodeInDirection( nextDirection:BRDirection?, fromNode node:GKGridGraphNode ) -> GKGridGraphNode? {
guard let _ = nextDirection else { return nil }
var nextPosition: vector_int2?
switch (nextDirection!) {
case .Left:
nextPosition = vector_int2(node.gridPosition.x + 1, node.gridPosition.y)
break
case .Right:
nextPosition = vector_int2(node.gridPosition.x - 1, node.gridPosition.y)
break
case .Down:
nextPosition = vector_int2(node.gridPosition.x, node.gridPosition.y - 1)
break
case .Up:
nextPosition = vector_int2(node.gridPosition.x, node.gridPosition.y + 1)
break;
case .None:
return nil
}
return level.gridGraph.nodeAtGridPosition(nextPosition!)
}
}