游戏开发日记7.12
开发里面的协程 (脚本:OldBaseScene.cs)
一、什么是协程(Coroutine)
在 Unity 中,协程是一种“异步执行机制”,允许你暂停一个函数的执行,并在后续某个时间点再继续执行。IEnumerator MyCoroutine() {Debug.Log("Step 1");yield return new WaitForSeconds(2f); // 暂停2秒Debug.Log("Step 2"); }
这段代码中,“Step 1”会立刻执行,然后暂停2秒后再打印“Step 2”。
你可以通过:
StartCoroutine(MyCoroutine());
来启动它。
二、为什么不用 Unity 的 StartCoroutine?Unity 提供的协程系统很方便,但有以下几个问题:
问题 描述 ❌ 不好统一管理 没法很方便地暂停/清除某一类协程 ❌ 生命周期不统一 GameObject 销毁后协程失效但不会报错 ❌ 跨模块控制复杂 比如你想在一个道具逻辑类中控制另一个角色类的协程很困难 所以实际大型项目中,经常自己封装协程管理器(如 CoroutineContainer)。
三、CoroutineContainer 是什么?我们根据命名与使用方式来分析:
public CoroutineContainer m_CoroutineContainer = new CoroutineContainer();
你可以把它理解成一个“多任务调度器”,负责管理所有该场景内的协程,常见的功能包括:
1. 启动协程
m_CoroutineContainer.Start(MyJob());
就像 Unity 的
StartCoroutine()
,但它可能附带tag
或id
,便于后期查找或取消。
2. 停止协程m_CoroutineContainer.Stop("MoveEffect");
支持按名称、类型、ID 等来清除单个或一类协程。
3. 清除全部协程(常见于退出场景时)
m_CoroutineContainer.Clear();
你在
OnRecycle()
看到这段代码:m_CoroutineContainer.Clear();
表示这个场景被回收时,要把所有正在执行的协程都清理掉,避免“幽灵协程”继续运行。
4. 更新调度(逻辑驱动协程执行)public void LogicUpdate(float dt) {m_CoroutineContainer.LogicUpdate(dt); }
这说明
CoroutineContainer
不是 Unity 的原生MonoBehaviour.StartCoroutine()
驱动,而是自定义逻辑帧调度的协程系统。这通常用于:
自定义逻辑帧系统(如服务器逻辑、帧同步)
需要在非 Unity 主线程环境中运行
四、CoroutineContainer 背后的设计思路(架构设计)public class CoroutineContainer {private List<ICoroutine> _coroutines = new List<ICoroutine>();public void Start(IEnumerator routine);public void Stop(ICoroutine routine);public void Clear();public void LogicUpdate(float dt); // 每帧推进所有协程 }
每个
ICoroutine
对象就像是一个“任务包装器”,内部实现可能长这样:public interface ICoroutine {bool IsDone { get; }void Update(float dt); // 协程的推进逻辑 }
它可能还支持链式调用、等待条件等功能,例如:
yield return Coroutine.WaitUntil(() => isBossDead);
五、这段代码中协程的用途有哪些?
总结
任务 使用协程的原因 怪物淡入淡出 SetMonsterHalfVisible()
中涉及到透明度渐变和移动,可能由协程推进图标位移动画 协程可以每帧推进位置而不影响主逻辑帧 状态机切换 怪物切状态后需要延迟完成(比如攻击动画完成) 渲染淡入 FadeGridVisualsInView()
中的格子淡入淡出可能是协程控制
点 说明 💡 自定义协程容器 更好地统一管理所有异步任务,跨模块可控 🧠 更适合大型项目 Unity 自带的 Coroutine 不适合复杂逻辑或多对象管理 🧱 自定义调度器 使用 LogicUpdate(float dt)
而非Update()
,便于自定义帧率与同步🔧 应用场景广泛 包括动画播放、战斗逻辑延迟、怪物状态控制、视觉表现等
一、数据结构层面(设计结构清晰性)
主数据容器
字段名 类型 含义 m_Items
List<SceneGridItem>
储存当前场景中所有格子对象 m_RemoveList
List<int>
用于记录需要移除的格子 ID(如出视野) m_CoroutineContainer
CoroutineContainer
管理场景中所有协程(异步逻辑) MOldGridRender
OldGridRender
地图渲染对象(绑定 Tilemap) m_SceneName
string
当前场景名,对应 prefab 名 m_SceneID
int
场景类型(0 主线、1 副本) m_MainRoleInitIndex
int
主角初始所在格子索引 m_ForWardCheckNum
int
视野内可查看前方格子数量
总体结构是一个网格格子管理器(scene grid manager),用于维护地图上每个可交互单位。
二、算法逻辑层面(场景运行的流程)✅ 1. 初始化流程(OnEnter)
读取地图名 → 加载
OldGridRender
组件(场景图层)。获取所有格子坐标,循环创建
SceneGridItem
。每个格子初始化其数据:
Position
EvtId
(事件 ID)
ExitType
、IconName
、SpineName
等✅ 2. 玩家相关逻辑
InitMainRole()
初始化主角。
throwDiceComponent.CheckGridEvent()
检查当前格子是否有事件(如战斗)。主角所在格子的 Tile 排序层级设置为最底层(-Max-1)
✅ 3. 战斗检测算法
if (isMainRole && throwDiceComponent.JumpEnd()) {// 判断主角跳跃结束// 获取下一个格子// 如果有怪物且事件为战斗 → 触发 StartFight() }
✅ 4. 渲染优化逻辑
ChangeGridLayer()
:根据格子 y 值递增/递减设置sortingOrder
AdjustMonstersSortingOrder()
:提高前方视野中怪物的渲染层级
FadeGridVisualsInView()
:视野外格子逐渐隐藏/显现三、语法与架构层面
面向对象架构:
OldBaseScene
继承RecycleObject
,利用对象池管理方法通过
virtual
+override
提供 可扩展的场景系统使用 Unity 组件系统
GetComponent<T>()
与 GameObject 配合管理渲染层模块化解耦:
场景管理:
OldSceneMgr
玩家管理:
OldPlayerMgr
数据访问:
DataMgr
、LubanConfig
渲染特效:
OldEffectRender
,SpriteRenderer
重构点设计:
格子使用
SceneGridItem
封装,数据SceneGridItemData
分离怪物半透明逻辑
OldGridMonsterEffectJob
+OldMoveState
分离显示与位移总结
特点 表现 代码结构 清晰、分层明确 可维护性 好,使用虚函数支持扩展 游戏逻辑 基于网格的地图构建、支持事件与怪物战斗 引擎特性 深度利用 Unity 的组件与排序系统 数据驱动 通过 Luban
配置表动态加载场景与事件数据
后续深入了解
✅ 1. 逐个讲解核心模块类结构(如SceneGridItem
,OldThrowDiceComponent
)
学会解读大型项目结构(模块分工、耦合方式)
掌握每个类的功能定位、职责范围
学会判断什么逻辑应该写在哪个类
✅ 2. 分析JumpEnd()
背后的状态机(StateMachine)逻辑
游戏行为(跳跃、攻击、死亡)是如何用状态管理器驱动的?
状态机的切换机制是“条件触发”还是“事件驱动”?
如何优雅地扩展一个状态机(比如加个“闪避”状态)
怪物行为控制
玩家技能释放/移动状态切换
任务触发流程