哈喽~又是我暴躁老哥酒九,上次我向大家介绍了一下技能系统的思路和使用方法。那么我们话不多说,这篇文章就是有关这些功能都是如何实现的,让我们开始吧。

素材准备工作做好之后我们就可以开始编写具体的脚本了。新建好我们的项目,场景搭建和角色素材这些操作就大家自己去选择准备吧。新建一个文件夹命名为SkillSystem,之后我们技能系统所有的脚本就放在这里面了(养成收拾脚本的习惯)。

PS:接下来的所有脚本我们都统一放进一个命名空间中,我这里命名为MOBASkill,后面代码中就不单独写出来。

1、系统总体规划
我们再来捋一捋我们这个技能系统的各个模块之间是怎么互相作用的。在每一拥有技能的角色身上都会有一个技能管理器负责该角色的技能数据存放和生成对应技能的预制体,在我们生成技能预制体(释放技能)的同时,对应的技能预制体身上挂载好的技能释放器就会在释放器配置工厂中找到对应的效果算法和目标选择算法,并且执行算法中的代码完成查找敌人并对敌人产生对应的效果,最后销毁预制体本身,这就是一次完整的技能释放流程。

2、技能数据类(SkillData)
我们新建脚本命名为SkillData,将我们技能中需要的数据都写在这个数据类中,这里可以大家根据自己的设计想法来进行添加,这里我就不过多啰嗦我们看着代码讲。

   public enum SkillAttackType{single,aoe,}public enum SelectorType{none,Sector,Rectangular,}   public enum DisappearType {TimeOver,CheckOver,}[Serializable]public class SkillData{public int skillId;//技能IDpublic string name;//技能名称public string description;//技能描述public int skillCd;//技能冷却时间public int cdRemain;//技能剩余冷却时间public int costMp;//法力值消耗public float attackDistance;//技能距离public float attackAngle;//技能攻击角度public string[] attackTargetTags = { "Enemy" };//能作用的目标Tag[HideInInspector]public Transform[] attackTargets;//作用目标对象数组public string[] impactType = { "CostMP", "Damage" };//技能影响类型public int nextBatterld;//连击的技能IDpublic float attackNum;//伤害数值public float durationTime;//持续时间public float attackInterval;//伤害间隔[HideInInspector]public GameObject owner;//技能所属的角色public string prefabName;//技能预制体名称[HideInInspector]public GameObject skillPrefab;//预制体对象public string animationName;//动画名称public string hitFxName;//受击特效名称[HideInInspector]public GameObject hitFxPrefab;//受击特效预制体public int level;//技能等级public SkillAttackType attackType;//AOE或者单体public SelectorType selectorType;//释放范围类型(圆形,扇形,矩形)public string skillIndicator;//技能指示器名字public string skillIconName;//技能显示图标名字[HideInInspector]public Image skillIcon;//技能事件图标public DisappearType disappearType;//技能预制体消失方式}

这里有几个变量的作用需要单独说一说他的作用:

1.attackTargetTags和attackTargets,看上去这两个变量的作用是一样的,但是前者是在释放技能之前该技能可以作用于那些物体上(敌人,队友,建筑等),后者是在执行技能范围选择算法之后返回的在技能范围中的敌人(实际攻击到的目标)。

2.owner在这里其实就是挂载管理器的物体自己,在后续算法中可以更方便的调用本身的数据,方法或者组件等。

3.skillIndicator是我们成功释放技能之前显示出来的辅助技能释放的工具。

PS:通常我们定义了公有变量之后就会在Inspector窗口中生成对应的输入框,而当我们在变量前加上[HideInInspector]之后就会该公有变量就不会在Inspector中显示。

3、技能管理器(SkillManager)
技能管理器的作用就是存好人物的所有技能数据,初始化技能数据中未填写的部分(owner,skillPrefab,hitFxPrefab),准备技能,生成技能等。因此首先我们需要准备一个数组来存放人物的全部技能,根据数据中的预制体名字(prefabName,hitFxName)给对应的GameObject赋值的方法,生成技能时各种内容等。那么我们来看代码。

 public class CharacterSkillManager : MonoBehaviour{public SkillData[] Skills;//技能列表private void Awake(){foreach (var s in Skills){InitSkill(s);}}//初始化技能private void InitSkill(SkillData data){if (data.prefabName != null){data.skillPrefab = Resources.Load<GameObject>("SkillPrefab/"+data.prefabName);data.owner = this.gameObject;}}//技能释放条件判断public SkillData PrepareSkill(int id){SkillData data = new SkillData();foreach (var s in Skills){if (s.skillId == id){data = s;}}if (data != null && data.cdRemain <= 0)//这里还有技能消耗值的判断{return data;}else{return null;}}//生成技能public void GenerateSkill(SkillData data){//创建技能预制体GameObject skillgo = GameObjectPool.instance.CreateObject(data.prefabName, data.skillPrefab, transform.position, transform.rotation);//传递技能数据给技能释放器SkillDeployer deployer = skillgo.GetComponent<SkillDeployer>();deployer.SkillData = data;//释放器释放技能deployer.DeploySkill();StartCoroutine(CoolTimeDown(data));//开启冷却}//协程实现技能冷却private IEnumerator CoolTimeDown(SkillData data){data.cdRemain = data.skillCd;while (data.cdRemain > 0){yield return new WaitForSeconds(1f);data.cdRemain -= 1;}}}

这里对于能否释放技能的条件有:该技能ID是否存在,技能的是否完成冷却(利用剩余冷却时间是否为0来判断),技能所需的消耗值玩家的对应数值是否足够。大家伙可以根据自己的需求来进行条件的添加删除,举个例子:一个技能拥有三段释放,那么就可以将上一段技能的是否释放来作为条件实现。生成技能这里由于我练习了一下对象池,所以使用了对象池,大家可以改成普通的实例化就好。后面的技能冷却就是通过协程实现啦。

4、释放器配置工厂(DeployerConfigFactory)
根据之前的技能流程图发现在技能管理器中释放技能的操作其实只有实例化预制体和将技能数据传入释放器中,而实际的选取敌人,执行技能效果都是在释放器中来完成的。那技能释放 器是怎么知道要用那些算法的呢?我们可以看到技能数据中有两个变量impactType和selectorType这两个变量中都存放的是字符串内容,那么我们就通过这些字符串内容来获取到对应的算法。这些操作就由释放器配置工厂来完成。

先准备好技能范围选择算法和效果算法的接口:

 public interface IImpactEffect //效果算法接口{void Execute(SkillDeployer deployer); }    public interface ISkillSelector //范围选择算法接口{Transform[] SelectTarget(SkillData data, Transform skillTF);//skillTF是技能预制体}

接下来我们的所有的相关算法就是通过继承这两个接口再在其中编写具体的逻辑。现在来看我们释放器配置工厂。

 public class DeployerConfigFactory//反射来实现{public static ISkillSelector CreateSkillSelector(SkillData data)//范围选择算法 {string className = string.Format("MOBASkill.{0}SkillSelector", data.selectorType);return CreateObject<IAttackSelector>(className);}public static IImpactEffect[] CreateImpactEffects(SkillData data) //效果算法{IImpactEffect[] impacts = new IImpactEffect[data.impactType.Length];for (int i = 0; i < data.impactType.Length; i++){string classname = string.Format("MOBASkill.{0}Impact", data.impactType[i]);impacts[i] = CreateObject<IImpactEffect>(classname);}return impacts;}private static T CreateObject<T>(string className) where T : class//创建对应算法{Type type = Type.GetType(className);return Activator.CreateInstance(type) as T;}}

在类中我们通过CreateObject方法来由字符串变量找到对应的算法,主要操作就是使用泛型(T)并且将我们的泛型约束为引用类型(where T : class),根据前文提到的算法的字符串变量的Type来创建对应类型的方法。而CreateAttackSelector和CreateImpactEffects两个方法就是把算法名补全之后返回指定接口类型的方法。

PS:这里的Activator.CreatInstance的作用是使用与指定参数匹配程度最高的构造函数来创建指定类型的实例。通俗的讲就是按照参数给出方法,不过通过它返回的是object类型所以要转化成我们需要的类型(as T)。

5、技能释放器(SkillDeployer)
解决了根据数据生成算法之后,在技能释放器中就只需要初始化释放器算法并且调用相应的算法就好了,代码如下:

public abstract class SkillDeployer : MonoBehaviour//技能释放器{private SkillData skillData;public SkillData SkillData //技能管理器提供{get { return skillData; }set { skillData = value; InitDeplopyer(); }}//范围选择算法private IAttackSelector selector;//效果算法对象 private IImpactEffect[] impactArray;//初始化释放器private void InitDeplopyer()//初始化释放器 {//范围选择selector = DeployerConfigFactory.CreateAttackSelector(skillData);//效果impactArray = DeployerConfigFactory.CreateImpactEffects(skillData);}//范围选择public void CalculateTargets() {skillData.attackTargets = selector.SelectTarget(skillData, this.transform);}//效果public void ImpactTargets() {for (int i = 0; i < impactArray.Length; i++){impactArray[i].Execute(this);}}public abstract void DeploySkill();//供技能管理器调用,由子类实现,定义具体释放策略}

这里释放器就可以作为我们所有释放器的父类,如果有释放情况的不同,就通过继承他在子类中进行重写方法。比如前文提到的:近战技能释放是如何释放,远程技能释放又是怎样的等等,这里就交给大家自己完成啦。

public class MeleeSkillDeployer : SkillDeployer//近战技能释放例子{public override void DeploySkill(){//执行选区算法CalculateTargets();//执行影响算法ImpactTargets();}}

6、技能范围选择算法(Selector)
顾名思义,这里要完成的内容就是将在规定范围中的敌人全部选择出来这里就给大家演示一下扇形范围的敌人检测。上代码。

public class SectorSkillSelector : ISkillSelector{GameObject[] tempGOArray;public Transform[] SelectTarget(SkillData data, Transform skillTF){//根据技能数据中得标签 获取所有目标List<Transform> taragets = new List<Transform>();for (int i = 0; i < data.attackTargetTags.Length; i++){tempGOArray = GameObject.FindGameObjectsWithTag(data.attackTargetTags[i]);}for (int i = 0; i < tempGOArray.Length; i++){taragets.Add(tempGOArray[i].GetComponent<Transform>());}//判断攻击范围taragets = taragets.FindAll(t =>Vector3.Distance(t.position, skillTF.position) <= data.attackDistance &&Vector3.Angle(skillTF.forward, t.position - skillTF.position) <= data.attackAngle / 2);//返回目标Transform[] result = taragets.ToArray();if (result.Length == 0){Debug.Log("没有敌人");return result;}else {for (int i = 0; i < result.Length; i++){Debug.Log(result[i].name);}return result;}}}

说明一下整体的流程,首先我们先根据技能数据中的attackTargetTags中的Tag找到所有带有该Tag的物体,再通过一个列表的FindAll方法来找符合条件的敌人,再返回一个数组到技能数据的attackTargets中去。这样就完成了敌人的查找。

PS:1.当我们命名脚本名是一定要是与释放器配置工厂中的命名规则一致,否则无法找到,在这里也就是范围选择算法名为xxxSkillSelector,而效果算法名应该为xxxImpactEffect。2.在FindAll中使用了Lamda表达式来完成条件的限制,Vector3.Distance负责在攻击距离内,Vector3.Angle负责在攻击角度内。3.这里还需要大家自己添加上敌人是否存活的选择条件判断,技能为作用于目标单个或者目标为多个的返回情况等。

7、技能效果算法(Impact)
技能的效果就有很多了可以是回复血量,消耗法力值,减少血量等等,大家可以根据自己的需求来自行进行编写啦。这里的例子了是给敌人添加一个击飞Buff。

public class BlowFlyImpact : IImpactEffect{private SkillData data;public void Execute(SkillDeployer deployer){data = deployer.SkillData;deployer.StartCoroutine(ContinuousBlowFly(deployer));}public void BlowFly(Transform transform)//给敌人添加Buff {CharacterData cd = transform.GetComponent<CharacterData>();BuffManager.instance.AllBuffs[0].currentTarget = cd;cd.AddBuff(BuffManager.instance.AllBuffs[0]);}IEnumerator ContinuousBlowFly(SkillDeployer deployer) //每隔0.05秒检测一次敌人{float time = 0;do{yield return new WaitForSeconds(0.05f);time += 0.05f;deployer.CalculateTargets();if (data.attackTargets.Length != 0) {foreach (var t in data.attackTargets) {BlowFly(t);}}} while (time < 0.4f);}}

为什么要用协程来持续检测敌人是因为这个技能是一个冲撞技能,在玩家向前位移的整个时间检测到的敌人都会被添加击飞的Buff。BuffManager是一个简单Buff系统中的管理器,这里不必深究。这里当我们呢释放技能时就会调用Execute中的内容啦。

好啦,本篇文章的内容就到这里啦,大家伙下篇文章见哦。拜拜~

在Unity中制作完整的技能系统(代码篇)相关推荐

  1. 在Unity中制作完整的技能系统(介绍篇)

    在Unity中制作完整的技能系统(介绍篇) 大噶吼呀,还是我暴躁老哥酒九.最近的我遇到了游戏荒,于是我就重新去玩了玩<英雄联盟>,结果发现还是一如既往的强了(不是).对于这款游戏我就不用多 ...

  2. [教程] 在Unity中制作物体破碎效果

    这篇教程将教大家如何在Unity中制作一个简单的碎片效果.当物体撞击或销毁时,我们将物体分裂为更小的碎片来取代之前的仅仅直接"删除"物体. 需求 这篇教程需要最新版本的Unity, ...

  3. 如何在unity中制作塔防游戏

       塔防游戏非常流行,毫无疑问--没有什么比看着自己的防御消灭讨厌的侵略者更让人满足!在这两部分教程中,用unity来制作一个塔防游戏! 将会学习怎样...... 创建一波敌人 让它们跟随线路点 ...

  4. 在Unity中制作4种不同的游戏

    流派:电子学习| MP4 |视频:h264,1280×720 |音频:AAC,48.0 KHz 语言:英语+中英文字幕(根据原英文字幕机译更准确)|大小解压后:8.6 GB 含课程素材 |时长:15h ...

  5. Unity中制作游戏的快照游戏支持玩家拍快照

    Unity中制作游戏的快照游戏支持玩家拍快照 有些游戏支持玩家"拍快照",也就是将游戏的精彩瞬间以图片的形式记录下来的功能.这个功能比较有趣,而且以后的用途也会很广,为此本节打算介 ...

  6. 在Unity中制作高质量的光照效果(上)

    原创: Unity Unity官方平台 本篇专家指南将介绍在Unity中制作高质量光照效果的多种高级方法,其中使用了2D和立方体贴图的光线遮罩,并且利用了Unity高清渲染管线HDRP中的高级着色器. ...

  7. 如何在 Unity 中制作一个道具系统

    原文:How to make a Power-Up System in Unity 作者:Kevin Small 译者:kmyhy 如果音速小子中没有金色戒指和电动鞋,超级马里奥中没有了蘑菇,或者吃豆 ...

  8. 【GamePlay】RPG中的战斗与技能系统

    前言 在B站很多开发教程中,打怪都是用碰撞器处理的,比如玩家攻击后,生成刀光动画,这个预制体上绑有多边形碰撞器,如果碰撞到敌人,就造成伤害. 但如果攻击方式很多,就不好管理了. 之前看到一种基于配表的 ...

  9. 如何在Unity中制作VR全景动画

    超简单的引言 本教程介绍的是如何一个人制作出像模像样的VR全景动画.笔者没有去详细的研究过动画的制作方式,或者说根本就不了解啊,所以本文章只是门外汉的一次自嗨,请勿认真,作为一种参考即可. 一 总览 ...

最新文章

  1. 查询三个月前的所有数据的sql语句
  2. 比RNN快136倍!上交大提出SRNN,现在RNN也能做并行计算了
  3. Exact跻身全球发展最快的云企业行列
  4. 【Google Play】管理目标受众群体 ( 加入“亲子同乐计划“ 由于政策原因 “更新被拒“ 后的处理 )
  5. oracle如何查询系统变量数据,Oracle如何对IN子查询使用绑定变量(转)
  6. 网易云信助春招上“云” ,疫情过后线上招聘或成常态
  7. [好消息]博客园期刊第二期发布
  8. ABAP vs Java, 蛙泳 vs 自由泳
  9. 数据流图 系统流程图 程序流程图 系统结构图联系与区别
  10. Hive安装与配置MySQL元数据库
  11. 写在开通博客的第一天
  12. Audio播放流程(四)---MediaPlayerService流程之AudioTrack的创建
  13. python运维书_python运维书
  14. 专升本高数——常用公式总结大全【补充扩展】
  15. Oracle日期函数和转换函数
  16. 计算机加入域无法访问登录,不加入域不能访问域资源
  17. built a JNCIS LAB系列:Chapter 7 MPLS
  18. linux 限速命令,linux上传限速脚本介绍
  19. 阿里云 杭州 ARM 云服务器性能评测
  20. java集合(超详细)

热门文章

  1. 谷歌浏览器手动同步设置
  2. 软件项目经理,如何确保项目管理三大目标(质量、进度、成本)的达成?
  3. js 自由变量的取值
  4. 如何检查Mac配备的显卡(GPU)?
  5. TikTok独立站推广教程
  6. swift 快速奔跑的兔几 本节的内容是:音频与视频 第一说 AVFoundation 以及简单的iOS视频app
  7. 夺命聘礼【三】- 原创中篇小说
  8. html文字居中加下划线,HTML文字对齐,斜体,下划线和删除线
  9. might和could的区别用法_情态动词could,would,might等的用法区别
  10. IT项目管理学习笔记(一)