[课程地址]B站傅老师Unity课程学习记录,仅代表个人理解。

【自看】黑魂复刻Unity脚本

  • 1、移动脚本
    • 设计思路
  • 2、动画
    • 动画脚本:ActorController + 输入脚本:PlayerInput
      • 基本按键播放动画脚本
      • 增加模型移动功能,使模型能实际移动
        • 方法1、在FixedUpdate()中赋值给刚体的position
        • 方法2、赋值给刚体的velocity
        • 运动动画的变换
          • 行走→奔跑动画变换
          • other→跳跃动画变换
            • 默认使用GetKey()函数实现跳跃按键(傅老师实现方法):
            • 直接使用GetKeyDown()函数代替:
            • 加入跳跃动画
            • 跳跃期间只播放一次动画(前提:不设二连跳)
            • 向父对象发送正在跳跃的信息
            • 跳跃时禁止方向的移动&& 保留跳跃时的初速度
            • 跳跃结束无速度解决方法
          • 跳跃/地面动画→掉落动画
            • 加入掉落动画
            • 设计动画变换
            • 判断是否在地面
            • 胶囊体稍微下移
          • 行走/掉落→翻滚→地面动画
            • 没什么好说的
          • 小后跳动画
            • 状态机添加一个jabvelocity变量
        • 摄像头控制
          • 摄像头输入控制
            • Note:Inspector界面滑动条控制参数
            • Note:Mathf.Clamp()钳制输入范围
          • 解决摄像机旋转角色跟着旋转的问题
          • 使摄像机平滑跟随
            • Note:当摄像机跟随放在Update中,而角色移动放在FixedUpdate中时
        • 攻击动画
          • Avatar Mask
          • 动画转换等控制
          • 解决除跑步行走外动画其他动画也能切换攻击动画的bug
            • 还有一个Bug未解决,攻击状态时可切换其他动画的bug
          • 添加物理材质
          • 修复动画层级硬过渡Bug
          • 动画事件Event
            • 重置Trigger
            • 注意
          • 动画移动量
            • 在脚本中获取动画本来的移动量
          • 添加武器模型
          • 动画镜面表现

1、移动脚本

设计思路


为了设备与游戏的兼容,不直接使用某按键直接控制,方便玩家随意更改

简单代码演示
 /// <summary>/// 设置方向输入的字符串/// keyup为前,down为后,right为右,left为左/// </summary>public string keyup = "w", keydown = "s", keyright = "d", keyleft = "a";/// <summary>/// 转换输入的方向字符串为信号量/// Dup为(-1,1)区间的前后方向信号量/// Dright为(-1,1)区间的左右方向信号量/// </summary>public float Dup,Dright;/// <summary>/// 把方向字符串转换成方向信号量/// </summary>private void input_signal(){Dup = (Input.GetKey(keyup) ? 1.0f : 0) - (Input.GetKey(keydown) ? 1.0f : 0);Dright = (Input.GetKey(keyright) ? 1.0f : 0) - (Input.GetKey(keyleft) ? 1.0f : 0);}

2、动画

在private变量上面添加[SerializeField]是可以使得该变量在Inspector窗口显示的

 [SerializeField]private Animator anim;

动画脚本:ActorController + 输入脚本:PlayerInput

该脚本负责连接方向输入与动画播放

基本按键播放动画脚本

1、获得anim的组件之后,使用SetFloat()函数赋值给动画状态机,因为手柄输出方向的特性(可不全力输出最大值),该值并不是使用单纯的直接赋值,直接赋值有个弊端,当后退时,Dup的值为-1,即使改变了模型方向,也不能使得动画播放,值为-1时动画不会播放,而通过勾股定理处理后的方向向量赋值,可保证该值为正,保证动画可播放,而且不用增加额外的后退动画,节省资源。
2、获得模型的对象(GameObject类型),通过更改该对象的transform.forward的值,去改变模型的方向转向问题,因为有个父对象,父对象的transform.forward值不变且与模型的forward一致,则可以通过父对象的forward值与方向输入结合,更改模型的方向;

具体代码:
 anim.SetFloat("forward", pi.Dmag);model.transform.forward = pi.Dvec;Dmag = Mathf.Sqrt(Dup * Dup + Dright * Dright);Dvec = Dright * transform.right + Dup * transform.forward;

