微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

unity技能系统

unity技能系统

类类型概览

  • CharacterSkillManager      角色技能管理器 挂载在角色 持有SkillData与释放器 通过释放器进行技能释放

  • SkillDeployer                       技能释放器基类 持有选取类型与影响效果两个接口 抽象函数释放方式

  • SkillData                             技能数据类 保存技能基本信息、攻击基本信息、影响效果、选择类型等

  • IImpactEffects                     影响效果接口 持有效果执行方法Execute(SkillDeployer deployer)

  • IAttackSelector                   选区算法接口 持有选区算法 SelectTarget(SkillData data)

  • DeployerConfigFactory      释放器算法工厂返回具体类方法

  • Enum SkillAttackType         攻击类型 群体/单体

  • Enum SelectorType           选择类型 扇形/矩形


  • MeleeSkillDeployer           近战释放器 继承自 SkillDeployer

  • CostSPImpact                 消耗法力值 实现IImpactEffects接口

  • HurtHPImpact                 伤害生命

  • AddSPImapact                 增加蓝耗

具体思路

  • 将技能拆分为 数据(伤害,范围) 效果 (浮空,减速) 根据数据不同例如 范围、持续时间等 根据不同效果构建不同效果 例如: 英雄联盟中 艾希的W 技能与E技能 前者向前方一定扇形的敌人造成伤害与减速 两个效果 ,后者 向释放方向发射弹体 对沿途进行视野暴露一个效果
  • 拿到相应技能该像哪里释放,如何获得效果对象,我们需要一个替我们处理技能释放的对象 去处理技能从初始化到释放的过程。 SkillDeployer 持有 技能数据、影响效果接口与选区算法接口 ,在具体创建时通过释放器工厂拿到相应具体实现。

代码具体流程

  • 技能管理器持有技能数据 与唯一选择器 检测到相应输入 通过唯一ID获取技能数据 根据技能数据 例如蓝耗 判断是否能够释放 达到释放条件后 创建选择器
  • 选择器根据传入技能数据 影响效果 通过工厂 拿到具体方法 进行释放

效果

释放前

释放后

结果

角色脚本1

角色脚本2


受击脚本

实现

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//人物状态
public class CharacterStateData : MonoBehavIoUr
{
     public float HP = 100;
     public float SP = 100;
     public float BeAttack = 10;//攻击力
     
    void Start()
    {
        
    }

    void Update()
    {
        
    }
}



using System.Collections;
using System.Collections.Generic;
using TestSkillSystem;
using UnityEngine;
using Debug = UnityEngine.Debug;
//技能管理器
public class CharacterSkillManager : MonoBehavIoUr
{

    
    private Transform m_Transform;

    private CharacterStateData m_PlayerData;

    private SkillData m_NowSKill;
    //技能列表
    public List<SkillData> Skills;
    private SkillDeployer m_Deployer;
    
    private void Awake()
    {
        m_Transform = GetComponent<Transform>();
        m_PlayerData = GetComponent<CharacterStateData>();
        m_Deployer = GetComponent<SkillDeployer>();
    }

    private void Start()
    {
        for (int i = 0; i < Skills.Count; i++)
        {
            InitSkill(Skills[i]);
        }
        
       
    }

  
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Q))
        {
           useSkill();
        }
        
    }

    private void useSkill()
    {
        m_NowSKill = PrepareSkill(1001);
        if (m_NowSKill == null)
        {
            return;
        }
        GenerateSkill(m_NowSKill);
    }




    /// <summary>
    /// 生成技能
    /// </summary>
    public void GenerateSkill(SkillData data)
    {
        GameObject skill = Instantiate(data.skillPrefab, m_Transform.position, m_Transform.rotation);
        
        SkillDeployer skillDeployer = skill.GetComponent<SkillDeployer>();
        skillDeployer.skillData = data;
        skillDeployer.DeploySkill();
        
        //定时销毁
        Destroy(skill, data.durationTime);
        //开启cd
        StartCoroutine(CoolTimeDown(data));
    }
    /// <summary>
    /// 准备释放技能
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public SkillData PrepareSkill(int id)
    {
        SkillData data = Skills.Find(a => a.id == id);

        if (data != null && data.coolRemain <= 0 && m_PlayerData.SP >= data.costSp)
        {
            return data;
        }
        Debug.Log("当前技能无法释放");
        return null;
    }
    /// <summary>
    /// 初始化技能
    /// </summary>
    /// <param name="data"></param>
    private void InitSkill(SkillData data)
    {
       data.skillPrefab =  SkillResManager.Load<GameObject>(data.prefabName);
       
       data.owner = gameObject;
       
    }

    /// <summary>
    /// 技能冷却
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    private IEnumerator CoolTimeDown(SkillData data)
    {
        data.coolRemain = data.coolTime;
        while (data.coolRemain > 0)
        {
            yield return new WaitForSeconds(1);
            data.coolRemain--;
        }
        Debug.Log("技能CD完毕over");
    }
}
using System;
using System.Collections.Generic;
using UnityEngine;
namespace TestSkillSystem
{
    
