在卡牌游戏中,无论是《炉石传说》、《杀戮尖塔》还是无数后起之秀,核心魅力都源于卡牌效果之间那令人眼花缭乱的连锁反应。一张卡牌打出,可能引发一连串的事件,例如召唤一个单位,单位的战吼触发了奥秘,奥秘的效果改变了费用,从而让玩家能打出下一张本不可能使用的卡牌。
对玩家而言,这是一种“神抽”、“combo”和“运营”的乐趣,而对开发者而言,这背后则是一套需要极致精密与健壮的系统工程。
一张卡牌上的描述,如“对所有敌人造成 3 点伤害,每有一个友方单位,伤害便 +1”,包含着目标筛选、数值计算、状态查询和效果执行等一系列复杂逻辑。如果将每个效果都单独硬编码,系统将很快变得庞杂,难以维护。
因此,本文将基于四川大学三山工作室的独立卡牌游戏《巷神》,深入剖析其内在的设计思想,将其提炼为一套通用的、可应用于绝大多数卡牌游戏或 RPG 技能系统的设计模式。
原子指令
任何复杂的卡牌效果,究其本质,都是一个或多个基础操作的有序组合。我们将这些不可再分的基础操作定义为原子指令。本文将其归纳为以下几个通用类别:
数值资源修改
这是最基础、最核心的指令。几乎所有卡牌游戏玩法都围绕着核心资源的修改而进行。
| 应用场景 | 指令示例 | 说明 |
|---|---|---|
| 生命 / 伤害(怪物) | Modify_Resource(Target_Monster, HP, -Y) | HP 等同于怪物生命值,-Y 为对怪物造成的伤害 |
| 生命 / 恢复(玩家) | Modify_Resource(Player, HP, +10) | +10 为恢复玩家 10 点生命值 |
| 费用 / 能量 | Modify_Resource(Player, Mana, ±X) | 对应玩家出牌消耗的法力水晶、能量点等资源,±X 为增减数值 |
| 特殊计数器 | Modify_Resource(Character, Counter, ±N) | 对应游戏内临时资源或计数,如额外行动点、角色怒气值、连击点数等 |
通过将所有数值变动统一到这个原子指令下,我们可以轻松地管理、追踪和响应任何资源的增减事件,为后续的触发机制(例如“每当一个角色受到伤害时...”)打下基础。
永久属性修改
与临时性的资源不同,属性是角色的内在能力基线,通常在游戏开始或通过特定升级事件发生改变。
| 应用场景 | 指令示例 | 说明 |
|---|---|---|
| 核心能力属性 | Modify_Attribute(Player, Max_HP, +10) | 提升玩家生命上限,增强生存能力 |
| 核心能力属性 | Modify_Attribute(Player, Max_Hand_Size, +1) | 提升玩家手牌上限,增加策略深度 |
| 衍生属性(光环效果) | Modify_Attribute(Ally_Unit, Attack, +1) | 所有友方单位攻击力 + 1,为永久光环类效果 |
| 其他衍生属性 | Modify_Attribute(Target, Defense/CritRate/RefreshRate, ±X) | 可用于调整防御力、暴击率、卡牌刷新概率等属性 |
将属性与资源区分开,有助于构建更清晰的角色成长和永久增益体系。
实体与场面操控
卡牌游戏的核心是场面。召唤、摧毁、变形、移动场上的实体(卡牌、单位、道具等)是必不可少的原子指令。
| 指令原型 | 指令格式 | 应用场景 |
|---|---|---|
| Card_Spawn | Card_Spawn(目标位置, 卡牌 ID, 数量) | 生成 / 召唤 / 获得卡牌 |
| Card_Destroy | Card_Destroy(目标, 类型) | 移除 / 摧毁卡牌 |
| Card_Replace | Card_Replace(原卡牌 ID, 新卡牌 ID) | 替换 / 变形 / 升级卡牌 |
| Card_Refresh | Card_Refresh(目标区域/状态) | 刷新抽卡、重置行动与状态 |
状态管理
状态(Buff / Debuff)是为游戏增加策略深度和动态变化的核心机制。
| 指令原型 | 指令格式 | 应用场景 |
|---|---|---|
| Add_Buff | Add_Buff(目标, 状态 ID, 持续回合数) | 附加增益效果 |
| Add_Debuff | Add_Debuff(目标, 状态 ID, 持续回合数) | 附加减益与规则限制 |
| 持续效果(DoT / HoT) | Add_Debuff(目标, Bleed, N) | 回合结束时触发持续伤害 |
流程控制与交互
这类指令直接干预游戏的基础流程和核心交互规则。
| 指令原型 | 指令格式 | 作用 | 应用解析 |
|---|---|---|---|
| End_Turn | End_Turn() | 回合控制 | 直接结束当前回合,干预游戏流程,用于抢占节奏或跳过危险阶段 |
| Prevent_Death | Prevent_Death(恢复到的生命值) | 死亡豁免 | 生命值即将≤0 时中断死亡结算,强制执行 Modify_Resource 保证单位存活 |
| Block_Attack | Block_Attack(目标, 减免值/次数) | 伤害格挡 / 防御 | 防御卡核心指令,如抵抗一次攻击,抵消一次 Modify_Resource(Player, HP, ...) |
| Record_Action | Record_Action(玩家操作/卡牌行为) | 行为记录与复现 | 记录玩家动作,后续可触发复现,支撑回响、模仿、回溯类卡牌效果 |
通过建立一套正交、完备的原子指令集,我们成功地将卡牌效果的“语义”与“执行”分离开。策划不需要关心代码实现,这极大地提升了开发效率,并从根本上保证了系统的一致性和可维护性。
事件总线与触发时机
触发时机定义了什么时候做。一个强大的效果系统,必须有一个清晰、有序、可预测的游戏事件流。我们将这个系统称为事件总线。
游戏进程可以被看作一条单向流动的时间线,其上镶嵌着无数个离散的事件节点。卡牌、技能、 Buff 的被动效果,就是注册在这些节点上的“监听器”。当游戏进程到达某个节点时,总线会广播一个事件,所有监听该事件的效果便会依次触发。
本文构建了一个非常典型的、适用于大多数回合制卡牌游戏的时序模型:
游戏 / 关卡初始化阶段
| 指令原型 | 作用 | 应用实例 |
|---|---|---|
| On_Game_Start | 游戏全局启动事件,建立初始全局状态 | 1. 触发天赋树永久属性加成,执行 Modify_Attribute2. 游戏开始时通过 Card_Replace 修改初始牌库 |
| On_Stage_Start | 关卡 / 阶段启动事件,初始化当前场景 | 初始化关卡怪物,实体生成时附加初始状态 |
| On_Spawn | 实体进场 / 生成时触发 | 实体生成时自动执行增益 |
回合开始 / 刷新阶段
| 事件原型 | 作用 | 应用实例 |
|---|---|---|
| On_Turn_Start | 新回合开始时统一结算,处理资源恢复、持续效果与触发类机制 | 1. 基础抽卡与场面刷新:执行 Card_Refresh2. 回合开始资源恢复:执行 Modify_Resource(Player, Mana, +X)3. 持续伤害 / 治疗(DoT / HoT)结算 4. 条件触发效果: On_Turn_Start → If (Def_Cards >= 3) Then …,搭配状态查询(Query)判断防御牌数量 |
玩家主动阶段
| 事件原型 | 作用 | 应用实例 |
|---|---|---|
| On_Card_Play | 玩家打出卡牌时触发,为核心决策与交互阶段,驱动卡牌主效果执行 | 先执行费用消耗 Cost(Mana, X),选定目标后结算伤害 Modify_Resource(Target, HP, -Y) |
| Before_Card_Play | 打出前预处理阶段,用于前置判定、限制与拦截 | 可用于检测是否满足出牌条件、限制目标类型等 |
| On_Cost_Calculation | 费用计算阶段,专门用于费用修改类效果 | 天赋树的费用减免节点,在此阶段修改费用结果 |
| On_Target_Selection | 目标选择阶段,用于干预或锁定目标 | 强制指定目标、禁止选择某类目标、自动选择最优目标 |
| After_Card_Play | 卡牌主效果结算完毕后触发,用于追加额外效果 | 打出进攻卡后,额外执行 Modify_Resource 造成追加伤害 |
敌人行动 / 回合结束阶段
| 事件原型 | 作用 | 应用实例 |
|---|---|---|
| On_Turn_End | 回合结束统一结算:怪物行动、持续效果、状态计时、手牌管理 | 1. 怪物攻击结算:If (Not_Blocked) Then Modify_Resource(Player, HP, ...)2. 持续伤害(流血): On_Turn_End → Modify_Resource(Buffed_Monster, HP, -1)3. Buff / Debuff 持续回合数减 1 |
反应式阶段
| 事件原型 | 作用 | 应用实例 |
|---|---|---|
| On_Take_Damage | 目标受到伤害时触发,用于反伤、受击回血、护盾判定等 | 玩家受击后回血 Modify_Resource(Player, HP, +X) |
| On_Entity_Destroyed | 单位 / 卡牌被摧毁时触发,用于掉落、击杀奖励、连锁效果 | 怪物被击杀后掉落资源 |
| On_Heal | 目标获得治疗时触发,可用于治疗增幅、触发被动等 | 治疗过量时转化为护盾、治疗后附加增益状态 |
| On_Card_Draw | 抽牌时触发,用于抽牌加成、追加效果、牌序操控 | 抽牌时额外获得资源、抽特定牌时触发额外效果 |
| 链式事件 | 动作触发事件 → 事件触发新动作,形成连锁逻辑 | On_Card_Play → 伤害 → On_Take_Damage → 击杀 → On_Entity_Destroyed → 获得资源 |
事件总线将游戏流程转变为一个事件驱动的网状结构。这种设计不仅让逻辑关系一目了然,更重要的是提供了可拓展的挂载点,让新卡牌、新机制的开发变成了实现一个监听器,并将其挂载到正确的事件节点上的简单操作,极大地增强了系统的可扩展性。
核心技术框架
事件总线与分发器
事件总线是一个全局或场景级的单例对象,负责:
- 事件注册: 任何游戏对象(卡牌、Buff、角色、管理器)都可以向事件总线“订阅”自己感兴趣的事件。
- 事件广播: 当某个事件发生时(例如,玩家生命值减少),伤害计算模块会调用
EventBus.Publish("On_Take_Damage", damageInfo)。 - 事件分发: 事件总线收到广播后,会遍历所有订阅了
On_Take_Damage事件的监听器,并调用它们注册的回调函数,将damageInfo作为参数传入。
这种设计模式实现了高内聚、低耦合的设计理念。伤害计算模块完全不需要知道卡牌的存在,它只负责广播“我受到了伤害”这一事实。反之,卡牌也不关心伤害是怎么来的,它只关心事件本身。这使得各个功能模块可以独立开发和测试。
标签系统
标签系统是实现效果目标筛选和规则判定的关键,它为游戏世界中的每一个对象都贴上了可供查询和分类的元数据。
- 卡牌标签: 这是最基础的应用。例如给玩家附加一个
Silence_Def的Debuff,这个Debuff会使所有带有【防御类】标签的卡牌无法被打出。 - 对象标签: 如【怪物】、【道具】。例如“摧毁所有怪物”的内部实现就是
Card_Destroy(Target_With_Tag("Monster"))。这比遍历所有场上实体再判断其类型要高效和优雅得多。 - 状态标签: 状态本身也可以有标签。例如【流血】标签则告诉回合结束管理器“这个单位需要在回合结束时结算持续伤害”。
标签系统支持了效果的动态查询,为策划提供了极大的灵活性,同时也降低了程序实现新功能的负担。
修饰器链与处理管道
对于游戏中频繁变化的动态数值(如伤害、费用、治疗量),使用修饰器链是一种极其强大的设计模式。它将一个数值的计算过程,从一个简单的赋值语句,变成了一个可被中途拦截和修改的数据处理管道。
以伤害计算为例,一个完整的流程应该是这样的:
- 原始事件触发: 怪物攻击,意图对玩家造成 5 点伤害。一个
DamageEvent对象被创建,包含{Source: Monster, Target: Player, BaseValue: 5}。 - 进入管道: 这个
DamageEvent被推入“伤害计算管道”。 - 第一层修饰器(减免): 系统检查玩家身上是否有伤害减免效果。该修饰器修改事件对象,
{..., BaseValue: 5, FinalValue: 4}。 - 第二层修饰器(护盾): 系统检查玩家是否有护盾。如果存在,护盾修饰器可能将
FinalValue直接置为 0,并消耗一层护盾。 - 第三层修饰器(易伤): 如果玩家此时有“受到的伤害 + 50%”的
Debuff,另一个修饰器会读取FinalValue,计算后将其修改为4 * 1.5 = 6。 - 管道输出: 事件对象流出管道,最终的
FinalValue为 6。 - 最终执行: 系统执行
Modify_Resource(Player, HP, -6)。 - 后处理事件: 伤害执行后,广播
On_Take_Damage事件,此时后处理事件才会响应,进行回血等操作。
这个管道模型同样适用于费用计算等。
修饰器链的优势在于,它将复杂的、顺序敏感的数值计算,分解成了一系列独立、可插拔、可排序的修饰器。增加一个新的减伤 Buff,只需要开发一个新的修饰器并插入到管道的正确位置即可,完全不会影响其他伤害效果的逻辑。
构建一套健壮的卡牌游戏效果系统,其核心在于解耦与抽象。通过将庞杂的卡牌描述拆解为正交的原子指令,以标签系统作为目标筛选的锚点,再利用全局事件总线与修饰器管道搭建起灵活的事件流与数据流,我们将原本牵一发而动全身的硬编码逻辑,转化为了一套高度模块化、可插拔的规则引擎。
这套设计模式不仅大幅降低了后期的开发与维护成本,赋予了策划极大的配置自由度,更为游戏未来海量卡牌机制的平滑迭代与无限 combo 的组合碰撞奠定了坚实的技术基础。