You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 20:30:42