2019年7月24日 学习日记

上一次(22日的学习日记中)我们最后提到了AI的制作,其实利用了时间的推移并把动作数据化按照参数传入方法,其中也不乏随机数。当然也有死亡时候特效的体现,复习了Instantiate方法的调用,参数为组件、位置、方向等。Destroy是用来销毁组件用,第二个参数是销毁时间。
今天我们主要是对于角色的抓取和animator不同层的设置,以及动画mask和动画本身的关系来进行描述和理解。
一、抓取
变量设置:

    public Transform grabSocket;//角色抓取物体的时候放置的点
    public Transform grabObject = null; //角色抓取后拿在手里的物体
                                        //总结就是放在点上的物体,当然放置点是相对于角色而言的
    public int armsAnimatorLayer = 1;

这里角色要抓取物体,就要有放置物体的位置,这里我们主要设置了两个点。一个是grabSocket(一个角色抓取点,用来固定抓取物的位置,其实这里有一点小bug,即抓取位置不能根据物体大小来调整,因此这里还可以增加代码量以增加游戏的趣味性)。而grabObject是用来判断角色是否已经抓取物体,其中物体和抓取点的相对位置由创建的grabSocket确定。这里armAnimatorLayer将在后面和animator的层来讲述。
1、设置动画层权重

    private void Start()
    {
        animator.SetLayerWeight(armsAnimatorLayer, 1f);
    }

2、重载Character中的Update函数

    protected override void Update()//重载函数
    {
        animator.SetBool("Grab", grabObject ? true : false);
        base.Update();
    }

这里涉及到了C#中的重载

概念:方法的重载指的就是方法的名称相同,但是参数不同,而参数不同分位两种情况
1)、如果参数个数相同,那么参数的类型就不能相同
2)、如果参数类型相同,那么参数的个数就不能相同
*****且方法的重载和返回值没有关系


1.函数重载可以用于非成员函数和类的成员函数,而虚函数只能用于类的成员函数
2.函数重载可用于构造函数,而虚函数不能用于构造函数
3.如果对成员函数进行重载,重载的函数与被重载的函数应该是用一个类中的成员函数,不能分属于两个不同继承层次的类,函数重载处理的是横向的重载。虚函数是对同一类族中的基类和派生类的同名函数的处理,即允许在派生类中对基类的成员函数重新定义。虚函数处理的是纵向的同名函数。
4.重载的函数必须具有相同的函数名,函数类型可以相同也可以不同,但函数的参数个数和参数类型二者中至少有一个不同,否则在编译时无法区分。而虚函数则要求同一类族中的所有虚函数的函数名,函数类型,函数的参数个数和参数类型都全部相同,否则就不是重定义了,也就不是虚函数了
5.函数重载是在程序编译阶段确定操作的对象的,属于静态关联。虚函数是在程序运行阶段确定操作对象的,属于动态关联。

由于PlayerCharacter继承于Character,因此其刷新函数需要重载与Character,这时就需要在Character中的Update函数做出一些改变:void Update()-----》protected virtual void Update()目前阶段先记住,继承时需要对Update函数进行函数重载
3.grab函数
代码:

    public void GrabCheck()
    {
        if (grabObject != null && rotateComplete)
        {
            //如果有抓取物体,且转向完成
            grabObject.transform.SetParent(null);//当前抓取物的父节点设为空,本应该为设置好的grabSocket,现在要放下就取消作为父节点啦
            grabObject.GetComponent().isKinematic = false;//非动力学状态,在搬起来的时候不受力
            grabObject = null;//自己设置为空,即搬运物体已经落地,不需要再被取到
        }
        else
        {
            //如果没有抓取物
            var dist = cc.radius;//由charactercontroller方法得到物体的半径
            RaycastHit hit;//创建射线类组件
            //Debug.DrawLine(transform.position, transform.position + transform.forward * (dist + 1f), Color.green,10f);
            if (Physics.Raycast(transform.position, transform.forward, out hit, dist + 2f))//位置,方向,输出out,检测半径
            {
                if (hit.collider.CompareTag("GrabBox"))//如果击中物的标签为GrabBox,则可以进行搬取
                {
                    grabObject = hit.transform;//调取碰撞物的transform字段
                    grabObject.SetParent(grabSocket);//将grabSocket设置为hit的父节idan
                    grabObject.localPosition = Vector3.zero;//局部位置设定为0,0,0
                    grabObject.localRotation = Quaternion.identity;//旋转坐标(四元数),即指Quaternion(0,0,0,0)
                    grabObject.GetComponent().isKinematic = true;//设置射线击中的物体被抓取后不受外力控制,即有固定的位置,由grabSocket决定                
                }
            }
        }
    }

