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

如何在运行时优雅变更程序行为?Android Kotlin游戏模式切换求助

嘿,这个问题我之前做类似Android游戏的时候也碰到过!用字符串解析确实太折腾了,尤其是模式复杂的时候很容易出错,维护起来也头大。给你几个更靠谱的方案,都是我实际验证过或者用过的:

方案1:数据类+工厂模式(最推荐,类型安全+逻辑灵活)

这个方案完全规避了解析的麻烦,还能让每个模式的逻辑独立扩展,非常适合你的场景。

首先定义一个抽象接口来统一所有棋盘模式的核心行为:

interface BoardMode {
    // 棋盘基础配置
    val rows: Int
    val cols: Int
    val obstaclePositions: List<Pair<Int, Int>>
    
    // 每个模式的独特逻辑,比如生成特殊道具、触发规则等
    fun generateSpecialTiles(): List<Tile>
}

然后每个关卡模式都实现这个接口,用Kotlin数据类来存储配置,同时自定义专属逻辑:

// 简单关卡模式
data class EasyLevelMode(
    override val rows: Int = 5,
    override val cols: Int = 5,
    override val obstaclePositions: List<Pair<Int, Int>> = listOf(Pair(2,2))
) : BoardMode {
    override fun generateSpecialTiles(): List<Tile> {
        // 简单模式只生成1个炸弹道具
        return listOf(Tile(1, 1, TileType.BOMB))
    }
}

// 困难关卡模式
data class HardLevelMode(
    override val rows: Int = 8,
    override val cols: Int = 8,
    override val obstaclePositions: List<Pair<Int, Int>> = listOf(Pair(3,3), Pair(5,5), Pair(2,6))
) : BoardMode {
    override fun generateSpecialTiles(): List<Tile> {
        // 困难模式生成多种特殊道具
        return listOf(
            Tile(0,0, TileType.SHIELD),
            Tile(7,7, TileType.BOMB),
            Tile(4,4, TileType.SPEED)
        )
    }
}

接下来写一个工厂类,根据当前关卡ID返回对应的模式实例:

object BoardModeFactory {
    fun createMode(level: Int): BoardMode {
        return when(level) {
            1 -> EasyLevelMode()
            2 -> HardLevelMode()
            // 依次添加剩下的20-30种模式...
            else -> throw IllegalArgumentException("无效关卡:$level")
        }
    }
}

运行时调用就很简单了:

val currentLevel = 2
val currentBoardMode = BoardModeFactory.createMode(currentLevel)
// 用currentBoardMode的属性和方法初始化游戏棋盘

优点:完全类型安全,编译时就能发现错误;每个模式的逻辑独立,新增/修改模式不会影响其他代码;维护起来直观清晰。
缺点:模式硬编码在代码中,新增模式需要重新编译APK(不过你说最多30种,这个完全可以接受)。

方案2:JSON序列化+数据类(适合动态配置场景)

如果以后需要支持无需更新APK就能修改模式(比如从后台拉取配置),可以用Kotlin官方的序列化库来处理JSON解析,比自己写字符串解析靠谱100倍。

首先在build.gradle中添加依赖:

plugins {
    id 'org.jetbrains.kotlin.plugin.serialization' version '1.9.0'
}

dependencies {
    implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1'
}

然后定义序列化的数据类(用密封类区分不同模式):

import kotlinx.serialization.Serializable

@Serializable
sealed class BoardMode {
    abstract val rows: Int
    abstract val cols: Int
    abstract val obstaclePositions: List<Pair<Int, Int>>
}

@Serializable
data class EasyLevelMode(
    override val rows: Int = 5,
    override val cols: Int = 5,
    override val obstaclePositions: List<Pair<Int, Int>> = listOf(Pair(2,2)),
    val specialTileCount: Int = 1
) : BoardMode()

@Serializable
data class HardLevelMode(
    override val rows: Int = 8,
    override val cols: Int = 8,
    override val obstaclePositions: List<Pair<Int, Int>> = listOf(Pair(3,3), Pair(5,5)),
    val specialTileCount: Int = 3
) : BoardMode()

把每个模式的JSON配置放在assets/board_modes目录下,比如easy_mode.json:

{
    "type": "EasyLevelMode",
    "rows": 5,
    "cols": 5,
    "obstaclePositions": [[2,2]],
    "specialTileCount": 1
}

最后写一个加载类读取并反序列化JSON:

import kotlinx.serialization.json.Json
import kotlinx.serialization.decodeFromString

class BoardModeLoader(private val context: Context) {
    private val json = Json { ignoreUnknownKeys = true }
    
    suspend fun loadMode(level: Int): BoardMode {
        val fileName = when(level) {
            1 -> "board_modes/easy_mode.json"
            2 -> "board_modes/hard_mode.json"
            // 对应其他关卡的JSON文件
            else -> throw IllegalArgumentException("无效关卡:$level")
        }
        
        val jsonString = context.assets.open(fileName).bufferedReader().use { it.readText() }
        return json.decodeFromString<BoardMode>(jsonString)
    }
}

优点:模式配置与代码分离,修改配置无需改代码;支持动态更新(比如从服务器下载JSON)。
缺点:需要处理IO和反序列化异常;复杂逻辑仍需在代码中判断,灵活性不如工厂模式。

方案3:枚举类(适合模式逻辑简单、仅参数不同的场景)

如果所有模式的核心逻辑一致,只是参数(比如行列数、障碍物位置)不同,用枚举类最省事。

enum class BoardMode(
    val rows: Int,
    val cols: Int,
    val obstaclePositions: List<Pair<Int, Int>>,
    val specialTileTypes: List<TileType>
) {
    LEVEL_1(5, 5, listOf(Pair(2,2)), listOf(TileType.BOMB)),
    LEVEL_2(8, 8, listOf(Pair(3,3), Pair(5,5)), listOf(TileType.SHIELD, TileType.BOMB)),
    // 依次添加其他20-30个模式...
    ;
    
    // 通用逻辑可以在这里实现
    fun generateSpecialTiles(): List<Tile> {
        return specialTileTypes.mapIndexed { index, type ->
            Tile(index, index, type)
        }
    }
}

运行时直接根据关卡取枚举:

val currentLevel = 1
val currentMode = BoardMode.valueOf("LEVEL_$currentLevel")

优点:代码量最少,实现最简单;枚举自带单例,性能优异。
缺点:无法为单个模式自定义复杂逻辑;新增模式仍需修改代码。

总结

  • 如果每个模式有独特的规则逻辑,优先选方案1(数据类+工厂模式),类型安全又灵活;
  • 如果模式仅参数不同、逻辑通用,选**方案3(枚举类)**最省心;
  • 如果需要动态配置模式,选方案2(JSON序列化)。

之前的字符串解析方案不仅容易出错,维护成本还高,上面这三个方案都能完美解决你的问题~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 03:43:23