unity项目素材在哪个文件夹

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了unity项目素材在哪个文件夹相关的知识,希望对你有一定的参考价值。

参考技术A Resources或者StreamingAssets 。
1、Resources是Unity3D系统指定文件夹,如果你新建的文件夹的名字叫Resources,那么里面的内容在打包时都会被打到发布包中。文件夹特点:只读,即不能动态修改。所以想要动态更新的资源不要放在这里。
2、StreamingAssets 也是Unity3D系统指定文件夹,和Resources文件的区别就是Resources文件夹中的内容在打包时会被压缩和加密。而StreamingAsset文件夹中的内容则会原封不动的打入。

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

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

素材准备工作做好之后我们就可以开始编写具体的脚本了。新建好我们的项目,场景搭建和角色素材这些操作就大家自己去选择准备吧。新建一个文件夹命名为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;//技能ID
        public 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;//连击的技能ID
        public 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.0SkillSelector", 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.0Impact", 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项目素材在哪个文件夹的主要内容,如果未能解决你的问题,请参考以下文章

用Unity做一个小Demo入门Unity

用Unity做一个小Demo入门Unity

unity进阶项目------保卫萝卜

Unity实现视频播放

Unity3DUnity3D中在创建完项目后自动创建文件夹列表

Unity笔记:Make Character Bone2D