MDD
Buff系统是一个万能系统,它最全能,也最无能。
概念
Buff这个词的起源似乎已经无从考证,貌似在电子游戏出现之前的桌游中就已存在。
现今约定俗成的buff debuff代表着很多游戏内的增益减益效果,已经是游戏制作中不可或缺的概念之一。
我愿将buff描述为一种外来的力量,他不是个体本身所拥有的,更像是一种从天而降附加在个体上的力量。
在MDDSkillEngine中也是以“buff是个体的外来力量”为基调,对buff系统进行实现。
buff系统的全能性
作为一个天降之物,一个buff内有什么皆有可能。
在程序实现中亦是如此,如果你愿意抽风,在一个buff中实现一个虚拟机跑一门自创语言也是可行的
buff系统的无能
越全能,越意味着什么都没有。
所以在程序实现中,buff本身空无一物如同一张白纸。
如果有着其他健壮的系统供buff系统调用,那么buff系统将很容易实现各种功能。
如果没有则一切都约等于从零开始。
MDDSkillEngine对buff系统全能性的刻意削弱
因为buff系统的不可或缺和其全能性,所以不可避免的会有”技能就是buff,buff就是技能“这种问题。
当然这个问题本身也是构建技能系统的方案之一。
但是我并不想这么做,所以通过概念上的约束来规避这个问题。
具体约束如下图:
即buff可以是一个技能的组成部分,但buff绝对不能完全代表一个技能。
BUFF系统的程序结构设计
BuffSystem Runtime
基础架构简图
单个buff基类
namespace MDDGameFramework
{
public abstract class BuffBase : IReference
{
private object m_Target;
private object m_From;
public BuffDatabase buffData;
/// <summary>
/// buff目标
/// </summary>
public object Target
{
get { return m_Target; }
protected set { m_Target = value; }
}
/// <summary>
/// buff释放者
/// </summary>
public object From
{
get { return m_From; }
protected set { m_From = value; }
}
/// <summary>
/// buff初始化
/// </summary>
/// <param name="buffSystem">归属的buff系统</param>
/// <param name="Target">buff的目标</param>
/// <param name="From">释放的buff的实体</param>
/// <param name="buffDatabase">basedata</param>
/// <param name="userData">用户自定义数据</param>
public virtual void OnInit(IBuffSystem buffSystem, object Target, object From, BuffDatabase buffDatabase = null, object userData = null)
{
m_Target = Target;
m_From = From;
buffData = buffDatabase;
}
/// <summary>
/// buff执行
/// </summary>
/// <param name="buffSytem"></param>
public abstract void OnExecute(IBuffSystem buffSytem);
/// <summary>
/// buff轮询
/// </summary>
/// <param name="buffSystem"></param>
/// <param name="elapseSeconds"></param>
/// <param name="realElapseSeconds"></param>
public virtual void OnUpdate(IBuffSystem buffSystem, float elapseSeconds, float realElapseSeconds)
{
buffData.PassDuration += elapseSeconds;
buffData.AccumulateDuration += elapseSeconds;
if (buffData.Duration == -1)
{
//持续时间为-1则默认为永久buff
}
else if (buffData.Duration <= buffData.PassDuration)
{
BuffSystem system = (BuffSystem)buffSystem;
system.Finish(this);
}
}
/// <summary>
/// buff物理轮询轮询
/// </summary>
/// <param name="buffSystem"></param>
/// <param name="elapseSeconds"></param>
/// <param name="realElapseSeconds"></param>
public virtual void OnFixedUpdate(IBuffSystem buffSystem, float elapseSeconds, float realElapseSeconds)
{
}
/// <summary>
/// buff结束
/// </summary>
/// <param name="buffSystem"></param>
public virtual void OnFininsh(IBuffSystem buffSystem)
{
buffData.PassDuration = 0f;
}
/// <summary>
/// buff刷新
/// </summary>
/// <param name="buffSystem"></param>
public virtual void OnRefresh(IBuffSystem buffSystem) { }
public virtual void Clear()
{
ReferencePool.Release(buffData);
buffData = null;
}
}
}
buff数据基类
namespace MDDGameFramework
{
/// <summary>
/// buff通用数据
/// </summary>
public abstract class BuffDatabase :IReference
{
private int m_Id;
private int m_Level;
private string m_Name;
private bool m_CanOverlying;
private float m_Duration;
private float m_PassDuration;
private float m_accumulateDuration;
/// <summary>
/// buffID
/// </summary>
public int Id
{
get { return m_Id; }
}
public int UId
{
get { return GetHashCode(); }
}
/// <summary>
/// buffname
/// </summary>
public string Name
{
get { return m_Name; }
set { m_Name = value; }
}
/// <summary>
/// buff等级
/// </summary>
public int Level
{
get { return m_Level; }
}
/// <summary>
/// 持续时间
/// 永久buff持续时间默认为-1
/// </summary>
public float Duration
{
get { return m_Duration; }
}
/// <summary>
/// buff在单次持续时间内已经持续的时间
/// </summary>
public float PassDuration
{
get { return m_PassDuration; }
set { m_PassDuration = value; }
}
/// <summary>
/// 积累时间
/// </summary>
public float AccumulateDuration
{
get { return m_accumulateDuration; }
set { m_accumulateDuration = value; }
}
/// <summary>
/// 是否可叠加
/// </summary>
public bool CanOverlying
{
get { return m_CanOverlying; }
set { m_CanOverlying = value;}
}
/// <summary>
/// buff行进百分比
/// 若是永久buff则直接返回百分之百
/// 永久buff持续时间默认为-1
/// </summary>
public float DurationRadio
{
get
{
return m_Duration > 0 ? m_PassDuration / m_Duration : 1f;
}
}
public virtual void Clear()
{
m_Id = 0;
m_Level = 0;
m_accumulateDuration = 0;
m_Duration = 0;
m_PassDuration = 0f;
}
public void Init(int id,string name,int level,float duration)
{
m_Id = id;
m_Name = name;
m_Level = level;
m_Duration = duration;
}
}
}
buff生命周期展示流程图
至此一个通用性极强的buff系统runtime构建完成
特化的业务逻辑实现继承buff数据基类以及buff基类具体实现即可
BuffSystem Editor
数据编辑规则不具有通用性
buff系统的数据构建规则很可能不具有通用性
因为不同游戏的buff业务需求或多或少都会有差异,甚至可能是大相径庭的两种规则
但是数据编辑方案还是有通用性的
Excel&NodeEditor
在具体的游戏业务需求中buff和buff之间也许会有很强的关联性。
所以NodeEditor是一种非常好的方案去构建buff数据,具体可以参照肛少的用法。
而Excel则是国内的游戏数据管理编辑大柱国,没什么好说的。
runtime中的临时数据当然也可能会是构建buff数据的关键组成部分,例如buff的效果强弱需要根据游戏中hero的实时属性进行调整。
MDDSkillEngine使用了Excel流程做了一个简易的数据流。
综上所述
对于BuffSystemEditor的部分 我们只需要根据游戏业务需求选好适合自己的数据编辑方案。
然后把编辑好的数据根据具体业务需求的规则注入buff中即可。
做法有无数种。
选择适合自己的就好。
Comments | NOTHING