【Unity】用Lerp()实现类杀戮尖塔手牌变化

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;
                    }
            }
      }
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值