该代码目前还有缺陷:1、停止输入后方向回正 2、大幅度转向无补帧(无过渡)

1问题修复:当有方向输入才转向,不会不输入时把forward向量继续赋值
 if (Dmag > 0.1f){Dvec = Dright * transform.right + Dup * transform.forward;}
2问题修复:转向值直接赋予模型,当然会直接转向,要过渡效果,只需把转向的值平滑赋予即可
(1)该v3变量不能使用Mathf的SmoothDamp函数,SmoothDamp函数不能传入v3类型的变量,只能float类型变量
(2)Slerp函数计算量大,但是效果比较好,lerp函数计算量小
PS:第三个参数越小,变化越慢,假如是1.0f,则等于没有平滑效果
//model.transform.forward = pi.Dvec;改为
model.transform.forward = Vector3.Slerp(model.transform.forward,pi.Dvec,0.2f);

增加模型移动功能,使模型能实际移动

定义一个输入方向大小与方向结合的变量

movingVec = pi.Dmag * model.transform.forward;//方向向量的大小 * 面向的方向

方法1、在FixedUpdate()中赋值给刚体的position

rigid.position += movingVec * Time.fixedDeltaTime * 3.5f;

方法2、赋值给刚体的velocity

Rigidbody.velocity = movingVec;
该行代码咋一看没啥问题,(方向向量的大小 * 面向的方向)赋值给刚体的velocity
赋值的时候应该清楚这值的特性,不能随便乱赋值,比如该movingVec是一个vector3的变量,(x,y,z)
x为摇杆深度*方向
z为摇杆深度*方向
而y恒定为0,因为对象的forward与right的方向都与y关系不大,只关乎x,z

应该改为:

Rigidbody.velocity = new Vector3 (movingVec.x,Rigidbody.velocity.y,movingVec.z);
x与z平常赋值,而y则保持Rigidbody原本的值

运动动画的变换

行走→奔跑动画变换

从状态机中设置,赋予的值小于0.5时走路,大于0.8时就奔跑

赋值给状态机的代码,值的大小由Dmag负责
anim.SetFloat("forward", pi.Dmag);

由上面那句代码可见,状态机更改动画(站立→奔跑)只由Dmag负责
所以对Dmag变量编写相应的代码即可

Dmag变量的代码,可见Dmag是随着输入改变的,所以对输入进行调整以达到相应的目标
Dmag = Mathf.Sqrt(Dup * Dup + Dright * Dright);

我们的目标是控制行走与奔跑,则我们需要加入一个条件,比如shift+方向才奔跑,老设定了
然后不奔跑时的赋值上限为行走的0.5,所以

只需把输入方向的上限改为0.5f即可
targetDup = (Input.GetKey(keyup) ? 0.5f : 0) - (Input.GetKey(keydown) ? 0.5f : 0);
targetDright = (Input.GetKey(keyright) ? 0.5f : 0) - (Input.GetKey(keyleft) ? 0.5f : 0);

当需要奔跑时,检测是否按下shift按键,把Dmag * 2 就可实现奔跑与行走的切换

直接赋值会出现bug,虽然游戏不会出错,但是如此赋值是没有过渡的,行走动画直接变为奔跑会显得很唐突
Dmag = Mathf.Sqrt(Dup * Dup + Dright * Dright) * 2.0f;

个人觉得最简单的解决方法:

因为targetDup赋给状态机的上限是0.5f,当赋值过去时是用的平滑函数
所以只需用三元表达式判断+平滑函数的目标值*2
则可以达到平滑效果
Dup = Mathf.SmoothDamp(Dup, targetDup * (Input.GetKey(keyrun) ? 2.0f : 1.0f), ref velocityDup, 0.2f);
Dright = Mathf.SmoothDamp(Dright, targetDright * (Input.GetKey(keyrun) ? 2.0f : 1.0f), ref velocityDright, 0.2f);

傅老师给出的解决办法:

