公司动态

Unity3D实战:休闲战争策略游戏的金币系统与评分设计

📅 2026/7/31 6:55:10
Unity3D实战:休闲战争策略游戏的金币系统与评分设计
1. 项目概述与核心思路最近几年休闲战争策略游戏在移动端和PC平台都挺火的。这类游戏通常节奏快、上手简单但又保留了策略游戏的深度和成长感比如经典的《皇室战争》就是典型代表。很多独立开发者或者小团队都想尝试这个方向而Unity3D无疑是实现这个想法最趁手的工具之一。今天我就以一个从业者的角度和大家聊聊如何从零开始用Unity3D打造一款属于自己的休闲战争策略游戏。我们不会只停留在“做个Demo”的层面而是会深入到两个核心系统——金币系统和评分设计把从经济循环到玩家反馈的完整流程走一遍。这个项目适合有一定Unity和C#基础的开发者你可能已经做过一些简单的跑酷或者射击游戏现在想挑战更有系统性的项目。整个流程会涉及到游戏循环设计、UI交互、数据持久化以及平衡性调整这些都是实战中绕不开的环节。我会尽量把每一步的“为什么”和“怎么做”讲清楚并分享一些我踩过的坑和优化技巧。2. 游戏核心循环与框架搭建2.1 定义你的休闲战争策略玩法在动手写代码之前我们必须先想清楚游戏的核心玩法是什么。休闲战争策略游戏的核心通常在于“资源管理”和“即时决策”的结合。玩家需要收集或生产某种资源比如金币然后用这些资源在战场上部署单位目标是摧毁对方的基地或达成特定条件。以一个简单的双人对战原型为例我们可以这样设计对战模式1v1地图分为上下或左右两半各有一个主基地。资源金币。金币随时间自动增长例如每秒5同时地图上会周期性刷新中立资源点玩家控制单位占领后可获得额外金币。单位设计几种基础单位如近战步兵消耗低、血量低、远程弓箭手中等消耗、中等射程、重型骑士高消耗、高血量。每种单位有明确的克制关系。胜利条件摧毁对方主基地或在一定时间后我方基地血量更高。这个框架看似简单但已经包含了策略游戏的核心要素经济运营金币管理、兵种搭配策略选择和时机把握即时操作。我们所有的后续系统包括金币和评分都将围绕这个核心循环来构建。2.2 Unity项目结构与核心管理器一个清晰的项目结构是后续高效开发的基础。我建议在Unity中创建以下核心文件夹和脚本Assets/ ├── Scripts/ │ ├── Managers/ │ │ ├── GameManager.cs // 游戏总控状态管理 │ │ ├── ResourceManager.cs // 资源金币管理器 │ │ └── UIManager.cs // UI管理器 │ ├── Gameplay/ │ │ ├── Units/ // 所有单位相关脚本 │ │ ├── Buildings/ // 建筑相关脚本如基地 │ │ └── Spawner.cs // 单位生成器 │ ├── Data/ │ │ ├── UnitData.cs // 单位数据ScriptableObject │ │ └── GameSettings.cs // 游戏平衡参数 │ └── UI/ │ └── 各种UI面板脚本 ├── Prefabs/ // 所有的预制体 ├── Scenes/ └── ...GameManager是这个项目的大脑。它负责初始化游戏、切换游戏状态如准备、战斗中、结束、判断胜负。我通常会用一个枚举来定义游戏状态并用事件C# Action来通知其他系统状态变化这样耦合度更低。public enum GameState { Menu, Preparation, Battle, Paused, GameOver } public class GameManager : MonoBehaviour { public static GameManager Instance; public GameState CurrentState { get; private set; } public static event ActionGameState OnGameStateChanged; private void Awake() { if (Instance null) Instance this; else Destroy(gameObject); } private void Start() { UpdateGameState(GameState.Menu); } public void UpdateGameState(GameState newState) { CurrentState newState; OnGameStateChanged?.Invoke(newState); // 根据状态执行不同逻辑如暂停物理模拟等 } }注意使用单例模式Singleton管理全局管理器很方便但要小心不要滥用。确保在场景切换时正确处理用DontDestroyOnLoad或在每个场景单独生成。对于中小型项目这种模式是高效且清晰的。3. 金币系统的深度设计与实现金币系统是休闲策略游戏的血液它驱动着玩家的每一个决策。一个好的金币系统必须是“可理解、可预期、有深度”的。3.1 金币的获取途径设计金币不能只靠自动增长那样会显得单调。我设计了一个多来源的金币获取模型自然增长被动收入这是经济的基础。在ResourceManager中维护一个每玩家的金币数通过协程Coroutine或计时器定期增加。public class ResourceManager : MonoBehaviour { public float goldPerSecond 5f; private float currentGold; private Coroutine goldGenerationCoroutine; void Start() { StartGoldGeneration(); } IEnumerator GenerateGoldOverTime() { while (true) { yield return new WaitForSeconds(1.0f); AddGold(goldPerSecond); // 每秒增加 } } }关键参数goldPerSecond。这个值直接影响游戏节奏。初期可以设低一点如3让玩家有更多时间思考中后期可以通过升级来提升。占领资源点主动争夺在地图上设置几个“金矿”或“圣坛”预制体。当玩家的单位进入其触发区域并停留一段时间例如3秒后该资源点归属于该玩家并每隔一段时间如5秒为其提供一笔额外金币。这引入了地图控制和争夺的维度。击败敌方单位战斗奖励每当玩家击败一个敌方单位根据该单位的造价或强度奖励一定比例的金币例如造价的一半。这鼓励进攻性玩法并能让劣势方有“翻盘”的可能因为击败一个高价值单位能获得大量资源。特殊技能或道具策略选择可以设计一些消耗性技能如“点石成金”短时间内双倍金币获取或“掠夺”立即偷取对方一定比例金币。这增加了策略的多样性。3.2 金币的消耗与单位经济平衡有收入就得有支出。金币的主要消耗途径就是生产战斗单位。这里的核心在于单位的经济平衡。我强烈建议使用ScriptableObject来管理单位数据。它为每个兵种创建一个可独立配置的数据资产修改平衡性时无需改动代码。[CreateAssetMenu(fileName NewUnitData, menuName Game Data/Unit Data)] public class UnitData : ScriptableObject { public string unitName; public GameObject unitPrefab; public float cost; // 金币消耗 public float buildTime; // 训练时间 public float health; public float attackDamage; public float attackRange; public float attackSpeed; public float moveSpeed; // 可以添加更多属性如护甲类型、攻击类型等用于克制计算 }平衡性设计心得成本与价值比一个单位的“战斗力总估值”应与其成本大致成正比。你可以粗略定义一个公式估值 生命值 * 攻击力 * 攻击速度 / (移动速度因子)。通过调整UnitData中的数值让所有单位的“单位金币战斗力”接近。克制关系让平衡更有深度。例如步兵成本低但对骑士伤害减半弓箭手对步兵有加成但被骑士近身后很脆弱。这种“石头剪刀布”的关系能极大丰富策略。人口或容量限制为了防止“爆兵”流可以引入人口上限或队伍容量概念。每生产一个单位占用一定人口人口上限可以通过升级基地来提升。这迫使玩家在数量和质量间做选择。实现单位生产 在Spawner脚本中接收生产指令检查玩家金币和人口是否足够然后扣除金币开始一个建造计时器时间到后在指定位置生成单位预制体。public class Spawner : MonoBehaviour { public Transform spawnPoint; public void SpawnUnit(UnitData data, Player owner) { if (ResourceManager.Instance.GetGold(owner) data.cost) { ResourceManager.Instance.DeductGold(data.cost, owner); StartCoroutine(BuildUnitCoroutine(data, owner)); } } IEnumerator BuildUnitCoroutine(UnitData data, Player owner) { // 显示建造进度条UI yield return new WaitForSeconds(data.buildTime); GameObject newUnit Instantiate(data.unitPrefab, spawnPoint.position, Quaternion.identity); newUnit.GetComponentUnitController().Initialize(owner, data); // 通知人口系统等 } }3.3 经济系统的数据持久化与扩展对于休闲游戏我们通常需要保存玩家的金币总数用于商店消费、解锁的单位等内容。SQLite是一个轻量级且强大的选择非常适合Unity移动端或PC端。集成SQLite使用Mono.Data.Sqlite或第三方插件如SQLite4Unity3d。在ResourceManager中初始化数据库创建表来存储玩家资源。// 初始化数据库连接和表 private void InitDatabase() { string connectionString URIfile: Application.persistentDataPath /game_data.db; using (var connection new SqliteConnection(connectionString)) { connection.Open(); using (var command connection.CreateCommand()) { command.CommandText CREATE TABLE IF NOT EXISTS player_resources (player_id INTEGER PRIMARY KEY, gold INTEGER); command.ExecuteNonQuery(); } } }保存与加载在游戏退出或特定检查点将当前金币数写入数据库。游戏开始时从中读取。踩坑记录务必在OnApplicationPause或OnApplicationQuit事件中执行保存操作因为移动端应用很容易被突然切换到后台。同时要考虑数据加密或校验防止玩家轻易修改存档文件。经济系统扩展随着游戏发展可以引入“银行”系统金币随时间产生利息、税收系统从占领的资源点按比例抽成、交易系统玩家间以物易物这需要服务器支持。这些都能让经济体系更加丰满。4. 评分系统的多维构建与心理激励评分系统是游戏给玩家的直接反馈一个好的评分系统能极大提升成就感和重复游玩的欲望。它不应该只是一个简单的“胜利/失败”而应该是一个多维度的评价体系。4.1 设计评分维度与权重我们可以从以下几个维度来评价一局游戏的表现评分维度描述计算方式示例权重战斗效率衡量单位消耗与造成的伤害比(总造成伤害) / (我方单位总成本)30%经济运营衡量金币获取和使用的效率(游戏结束时总金币获取量) / (游戏时长)25%策略执行衡量是否有效利用兵种克制(通过克制造成的额外伤害) / (总造成伤害)20%速度评价鼓励快速结束战斗基础分 - (游戏用时(秒) * 扣分系数)胜利时间越短分越高15%生存能力鼓励减少战损(我方基地剩余生命值百分比) * 系数10%权重分配的逻辑战斗效率和经济运营是核心策略能力的体现所以权重最高。策略执行鼓励玩家深入理解游戏机制。速度和生存则提供了不同的游玩风格导向速攻流 vs 稳健流。4.2 实时评分反馈与UI实现评分不应该只在游戏结束时才出现。实时的、渐进式的反馈更能激励玩家。实时评分计算在GameManager或一个专门的ScoreManager中订阅相关事件如单位死亡、资源点占领、造成伤害实时更新上述各个维度的中间数据。public class ScoreManager : MonoBehaviour { private float totalDamageDealt; private float totalUnitCost; private float combatEfficiencyScore; // 战斗效率分 void OnEnable() { Unit.OnUnitDealDamage HandleDamageDealt; Unit.OnUnitDeath HandleUnitDeath; } void HandleDamageDealt(float damage, Unit dealer) { if (dealer.Owner localPlayer) { totalDamageDealt damage; UpdateCombatEfficiency(); UIManager.Instance.UpdateScoreDisplay(combatEfficiencyScore, ScoreType.Combat); } } // ... 其他事件处理 }动态UI展示在游戏UI的角落例如屏幕左上角设计一个评分面板。可以显示当前总分或者用进度条的形式展示各个维度的实时得分。当玩家完成一次精彩操作如用低成本单位反杀高成本单位时可以弹出一个小的“XX 战斗效率分”的浮动文字给予即时正反馈。游戏结束评分界面这是重头戏。设计一个总结界面清晰地展示五个维度的得分、每个维度的具体计算过程例如“战斗效率造成1500伤害 / 消耗800金币 1.88”、以及根据权重计算出的最终评级如D/C/B/A/S。配上相应的音效和动画如分数滚动、评级星星点亮仪式感十足。4.3 评分数据的应用与长期驱动评分不能只是一个数字它必须和玩家的成长系统挂钩。经验值与等级根据每局最终评分或评级奖励经验值。S评级奖励最多D评级奖励最少。积累经验提升玩家等级每升一级解锁新的奖励如头像框、表情、或少量付费货币。成就系统评分系统的各个维度天然就是成就的来源。例如“经济大师”单局游戏经济运营维度获得S评级。“完美克制”单局游戏策略执行维度得分超过90%。“闪电战”在3分钟内赢得比赛并获得胜利。 完成成就奖励独特的称号、徽章或大量资源满足玩家的收集欲和炫耀心理。回放与复盘保存每局游戏的关键数据初始种子、玩家操作序列、单位生成记录等。结合评分可以允许玩家在历史记录中回看对局并高亮显示“这里你的经济运营出现了断层”或“这次完美的克制为你赢得了大量策略分”。这对于希望提升技术的硬核玩家来说是无价之宝。实现技巧回放系统可以通过记录每帧的“命令”如在时间t玩家A在位置(x,z)生产了单位U来实现。回放时只需用同样的初始状态重新执行这些命令即可数据量很小。Unity的JsonUtility或BinaryFormatter可以用于序列化这些命令列表并保存到本地。5. 核心玩法功能的实现与打磨有了经济和评分框架我们需要把游戏最核心的“战斗”部分做扎实。5.1 单位控制与战斗AI对于休闲游戏单位通常采用自动战斗Auto-Battler模式即玩家只负责生产和部署单位会自动寻找并攻击敌人。有限状态机FSM这是实现单位AI的经典模式。一个战斗单位通常有Idle闲置、Move移动、Attack攻击、Die死亡等状态。public class UnitController : MonoBehaviour { public enum UnitState { Idle, Moving, Attacking, Dead } private UnitState currentState; private UnitData data; private Transform target; void Update() { switch (currentState) { case UnitState.Idle: FindNearestEnemy(); break; case UnitState.Moving: MoveTowardsTarget(); break; case UnitState.Attacking: if (target ! null InAttackRange()) { PerformAttack(); } else { currentState UnitState.Idle; } break; } } void FindNearestEnemy() { // 使用Physics.OverlapSphere或维护一个单位列表找到最近敌方单位 // 如果找到设置target状态转为Moving } }攻击与伤害计算在PerformAttack()中启动一个攻击间隔计时器。计时器到期时对目标调用TakeDamage()方法。伤害计算可以引入简单的公式例如最终伤害 攻击力 - 目标护甲。护甲可以通过UnitData定义并设计减伤公式如每点护甲减少1%伤害。寻路与移动对于2D游戏可以使用Rigidbody2D加力或直接修改Transform.position。对于3D或更复杂的地形Unity的NavMesh系统是首选。烘焙好导航网格后单位AI就可以自动绕过障碍物寻找路径。注意大量单位同时使用NavMesh寻路会造成性能压力。优化方法包括使用更简单的代理Agent类型、降低寻路更新频率、对于群体单位采用“领头羊”寻路其他跟随的方式。5.2 地图、基地与胜负判定地图设计使用Unity的Terrain工具或Tilemap2D快速搭建对战地图。关键是要设计出有战略意义的地形比如狭窄通道易守难攻适合放置防御塔或埋伏。资源点密集区必争之地会爆发早期冲突。视野盲区可以隐藏单位发起突袭。 地图的对称性对于1v1对战很重要能保证基本的公平。基地与生命值双方主基地是一个拥有大量生命值的建筑Building脚本。它可能具备被动技能如缓慢回复周围单位生命值或主动技能如一次性召唤护卫。当一方基地生命值降至0游戏结束。胜负判定逻辑在GameManager中持续检查胜利条件。void Update() { if (CurrentState ! GameState.Battle) return; if (playerBase.health 0) { // 玩家失败 EndGame(GameResult.Lose); } else if (enemyBase.health 0) { // 玩家胜利 EndGame(GameResult.Win); } else if (battleTime maxBattleTime) { // 超时根据基地剩余血量判定 EndGame(playerBase.health enemyBase.health ? GameResult.Win : GameResult.Lose); } }5.3 性能优化与调试技巧当单位数量多起来游戏可能会出现卡顿。以下是一些立竿见影的优化点对象池单位的频繁创建Instantiate和销毁Destroy是性能杀手。使用对象池在游戏开始时预先创建好一批单位预制体需要时激活不需要时禁用并放回池中。public class ObjectPool : MonoBehaviour { public GameObject prefab; private QueueGameObject pool new QueueGameObject(); public GameObject GetObject() { if (pool.Count 0) { GameObject obj pool.Dequeue(); obj.SetActive(true); return obj; } return Instantiate(prefab); } public void ReturnObject(GameObject obj) { obj.SetActive(false); pool.Enqueue(obj); } }距离检测优化FindNearestEnemy如果每帧对所有单位进行两两距离计算O(n²)复杂度在单位多时会非常慢。可以使用空间划分算法如网格法或四叉树/八叉树或者利用Unity的Physics.OverlapSphere并合理设置LayerMask来减少检测范围。使用ProfilerUnity的Profiler窗口是你的最佳朋友。定期用它分析CPU、GPU、内存和渲染的瓶颈。常见问题包括过多的Update调用、复杂的单帧逻辑、过高的Draw Call。调试心得为了平衡游戏我经常需要快速调整参数。我会把所有重要的平衡参数如单位属性、金币增长率放在一个GameSettingsScriptableObject中并在编辑器中为其创建一个简单的自定义编辑器窗口这样我可以在游戏运行时动态调整数值并立即看到效果极大提升了迭代效率。6. 常见问题与实战避坑指南在实际开发中你一定会遇到各种各样的问题。这里我总结了一些高频问题和解决方案。6.1 开发流程中的典型问题问题现象/原因解决方案与技巧单位卡住或抖动多个单位寻路目标点过于接近或碰撞体互相挤压。1. 增加NavMesh Agent的“回避优先级”。2. 使用RVO局部避障算法Unity有RVO插件。3. 简化单位碰撞体或对友方单位使用忽略碰撞的Layer。游戏后期严重卡顿单位数量过多Update循环负担重或存在内存泄漏。1. 实现对象池杜绝频繁Instantiate/Destroy。2. 优化AI逻辑将部分计算如寻敌从每帧改为每隔几帧执行一次分帧处理。3. 使用Profiler定位具体是脚本逻辑还是渲染问题。数据保存失败或错乱移动端应用被切到后台时保存协程可能被中断多线程读写冲突。1. 在OnApplicationPause(true)和OnApplicationQuit中同步调用保存方法而非启动协程。2. 对数据库操作加锁确保同一时间只有一个读写操作。UI更新不及时或闪烁在非主线程中修改了UI元素或者Update中频繁创建/销毁UI对象。1. 所有UI更新操作务必放在主线程。可以使用MainThreadDispatcher插件或UnityMainThreadDispatcher。2. 对频繁变化的UI如血量条使用对象池复用其GameObject。不同设备分辨率适配差UI布局使用绝对坐标或锚点设置不当。1. 全面使用Unity的Canvas Scaler和锚点系统。2. 针对关键UI元素进行多分辨率如16:9, 18:9, 19.5:9下的预览和测试。6.2 游戏设计与平衡性调整感觉游戏节奏太慢/太快调快提高金币自然增长率(goldPerSecond)降低高级单位成本缩短单位生产时间(buildTime)。调慢反之。关键技巧不要只调一个参数。例如想加快节奏可以同时“提高金币获取”和“降低单位血量”让战斗更激烈决策更频繁。某个兵种过于强大IMBA第一步数据分析。查看后台数据或自己测试是这个兵种输出太高还是太肉还是成本太低第二步微调。优先调整其克制关系。例如一个高伤害的远程单位过于强大可以为其增加一个“被近战单位攻击时承受额外伤害”的弱点而不是直接削弱其伤害这样保留了其特色但引入了反制手段。第三步环境调整。如果某个兵种在特定地图所向披靡可以考虑修改地图增加其移动路径上的障碍或者增加一种新的、能有效针对它的中立生物或环境机制。玩家抱怨经济系统“滚雪球”优势方资源越来越多劣势方毫无翻盘希望。引入动态平衡机制例如当一方金币差距超过一定阈值时为劣势方提供微弱的金币获取加成。增加高风险高回报机制设计一个地图中央的“超级资源点”占领难度高但奖励极其丰厚给劣势方一个搏命翻盘的机会。强化防御优势让处于防守状态单位在己方半场的单位获得轻微的伤害或血量加成让优势方推进时也需要付出代价。6.3 发布前的最后检查清单在准备将游戏打包发布或提交测试前请务必核对以下清单[ ]功能完整性核心循环生产-战斗-胜利/失败是否完整且无恶性Bug[ ]数据持久化玩家金币、等级、解锁内容是否能在退出重启后正确加载[ ]UI/UX所有按钮是否有反馈音效、动画提示信息是否清晰评分界面等关键信息是否易于理解[ ]性能在目标设备如中低端手机上单位数量达到设计上限时帧率是否稳定至少30fps[ ]平衡性邀请不同水平的朋友进行至少20局测试收集他们对各个兵种强度、经济节奏、胜负体验的反馈。[ ]新手引导是否有简单明了的前1-3局引导教会玩家基本操作和核心规则[ ]设置选项是否提供了音量控制、画质选项等基本设置最后我想分享一个我个人非常受用的习惯为你的游戏建立一个简单的“数据仪表盘”。在开发过程中我通常会写一个隐藏的调试界面实时显示当前帧率、单位总数、每秒金币收入、双方实时战斗力估值等数据。在测试平衡性时这个仪表盘能提供最直观的依据帮你从“我感觉”进化到“数据证明”。游戏开发尤其是系统设计是一个不断迭代和验证的过程耐心和基于数据的决策是做出好作品的关键。