12 KiB
奖励机制
**本文档引用文件** - [Mission.ts](file://assets/script/game/common/config/Mission.ts) - *奖励常量配置* - [MissionComp.ts](file://assets/script/game/map/MissionComp.ts) - *奖励数据管理与实体销毁逻辑* - [VictoryComp.ts](file://assets/script/game/map/VictoryComp.ts) - *奖励界面展示* - [GameEvent.ts](file://assets/script/game/common/config/GameEvent.ts) - *事件定义* - [MissionMonComp.ts](file://assets/script/game/map/MissionMonComp.ts) - *怪物生成与事件处理* - [RogueConfig.ts](file://assets/script/game/map/RogueConfig.ts) - *肉鸽关卡配置* - [Design.md](file://assets/script/Design.md) - *系统设计概述*更新摘要
变更内容
- 更新了
MissionComp中实体销毁逻辑,修复空引用问题 - 修正奖励发放流程中组件清理的实现方式
- 更新相关章节以反映最新的代码实现
- 增强源码追踪信息,标注关键修改点
目录
奖励系统概述
本游戏采用肉鸽(Roguelike)塔防玩法,奖励系统是核心成长机制之一。玩家通过击败怪物获得金币,并在每波战斗结束后从三个奖励选项中选择一个,用于强化英雄能力。奖励类型包括属性提升、技能升级、装备获取、新技能解锁等,不同奖励具有不同的战力评分和金币消耗,玩家需要根据当前局势进行策略性选择。
奖励系统通过事件驱动机制实现,主要由MissionComp组件负责奖励数据的收集与分发,VictoryComp组件负责奖励界面的展示与交互。系统采用ECS架构,通过事件总线(oops.message)进行组件间通信,确保了高内聚低耦合的设计原则。
Section sources
三选一奖励机制
三选一奖励机制是游戏的核心决策系统,其触发条件为每波怪物被全部击败后。当MissionMonComp组件检测到当前波次怪物全部死亡时,会触发FightEnd事件,进而启动奖励选择流程。
该机制的实现逻辑如下:
- 战斗结束时,
MissionComp组件监听到FightEnd事件 - 收集本局战斗的奖励数据(金币、经验、钻石等)
- 构建奖励选项数组,通常包含三个不同强度的奖励
- 通过GUI系统打开胜利界面(Victory界面),并传递奖励数据
- 玩家在界面中选择一个奖励,系统应用对应效果
奖励选项的设计遵循"弱、一般、强"的梯度原则,不同选项消耗的金币数量不同,战力评分也不同,鼓励玩家进行策略性决策。
sequenceDiagram
participant MissionMonComp as MissionMonComp
participant MissionComp as MissionComp
participant VictoryComp as VictoryComp
participant GUI as GUI系统
MissionMonComp->>MissionComp : MonDead事件(最后一只怪物死亡)
MissionComp->>MissionComp : 检查怪物数量是否为0
MissionComp->>MissionComp : 触发FightEnd事件
MissionComp->>MissionComp : 收集game_data(金币、经验等)
MissionComp->>GUI : 打开Victory界面并传递数据
GUI->>VictoryComp : 初始化奖励界面
VictoryComp->>Player : 显示三个奖励选项
Player->>VictoryComp : 选择奖励
VictoryComp->>MissionComp : 应用所选奖励效果
Diagram sources
FightSet常量配置与奖励类型
FightSet枚举定义在Mission.ts文件中,包含了游戏的核心配置常量,其中与奖励系统直接相关的配置项包括:
GREEN_GOLD=1:绿色金币,基础奖励单位BLUE_GOLD=2:蓝色金币,中级奖励单位PURPLE_GOLD=3:紫色金币,高级奖励单位ORANGE_GOLD=4:橙色金币,稀有奖励单位ATK_ADD_GLOD=1:伙伴攻击力增加对应的金币奖励值
这些常量定义了不同品质奖励的数值基准,系统根据这些基准值计算实际奖励数量。例如,普通怪物可能掉落GREEN_GOLD数量的金币,而精英怪物或Boss可能掉落PURPLE_GOLD或ORANGE_GOLD数量的金币。
此外,FightSet还定义了其他影响奖励获取的参数,如MORE_RC=10表示通过观看广告可获得的额外次数,这与奖励系统的双倍奖励功能相关联。
Section sources
奖励数据结构与流程
数据结构分析
MissionComp组件中定义了两个关键数据结构:
- rewards数组:用于存储掉落物品列表
rewards:any[]=[]
- game_data对象:用于存储游戏数据
game_data:any={
exp:0,
gold:0,
diamond:0
}
rewards数组存储具体的物品奖励,如装备、技能书等;game_data对象则存储基础资源类奖励,包括经验值、金币和钻石。
奖励收集流程
奖励数据的收集与分发流程如下:
- 初始化阶段:在
data_init()方法中,重置rewards数组和game_data对象 - 战斗阶段:通过监听
MonDead事件,逐步累积奖励数据 - 结束阶段:当战斗结束时,将收集到的奖励数据传递给胜利界面
数据流从怪物死亡事件开始,经过MissionComp组件的收集处理,最终传递给VictoryComp组件进行展示,形成了完整的奖励数据流转闭环。
flowchart TD
A[怪物死亡] --> B{是否为最后一只}
B --> |是| C[触发FightEnd事件]
B --> |否| D[继续战斗]
C --> E[收集奖励数据]
E --> F[填充rewards数组和game_data]
F --> G[打开Victory界面]
G --> H[展示奖励选项]
H --> I[玩家选择奖励]
I --> J[应用奖励效果]
Diagram sources
掉落处理与数据更新
do_drop方法是处理掉落物品和更新游戏数据的核心方法。该方法接收两个参数:drop_item数组和game_data对象,分别表示掉落的物品列表和基础资源奖励。
do_drop(drop_item:any[],game_data:any={exp:0,gold:0,diamond:0}){
// console.log("[MissionComp] do_drop",drop_item,game_data)
}
虽然当前实现为空,但从方法签名可以看出其设计意图:
drop_item参数接收掉落的物品数组,可能包含装备、道具等game_data参数接收基础资源奖励,默认值为零- 方法内部应实现将掉落物品添加到
rewards数组,并将资源奖励累加到game_data对象中
在MissionMonComp组件中,可以找到实际的奖励发放逻辑。当关卡类型为"event"(事件关卡)时,会触发随机事件,其中EventType.TREASURE(宝藏事件)会直接增加50金币:
switch (this.currentEvent) {
case EventType.TREASURE:
smc.vmdata.mission_data.gold += 50; // 增加50金币
break;
}
这种设计模式表明,实际的奖励发放可能分散在多个组件中,最终由MissionComp统一收集和管理。
Section sources
奖励界面触发与数据传递
事件监听机制
MissionComp组件通过事件监听机制触发奖励界面的展示。在onLoad方法中,注册了对FightEnd事件的监听:
this.on(GameEvent.FightEnd,this.fight_end,this)
当战斗结束时,fight_end方法会被调用,该方法通过oops.gui.open方法打开胜利界面,并传递奖励数据:
oops.gui.open(UIID.Victory,{victory:false,rewards:this.rewards,game_data:this.game_data})
数据传递机制
数据传递通过open方法的参数实现,传递了三个关键数据:
victory:布尔值,表示战斗结果rewards:奖励物品数组game_data:基础资源数据对象
在VictoryComp组件的onAdded方法中接收这些数据:
onAdded(args: any) {
if(args.game_data){
this.game_data=args.game_data
}
}
这种事件驱动的数据传递机制确保了组件间的松耦合,MissionComp只需关注奖励数据的收集,而VictoryComp只需关注奖励数据的展示,职责分离清晰。
classDiagram
class MissionComp {
+rewards : any[]
+game_data : any
+onLoad()
+fight_end()
+do_drop()
}
class VictoryComp {
+rewards : any[]
+game_data : any
+onAdded(args)
+victory_end()
}
class GameEvent {
+FightEnd : "FightEnd"
+MissionEnd : "MissionEnd"
}
class UIID {
+Victory : 2
}
MissionComp --> GameEvent : "监听"
MissionComp --> UIID : "使用"
MissionComp --> VictoryComp : "传递数据"
VictoryComp --> GameEvent : "触发"
Diagram sources
实体销毁逻辑优化
根据最新代码变更,MissionComp中的cleanComponents方法已优化,修复了实体销毁时可能出现的空引用问题。新的实现直接销毁实体,让ECS系统自动处理组件清理,避免在组件reset方法中访问已被销毁的实体引用。
private cleanComponents() {
// 优化销毁顺序:直接销毁实体,让ECS系统自动处理组件清理
// 这样可以避免在组件reset方法中访问已经被销毁的实体引用
ecs.query(ecs.allOf(HeroViewComp)).forEach(entity => {
entity.destroy();
});
ecs.query(ecs.allOf(AtkConCom)).forEach(entity => {
entity.destroy();
});
ecs.query(ecs.allOf(SkillViewCom)).forEach(entity => {
entity.destroy();
});
}
此变更确保了在战斗结束后的清理过程中,不会因组件访问已被销毁的实体而导致运行时错误,提高了系统的稳定性。
Section sources
- MissionComp.ts - 修复实体销毁空引用问题
扩展新奖励类型
配置修改步骤
- 在Mission.ts中添加常量:在
FightSet枚举中添加新的奖励类型常量
NEW_REWARD_TYPE=5 // 新奖励类型
- 在RogueConfig.ts中定义事件:如果涉及新事件类型,需在
EventType枚举中添加
NEW_EVENT = "new_event"
- 更新事件概率配置:在
EventConfig中添加新事件的触发概率
代码集成步骤
-
修改MissionComp.ts:
- 在
game_data对象中添加新奖励字段 - 在
do_drop方法中处理新奖励类型的逻辑 - 确保
rewards数组能正确存储新类型的奖励物品
- 在
-
更新VictoryComp.ts:
- 在
onAdded方法中处理新奖励数据 - 更新界面展示逻辑以支持新奖励类型的显示
- 在
-
添加事件处理:在
MissionMonComp或相关组件中添加对新奖励事件的处理逻辑 -
更新UI配置:确保GUI系统能正确加载和显示新奖励类型的界面元素
通过以上步骤,可以无缝集成新的奖励类型,系统的设计具有良好的扩展性,新增奖励类型不会影响现有功能的稳定性。
Section sources