    /// <summary>
    /// 技能释放器
     /// </summary>
    public abstract class SkillDeployer : MonoBehavIoUr
    {
        private SkillData m_SkillData;

        public SkillData skillData
        {
            get { return m_SkillData; }
            set
            {
                m_SkillData = value;
                InitDeployer();
            }
        }

        private IAttackSelector m_Selector;
        private List<IImpactEffects> m_Effects;

        private void InitDeployer()
        {
            m_Effects=new List<IImpactEffects>();
           
            //选取类型
            m_Selector = DeployerConfigFactory.CreateAttackSelector(skillData);
            

            //影响效果
            m_Effects = DeployerConfigFactory.CreateImpactEffects(skillData);
            
            //Debug.Log("go");
        }
        
        //执行算法对象
        //选区
        public void CalculateTargets()
        {
            skillData.attackTargets = m_Selector.SelectTarget(skillData, transform);
            
            
        }
        //执行影响效果
        public void ImpactTargets()
        {
            for (int i = 0; i < m_Effects.Count; ++i)
            {
                m_Effects[i].Execute(this);
            }
        }
        //释放方式
        //技能管理器调用,有子类实现,定义具体释放策略
        public abstract void DeploySkill();
    }
    
}

using System;
using System.Collections.Generic;
using UnityEngine;

namespace TestSkillSystem
{
    /// <summary>
    /// 攻击类型 群体/单体
    /// </summary>
    public enum SkillAttackType
    {
        Group,
        Alone
    }

    /// <summary>
    /// 选择类型 扇形/矩形
    /// </summary>
    public enum SelectorType
    {
        Sector,
        Rectangle
    }
    //技能数据
   [Serializable]
    public class SkillData
    {
        /// <summary>技能ID</summary>
        public int id;
        /// <summary>技能名称</summary>
        public string name;
        /// <summary>技能描述</summary>
        public string description;

        /// <summary>冷却时间</summary>
        public float coolTime;

        /// <summary>冷却剩余时间</summary> 
        public float coolRemain;

        /// <summary>魔法消耗</summary>    
        public float costSp;

        /// <summary>攻击距离</summary>    
        public float attackdistance;

        /// <summary>攻击角度</summary>    
        public float attackAngle;

        /// <summary>攻击目标Tags</summary>    
        public List<string> attackTargetTags ;

        /// <summary>攻击目标对象(数组)</summary>    
        public List<Transform> attackTargets;
       [Tooltip("技能影响类型")]
        /// <summary>技能影响类型</summary>    
        public List<string> impactype ;

        /// <summary>连击的下一个技能ID</summary>
        public int nextBatterid;

        /// <summary>伤害比率</summary>    
        public float atkRatio;

        /// <summary>持续时间</summary>    
        public float durationTime;

        /// <summary>伤害间隔</summary>    
        public float atkInterval;

        /// <summary>技能所属对象(OBJ)</summary>
       [HideInInspector]  public GameObject owner;

        /// <summary>技能预制件名称</summary>
         public string prefabName;
        /// <summary>技能预制件对象</summary>
        [HideInInspector] public GameObject skillPrefab;
        /// <summary>动画名称</summary>    
        public string aniamtorName;

        /// <summary>受击特效名称</summary>    
        public string hitFxName;

        /// <summary>受击特效预制件</summary>    
        [HideInInspector] public GameObject hitFxPrefab;

        /// <summary>技能等级</summary>    
        public int level;

        /// <summary>攻击类型</summary>    
        public SkillAttackType attackType;

        /// <summary>选择类型 扇形/矩形</summary>    
        public SelectorType selectorType;

    }
    

}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace TestSkillSystem
{
    //选区接口
    public interface IAttackSelector
    {
        /// <summary>
        /// 搜索目标
        /// </summary>
        /// <param name="data"></param>
        /// <param name="skillTF"></param>
        /// <returns></returns>
        List<Transform> SelectTarget(SkillData data, Transform skillTF);

    }


}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace TestSkillSystem
{
    //影响效果接口
    public interface IImpactEffects
    {
        
        //执行
        void Execute(SkillDeployer deployer);
    }

}


using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;

namespace TestSkillSystem
{
    //算法工厂
    public static class DeployerConfigFactory 
    {
        
