这次来解决上节遗留的问题:
1.斜向走移速快40%的问题
不废话,直接上图:
图中可以看到,当角色斜向走时,Dmag和forward的值都是1.414,而斜向奔跑时,两者竟达到了2.8多,而Dmag与角色的移动速度有着莫大的关系:
movingVec = pi.Dmag * model.transform.forward * (pi.run ? runSpeed : 1.0f); //移动信息
这会令玩家更依赖斜向移动来在游戏中赶路,那么其他正向的移动就显得没什么用途了,但在这个问题上并不用分清孰是孰非,只要与你的游戏理念相符,这都是可以的,例如任天堂以前红白机的大部分游戏的斜向移动都是1.414倍移速,没有任何修改(道途听说)。
不过这在RPG游戏是不可忍受的,平白无故多40%移速加成,这可不能便宜了玩家(雾,所以还是应该对其进行修改。
图底下的两条公式就是要用到的公式,其效果就是图中的第二个变换过程,可以看到它把一个正方形转换为一中间看似凸起的圆形,如果在三维上,某一坐标到原点坐标的路径距离(一条曲线)没有变(凸起),但在二维上,距离就已经缩短了。这只是我的理解,可能不对,需高人指点。好摩拳擦掌,把公式转为代码试试看。
我的做法是,定义一个函数,然后在函数里实现这个椭圆映射的功能。
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都不会超过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,这样效果明显一点:
能看出来,现在起跑的动作连贯很多,看角色的左手起跑时的摆动就很流畅自然。如果没做平滑处理呢,角色的左手是上一刻在自己的胸前,下一刻就已经在大腿外侧了,没有这个摆动的动作的。