可见,即使是一个简单的抓取也有众多限定 条件,比如旋转完了之后才能放下箱子,射线要以身子向前半径加点为长度,触碰到了物体就可以触发方法以判断是否可以搬取。由于搬取的本质是让箱子和人的相对速度为0,把箱子放在一个和人相对静止的位置,像极了绑定。将grabSocket当作碰撞物体(Box)的父节点,这个就是绑定了。将位置和旋转全部初始化,之后取到当前组件的Rigidbody控件后调用isKinematic设置为不受外力的东西,只和角色的相对运动有关
二、加上半身动画,层次(权重)的设置
我们在原animatorcontroller中继续进行操作,只不过要再原先Base Layer的基础上再新建一个Arms层,先新建一个空动作,作为初始动作。因为我们在搬运之前就是没有动作。拉入ArmsPull动画和Nothing(空动作)双向连接,这个时候就要考虑用于判断动作转换的bool条件,从左上角的Layer设置切换到Parameters,添加一个bool类型变量Grab,从无动作到有动作是grab为真,反之为false。当然,单单有可视化animatorcontroller脚本可运行不了,我们还需要Script的帮忙来对bool类型的判断变量进行复制与更新,以此激活动作。
这里就有一个重点,我们需要时时刻刻对bool类型的变化进行检测以此对动作做出相应的调整,而PlayerCharacter又继承于Character,因此可以共用一个Update函数,即可以进行重载,这里要注意的是,重载过程中,原主Update函数要变成虚函数并保护。
具体写法参照上方,虚函数的定义和说明:

https://www.cnblogs.com/jiajiayuan/archive/2011/09/14/2176015.html

这里进行一下对照:
PlayerCharacter中:

    protected override void Update()//重载函数
    {
        animator.SetBool("Grab", grabObject ? true : false);
        base.Update();//调用之后再进行一次主刷新函数
    }

在Character中:

protected virtual void Update()
    {
        pendingVelocity.z = 0f;//z轴速度锁定
        cc.Move(pendingVelocity * Time.deltaTime);//移动方向为速度乘以前后帧差时间 
        //更新动画
        animator.SetFloat("Speed", cc.velocity.magnitude);//magnitude返回了向量的长度,即距离(0,0,0)。最常用来返回物体的移动速度,这个只能读取,也可以用标准化后除以速度
        animator.SetBool("Grounded", cc.isGrounded);//methodname,bool
        //更新重力
        pendingVelocity.y += cc.isGrounded ? 0f : Physics.gravity.y * 10f * Time.deltaTime;//在地上就没有y速度,没在地上就要受到重力作用,单位i力*g*时间帧差
        AttackCheck();
    }

明显重载之后的都被保护起来了,且被重载的主函数变为虚方法。重载的字段为override

三、Mask

我们的物体在运动的时候依赖于animator组件,这个组件也依赖于模型本身的动画,动画又是由好多个关节互相配合组成,就像我们身上的肌肉和骨骼相互配合以便于我们的身体做出一套套复杂又灵巧的动作。
2019年7月24日 学习日记_第1张图片
这些都是配和我们的animator组件工作的骨头哦~

对了,还有一个问题,就算是继承于主方法的方法,其更新函数能不能直接用Update而不是重载,我进行了实验,对游戏有一些影响,玩家角色会直接到谷底,就像加了trigger一样,因此我想知道是继承关系的情况下对于update函数重载和不重载的区别

你可能感兴趣的:(学习日记)