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

SpriteKit随机生成数组敌人 重复调用报SKNode父节点错误

问题原因

触发Attemped to add a SKNode which already has a parent报错的核心逻辑错误非常明确:
你存到EnemyArray里的Enemy1~Enemy6全是提前初始化完成的固定SKNode实例,不是可以反复生成新对象的模板。SpriteKit 有硬性规则:同一个SKNode实例同一时间只能挂载到一个父节点上。第一次生成敌人时你把对应实例加到了场景中,哪怕后续动作执行了removeFromParent(),只要随机逻辑再次抽到同一个实例、且上一次的移除流程还没执行完,再次调用addChild就会直接抛出这个错误。
另外你提前写好的两个随机数计算函数完全没有被调用,现有逻辑根本没实现“随机位置生成敌人”的需求。

修复方案

核心修复思路是每次生成敌人时创建全新的节点实例,从根源避免同一个节点被重复添加的问题,同时补上随机位置生成的逻辑:

  • 敌人数组不要存固定节点实例,改为存敌人的构造方法/配置参数,每次触发生成时调用构造方法得到全新的节点对象
  • 新节点生成后先计算随机出生坐标,再添加到场景节点树
  • 原有的移动、移除动作逻辑可以保留,出屏后自动移除节点不会造成内存泄漏

修复后的可运行代码如下:

// 保留原有随机工具函数,用于计算随机出生坐标
func random() -> CGFloat {
    return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}

func random(min: CGFloat, max: CGFloat) -> CGFloat {
    return random() * (max - min) + min
}

func spawnEnemy() {
    // 数组存储敌人构造方法,而非固定节点实例
    let enemyGenerators: [() -> SKNode] = [
        { Enemy1() }, // 替换为你自己的Enemy1初始化逻辑,比如传入纹理、物理体参数等
        { Enemy2() },
        { Enemy3() },
        { Enemy4() },
        { Enemy5() },
        { Enemy6() }
    ]

    guard let randomGenerator = enemyGenerators.randomElement() else { return }
    let newEnemy = randomGenerator()

    // 补上随机位置逻辑:x轴在屏幕可视范围内随机,y轴从屏幕顶部外生成
    newEnemy.position = CGPoint(
        x: random(min: newEnemy.frame.width/2, max: self.size.width - newEnemy.frame.width/2),
        y: self.size.height + newEnemy.frame.height
    )

    self.addChild(newEnemy)

    let moveEnemy = SKAction.moveTo(y: -800, duration: 4.0)
    let deleteEnemy = SKAction.removeFromParent()
    let enemySequence = SKAction.sequence([moveEnemy, deleteEnemy])
    newEnemy.run(enemySequence)
}

func runEnemy() {
    run(SKAction.repeatForever(SKAction.sequence([
        SKAction.run(spawnEnemy),
        SKAction.wait(forDuration: 2.0)
    ])))
}
补充说明
  • 如果你之前的Enemy1~Enemy6是在场景编辑器里预置、通过拖线绑定的属性,不要直接复用这些和场景生命周期绑定的实例。可以把这些预置节点的纹理、尺寸、物理体配置抽成统一参数,每次生成新节点时复用参数即可。
  • 如果不想写构造闭包,也可以直接把敌人对应的图片资源名存在数组里,每次随机取资源名后用SKSpriteNode(imageNamed: 资源名)生成新节点,实现逻辑更简单。
  • 原代码里的变量名比如EnemyArray、EnemySequence不符合Swift小驼峰命名规范,建议调整为小写开头的变量名,不影响运行但可读性更好。
  • 如果需要避免同一种敌人连续重复出现,可以加一个变量记录上一次生成的敌人类型,随机抽取时排除上一次的类型即可,不影响核心生成逻辑。

内容的提问来源于stack exchange,提问作者dr. mango

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 06:12:21