Swift中添加至数组的GKEntity实例为何被销毁?
问题:GameScene中sceneDidLoad添加的GKEntity消失,update方法中entities数组为空
在GameScene的sceneDidLoad()方法中,我将一个GKEntity添加到entities数组,但在update()方法中,entities数组为空。按预期,entities数组的引用应该保留实例,为何实例会在sceneDidLoad()结束时被销毁?
将sceneDidLoad()中的代码移到didMove()后一切正常,查看Xcode生成的GameViewController模板代码发现:在sceneDidLoad()向scene.entities添加实体后,进入if let scene = GKScene(fileNamed: "GameScene")语句时,scene.entities数组为空,是否是该语句创建了临时GKScene?
相关代码
GameScene代码
import GameplayKit class GraphicsComponent: GKComponent { override func update(deltaTime: TimeInterval) { // ... } } class GameScene: SKScene { var entities = [GKEntity]() override func sceneDidLoad() { let entity = GKEntity() entity.addComponent(GraphicsComponent()) entities.append(entity) } override func update(_ currentTime: TimeInterval) { // ... for entity in entities { entity.update(deltaTime: dt) } } }
GameViewController代码
class GameViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // 该if let会调用sceneDidLoad(),向scene.entities添加新entity if let scene = GKScene(fileNamed: "GameScene") { // scene.entities.count = 0 if let sceneNode = scene.rootNode as! GameScene? { sceneNode.entities = scene.entities sceneNode.graphs = scene.graphs sceneNode.scaleMode = .aspectFill if let view = self.view as! SKView? { view.presentScene(sceneNode) // 调用didMove() view.ignoresSiblingOrder = true view.showsFPS = true view.showsNodeCount = true } } } } }
原因分析
- 临时实例被丢弃+entities被覆盖:
GKScene(fileNamed:)初始化时,会创建一个临时的GameScene实例来触发sceneDidLoad(),你在这个临时实例的entities数组中添加了实体,但这个临时实例后续会被丢弃。 - GKScene与SKScene的entities是独立的:代码中最终展示的是
scene.rootNode对应的GameScene实例,而后续执行的sceneNode.entities = scene.entities,是把GKScene自身的空entities数组赋值给了最终场景的entities,直接覆盖了你在临时实例中添加的内容,导致最终场景的entities数组为空。 - didMove的调用时机更可靠:
didMove(to view:)是在场景被SKView正式展示时调用,此时操作的是最终要显示的场景实例,添加的实体不会被后续代码覆盖,所以能正常保留。
解决方法
- 推荐方案:将实体创建和添加的代码从
sceneDidLoad()移到didMove(to view:)方法中,直接操作最终展示的场景实例,避免临时实例和赋值覆盖的问题。 - 备选方案(不推荐):修改GameViewController中的代码,删除
sceneNode.entities = scene.entities这一行,或者改为合并数组:sceneNode.entities.append(contentsOf: scene.entities),这样不会覆盖你在sceneDidLoad()中添加的实体,但需要注意GKScene和SKScene的entities边界,容易引发其他混淆问题。
内容的提问来源于stack exchange,提问作者Peter Smyth
相关产品推荐
相关产品推荐

