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

SwiftUI中如何让随机关卡视图实例固定且可复现

问题描述

我正在开发一款游戏,主视图可启动不同关卡,所有关卡复用同一个视图。该视图初始化时会生成随机内容,但要求每个关卡的随机内容在整个游戏过程中保持不变:每个关卡仅生成一次,通过初始化参数区分,即便关卡内子视图刷新,关卡本身也不会重新生成。目前遇到两个问题:

  • 不设置随机种子时,部分场景下关卡会在游戏中途更新;
  • 设置种子后,所有子视图又会使用相同的种子初始化。

请问如何实现:每个关卡视图实例仅创建一次,关闭重开也保持不变,同时具备可复现的随机性(通过种子初始化)?


解决方案

核心思路

将关卡的随机生成逻辑从视图层剥离到全局数据缓存层,通过单例缓存存储已生成的关卡数据,确保同一关卡(对应唯一种子)的随机内容只生成一次;同时为每个关卡分配唯一种子,保证不同关卡的随机内容独立且可复现。

具体实现步骤

1. 新增全局关卡数据缓存类

创建单例缓存,存储已生成的关卡数据,避免重复生成:

import Foundation

// 全局关卡数据缓存,确保同一种子的关卡只生成一次
class LevelDataCache {
    static let shared = LevelDataCache()
    private var cache: [Int: (original: [Int], masked: [Int?])] = [:]
    
    private init() {} // 单例模式,禁止外部实例化
    
    func getSequence(for seed: Int) -> (original: [Int], masked: [Int?]) {
        // 优先返回缓存数据
        if let cachedData = cache[seed] {
            return cachedData
        }
        // 无缓存则生成新数据并存入缓存
        let sequenceGenerator = NumberSequence(seed: seed)
        let newSequence = sequenceGenerator.generateSequence()
        cache[seed] = newSequence
        return newSequence
    }
}

2. 修改关卡视图的初始化逻辑

让NumberSequenceView从缓存获取数据,而非每次初始化都重新生成:

struct NumberSequenceView: View {

    var original: [Int]
    var masked: [Int?]
    @State var guesses: [Int?]
    @Binding var checkState: CheckState
    let seed: Int
    
    init(checkState: Binding<CheckState>, seed: Int) {
        self.seed = seed
        // 从缓存获取关卡数据,避免重复生成
        let sequence = LevelDataCache.shared.getSequence(for: seed)
        original = sequence.original
        masked = sequence.masked
        guesses = sequence.masked
        self._checkState = checkState
    }
    
    // 其余视图代码保持不变...
}

3. 确保每个关卡使用唯一种子

在GameBoardView中,直接用关卡编号作为唯一种子,保证不同关卡的随机性独立:

private func destinationView(for level: Int) -> some View {
    // 用关卡编号作为唯一种子,确保每个关卡生成独立的随机内容
    return AnyView(NumberSequenceView(checkState: $checkState, seed: level))
}

4. 修正随机生成器的种子作用逻辑

确保NumberSequence中所有随机操作都使用传入的种子生成器:

class NumberSequence {
    let hiddenNumbers = 1
    let listLength: Int = 10
    var generator: RandomNumberGeneratorWithSeed
    
    init(seed: Int) {
        generator = RandomNumberGeneratorWithSeed(seed: seed)
    }
    
    func generateSequence() -> (original: [Int], masked: [Int?]) {
        var original: [Int] = []
        
        while true {
            original = []
            // 所有随机操作都绑定到当前种子的生成器
            let sign1 = Sign.allCases.randomElement(using: &generator)!
            let val1 = Int.random(in: 1..<2, using: &generator)
            let startValue = Int.random(in: 1..<50, using: &generator)
            
            var newValue = startValue
            for _ in 0..<listLength {
                original.append(newValue)
                newValue = sign1.op()(newValue, val1)
            }
            if (original.max()! < 150) && (original.min()! > 0) {
                break
            }
        }
        
        var randomIndexes = Set<Int>()
        while randomIndexes.count < hiddenNumbers {
            randomIndexes.insert(Int.random(in: 0..<listLength, using: &generator))
        }
        var masked = [Int?]()
        for i in 0..<listLength {
            masked.append(randomIndexes.contains(i) ? nil : original[i])
        }
        return (original: original, masked: masked)
    }
}

方案说明

  • 缓存机制:同一关卡(对应同一种子)的随机数据仅生成一次,后续无论视图如何初始化/刷新,都直接读取缓存,彻底解决关卡内容中途更新的问题;
  • 种子唯一性:每个关卡使用唯一种子(关卡编号),保证不同关卡的随机内容独立不重复;
  • 数据与视图分离:将随机生成逻辑从视图层剥离到缓存类,视图仅负责展示数据,避免因视图生命周期导致的重复生成。

内容的提问来源于stack exchange,提问作者charelf

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 10:44:52