【发布时间】:2015-12-31 17:39:30
【问题描述】:
我正在尝试在场景中移动 SCNNode,并限制为 GKGridGraph。沿用 PacMan 的思路,但以 3D 形式思考。
我有一个ControlComponent 来处理我的 SCNNode 的移动。逻辑应该是这样的......
- 设置
queuedDirection属性。 - 如果
isMoving标志为假,调用move()方法 -
使用 GKGridGraph 评估下一步行动
3.1 如果实体可以使用
direction属性移动到GKGridGraphNode,nextNode = nodeInDirection(direction)3.2 如果实体可以使用
queuedDirection属性nextNode = nodeInDirection(queuedDirection)移动到GKGridGraphNode3.3 如果实体无法从任一方向移动到节点,请将
isMoving标志设置为false 并返回。 - 创建
moveTo操作 - 创建一个调用
move()方法的runBlock - 将
moveTo和runBlock操作作为序列应用到SCNNode
我已经粘贴了下面的完整课程。但我会先解释我遇到的问题。上述逻辑有效,但只是间歇性的。有时动画几乎立即停止工作,有时它会运行长达一分钟。但在某些时候,由于某种原因,它会停止工作 - setDirection() 将触发,move() 将触发,SCNNode 将在指定方向移动一次空间,然后 move() 方法将停止调用。
我不是 100% 相信我目前的方法是正确的,所以我很高兴听到是否有更惯用的 SceneKit/GameplayKit 方法来做到这一点。
这是完整的课程,但我认为重要的是 setDirection() 和 move() 方法。
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!)
}
}
【问题讨论】:
标签: swift scenekit gameplay-kit