美文网首页工作生活
Unity——#03 Run_2

Unity——#03 Run_2

作者: MisakiMel | 来源:发表于2019-07-03 07:13 被阅读0次

  这次来解决上节遗留的问题:

1.斜向走移速快40%的问题

  不废话,直接上图:


  图中可以看到,当角色斜向走时,Dmag和forward的值都是1.414,而斜向奔跑时,两者竟达到了2.8多,而Dmag与角色的移动速度有着莫大的关系:

movingVec = pi.Dmag * model.transform.forward * (pi.run ? runSpeed : 1.0f); //移动信息

  这会令玩家更依赖斜向移动来在游戏中赶路,那么其他正向的移动就显得没什么用途了,但在这个问题上并不用分清孰是孰非,只要与你的游戏理念相符,这都是可以的,例如任天堂以前红白机的大部分游戏的斜向移动都是1.414倍移速,没有任何修改(道途听说)。
  不过这在RPG游戏是不可忍受的,平白无故多40%移速加成,这可不能便宜了玩家(雾,所以还是应该对其进行修改。

  我们想做到的就是在坐标是(1,1)的时候,原点距其的距离不是√2,而是转换为1,这就要将(1,1)、(-1,1)、(-1,-1)、(1,-1)这四个坐标组成的正方形转换成矩形(距离上)。要实现正方形到球形变换,这需要两条公式助力,称椭圆映射法: Eliptical Grid Mapping
  图底下的两条公式就是要用到的公式,其效果就是图中的第二个变换过程,可以看到它把一个正方形转换为一中间看似凸起的圆形,如果在三维上,某一坐标到原点坐标的路径距离(一条曲线)没有变(凸起),但在二维上,距离就已经缩短了。这只是我的理解,可能不对,需高人指点。好摩拳擦掌,把公式转为代码试试看。
  我的做法是,定义一个函数,然后在函数里实现这个椭圆映射的功能。
    Vector2 SquareToCircle(Vector2 temp){
        Vector2 output = Vector2.zero;
        output.x = temp.x * Mathf.Sqrt (1 - (temp.y * temp.y) / 2);
        output.y = temp.y * Mathf.Sqrt (1 - (temp.x * temp.x) / 2);
        return output;
    }

  接下来就是要调用它,我们原本的坐标是Dup跟Dright,调用SquareToCircle时要喂给它吃,然后定义一临时Vector2变量去接其返回出来的值:

        Vector2 tempDAxis = SquareToCircle (new Vector2 (Dup, Dright));

        float Dup2 = tempDAxis.x;
        float Dright2 = tempDAxis.y;

        Dmag = Mathf.Sqrt (Dup2 * Dup2 + Dright2 * Dright2);
        Dvec = Dup2 * transform.forward + Dright2 * transform.right;
  我们新定义了两个float的变量Dup2和Dright2来接临时变量的值,然后再计算Dmag(速度)跟Dvec(移动方向),这时movingVec的值也会跟着变化。事不宜迟,赶紧看看效果如何: Dmag = 0.99999

  现在无论是走还是跑Dmag都不会超过1,可喜可贺可喜可贺。

2.平滑起跑

  上节中遗留的另外一个问题就是在走到跑的动画切换时,没有过渡,立马跑起来,这很不流畅。解决这个问题要用到之前提到的概念——插值,不过上次用的是球形插值,这次可以用线性插值,因为两离散点间不会经过零点(1到2),所以不用考虑绕开零点。
  线性插值应该用Mathf.Lerp(),每遇到一个新函数,都要看看官网的描述,看它能干嘛,怎么用最好:

public static float Lerp(float a, float b, float t);
Returns
float The interpolated float result between the two float values.

Description
Linearly interpolates between a and b by t.

The parameter t is clamped to the range [0, 1].

When t = 0 returns a.
When t = 1 return b.
When t = 0.5 returns the midpoint of a and b.

  能实现线性插值,吃三个参数,前两个定义一个范围(between a and b),而t是用来表示完成这个过程的程度,t = 0.5时返回a跟b的中点。那么如何实现两点间的逐渐靠近以致a最终渐变到b呢?调用方式为a=Lerp(a,b,t);,在经过多次Update()之后,把a渐变成b。这里很容易会有一个误区就是认为参数t是完成这个过程的时间,认为t = 1就是在1秒内完成a到b的渐变,一开始我也是这么理解的,后来发现是错的。实现代码如下:

        float temp = Mathf.Lerp(anim.GetFloat("forward"),pi.Dmag*(pi.run?2.0f:1.0f),0.5f);
        anim.SetFloat("forward", temp);

  首先要做的就是把当前的forward值取出来作为区间起点,把玩家移动的信号反馈做区间的终点,这样无论是forward从2变1或0还是从0或1变2都能照顾到。定义一临时变量是能理清思路,知道自己该做什么,测试无误后,就把它取消,合并代码。

        anim.SetFloat("forward", Mathf.Lerp(anim.GetFloat("forward"),pi.Dmag*(pi.run?2.0f:1.0f),0.1f));

  我把渐进的程度设为0.1,这样效果明显一点:


Run

  能看出来,现在起跑的动作连贯很多,看角色的左手起跑时的摆动就很流畅自然。如果没做平滑处理呢,角色的左手是上一刻在自己的胸前,下一刻就已经在大腿外侧了,没有这个摆动的动作的。

相关文章

网友评论

    本文标题:Unity——#03 Run_2

    本文链接:https://www.haomeiwen.com/subject/liathctx.html