Unity——#04 跳跃
单纯只考虑跳跃的实现的话,只需考虑5个问题:
1.使用Trigger Type Signal触发型信号
2.要解决臭名昭著的问题:在一次触发时间里累积了两次的跳跃
3.锁死玩家输入,防止跳跃过程中的转向
4.锁死输入同时保留速度,使其能够在跳跃过程中向前移动
5.提供y分量的冲量,让其真正跳离地面
OK,摩拳擦掌,跃跃欲试。
1.使用触发型信号(Trigger Type Signal)
在实现Run中,使用的是按压型信号(Pressing Type Signal),与其不同的是,触发型信号的取信号时间段为当玩家键入指定键的那一刻,这就很符合用来实现跳跃,按一次跳一次,而不是按住一直跳。
首先宣告一个string变量记录触发跳跃的按键信息,然后宣告一个bool变量记录触发型信号:
public string keyB = "space";
public bool jump;
然后使用Input.GetKeyDown()来判断玩家是否键入了空格,是就返回true给jump变量;反之返回false。
jump = Input.GetKeyDown (keyB);
在实现Run时就有提到过Input.GetKeyDown(),它是在当用户键入指定键的那一帧时就返回true,就很符合用来实现触发型信号。
代码部分OK,再来看看Animator视窗,添加jump动画然后让其与ground联系起来(两者之间Make Transition) :
Animator
然后在ground到jump的Transition里消钩Has Exit Time,这意味着当接收到跳跃的触发信号时,不用等ground的动画播放完,直接中断后播放jump的动画。
消钩Has Exit Time
而jump到ground的Transition默认设置就好。
现在我们应该在Animator视窗里的Parameters那一栏(参数栏)创建一个Trigger型参数,命名为jump。
命名为jump的Trigger型参数
利用这Trigger型参数将ground到jump的Transition激活起来,就是说当jump信号被触发时,将打断ground的动画,转换到播放jump的动画,具体操作是在这个Transition里新增一Condition,然后选中jump参数即可:
剩下的就是将Parameters里的jump与代码中的bool变量jump联系起来,在Animator组件的Controller的脚本文件上敲上:
if (pi.jump) {
anim.SetTrigger ("jump");
}
当bool变量的jump为true时,就设置Animator里的jump为触发状态。至此就应该能实现跳跃动画的播放了,当然只是播放,并不是真正的跳跃:
Jump2.累积触发
当玩家连续的键入两次跳跃键时(在一次跳跃动画播放期间),就会累积一次Trigger在里面,造成角色在跳跃完一次之后,继续跳跃第二次,即使之后没再键入跳跃键。而正常情况是,当玩家键入第一次跳跃键后,跳跃动画开始播放,在这播放期间,即使玩家键入第二次跳跃键,也不会有反馈,直到跳跃动画播放完毕,回到ground状态后,键入的跳跃键才有效果。如图就是我连续快速键入两次跳跃键的结果:解决这个问题需要在ground这个Blend Tree上加一个行为文件,在Inspector视窗内点击Add Behaviour,创建一命名为FSMClearSignal的脚本文件:
Add Behaviour
双击打卡文件,发现有好几组重载函数被注释了,下面简述一下其中几个的被调用的时机:
// OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
//当进入这个状态时调用这个函数,进行相应操作
}
// OnStateUpdate is called on each Update frame between OnStateEnter and OnStateExit callbacks
override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
//当在这个状态的期间时一直调用这个函数,进行相应操作
}
// OnStateExit is called when a transition ends and the state machine finishes evaluating this state
override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
//当离开这个状态时调用这个函数,进行相应操作
}*/
以后一般经常用到这三个函数,还有两个暂时用不上就不再赘述。这次呢是只用到OnStateEnter()这个函数,要做的就是当进入到ground这个状态时,清空jump这个Trigger,这样就能把在播放jump动画期间累积到多一次的Triggeri信号给清除了,具体实现如下:
public string[] clearEnter;
public string[] clearExit;
// OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
foreach (var signal in clearEnter) {
animator.ResetTrigger (signal);
}
}
首先宣告一个string数组,来接收需要被清空的信号,由于是public修饰符,所以可以在Inspector视窗内直接填上要被清空的信号的数量和名字,这里要清空的只有一个就是jump信号(如图)。由于这个函数接收了一个Animator对象,所以可以利用这个对象调用其ResetTrigger()函数来清空信号,通过使用foreach来把数组里的信号依次reset掉。
Reset jump signal
看看效果: Jump
可以看到,现在即使我快速按3次跳跃键,它都只是完成了一次跳跃。
3.锁死玩家输入
在第一点的效果图中,能看到角色在跳跃过程中竟可以做到向后转身,这是什么异于常人的轻功?为了防止玩家在游戏中做出这种惊为天人的骚操作,需要在跳跃过程中锁死玩家的输入,使角色不能转向。具体实现我们应该宣告一个bool变量来控制Dmag:
//在PlayerInput.cs文件里
public bool InputEnabled=true;
void Update(){
...
if (!InputEnabled) {
Dmag = 0;
}else
Dmag = Mathf.Sqrt (Dup2 * Dup2 + Dright2 * Dright2);
...
}
该bool变量默认为true,即玩家默认情况是可以输入进行移动;当InputEnabled变为false,Dmag立刻置0,这样玩家就无法操控角色了。
双击点开,在OnStateEnter定义发射信息的操作:
//在OnStateEnter()文件里
public string [] onEnterMessage;
// OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
foreach (var msg in onEnterMessage) {
animator.gameObject.SendMessageUpwards (msg);
}
}
由于一般处理动画的代码我都放在了同一个命名为ActorController.cs的文件里,所以关闭玩家的输入也应该放在这里,而这个文件是我添加在胶囊的组件,胶囊与模型是父子关系,而Animator组件在模型里。如果从OnStateEnter函数给出的参数Animator animator
出发,在进入了跳跃状态时,发送的信息应该从animator
→animtor.gameObject
,得到拥有这个Animtor组件的物体(这里是模型),到animator.gameObject
→animator.gameObject.SendMessageUpward()
,最终往物体的上层(Upward)即胶囊发送信息。这样在胶囊里的全部组件都能以发送的消息作名字创建函数,一旦消息发出,这些函数就会被调用,进行相应的操作。首先我们定义发送消息的内容为"OnJumpEnter":
那么就应该在ActorController.cs文件定义一个命名为OnJumpEnter()的函数:
//在ActorController.cs文件里
void OnJumpEnter(){
pi.InputEnabled = false;
}
看看效果:
Jump
现在跳跃时我已经不能进行任何的输入了,所以不能转向,但同时引发了另外一个问题,那就是因为之前的实现锁死的代码是:
if (!InputEnabled) {
Dmag = 0;
}else
Dmag = Mathf.Sqrt (Dup2 * Dup2 + Dright2 * Dright2);
Dmag = 0;
在玩家不能输入的同时也使角色的速度变为0,所以出现了在跳跃时不能前进的问题。这就是第4点要讨论的内容:锁死输入同时保留速度,使其能够在跳跃过程中向前移动。
好了,今天花在这里的时间已经够多了,休息一下,剩下的两点内容日后在谈。共勉!