如何获取SceneKit场景中节点的实时当前位置
问题场景
- 基于SceneKit开发游戏时,需要获取指定节点的
SCNVector3坐标,用于判定每回合玩家与场景节点的交互逻辑。 - 逻辑触发时机设置在玩家完成移动动作之后,但出现异常:即使画面上可推动方块的实际位置已经发生变化,存储在
mapList中的对应坐标始终没有同步更新。 - 排查时的疑问:怀疑当前逻辑仅遍历了从
gameScene.scn文件加载的初始静态节点,没有获取到游戏运行时的实时节点状态,且在苹果官方文档中没有找到遍历运行时实时节点的相关说明。
现有实现代码
var mapList: [SCNNode] = [] func mapLocationSave() { let pushableNode = self.gameScene.rootNode.childNode(withName: "Pushables", recursively: true) let wallNodes = gameScene.rootNode.childNode(withName: "Boxes", recursively: true) let coinNodes = gameScene.rootNode.childNode(withName: "Coins", recursively: true) for pushNode in pushableNode!.childNodes { mapList.append(pushNode) } for wallNode in wallNodes!.childNodes { mapList.append(wallNode) } for coinNode in coinNodes!.childNodes { mapList.append(coinNode) } for node in mapList { print(node.name!, node.worldPosition) } }
问题根因与修正方法
- 先明确基础常识:SCNNode是引用类型,你存到数组里的不是某一时刻的坐标快照,只是节点本身的引用。只要节点还存活在场景树中,读取它的
worldPosition理论上能拿到实时状态,你遇到的坐标不更新问题,基本都是三个场景开发中常见的隐形坑:- 函数缺少前置清空逻辑:当前代码每次调用
mapLocationSave时,都会把三个分组下的所有子节点重复append到mapList中,多次调用后数组会累积大量重复的节点引用,遍历打印时多轮输出混杂,很容易误判为坐标没有更新。只需要在函数最开头添加一行mapList.removeAll(),就能解决重复累加的问题。 - 坐标读取时机不匹配渲染流程:SceneKit的世界坐标是基于节点的渲染变换矩阵计算的,如果在移动动作刚启动、渲染帧还没完成矩阵更新的瞬间就读取坐标,拿到的一定是上一帧的旧值。如果用
SCNAction实现移动动画,需要把坐标读取逻辑放到动作完成的回调中,不要在动作触发的第一时间执行读取操作。 - 动画过程中读错了属性:如果需要在动画播放过程中获取节点的实时显示位置,不要直接读
node.worldPosition——这个属性对应节点模型层的位置,只会在动画完全结束后才同步最终位置,动画播放过程中画面实际显示的位置,存在节点的presentation属性上,读取node.presentation.worldPosition才能拿到和画面显示完全一致的实时坐标。
- 函数缺少前置清空逻辑:当前代码每次调用
- 关于“仅获取到初始scn文件静态节点”的疑虑:只要没有主动将节点从场景树中移除、替换为新的实例,通过
childNode(withName:recursively:)获取到的节点,永远是场景树中当前存在的活跃实例,和节点是否初始从scn文件加载没有关系,不存在“拿不到运行时节点”的情况。这部分逻辑是场景树的基础能力,苹果文档没有单独说明也是因为无需额外解释。
内容的提问来源于stack exchange,提问作者ViktorEvil
相关产品推荐
相关产品推荐