        //命名规则 TestSkillSystem + 枚举名 +AttackSelector
        //例如扇形: TestSkillSystem
        /// <summary>
        /// 创建选区算法
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static IAttackSelector CreateAttackSelector(SkillData data)
        {
            string name = $"TestSkillSystem.{data.selectorType}AttackSelector";
           // Debug.Log(name);
            return CreateObject<IAttackSelector>(name);
        }
        //影响效果命名规范
        //同上 TestSkillSystem. + impactype[?] +Impact 
        /// <summary>
        /// 创建影响算法
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static List<IImpactEffects> CreateImpactEffects(SkillData data)
        {
            List<IImpactEffects> temp= new List<IImpactEffects>();
            for (int i = 0; i < data .impactype.Count; ++i)
            {
                string className = $"TestSkillSystem.{data.impactype[i]}Impact";
                //Debug.Log(className);
                temp.Add( CreateObject<IImpactEffects>(className));
            }

            return temp;
        }
        
        private static T CreateObject<T>(string className) where T : class
        {

            Type type = Type.GetType(className);
            if (type == null)
            {
                Debug.Log($"Type为空ClssName为:{className} ");
            }
            return Activator.CreateInstance(type) as T;
        }
    }

}


using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace TestSkillSystem
{
    
    public class SectorAttackSelector : IAttackSelector
    {
       /// <summary> 
       /// 扇形/圆形选区
       /// </summary>
       /// <param name="data"></param>
       /// <param name="skillTF">技能所在位置</param>
       /// <returns></returns>
        public List<Transform> SelectTarget(SkillData data, Transform skillTF)
       {
           //获取目标
           List<string> tags = data.attackTargetTags;
           List<Transform> resTrans = new List<Transform>();
           for (int i = 0; i < tags.Count; ++i)
           {
                GameObject[] tempGOArray = GameObject.FindGameObjectsWithTag(tags[i]);
                if (tempGOArray.Length == 0)
                {
                    Debug.Log("标签数组为空");
                }
                    
                foreach (var VARIABLE in tempGOArray)
                {
                    resTrans.Add(VARIABLE.transform);
                }
           }
           //判断攻击范围
           resTrans.FindAll(res => Vector3.distance(res.position, skillTF.position) <= data.attackdistance
                                   && Vector3.Angle(skillTF.forward,res.position-skillTF.position)<=data.attackAngle/2);
           //筛选出活得角色
           resTrans.FindAll(res => res.GetComponent<CharacterStateData>().HP > 0);
           //返回目标
           //依据 单体/群体
           if (data.attackType == SkillAttackType.Group||resTrans.Count==0 )
               return resTrans;
           //huoqu 距离最近的
           int index = 0;
           float min = Vector3.distance(resTrans[0].position, skillTF.position);
           for (int i=0;i<resTrans.Count;++i)
           {
               float temp = Vector3.distance(resTrans[i].position, skillTF.position);
               if (temp < min)
               {
                   min = temp;
                   index = i;
               }
           }
           return new List<Transform> {resTrans[index]};
       }
    }
}
using System.Collections.Generic;
using UnityEngine;

namespace TestSkillSystem
{
    //具体影响效果
    public class CostSPImpact: IImpactEffects
    {
        
        public void Execute(SkillDeployer deployer)
        {
            Debug.Log($"消耗法力值执行:{deployer.skillData.owner.name}");
            var staus = deployer.skillData.owner.GetComponent<CharacterStateData>();
            if(staus==null)
                Debug.Log("状态为空");
            staus.SP -= deployer.skillData.costSp;
        }
    }

    public class HurtHPImpact : IImpactEffects
    {
        public void Execute(SkillDeployer deployer)
        {
            Debug.Log($"伤害生命执行:{deployer.skillData.owner.name}");
            List<Transform> Temp = deployer.skillData.attackTargets;
            for (int i = 0; i < Temp.Count; ++i)
            {
                Debug.Log($"{Temp[i].gameObject.name} hp执行前: {Temp[i].GetComponent<CharacterStateData>().HP}");
                Temp[i].GetComponent<CharacterStateData>().HP-=deployer.skillData.owner.GetComponent<CharacterStateData>().BeAttack;
                Debug.Log($"{Temp[i].gameObject.name} hp施行后: {Temp[i].GetComponent<CharacterStateData>().HP}");
            }


        }
    }
    public class AddSPImpact : IImpactEffects
    {
        public void Execute(SkillDeployer deployer)
        {
            Debug.Log($"回复法力值执行:{deployer.skillData.owner.name}");
            var staus = deployer.skillData.owner.GetComponent<CharacterStateData>();
            if(null==staus)
                Debug.Log("状态为空");
            staus.SP += deployer.skillData.owner.GetComponent<CharacterStateData>().BeAttack*0.5f;
        }
    }
}
using UnityEngine;

namespace TestSkillSystem
{
    //近战释放器 测试
    public class MeleeSkillDeployer: SkillDeployer
    {
        public override void DeploySkill()
        {
            //执行选区算法
            CalculateTargets();
            
            //执行影响算法
            ImpactTargets();
            
            //其他策略 
            skillData.owner.transform.position=new Vector3(skillData.owner.transform.position.x+1,skillData.owner.transform.position.y,skillData.owner.transform.position.z);
        }
        
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