该方法是直接在赋值的地方更改
原先输入Dmag * 平滑变化的值(状态机目前的值→达到奔跑的值)
如果前面没有Dmag,则会一直保持0.5的值,即使不输入也站立保持行走的动画
anim.SetFloat("forward",pi.Dmag * Mathf.Lerp(anim.GetFloat("forward"),((Pi.run)? 1.0f : 0.5f),0.5f))

目前还有一个bug,就是x,y轴同时输入时Dmag(控制移动状态机的值)会大于1,为根号2,使得斜向行走时会有点过渡到奔跑动画,奔跑时也超速

解决办法:把正方形的坐标映射到一个圆上,引用论文:https://arxiv.org/ftp/arxiv/papers/1509/1509.06344.pdf
x为原正方形x值,u为映射后的x值
y为原正方形y值,v为映射后的y值
该方法有个缺陷,就是x与y的源坐标值大小不能大于根号2,否则NaN


使用这个方法就不能在输入的时候直接判断有没有按奔跑键了,只能在赋值给状态机的时候判断,否则输入会大于根号2导致出错。这里用回傅老师的判断方法。

滑动目标输入不作更改
Dup = Mathf.SmoothDamp(Dup, targetDup, ref velocityDup, 0.2f);
Dright = Mathf.SmoothDamp(Dright, targetDright, ref velocityDright, 0.2f);
赋予状态机的值作判断
该方法并没有原先直接在输入判断的方法效果好
用之前方法判断加上正方形到圆的映射算法即使能使用,也会有个缺陷,就是不奔跑的时候达不到斜向不加速的完美效果
anim.SetFloat("forward", pi.Dmag * Mathf.Lerp(anim.GetFloat("forward"), (Input.GetKey(pi.keyrun) ? 1.0f : 0.5f), 0.3f));
other→跳跃动画变换
默认使用GetKey()函数实现跳跃按键(傅老师实现方法):
该方法其实就是实现了GetKeyDown()函数,功能与GetKeyDown()函数一样
bool lastjump ;
void Update()
{bool newjump = Input.GetKey(keyjump);if (newjump != lastjump && newjump){Debug.Log("jump");jump = true;}else{Debug.Log(newjump);jump = false;}lastjump = newjump;
}
直接使用GetKeyDown()函数代替:
少定义了两个bool变量,代码量稍微减少,只是不知效率谁高
void Update()
{if(GetKeyDown(keyjump)){Debug.Log("jump");}
}
加入跳跃动画


平面移动已经不需要更改了,跳跃也不需要加进Ground树里面
设置两个状态的互相转换
在Ground转换到Jump时需要设置个条件,一个Trigger的参数
不对trigger进行限制的话,按键按多少次就会执行多少次,动画播放完继续播放
直到播放完按的次数为止

跳跃期间只播放一次动画(前提:不设二连跳)

在Ground状态数上新建一个脚本FSMClearSignals()


该脚本功能:重置Trigger变量的信号

定义string数组clearAtEnter
在出发该动画时OnStateEnter就会执行里面的代码
重置数组clearAtEnter里面的trigger元素signal
    public string[] clearAtEnter;public string[] claerAtExit;// OnStateEnter is called when a transition starts and the state machine starts to evaluate this stateoverride public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex){foreach (var signal in clearAtEnter){animator.ResetTrigger(signal);}}
向父对象发送正在跳跃的信息

在状态机中jump动画添加FSMOnEnter脚本

在动画开始时向父对象发送跳跃的信息
    public string[] onEntermessage;// OnStateEnter is called when a transition starts and the state machine starts to evaluate this stateoverride public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex){foreach (var msg in onEntermessage){animator.gameObject.SendMessageUpwards(msg);}}
动画结束时也发送一次,步骤同上
个人觉得都在同一个脚本中执行也没什么问题,Mr.fu可能觉得是在editor界面不好观察所以开了两个脚本
 public string[] onExitmessage;override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex){foreach (var msg in onExitmessage){animator.gameObject.SendMessageUpwards(msg);}}

在父对象中的相应脚本添加与数组中元素同名的方法,当动画播放时则会运行该脚本

在ActorController脚本中添加Onjump方法
public void Onjump(){print("on jump");}
跳跃时禁止方向的移动&& 保留跳跃时的初速度

跳跃时禁止方向移动:

