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

如何从游戏引擎类向ViewController传递数据?分数传递求助

解决游戏引擎分数传递到ViewController的几种方案

Hey there! 作为刚入门的开发者,遇到跨类数据传递的问题真的太常见了,别慌,咱们一步步来搞定它~

你现在的核心需求是把nextMove()方法里的scores值传递给ViewController,用来更新Label。下面是几种iOS开发里常用且靠谱的实现方式:

方案一:用闭包(Closure)传递数据

这种方式简单直观,适合轻量的回调场景:

  1. 首先在你的游戏引擎类里,定义一个闭包属性,用来传递分数:
class YourGameEngineClass {
    // 定义闭包,参数是分数,无返回值
    var onScoreUpdated: ((Int) -> Void)?
    
    // 你的其他属性和方法...
    private func nextMove() {
        current = .random(config)
        if (well.hasCollision(current)) {
            stopTimer()
            scene.showGameOver(scores)
            // 触发闭包,把分数传出去
            onScoreUpdated?(scores)
        } else {
            scene.show(current)
        }
    }
}
  1. 然后在ViewController里,初始化游戏引擎的时候绑定这个闭包:
class ViewController: UIViewController {
    @IBOutlet weak var scoreLabel: UILabel!
    let gameEngine = YourGameEngineClass()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 绑定闭包,接收分数并更新UI
        gameEngine.onScoreUpdated = { [weak self] score in
            // UI操作必须在主线程执行!
            DispatchQueue.main.async {
                self?.scoreLabel.text = "最终分数:\(score)"
            }
        }
    }
}

👉 注意用[weak self]避免循环引用,防止内存泄漏。

方案二:用代理(Delegate)模式

如果后续游戏引擎和ViewController还有更多交互(比如游戏暂停、重启的回调),代理模式会更清晰:

  1. 先定义一个代理协议:
// 定义代理协议,用AnyObject确保只能被类遵守
protocol GameEngineDelegate: AnyObject {
    func gameEngineDidEndGame(with score: Int)
}
  1. 在游戏引擎类里添加代理属性:
class YourGameEngineClass {
    weak var delegate: GameEngineDelegate?
    
    private func nextMove() {
        current = .random(config)
        if (well.hasCollision(current)) {
            stopTimer()
            scene.showGameOver(scores)
            // 调用代理方法传递分数
            delegate?.gameEngineDidEndGame(with: scores)
        } else {
            scene.show(current)
        }
    }
}
  1. 让ViewController遵守这个协议,并设置代理:
class ViewController: UIViewController, GameEngineDelegate {
    @IBOutlet weak var scoreLabel: UILabel!
    let gameEngine = YourGameEngineClass()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        gameEngine.delegate = self
    }
    
    // 实现代理方法
    func gameEngineDidEndGame(with score: Int) {
        DispatchQueue.main.async {
            self.scoreLabel.text = "最终分数:\(score)"
        }
    }
}

👉 代理属性一定要用weak修饰,同样是为了避免循环引用。

方案三:用单例(Singleton)存储全局分数

如果你的分数需要在多个类里访问,单例会是一个简单的选择,但要注意别滥用(单例生命周期长,可能占用不必要的内存):

  1. 把游戏引擎类改成单例,并添加存储分数的属性:
class YourGameEngineClass {
    // 单例实例
    static let shared = YourGameEngineClass()
    // 私有化初始化方法,防止外部创建多个实例
    private init() {}
    
    // 用来存储全局可访问的分数
    var currentScore: Int = 0
    
    private func nextMove() {
        current = .random(config)
        if (well.hasCollision(current)) {
            stopTimer()
            currentScore = scores // 把scores赋值给全局变量
            scene.showGameOver(scores)
        } else {
            scene.show(current)
        }
    }
}
  1. 在ViewController里直接访问这个单例的分数:
// 比如在游戏结束后获取分数
let finalScore = YourGameEngineClass.shared.currentScore
scoreLabel.text = "最终分数:\(finalScore)"

小提示

  • 所有UI更新操作都必须放在主线程,所以要用DispatchQueue.main.async包裹Label的赋值代码;
  • 优先推荐闭包或代理模式,单例适合简单场景,复杂项目里尽量少用,避免代码耦合度太高。

内容的提问来源于stack exchange,提问作者Bilal Şimşek

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 10:09:24