Lerp的种类
- math类里的Lerp函数可以对很多种数据类型进行插值,这里主要用的是Vector3的,因为变换position、scale都要用
- 这里的难点是rotation,我试了用Quaternion.Lerp但是放弃了,因为参数必须也是Quaternion类型的(注意大写),
`public static Quaternion Lerp(Quaternion a, Quaternion b, float t);`
所以transform.rotation是不能作为参数的,quaternion.Eular(transform.eularangle)转换成的欧拉角同样也是用不了的,所以只能从欧拉角或借助LookRotation函数里加Lerp来变换
Hover时手牌强调动画
- 强调分三部分:放大、向上移动、取消原有的旋转,既然是Lerp做平滑,就一定是在update里,但是获取鼠标悬浮事件又需要OnMouseEnter,我的解决方法是用bool类型控制update中Lerp的开启或关闭
//鼠标进入
if (zoomIn && !zoomOut)
{
Focus();
}
//鼠标退出
if (zoomOut && !zoomIn)
{
UnFocus();
}
- 放大和向上移动很简单,注意transform.position就已经是世界坐标了,OnMouseExit的动画其实也就是lerp的一二两个参数对调一下
lerpTime += Time.deltaTime * zoomSpeed;
if (lerpTime <= 1)
{
transform.localScale = math.lerp(originscale, originscale * scaleRate, lerpTime);
transform.position = math.lerp(originPosition, originPosition + Vector3.up * moveDistance, lerpTime);
transform.localRotation = quaternion.LookRotationSafe(Vector3.forward,math.lerp(Vector3.up,originEular,lerpTime));
//Debug.Log(transform.localEulerAngles);
}
- 注意旋转值插值的lerp我放在了lookRotation里面,为什么不直接用欧拉角Lerp呢?毕竟它就是Vector3,理论上可以。因为Lerp对欧拉角的Lerp有坑,例如明明一开始欧拉角是(0,0,-15),要Lerp到(0,0,0),但是过程中会出现(0,0,200),查了半天也不知道怎么会有这种问题,所以最后用的是在(0,0,15)和(0,1,0)间的插值,插值的结果再进入lookRotation,实现卡牌被hover时摆正的效果
- 在lookRotation的使用中,理解起来还挺抽象的,表示三维空间的朝向为什么要两个三维向量呢?例如希望卡牌欧拉角为(0,0,0),正对摄像机,那么lookRotation就是(Vector3.forward,Vector3.up)。我的理解是,想象卡牌是个人,你想让它看着你,那就是面对着你(forward),头顶朝天(up),刚才我的旋转插值本质上就是在卡牌头顶正对天和斜对天进行插值
- 比较容易被忽略的是,Lerp的插值插值其实并不能保证变换的程度完全是设置的程度,例如一次缩放结束后,可能和变换前有0.01的误差,虽然它很小,但积累起来就麻烦了,那怎么办呢?保险起见我在每次Lerp的最后加了一个直接变换
transform.localScale = originscale;
transform.position = originPosition;
动态生成数量不同的手牌
基础位置也就是手牌区正中间我已经定好了,在此基础上计算每张卡的xy相对位移
private Vector3 CalTargetPos(int cardIndex,int cardTotal)
{
//发的手牌数为奇数
if(cardTotal % 2 == 1)
{
var xPos = Vector3.left * (math.ceil(cardTotal / 2)) + Vector3.right * cardIndex;
var yPos = Vector3.down * math.abs(math.ceil(cardTotal / 2) - cardIndex);
return transform.position + CardXSpace * (xPos) + CardYSpace * (yPos);
}
//为偶数
else
{
var xPos = Vector3.left * ((cardTotal / 2) - 0.5f) + Vector3.right * cardIndex;
var yPos = Vector3.down * math.abs(cardTotal / 2 - cardIndex - 0.5f);
return transform.position + CardXSpace * (xPos) + CardYSpace * (yPos);
}
private quaternion CalTargetQua(int cardIndex,int cardTotal)
{
//发的手牌数为奇数
if (System.DelivercardNum % 2 == 1)
{
return quaternion.LookRotation(Vector3.forward,Vector3.up + CardEular * Vector3.right * (cardIndex - math.ceil(cardTotal / 2)));
}
//为偶数
else
{
return quaternion.LookRotation(Vector3.forward, Vector3.up + CardEular * Vector3.right * (cardIndex - ((cardTotal / 2) - 0.5f))); ;
}
}
}
- 每局发牌的数量多少和奇偶性都会对卡牌位置的计算产生影响,如果是奇数,那就一张卡在中间,旁边的依次排开,如果是偶数,就是对称分布,所以要先判断此局发牌数的奇偶性
- 公式比较好理解,就不细讲了
- 注意旋转的函数,虽然这里可以用rotate,但是为了避免四元数和欧拉角转换过程中可能的问题,还是用LookRotation
手牌数量减少后的重新布局
本质上还是运用上述的计算公式,在有卡牌被销毁时重新计算,但是难点在于,在原有的脚本调用顺序中,无论如何也只能在卡牌被销毁之前获取消息,那么此时关卡中的卡牌总数还未变化(因为还未销毁),所以计算的结果没变化,我的解决方案就是
- 把将删除的卡的tag改了
- 按tag查找不会被销毁的卡、
- 如果还有卡
- 调用重计算位置的方法
- 否则不调用
当卡牌的运动状态过多时用枚举类型避免混乱
现在卡牌已经有了好几种行为了:放大、缩小、发牌时移动、重布局时移动……,为了避免卡牌移动时被hover可能产生的问题,需要在lerp之前加入判断,但是都设置个bool值就太麻烦了,这时候就需要枚举类型,我是在brackey的类宝可梦回合制教程里学会的,这是B站视频链接
最后在Updete里用switch判断状态,再调用封装好的函数,比用bool清晰整洁多了
public enum BattleCardState { zoomIn, zoomOut, returning, dragging, move, settle }
void Update()
{
if (!GameObject.FindGameObjectWithTag("CardView"))
{
switch (cardState)
{
//拖拽状态
case BattleCardState.dragging :
{
Vector3 mousePos = MouseInWorldCoords();
da.OnDraggingInUpdate();
break;
}
//松开后返回状态
case (BattleCardState.returning):
{
LerpToOrigin();
break;
}
//放大状态
case (BattleCardState.zoomIn):
{
Focus();
break;
}
//缩小状态
case (BattleCardState.zoomOut):
{
UnFocus();
break;
}
//移动状态
case (BattleCardState.move):
{
Debug.Log("move");
transform.position = math.lerp(transform.position, originPosition, Time.deltaTime * 3);
transform.localRotation = quaternion.LookRotationSafe(Vector3.forward, math.lerp(Vector3.up, originEular, Time.deltaTime * 3));
if ((transform.position - originPosition).magnitude < 0.1f)
{
cardState = BattleCardState.settle;
transform.position = originPosition;
transform.eulerAngles = originEular;
}
break;
}
}
}
}