结合上个小标题《向父对象发送正在跳跃的信息》,在Onjump脚本中更改输入脚本中的inputEnabled值

结合动画状态机的脚本触发OnjumpEnter,当跳跃动作结束时触发OnjumpExit
    /// <summary>/// 控制输入(输入是否有效)/// </summary>public bool inputEnabled = true;public void OnjumpEnter(){//print("on jump");pi.inputEnabled = false;//lockPlanar = true;}public void OnjumpExit(){pi.inputEnabled = true;//lockPlanar = false;}
因为当inputEnabled是false的时候目标方向输入就保持为0

这个控制使得觉得换动画时停顿,其实在空中能换方向小猫咪也能做到
所以
砍掉该控制 目前先整体做完再进行个人修改

if(inputEnabled == false){targetDup = 0;targetDright = 0;}
此时虽然跳跃时不可以随便移动,但是直接原地不动了,因为在Update的时候rigidbody还是在赋值
如果targetup/right的值突然为0,则Dmag的值也为0,所以就不动了

######保持跳跃时的初速度

想要保持跳跃时的初速度,就要找到赋值rigidbody向量速度的语句
根据该代码,只要跳跃时暂停planarVec的赋值,planarVec的值就会保持到jump动画结束
rigid.velocity = new Vector3 (planarVec.x,rigid.velocity.y,planarVec.z);

设定一个bool变量lockPlanar

