做网上夫妻去哪个网站关键词排名优化教程
更新日期:2024年6月20日。
项目源码:第五章发布(正式开始游戏逻辑的章节)
索引
- 简介
- 要诀数据集(AbilityDataSet)
- 一、定义要诀数据集类
- 二、要诀属性
- 1.要诀类型
- 2.攻击距离
- 3.基础命中、暴击率
- 4.基础属性加成
- 5.要诀特效
- 三、要诀显示面板
- 要诀(Ability)
- 一、定义要诀类
- 二、角色挂载要诀
- 三、要诀栏位
- 四、要诀栏位激活
简介
上一篇有了角色
类, 这一章我们将实现要诀(也即是技能,只是这里披了另一个名字)
类,同理,要诀也应当有要诀数据集
类,用以描述要诀属性中的共用部分。
要诀数据集(AbilityDataSet)
与角色同理,比如20个士兵,当他们需要使用同一个要诀时,理应只需要持有同一个要诀数据集
即可。
一、定义要诀数据集类
首先,我们定义要诀数据集类AbilityDataSet
:
/// <summary>/// 要诀数据集/// </summary>[Serializable][CreateAssetMenu(menuName = "HTFramework/★ GameComponent/RPG2D/Ability Asset", order = 300)]public sealed class AbilityDataSet : DataSetBase{}
二、要诀属性
根据前车之鉴,我们先定义如下一系列属性:
public sealed class AbilityDataSet : DataSetBase{/// <summary>/// 要诀名称/// </summary>[Label("要诀名称")] public string Name;/// <summary>/// 要诀类型/// </summary>[Label("要诀类型")] public AbilityType Type;/// <summary>/// 攻击距离/// </summary>[Label("攻击距离"), Display("IsNoBuffProperty")] public int AttackDistance = 1;/// <summary>/// 基础命中率/// </summary>[Label("基础命中率"), Display("IsAttackProperty")] public int BaseHitRate = 100;/// <summary>/// 基础暴击率/// </summary>[Label("基础暴击率"), Display("IsAttackProperty")] public int BaseCriticalRate = 100;/// <summary>/// 速度加成/// </summary>[Label("速度加成"), Display("IsBuffProperty")] public int AddedSpeed;/// <summary>/// 生命加成(初始值)/// </summary>[Label("生命加成(初始值)"), Display("IsBuffProperty")] public int AddedHP;/// <summary>/// 攻击加成(初始值)/// </summary>[Label("攻击加成(初始值)")] public int AddedATK;/// <summary>/// 防御加成(初始值)/// </summary>[Label("防御加成(初始值)"), Display("IsBuffProperty")] public int AddedDEF;/// <summary>/// 敏捷加成(初始值)/// </summary>[Label("敏捷加成(初始值)"), Display("IsBuffProperty")] public int AddedDEX;/// <summary>/// 会心加成(初始值)/// </summary>[Label("会心加成(初始值)"), Display("IsBuffProperty")] public int AddedCRI;/// <summary>/// 生命加成(成长值)/// </summary>[Label("生命加成(成长值)"), Display("IsBuffProperty")] public int AddedHP_Inc;/// <summary>/// 攻击加成(成长值)/// </summary>[Label("攻击加成(成长值)")] public int AddedATK_Inc;/// <summary>/// 防御加成(成长值)/// </summary>[Label("防御加成(成长值)"), Display("IsBuffProperty")] public int AddedDEF_Inc;/// <summary>/// 敏捷加成(成长值)/// </summary>[Label("敏捷加成(成长值)"), Display("IsBuffProperty")] public int AddedDEX_Inc;/// <summary>/// 会心加成(成长值)/// </summary>[Label("会心加成(成长值)"), Display("IsBuffProperty")] public int AddedCRI_Inc;/// <summary>/// 要诀描述/// </summary>[Label("要诀描述"), TextArea(2, 10)] public string Description;/// <summary>/// 要诀特效/// </summary>[Label("要诀特效"), Display("IsNoBuffProperty")] public AbilityEffect Effect;}
接下来我们强行解释。
1.要诀类型
要诀类型
可分为如下三种:
/// <summary>/// 要诀类型/// </summary>public enum AbilityType{/// <summary>/// 攻击型(攻击敌方)/// </summary>[Remark("攻击型")]Attack,/// <summary>/// 加成型(被动生效,永久提升属性)/// </summary>[Remark("加成型")]Buff,/// <summary>/// 治疗型(治疗友方)/// </summary>[Remark("治疗型")]Health}
2.攻击距离
这是一个相当重要的属性,他决定了攻击、治疗(忽略加成)
型要诀的另一个重要归类
:
攻击距离 | 归类 | 原地使用 | 移动后立即使用 |
---|---|---|---|
1 | 近程攻击、治疗要诀 | 能 | 能 |
>1 | 远程攻击、治疗要诀 | 能 | 不能 |
也即是说,角色移动后是无法立即使用远程攻击、治疗要诀
的,这避免了无脑风筝对手,此对战机制照搬第二次超级机器人大战。
3.基础命中、暴击率
基础命中率、基础暴击率为此要诀在计算命中率、暴击率
时的基础值,基础命中率、基础暴击率越高的要诀,越容易命中、暴击对手。
4.基础属性加成
加成型
要诀可提供速度、生命、攻击、防御、敏捷、会心
等基础属性的加成,同时伴有成长值,随角色等级成长,同理,速度
是不能成长的。
5.要诀特效
攻击、治疗型
要诀会在释放时播放对应的特效,这里我们使用另一个类AbilityEffect
来描述,其挂载到GameObject
上后,再将创建后的预制体引入到此处便可:
/// <summary>/// 要诀特效/// </summary>[DisallowMultipleComponent]public class AbilityEffect : HTBehaviour{/// <summary>/// 发射特效(发射时播放,可为空)/// </summary>[Label("发射特效"), SerializeField] protected PlayableDirector LaunchEffect;/// <summary>/// 飞行特效(飞行中播放,如果是近程攻击型,不需要远距离飞行,可为空)/// </summary>[Label("飞行特效"), SerializeField] protected PlayableDirector FlightEffect;/// <summary>/// 击中特效(击中时播放,可为空)/// </summary>[Label("击中特效"), SerializeField] protected PlayableDirector HitEffect;/// <summary>/// 飞行特效飞行时间/// </summary>[Label("飞行特效飞行时间"), SerializeField] protected float FlightTime = 1;/// <summary>/// 发射音效/// </summary>[Label("发射音效"), SerializeField] protected AudioClip LaunchAudio;/// <summary>/// 击中音效/// </summary>[Label("击中音效"), SerializeField] protected AudioClip HitAudio;}
这里的特效采用TimeLine(PlayableDirector)
配置。
播放特效时,应当是具备延时性的,所以我们定义播放特效
为协程方法:
public class AbilityEffect : HTBehaviour{/// <summary>/// 播放特效/// </summary>/// <param name="self">要诀所属角色</param>/// <param name="other">要诀释放的目标角色</param>public virtual IEnumerator PlayEffect(Role self, Role other){//初始所有特效不可见gameObject.SetActive(true);if (LaunchEffect != null) LaunchEffect.gameObject.SetActive(false);if (FlightEffect != null) FlightEffect.gameObject.SetActive(false);if (HitEffect != null) HitEffect.gameObject.SetActive(false);//发射特效if (LaunchEffect != null){LaunchEffect.gameObject.SetActive(true);LaunchEffect.transform.position = self.transform.position;LaunchEffect.Play();if (LaunchAudio != null) Main.m_Audio.PlayOneShoot(LaunchAudio);yield return YieldInstructioner.GetWaitForSeconds((float)LaunchEffect.duration);LaunchEffect.gameObject.SetActive(false);yield return null;}//飞行特效(飞行特效应当是连贯的,除非让飞行时间完全等于特效的播放时长)if (FlightEffect != null){FlightEffect.gameObject.SetActive(true);FlightEffect.transform.position = self.transform.position;FlightEffect.Play();FlightEffect.transform.DOMove(other.transform.position, FlightTime);yield return YieldInstructioner.GetWaitForSeconds(FlightTime);FlightEffect.gameObject.SetActive(false);yield return null;}//击中特效(击中特效应当始终播放,即便未命中敌人)if (HitEffect != null){HitEffect.gameObject.SetActive(true);HitEffect.transform.position = other.transform.position;HitEffect.Play();if (HitAudio != null) Main.m_Audio.PlayOneShoot(HitAudio);yield return YieldInstructioner.GetWaitForSeconds((float)HitEffect.duration);HitEffect.gameObject.SetActive(false);yield return null;}//直接销毁特效,由于特效分类较多,就不使用对象池缓存了,当然切换成对象池也很简单Destroy(gameObject);}}
三、要诀显示面板
在要诀数据集
中,有些属性是攻击型
特有的,有些属性又是加成型
特有的,我们如何做到在检视器面板根据要诀类型只显示对它有效的属性(毕竟全都显示太过杂乱)?
这就需要HT.Framework.Display
特性了。
比如攻击距离
这个属性,很明显只有攻击、治疗型
才有效:
/// <summary>/// 攻击距离/// </summary>[Label("攻击距离"), Display("IsNoBuffProperty")] public int AttackDistance = 1;
那么此处为其标记了Display
特性,特性指定的方法将作为此属性的显示规则,比如此处的方法IsNoBuffProperty
,根据字面意义可理解为:不是加成型要诀的属性。
而事实上它的判断规则也就是这样的:
#if UNITY_EDITOR/// <summary>/// 是否为非Buff要诀属性/// </summary>private bool IsNoBuffProperty(){return Type != AbilityType.Buff;}
#endif
Display
特性仅在编辑器生效(以避免运行时多余开销),所以IsNoBuffProperty
方法也请包含在UNITY_EDITOR
宏定义中。
那么最终,我们的要诀数据集
检视器面板显示如下:
要诀(Ability)
要诀数据集
有了,同样要使用一个要诀
类来持有它。
一、定义要诀类
/// <summary>/// 要诀/// </summary>[DisallowMultipleComponent]public class Ability : HTBehaviour{/// <summary>/// 所属的角色/// </summary>[Label("所属的角色")] public Role BelongRole;/// <summary>/// 要诀栏位/// </summary>[Label("要诀栏位")] public int Index = 1;/// <summary>/// 要诀数据集/// </summary>[Label("要诀数据集"), SerializeField] internal AbilityDataSet DataSet;}
要诀(Ability)
类应当再次被角色(Role)
类所持有,如此才能形成完备的工作链,所以Ability
必须知道它所属的角色
是谁。
目前我的想法是为角色标配8个要诀栏位,所以每个要诀必须知道自身所属的要诀栏位
。
二、角色挂载要诀
上面说了要诀
将被角色
持有,所以我们将焦点切回到Role
类,以定义一些新东西:
public class Role : HTBehaviour{/// <summary>/// 要诀(持有的8个要诀)/// </summary>[Label("要诀")] public Ability[] Abilities;}
然后,挂载要诀(Ability)
类的物体,应当是角色(Role)
类的物体的子级,如下这样:
三、要诀栏位
这8个子级物体,也即是代表了8个要诀栏位
,要诀栏位检视面板如下:
要诀栏位
持有何种要诀数据集
,便决定了此栏位成为何种要诀,如果为null,则是一个空位置,可以学习其他要诀(要诀研习系统
)。
四、要诀栏位激活
8个要诀栏位
并非一出生就都激活,不然几个1级的角色也能7、8个要诀打来打去,打得天昏地暗,而所有要诀都有了,也将降低升级带来的成就感。
所以初步定义一个要诀栏位激活规则:
- 要诀栏位1:初始激活。
- 要诀栏位2:初始激活。
- 要诀栏位3:初始激活。
- 要诀栏位4:角色20级后激活。
- 要诀栏位5:角色40级后激活。
- 要诀栏位6:角色60级后激活。
- 要诀栏位7:角色80级后激活。
- 要诀栏位8:角色100级后激活。
此规则的核心奥义是:
初始3个要诀够用,每提升20级将拥有一个新的要诀栏位,使得每多出一个要诀栏位都弥足珍贵,而在角色设计之初就可以为其赋予8个完整的要诀,只是要诀栏位未激活时,要诀是不可用的。
代码实现:
/// <summary>/// 要诀位是否激活/// </summary>[PropertyDisplay("要诀位是否激活")]public virtual bool IsActive{get{if (DataSet != null){switch (Index){case 1:case 2:case 3:return true;case 4:return BelongRole.Grade >= 20;case 5:return BelongRole.Grade >= 40;case 6:return BelongRole.Grade >= 60;case 7:return BelongRole.Grade >= 80;case 8:return BelongRole.Grade >= 100;}return false;}else{return false;}}}
好了,要诀部分暂时就这些,不过我脑海中灵光一闪,第二次机战中耳熟能详的间无
机制以及二反
机制瞬间就有了照搬的方案,我将其命名为特殊加成型要诀
,那么,期待下一次登场吧!