【Unity】动作游戏开发实战详细分析-15-可扩展的战斗系统
【Unity】动作游戏开发实战详细分析-15-可扩展的战斗系统
系统设计
攻击信息传递
通常情况下,伤害、属性、判定都会被封装到类中,在触发动画事件后将战斗信息发送给受击者。
我们可以结合Unity碰撞/触发,在发生事件后获取对应信息,而非主动将战斗信息发送给目标,这有利于后期受击判定的调试。
战斗系统设计
通过下图的组件模式设计战斗系统,并通过统一的回调函数进行战斗信息发送
只需要将所有组件的信息函数绑定到BattleObject的回调函数进行统一调用。
通过这样的组件模式构建可扩展的战斗系统,以及高度自定义的战斗系统。
设计所有组件的基类,并让所有组件通过它进行泛化,即可实现扩展。
战斗组件基类
基类是一个抽象类,它是所有组件的抽象。
具备基本字段,战斗对象(存储挂载的战斗对象本身),并在初始化函数中进行赋值。
public abstract class BattleObjectComponentBase : MonoBehaviour
{protected BattleObject mBattleObject;public virtual void Initialization(BattleObject battleObject)//初始化{mBattleObject = battleObject;}
}
战斗阵营
这是一个静态类,设计所有的战斗阵营,依次对队友与敌人进行区分。
public static class EasyFactionConst
{public const int PLAYER = 1;//玩家public const int ENEMY = 2;//敌人
}
战斗对象类
战斗对象类是个十分重要的类。
首先是基本字段
- 战斗组件数组,存储该战斗对象存储的所有战斗组件
- 战斗阵营
- 接触其他阵营的回调事件,用于传递战斗信息
- 常规回调事件,可用于处理同阵营的Buff等,这是一个基础回调事件。在本示例中,该事件会对所有阵营反馈调用。
其次是函数
- 获取组件函数,这是一个泛型函数,用于获取该战斗对象上的任意战斗组件。
- 遍历所有组件,返回对应的组件
- 在Awake函数中初始化所有战斗组件
- 触发器函数
- 首先尝试获取战斗对象组件,并判断战斗阵营,其次就是回调事件的调用。
public class BattleObject : MonoBehaviour
{[SerializeField]BattleObjectComponentBase[] battleObjectComponents = new BattleObjectComponentBase[0];public int faction = EasyFactionConst.PLAYER;public event Action<BattleObject, BattleObject> OnContactOtherFaction;//接触到其他阵营public event Action<BattleObject, BattleObject> OnBattleTriggerEnter;//常规碰撞回调public T GetBattleObjectComponent<T>()//获取战斗对象组件where T : BattleObjectComponentBase{var result = default(T);for (int i = 0; i < battleObjectComponents.Length; i++){var item = battleObjectComponents[i];if (item.GetType() == typeof(T))//匹配对应类型{result = item as T;break;}}return result;}protected virtual void Awake(){for (int i = 0; i < battleObjectComponents.Length; i++)battleObjectComponents[i].Initialization(this);//初始化战斗对象组件}void OnTriggerEnter(Collider collider){var otherBattleObject = collider.transform.GetComponent<BattleObject>();if (otherBattleObject == null) return;//战斗对象过滤if (otherBattleObject.faction != faction){if (OnContactOtherFaction != null)OnContactOtherFaction(this, otherBattleObject);//接触到其他阵营}if (OnBattleTriggerEnter != null)OnBattleTriggerEnter(this, otherBattleObject);//常规进入回调}
}
伤害传递组件
我们来添加伤害传递组件
该组件具有基本字段
- 生命值,战斗数值比较复杂可自行定义类
- 攻击状态判断
- 伤害值
- 上帝模式
- 死亡判断
- 事件(可根据自定义添加更多的回调事件)
- HP改变回调事件
- 死亡回调事件
- 受伤回调事件
- 攻击成功回调事件
- 击杀回调事件
这些事件,只需要通过其他脚本进行回调绑定即可完成基本动作行为,包括:死亡,受伤,攻击,击杀等等,并且可以通过回调函数完成很多能力判定,例如,生命值改变触发能力增强,击杀触发生命值吸收等等。
函数说明
重写初始化事件,并绑定战斗对象的阵营接触回调事件
生命值改变函数
阵营接触回调函数
- 伤害传递,调用其他阵营对象的生命值改变函数,受伤事件,判断死亡事件等等。
这里有一点需要重点说明,如果攻击方式触发动画事件创造了带有伤害信息的预制体来造成伤害,那么那个预制体上就必须要带有伤害传递组件,因为这是一个战斗对象。
其次还需要配置动画事件,初始化预制体的战斗阵营(可以在之前的动画事件接收脚本里完成),以及自我销毁脚本等等功能。
伤害传递组件具备了受伤功能,伤害功能,死亡,受伤等功能。
public class DamageBattleComponent : BattleObjectComponentBase
{public int hp;//生命值public bool isAttackState;//是否为攻击状态public int toEnemyDamage;//给予敌人伤害public bool godMode;//上帝模式bool mIsDied;/// <summary>/// 参数1 - 事件发送者, 参数2 - 旧的HP值, 参数3 - 新的HP值/// </summary>public event Action<BattleObject, int, int> OnHPChanged;//HP改变事件/// <summary>/// 参数1 - 事件发送者。/// </summary>public event Action<BattleObject> OnDied;//死亡事件/// <summary>/// 参数1 - 事件发送者,参数2 - 攻击者, 参数3 - 伤害值/// </summary>public event Action<BattleObject, BattleObject, int> OnHurt;//受伤事件/// <summary>/// 参数1 - 事件发送者, 参数2 - 受击者/// </summary>public event Action<BattleObject, BattleObject> OnAttackCompleted;//攻击成功事件/// <summary>/// 参数1 - 事件发送者, 参数2 - 受击者/// </summary>public event Action<BattleObject, BattleObject> OnKilled;//击杀事件public override void Initialization(BattleObject battleObject)//初始化函数{base.Initialization(battleObject);battleObject.OnContactOtherFaction += OnContactOtherFactionCallback;}public void ChangeHP(int newHP){newHP = Math.Max(newHP, 0);if (hp == newHP) return;if (OnHPChanged != null)OnHPChanged(mBattleObject, hp, newHP);hp = newHP;if (hp <= 0 && !mIsDied){if (OnDied != null)OnDied(mBattleObject);mIsDied = true;}}void OnContactOtherFactionCallback(BattleObject sender, BattleObject other)//接触不同阵营后的处理{var anotherDamageComponent = other.GetBattleObjectComponent<DamageBattleComponent>();if (anotherDamageComponent != null){if (toEnemyDamage <= 0) return;//没有伤害信息则跳出if (anotherDamageComponent.godMode) return;//上帝模式则跳出var isAlive_Before = anotherDamageComponent.mIsDied;anotherDamageComponent.ChangeHP(anotherDamageComponent.hp - toEnemyDamage);//扣血处理var isAlive_After = anotherDamageComponent.mIsDied;if (anotherDamageComponent.OnHurt != null)//敌方触发受伤回调anotherDamageComponent.OnHurt(other, sender, toEnemyDamage);if (anotherDamageComponent.OnAttackCompleted != null)//己方触发攻击成功回调anotherDamageComponent.OnAttackCompleted(sender, other);if (!isAlive_Before && isAlive_After)//若扣血之后死亡则己方触发击杀回调{if (OnKilled != null)OnKilled(mBattleObject, other);}}}
}
受击僵直组件
受击僵直组件
基本字段
- 僵直时间
- 僵直回调事件
方法说明
- 初始化函数
- 回调函数
该组件具备造成僵直能力与受到僵直的功能。
public class HitStopBattleComponent : BattleObjectComponentBase
{public float toEnemyHitStopTime;//赋予敌人的僵直时间/// <summary>/// 进入僵直状态事件,参数1 - 僵直时间/// </summary>public event Action<float> OnHitStopTriggered;public override void Initialization(BattleObject battleObject)//初始化函数{base.Initialization(battleObject);battleObject.OnContactOtherFaction += OnContactOtherFactionCallback;}void OnContactOtherFactionCallback(BattleObject sender, BattleObject other)//接触不同阵营后的处理{var anotherHitStopComponent = other.GetBattleObjectComponent<HitStopBattleComponent>();if (anotherHitStopComponent != null){if (toEnemyHitStopTime <= 0) return;//没有僵直信息则跳出if (anotherHitStopComponent.OnHitStopTriggered != null)//触发僵直事件,并传入僵直时间anotherHitStopComponent.OnHitStopTriggered(toEnemyHitStopTime);}}
}
这是一个与受击僵直组件耦合的脚本,它具有会绑定受击僵直组件的回调事件,它会单独处理僵直逻辑。
public class GenericHitStopProcess : MonoBehaviour
{public HitStopBattleComponent hitStopBattleComponent;public Animator animator;float mHitRecoverTimer;int mIsHitAnimatorHash;public bool HitStop { get { return mHitRecoverTimer > 0; } }//是否处于僵直状态void Awake(){hitStopBattleComponent.OnHitStopTriggered += OnHitStopTriggered;mIsHitAnimatorHash = Animator.StringToHash("IsHit");}void Update(){if (mHitRecoverTimer > 0)//简易的僵直度反馈animator.SetBool(mIsHitAnimatorHash, true);elseanimator.SetBool(mIsHitAnimatorHash, false);mHitRecoverTimer = Mathf.Max(0f, mHitRecoverTimer - Time.deltaTime);//僵直恢复}void OnHitStopTriggered(float hitStopValue){mHitRecoverTimer = Mathf.Max(mHitRecoverTimer, hitStopValue);//更新僵直时间}
}
物理位移组件
物理位移组件,它负责所有的战斗相关的物理移动能力:击退,浮空等等。
基本字段
- 力的类型
- 力度大小
- 角色驱动
- 物理行为回调事件
方法说明
- 初始化函数
- 施加力量函数(这是被动调用的函数,也就是受到物理位移攻击)
- 阵营接触回调函数
public class PhysicsBattleComponent : BattleObjectComponentBase
{public enum EType { Push, VerticalPush, AirPush, Max }public EType type;//力的类型public Vector3 forceValue;//力的值public CharacterMotor characterMotor;/// <summary>/// 当物理行为触发,参数1 - 类型/// </summary>public event Action<EType> OnPhysicBehaviourTriggered;public override void Initialization(BattleObject battleObject)//初始化函数{base.Initialization(battleObject);battleObject.OnContactOtherFaction += OnContactOtherFactionCallback;}public void SetForce(Vector3 forceVector, EType type)//设置力的传入{if (characterMotor == null) return;var upAxis = -Physics.gravity.normalized;switch (type){case EType.Push:characterMotor.SetForce(Vector3.ProjectOnPlane(forceVector, upAxis));//消除Y轴力break;case EType.VerticalPush:characterMotor.SetForce(Vector3.Project(forceVector, upAxis));//消除平面方向力break;case EType.AirPush:characterMotor.SetForce(forceVector);//直接将力赋予Motorbreak;}OnPhysicBehaviourTriggered(type);}void OnContactOtherFactionCallback(BattleObject sender, BattleObject other)//接触不同阵营处理{var anotherPhysicsBattleComponent = other.GetBattleObjectComponent<PhysicsBattleComponent>();if (anotherPhysicsBattleComponent != null){forceValue = mBattleObject.transform.rotation * forceValue;anotherPhysicsBattleComponent.SetForce(forceValue, type);//传入力}}
}
【Unity】动作游戏开发实战详细分析-15-可扩展的战斗系统相关推荐
- 【Unity】动作游戏开发实战详细分析-07-连续技与组合技功能设计
[Unity]动作游戏开发实战详细分析-07-连续技与组合技功能设计 基本思路 在一些动作游戏中,存在着连续技这一功能,具体来说就是连续按下规定的按键会触发能力的功能,或者是长按触发等等. 实现解析 ...
- 【Unity】动作游戏开发实战详细分析-16-敌人AI设计
[Unity]动作游戏开发实战详细分析-16-敌人AI设计 基本思想 本文来实现简单的敌人AI,使用协程来开发AI.如果想要使用行为树插件可自行学习使用. 代码实现 敌人的目标信息结构 用于存储所有的 ...
- 【Unity】动作游戏开发实战详细分析-06-技能系统设计
[Unity]动作游戏开发实战详细分析-06-技能系统设计 基本思想 不同的技能可以设计为技能模版,当角色释放技能时,会通过模版ID将它进行实例化,这个实例技能类可以是一个挂载的MonoBehavio ...
- 【Unity】动作游戏开发实战详细分析-25-角色残影效果的实现
[Unity]动作游戏开发实战详细分析-25-角色残影效果的实现 基本思路 Unity中的蒙皮网格组件提供了一个接口BakeMesh,允许我们拿到当前动画帧的网格数据,借此可对烘焙网格使用半透明的边缘 ...
- 【Unity】动作游戏开发实战详细分析-24-流血喷溅程序
[Unity]动作游戏开发实战详细分析-24-流血喷溅程序 溅落血迹效果 实现思路 利用对象池的代码设计思路,通过随机性来实现随机的溅落血迹效果. 代码 public class DripBloodF ...
- 五分钟详述:一文理解动作游戏开发中的攻击逻辑和受击逻辑
前言: 本文节选自机械工业出版社出版的<Unity3D动作游戏开发实战>一书,略有改动. 攻击逻辑 一个好的操作手感对于战斗非常重要,玩家会根据当前动画状态来确定下一步输入,若手感混乱则会 ...
- 【游戏开发实战】Unity从零开发多人视频聊天功能,无聊了就和自己视频聊天(附源码 | Mirror | 多人视频 | 详细教程)
文章目录 一.前言 二.思考问题与解决方案 1.思考问题 2.解决方案 2.1.Unity中如何开启摄像头并对图像进行采样 2.2.图像如何中转给其他客户端 2.3.如何实现清晰度切换 2.4.客户端 ...
- 《Unity 2D与3D手机游戏开发实战》简介
#好书推荐##好书奇遇季#<Unity 2D与3D手机游戏开发实战>,京东当当天猫都有发售.彩色印制,定价89元,网店打折销售更便宜.本书配套源码.PPT课件,适合Unity游戏开发初学者 ...
- 《Unity 5.x游戏开发实战》一1.9 添加一个水平面
本节书摘来异步社区<Unity 5.x游戏开发实战>一书中的第1章,第1.9节,作者: Alan Thorn 译者: 李华峰 责编: 胡俊英,更多章节内容可以访问云栖社区"异步社 ...
最新文章
- 使用MSBuild实现完整daily build流程
- 配置 html-webpack-plugin 生成预览页面||配置自动打包相关的参数
- 写给新手程序员的一封信
- Spring Boot 入门 IDEA 版本 2小时学会springBoot 代码上传至gitee 或者github 事务没做出来
- 编程语言对比 面向对象
- RNN、GRU、LSTM
- 5图片加载优化_开发说做了性能优化,到底做了啥
- 两个子数组和的差最小
- 京瓷Kyocera TASKalfa 6002i 一体机驱动
- 网易有道笔试题(2014届,2013.10北邮站)
- php页眉,如何在php中创建Word文档页眉/页脚
- 【Spring】IoC,DI,两种代理方式,AOP定义和使用
- mac怎么更新python_mac上更新python的方法
- 爬虫之旅(二):爬取b站搜索数据
- alibaba人一起写过的技术丛书
- python美元和人民币双向兑换程序_Python:程序练习题(二)
- 2017全国计算机高校排名,全国计算机专业大学排名_2017计算机专业大学排名
- cufft1d c2c
- CDN工作原理及淘宝双十一图片访问实战
- 计算机html大作业聊天室,java大作业设计报告-JAVA聊天室.docx