当lockPlanar为false的时候对planarVec赋值
再对是否处于跳跃状态判断bool值
问题解决PS:planarVec为前movingVec变量
void Update
{if(lockPlanar == false){planarVec = pi.Dmag * model.transform.forward * Player_Speed * (Input.GetKey(pi.keyrun) ? Player_Runspeed : 1.0f);}
}
public void OnjumpEnter(){//print("on jump");pi.inputEnabled = false;lockPlanar = true;}public void OnjumpExit(){pi.inputEnabled = true;lockPlanar = false;}
↑该问题有几个不合理的设计,如果初速度足够大或者高度足够高时,跳跃状态结束会处于没有速度的尴尬地步
并且再高的水平面上掉落时跳跃状态结束就可在空中移动
跳跃结束无速度解决方法

把FSMOnEnter挂到Ground动画上,当转换到地面动画时才允许操作

删掉OnjumpExit()方法,加上该方法
public void OnGroundEnter(){pi.inputEnabled = true;lockPlanar = false;}
跳跃/地面动画→掉落动画
加入掉落动画

在状态机设置一个bool类型的IsGround变量

设计动画变换

G:地面动画
J :跳跃动画
F:掉落动画

1.G->J(jump) 2.G->F( ! IsGround)
1.J->G(IsGround) 2.J->F
1.F->G(IsGround)

判断是否在地面

使用Physics.OverlapCapsule的方法判断

/// <summary>
/// p1表示所给胶囊体下面半圆的圆心坐标
/// p2表示所给胶囊体上面半圆的圆心坐标
/// radius表示胶囊体半圆的半径
/// LayerMask.GetMask("name")表示只检测该Layer名字的遮罩层
/// </summary>
 OverlapCapsule(p1,p2,radius,LayerMask.GetMask("name"));
计算出当前对象的胶囊体上下半圆的圆心坐标p1,p2
因为transform.up的值为vector3类型的(0,1,0)的值
而p1圆心坐标就是目前对象坐标position.y+圆心半径radius,与x,z向量无关
p2就等于胶囊体的高度-半径
全程transform.up只当作常量使用,可用new vector3(0,1,0)代替
p1 = transform.position + transform.up * radius;
p2 = transform.position + transform.up * capcol.height - transform.up * radius;
Collider[] outputCols = Physics.OverlapCapsule(p1, p2, radius,LayerMask.GetMask("Ground"));

当胶囊体碰到Layer是“Ground”的对象时,向父物体发出信息(父类的其他子类对象也能收到信息)

        if (outputCols.Length != 0){SendMessageUpwards("IsGround");}else{SendMessageUpwards("IsNotGround");}

在行为控制脚本ActorController中添加收到信息后执行的方法

在该方法中控制动画状态机中的IsGround参数
    public void IsGround(){anim.SetBool("IsGround", true);}public void IsNotGround(){anim.SetBool("IsGround", false);}
胶囊体稍微下移

目前胶囊体底部正好在角色底部,这样在平地/斜坡运动的时候有可能会出现Fall动画
所以胶囊体稍微整体下移可以比较好解决这个问题

定义一个比较小的变量
想要胶囊体下半圆下沉,可减少计算圆心时的半径(非实际半径)
因为p1是 = 目前对象位置transform.position + y坐标上移半径大小
public float offset = 0.1f;
p1 = transform.position + transform.up * (radius - offset);
胶囊体上半圆下沉,可减少胶囊体高度
p2 = 目前对象位置transform.position + 胶囊体高度 - 胶囊体半圆半径
p2 = transform.position + transform.up * (capcol.height - offset) - transform.up * radius;
行走/掉落→翻滚→地面动画

Fall→Roll(roll && isGround)
Roll→Ground(isGround)
Ground→Roll(jump &&forward<0.5)
Jump→Roll(IsGround&&Roll)

没什么好说的
小后跳动画

Ground→Jab(jump && forward<0.1)

状态机添加一个jabvelocity变量
 在jab动画的curves里面添加与该变量名字完全一致的curves改变曲线使得播放动画时速度有曲线变化在jab动画中添加FSMOnUpdate脚本当动画Update时发送信息ActorController脚本接收

    public void OnJabUpdate(){thrustVec = model.transform.forward * anim.GetFloat("jabVelocity") *  jabMultiplier;}
确实不明白为何直接加一个rigidbody.velocity速度达不到效果,而高度可以

摄像头控制

框架:

角色为父对象Camera为角色的子对象的子对象
因为摄像头面对角色,角色作为摄像机的圆心旋转点,Camera本身Z轴向负方向移动一点位置
然后旋转角色的子对象(包含Camera子对象的对象)就可以得到面对着角色的摄像机上下旋转
因为角色的左右旋转是不带动rotation.y变化的,所以摄像头的可以控制rotation.y的值使摄像头旋转
并且更新角色的Forward值

摄像头输入控制

跟移动输入控制一样

Mup = (Input.GetKey(keyMup) ? 1.0f : 0) - (Input.GetKey(keyMdown) ? 1.0f : 0);
Mright = (Input.GetKey(keyMright) ? 1.0f : 0) - (Input.GetKey(keyMleft) ? 1.0f : 0);
通过输入得到的数值控制镜头移动
1.获得对象
2.进行旋转
这段是左右旋转,不限制范围,直接用Rotate比较方便
cameraHandle = transform.parent.gameObject;
playerHandle = cameraHandle.transform.parent.gameObject;
playerHandle.transform.Rotate(Vector3.up, pi.Mright * mrightSpeed * Time.deltaTime);
这里上下旋转,限制方位,使用四元数进行限制比较方便高效
不过要提前找好限制的旋转值(虽然欧拉角旋转也要)
//钳制摄像头上下移动范围&速度
cameraMuprange = Mathf.Clamp(cameraMuprange+=(pi.Mup * mupSpeed), 0, 1);
//摄像头旋转cameraHandle.transform.localRotation = Quaternion.Slerp(upMax, upMin,cameraMuprange);
上下旋转Mr.fu版
tempEulerx为cameraHandle的欧拉角的x值
tempEulerx -= pi.Mup * verticalSpeed * Time.deltaTime;
tempEulerx = Mathf.Clamp(tempEulerx,-40,30);
cameraHandle.transform.localEulerAngles = new Vector3(tempEulerx,0,0);
Note:Inspector界面滑动条控制参数
[Range(0.01f, 0.1f)]
public float mupSpeed = 0.05f;

Note:Mathf.Clamp()钳制输入范围
//钳制摄像头上下移动范围&速度
cameraMuprange = Mathf.Clamp(cameraMuprange+=(pi.Mup * mupSpeed), 0, 1);
解决摄像机旋转角色跟着旋转的问题
只要在旋转操作前保持目前的角色欧拉角值
在操作后保持操作前的欧拉角即可
tempmodelEuler = model.transform.eulerAngles;//////执行旋转的代码///model.transform.eulerAngles = tempmodelEuler;
使摄像机平滑跟随
这个功能把摄像机脱离角色比较好理解和控制,用一个新的对象代替主摄像机
然后把该对象的值赋给摄像机,然后用上平滑函数即可实现
    private void Cameratrack(){//camera.transform.position = transform.position;camera.transform.position = Vector3.SmoothDamp(camera.transform.position,transform.position,ref cameraDampvelocity, 0.1f);camera.transform.eulerAngles = transform.eulerAngles;
Note:当摄像机跟随放在Update中,而角色移动放在FixedUpdate中时
会出现卡顿现象,角色会卡顿,所以要放在同一种刷新函数中

攻击动画

Avatar Mask

动画不同层需要不同的AvatarMask

可以创建新的AvatarMask文件,设置该层可以活动的部位(人类模型)
动画转换等控制
当进入攻击动画时设置该层动画权重值为1
进入该层动画的Idle动画时设置权重为0
在攻击动画播放的时候禁止方向输入
public void OnAttack1hAEnter(){pi.inputEnabled = false;//lockPlanar = true;anim.SetLayerWeight(anim.GetLayerIndex("attack"), 1.0f);}public void OnAttackIdleEnter(){pi.inputEnabled = true;//lockPlanar = false;anim.SetLayerWeight(anim.GetLayerIndex("attack"), 0);}
######这样设置层的权值会影响攻击动画的播放,直接设置为0的话,当动画还没过渡完权值就为0会导致攻击动画卡顿
在攻击动画播放过程中,为了避免一直滑动的情况
在攻击动画中添加一个Curves(使得速度从1x到0x)
因为在进入攻击动画时没有锁定平面的速度
lockPlanar(锁定角色平面速度的输入(为true时只有惯性方向的速度,false时持续输入Dmag))
所以当进入攻击动画时不能输入方向,Dmag就为0,就会停下来
但是加了attack1hAVelocity的thrustVec后,会在攻击的时候稍微向前移动一点
    public void OnAttack1hAUpdate(){thrustVec = model.transform.forward * anim.GetFloat("attack1hAVelocity");}
解决除跑步行走外动画其他动画也能切换攻击动画的bug
添加一个判断是否处在Ground动画的方法
/// <summary>
/// 判断是否处在动画状态机某个状态
/// </summary>
/// <param name="stateName">动画状态名称</param>
/// <param name="layerName">动画状态机Layer层</param>
/// <returns>该层该名</returns>
    private bool CheckState(string stateName,string layerName = "Base Layer"){int layerIndex = anim.GetLayerIndex(layerName);return anim.GetCurrentAnimatorStateInfo(layerIndex).IsName(stateName );}
仅仅是这样判断不足以解决问题,因为动画的转换需要时间,在这段时间中仍然是判断处于Ground状态
所以要加入canAttack的bool变量
在跳跃时判为False
在进入Ground状态时判为True
    if (pi.jump){anim.SetTrigger("jump");canAttack = false;}public void OnGroundEnter(){canAttack = true;pi.inputEnabled = true;lockPlanar = false;}
还有一个Bug未解决,攻击状态时可切换其他动画的bug
添加物理材质

在Assets文件夹里面创建新的物理物质文件Physi Material

添加了两个物理材质,一个是0摩擦力的,一个是1摩擦力的(上图)

使用这两个物理材质的变换解决在墙上卡住不掉落的Bug
如何切换物理材质
获得角色的collider组件
当进入Ground动画时就设置摩擦力为1的物理材质
当退出Ground动画时就设置摩擦力为0的物理材质
    public void OnGroundEnter(){canAttack = true;pi.inputEnabled = true;lockPlanar = false;col.material = firctionOne;}public void OnGroundExit(){col.material = firctionZero;}
修复动画层级硬过渡Bug
只需要把层级的权值用lerp过渡就行
此处有个需要注意的点:在动画过渡时如果两个动画的Update都有设置权重的代码,该部分则会不符合预期
PS:一个是用一行代码写,可读性-max,一个是用三行代码写,可读性+++
weightLerpTarget = 1.0f;anim.SetLayerWeight(anim.GetLayerIndex("attack"), Mathf.Lerp(anim.GetLayerWeight(anim.GetLayerIndex("attack")), weightLerpTarget, 0.1f));weightLerpTarget = 0f;float currentWeight = anim.GetLayerWeight(anim.GetLayerIndex("attack"));
currentWeight = Mathf.Lerp(currentWeight, weightLerpTarget, 0.01f);
anim.SetLayerWeight(anim.GetLayerIndex("attack"), currentWeight);
动画事件Event
在动画文件中编辑,在需要的节点上添加Event事件
定义事件名字(在该动画中发出的信息)
定义传入参数类型和名字

然后该Animator组件对象中的脚本可以接收到名为ResetTrigger的信息
并当收到信息时,调用脚本里面的方法

public class ResetTriggerEvent : MonoBehaviour
{public void ResetTrigger(string triggerName){print(triggerName);}
}

重置Trigger
使用animator自带的ResetTrigger方法即可
获得trigger所在的动画状态机
结合上面的event事件进行重置trigger
    private Animator anim;private void Awake(){anim = GetComponent<Animator>();}public void ResetTrigger(string triggerName){print(triggerName);anim.ResetTrigger(triggerName);}
注意
在此功能(重置trigger)中,需要注意动画过渡与事件发生的时间点,如果trigger重置后立即准备过渡了
这样给玩家的反应时间就很短,操作变得很难,需要留出足够的时间给玩家激活trigger进行下一个动作的转换
(即:动画过渡期间已经算是下一个动画了,就算激活了trigger也不会跳到所需要的下一个动画)
动画移动量

在Animator组件中的ApplyRootMotion选项中打勾,即可把动画中的移动量赋值进position的值

图中显示HandledbyScript是因为在脚本中引用了相关脚本

如果直接打勾选项,则会把普通移动效果与动画移动量叠加,造成超级加速效果

在脚本中获取动画本来的移动量

获得动画组件后,可以使用OnAnimatorMove方法

这个方法在整个动画状态机计算完之后调用一次
在下面代码中,当整个动画状态机计算完一帧后,调用OnAnimatorMove方法
向父对象发送一个信息,包含anim.deltaPosition(动画本身设定的移动量)
    private void OnAnimatorMove(){SendMessageUpwards("OnUpdateRM", (object)anim.deltaPosition);}
在父对象的脚本中添加方法OnUpdateRM
接收OnAnimatorMove的信息,检查是否为第三次攻击的状态,是的话就解封装赋值给v3变量deltaPos
 rigid.position += deltaPos;//改变对象Pos位置deltaPos = Vector3.zero;//使v3变量归零,否则会一直叠加public void OnUpdateRM(object _deltaPos){if (CheckState("attack1hc", "attack")){deltaPos += (Vector3)_deltaPos;}}
添加武器模型

在手中加入武器的模型,只需在ybot(人物模型)的手节点位置添加模型即可

动画镜面表现

本来是右手攻击动画的,调整为镜面之后则会变成左手攻击的动画
具体操作:

选中某个动画状态机中的动画,Mirror选项就是动画镜面的选项,可以使用bool类型的参数控制是否镜面

【Mib自看】黑魂复刻Unity脚本相关推荐

  1. Unity黑魂复刻经典教程心得(三)-CameraController

    CameraController 1.根据角色的位置来计算camera的位置 targetLookAt = new GameObject("targetLookAt").trans ...

  2. Unity黑魂复刻经典教程心得(一)

    b站上傅老师的黑魂复刻教程,是比较好的,傅老师风情幽默,值得种草 https://www.bilibili.com/video/BV1gW411T7yb?p=55

  3. 黑魂复刻游戏的玩家控制器(基础移动,动画实现及优化)——Unity随手记(2021.3.15)

    文章目录 今天实现的内容: 动画机设计理念 动画机的运用及模型旋转 玩家角色的位移 爬坡测试 跑步 旋转的优化 跑步动画的优化 BUG以及缺陷: 值得注意的: 今天实现的内容: 动画机设计理念 要我说 ...

  4. 黑魂复刻游戏的碰撞摩擦问题——Unity随手记(2021.4.27)

    文章目录 前言 今天实现的内容: 角色跳起来时的卡墙问题描述 修改物理材质方案 修改m_planarVec方案 BUG以及缺陷: 值得注意的: 前言 本篇博客旨在解决黑魂like游戏开发时遇到的一个问 ...

  5. Unity项目-黑魂复刻(四)玩家控制器(翻滚以及跳跃操作改动)

    翻滚以及跳跃操作改动 黑魂游戏中,翻滚的触发条件:1.走路按跳跃键 2.助跑按跳跃键 3.高处掉落 所以之前的跳跃键需要改成翻滚键 翻滚动画以及触发逻辑: 注意:由于翻滚的动画会出现y方向的根tran ...

  6. 黑魂复刻游戏的玩家控制器(翻滚及跳跃的重新设计)——Unity随手记(2021.3.31)

    文章目录 今天实现的内容: 翻滚和跳跃的重新设计 翻滚动画及触发逻辑 翻滚的向前冲量 跳跃并向前翻滚 BUG以及缺陷: 值得注意的: 今天实现的内容: 翻滚和跳跃的重新设计 按照黑魂游戏的设计,跳跃其 ...

  7. 黑魂复刻游戏的玩家输入模块——Unity随手记(2021.3.14)

    文章目录 前言 今天实现的内容: 按键封装 获取输入及输入信号优化 输入的渐变 模块的软开关 处理输入 BUG以及缺陷: 值得注意的: 前言 好久不见,在接下来的Unity随手记里,我会学着B站上的视 ...

  8. Unity项目-黑魂复刻(二)玩家控制器(跳跃)

    跳跃信号 要实现跳跃,首先要实现跳跃的输入.跳跃信号是按下的当场触发的一次性触发控制(Trigger Once Signal),这个信号转化为编程语言就是bool值,通过控制真还是假判定是否按下跳跃键 ...

  9. Unity项目-黑魂复刻(三)玩家控制器(跳跃)

    新增跳落动画 在jump动画播放完后过渡到下落动画,之后再跳回地面动画,需要注意回去的顺序,Exit Time越短就先回去 新增落地侦查器 解决落地之前都是fail动画的问题,而不是fail落地动画播 ...

最新文章

  1. 工作方法及总结(给自己的忠告)
  2. 定义一个属性_CocosCreator脚本属性个性化定制——下拉列表属性、滑动条属性
  3. aspose 生成word 简单的文档操作
  4. 解析.sens数据集
  5. leetcode 67 Add Binary
  6. 推翻相对论的专家,就差安排明天几点日出了
  7. 金三银四网络面经之 DNS 详解!
  8. fiddler https
  9. java完全自学手册 pdf_fortran教程下载
  10. 00-各种工具下载链接汇总
  11. 基桩测试软件,智博联ZBL-U5700/5600机内软件测桩模块更新软件
  12. python优化网站_利用python做seo优化
  13. 电子工程师常用的单位转换
  14. 带农历日期的html代码,很全的显示阴历(农历)日期的js代码
  15. iOS UIViewController跳转
  16. php中合并图片并添加水印,php通过imagecopymerge 函数给图片制作水印
  17. 1分钟了解 rap2
  18. 正则只保留括号里的内容
  19. 微信小游戏个人开发者如何盈利
  20. 微电子电路——反相器级联

热门文章

  1. vue.runtime.esm.js?2b0e:619 [Vue warn]: Error in render: “TypeError: Cannot read property ‘matched‘
  2. C语言库函数的模拟实现
  3. Linux下修改(NTFS)磁盘(卷标)显示名称
  4. Facebook、微信团队、Twitter、微软开源软件列表一览
  5. 清洁工和总裁阅读和理解_您应该如何(以及为什么)清洁手机和其他电子设备...
  6. python除法取整数部分_python3 除法去掉小数,保留整数的做法
  7. java中题目:输入两个正整数m和n,求其最大公约数和最小公倍数。
  8. 第十二届蓝桥杯大赛软件类省赛第一场 Java 大学 B 组题目蓝桥杯JavaB组大赛软件类省赛第十二届第一场
  9. linux 触摸结构体,xboot-x4412ibox项目实战54-Linux触摸屏驱动之I2C驱动实验 - Powered by Discuz!...
  10. ubuntu系统 ssh远程链接X Server