工作生活

Unity——#04 跳跃

2019-07-04  本文已影响0人  MisakiMel

  单纯只考虑跳跃的实现的话,只需考虑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为触发状态。至此就应该能实现跳跃动画的播放了,当然只是播放,并不是真正的跳跃:

Jump
2.累积触发
  当玩家连续的键入两次跳跃键时(在一次跳跃动画播放期间),就会累积一次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,这样玩家就无法操控角色了。

  在另一方面,应该在恰当时机调用这个变量使其发生bool or false的转换,而时机就是在进入跳跃的状态时锁死玩家输入,这个时间节点与OnStateEnter()函数调用时间是一致的,所以要做的就是当进入跳跃状态后,在OnStateEnter()里发送消息告知某一文件应该要锁死玩家输入啦。由于是在进入jump状态时要执行上述操作,所以包含此函数的文件应该是在jump动画下(通过Add Behaviour新建),命名为FSMCallOnEnter: FSMCallOnEnter
  双击点开,在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出发,在进入了跳跃状态时,发送的信息应该从animatoranimtor.gameObject,得到拥有这个Animtor组件的物体(这里是模型),到animator.gameObjectanimator.gameObject.SendMessageUpward(),最终往物体的上层(Upward)即胶囊发送信息。这样在胶囊里的全部组件都能以发送的消息作名字创建函数,一旦消息发出,这些函数就会被调用,进行相应的操作。首先我们定义发送消息的内容为"OnJumpEnter":

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点要讨论的内容:锁死输入同时保留速度,使其能够在跳跃过程中向前移动。
  好了,今天花在这里的时间已经够多了,休息一下,剩下的两点内容日后在谈。共勉!

上一篇下一篇

猜你喜欢

热点阅读