预备知识介绍
Unity Assembly DefUnity默认程序集和自定义程序集在编辑器模式下都生成在“盘符:\Unity工程文件夹\Library\ScriptAssemblies”里
使用Unity的自定义程序集时,定义的程序集并没有真正生成一个库,不能使用using指令来引用其他的程序集。我们需要在程序集定义的Inspector面板中的“References”里面添加依赖,按下加号然后将需要引用的dll拖拽进来。
Unity默认CSharp.dll自动引用所有程序集。
Unity真正编译出来的程序集名和程序集定义的Inspector面板中的“Name”一致,尽量让Editor内程序集定义文件名和Inspector面板中的一致。
如果程序集定义中勾选了“Test Assemblies”,那么此程序集内的代码都不会被打包,可以利用此功能将需要测试的代码和拓展编辑器的代码放在Test程序集里。
Unity资源加载方式AssetBundle首先将需要加载的Prefab设定一个AssetBundle名称,然后在Editor文件夹内设定编辑器脚本
123456789101112131415161718 ...
血条、暴击、闪避
UI设置血条、暴击提示、伤害血量提示都在一个UIPrefab里面,场景中的每个Entity都动态地在UI中分配一个血条。
如下图,每一个Entity都动态添加一个Item Entity HP。
ItemEntityHP脚本
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.UI;using TMPro;namespace DarknessWarGodLearning{ public class ItemEntityHP : MonoBehaviour { #region UIDefine ...
怪物状态
怪物动画状态
增加Born状态StateBorn:
123456789101112131415161718192021222324namespace DarknessWarGodLearning{ public class StateBorn : IState { public void Enter(EntityBase entity, params object[] args) { entity.currentAniState = AniState.Born; } public void Exit(EntityBase entity, params object[] args) { } public void Process(EntityBase entity, params object[] args) { //播放出生动画 ...
怪物属性
怪物增加属性修改monster.xml配置
123456789101112131415161718192021222324252627<?xml version="1.0" encoding="UTF-8" standalone="yes"?><root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <item ID="1001"> <mName>铁甲卫士</mName> <resPath>PrefabNPC/MonsterSoldier_1</resPath> <hp>200</hp> <ad>100</ad> <ap>80</ap> <addef>5</addef> <apdef>10</apdef> & ...
技能伤害
技能伤害入口函数技能伤害函数写在EntityBase里面,转到SkillMgr里面分担EntityBase的逻辑,此函数被StateAttack调用
EntityBase:
1234public virtual void AttackDamage(int skillID){ skillMgr.AttackDamage(this, skillID);}
SkillMgr:
1234public void AttackDamage(EntityBase entity,int skillID){}
StateAttack:
12345public void Process(EntityBase entity, params object[] args){ entity.AttackDamage((int)args[0]); entity.AttackEffect((int)args[0]);}
技能伤害点配置在一次技能中,可能会造成多段伤害,我们将每段伤害称为一次“技能伤害点(skillAction)”。
在 ...
怪物配置
怪物配置怪物配置V1
123456789101112<?xml version="1.0" encoding="UTF-8" standalone="yes"?><root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <item ID="1001"> <mName>铁甲卫士</mName> <resPath>PrefabNPC/MonsterSoldier_1</resPath> </item> <item ID="1002"> <mName>地狱魔卫</mName> <resPath>PrefabNPC/MonsterBoss_1</resPath> </item> <item/></root>
怪物 ...
技能位移
技能位移一般在动画素材中包含,但如果不包含,就需要在程序中实现
技能位移作用于Controller类,类似于PlayerController,但是有不一样的速度
修改Controller父类在Controller父类中添加skillMove的bool变量,和skillMoveSpeed的float变量,并添加SetSkillMove公共方法
12345678protected bool skillMove = false;protected float skillMoveSpeed = 0f;public void SetSkillMoveState(bool move,float skillSpeed=0f){ skillMove = move; skillMoveSpeed = skillSpeed;}
修改PlayerController在PlayerController中检测skillMove变量,并添加SerSkillMove私有方法产生移动
123456789101112131415private void Update(){ ...
技能动画和特效
角色的技能动画都用一个数值来表示,在程序中通过设定数值来直接切换对应的技能动画。
Tip在动画的Transition中,“Has Exit Time”表示从当前状态转到下一状态需要在动画过渡部分播放完成后才转换,如果取消勾选,那么当前的
Transition需要指定一个Parameter,否则将无法完成状态的转换。
我们直接添加一个int值作为Parameter,命名为Action,默认为-1,接下来,所有的技能动画都通过这个值来设置。
把动画过渡的“Has Exit Time”取消勾选后,动画还是有过渡的,我们这时可以把Transition Duration的值设为0。
使用下方的代码来改变和重置“Action”的值:
1234567891011public void ClickSkill1Btn(){ playerAnimator.SetInteger("Action", 1); StartCoroutine(Delay());}IEnumerator Delay(){ yield return new Wait ...
Controller
提取公共方法我们将PlayerController的Animator引用提取到它的父类Controller中,并且将其中的SetBlend(改名前叫做SetAnimTreeBlend)方法提取到父类Controller中,父类中的SetBlend是个虚方法,这样子类Controller通过覆写它就能实现不同的角色控制效果。
我们将PlayerController的isMove、dir、Dir变量和属性提取到它的父类Controller中
Controller:
12345678910111213141516171819202122232425262728293031using UnityEngine;namespace DarknessWarGodLearning{ public abstract class Controller : MonoBehaviour { protected bool isMove = false; private Vector2 dir = Vector2.zero; public ...
状态机
在战斗场景中,主角不能像主城系统中一样只需要移动就可以了,主角在移动时可能会受到伤害,受伤时就要播放受伤动画和音效;主角也有可能释放技能,释放技能时就可能不受控制,有一些技能可能会被中断。
总之,我们把复杂的逻辑封装到逻辑实体和状态机里来实现。
在战斗场景中,通过操作各种逻辑实体(Entity)——逻辑实体调用它的状态机并将自己传入——状态机调用具体的状态类的生命周期方法——状态类通过各个生命周期方法使用传入逻辑实体(Entity)作为参数修改这个实体的状态和调用实体的API。
我们之所以创建各种逻辑实体,是为了减少状态机的数量,减少状态机的数量就能减少状态类的创建数量。在战斗场景中,我们只有一个状态机StateMgr,它也只创建一次各种状态类,各种状态类具体的逻辑都丢给各个逻辑实体实现。
逻辑实体基类逻辑实体需要持有状态机的引用,还需要持有表现层Controller的引用
123456789101112131415161718namespace DarknessWarGodLearning{ public abstract class EntityBase ...