今天实现的内容:

攻击动画层

为了加入新的攻击动画,我们要设计一个攻击动画层。新的动画层一是为了使老动画层不至于臃肿,而是为了配置Avatar Mask。

黑魂复刻游戏的玩家控制器(攻击的基础实现)——Unity随手记_unity3d

配置Avatar Mask

新建一个Avatar Mask,我知道这里是多个Avatar Mask。我们需要的是一个全身都被选择的Mask。将Mask放到动画层设置里面。

黑魂复刻游戏的玩家控制器(攻击的基础实现)——Unity随手记_unity3d_02

黑魂复刻游戏的玩家控制器(攻击的基础实现)——Unity随手记_unity3d_03

接下来要做的就是,在攻击时调整动画层权重。这要在代码中做到。在进入代码之前,给idle添加StateMachineBehaviour脚本FSMClearSignals。我们需要清除的是attack信号来防止累积。黑魂复刻游戏的玩家控制器(攻击的基础实现)——Unity随手记_unity3d_04

攻击信号输入

和翻滚一样攻击属于一次性触发信号。由于使用键盘,所以我们采用MouseButtonDown(0)。

    // 一次性信号
    public bool attack; //攻击信号
    
    // Update is called once per frame
    void Update()
    {
		// ...
		
        // 攻击信号
        attack = Input.GetMouseButtonDown(0);
        attack = Input.GetKeyDown(keyAttack);
	}

攻击动画的基本代码逻辑

由于有了attack层,我们的动画机控制代码复杂了一点。我们需要使用代码修改动画层的权重。

	// 进入Attack层的动画节点attack_oneHand_A时执行的方法
    // 通过PlayerController动画机中的attack_oneHand_A节点上挂载的FSMOnEnter调用
    public void OnAttack_oneHandAEnter()
    {
        // 关闭输入模块
        pi.inputEnabled = false;
        // 修改攻击动画层权重为1
        m_anim.SetLayerWeight(m_anim.GetLayerIndex("Attack"), 1.0f);
    }

    // 在Attack层的动画节点attack_oneHand_A更新时执行的方法
    // 通过PlayerController动画机中的attack_oneHand_A节点上挂载的FSMOnEnter调用
    public void OnAttack_oneHandAUpdate()
    {
        // 计算攻击时的冲量
        m_thrustVec = model.transform.forward * m_anim.GetFloat("attackOneHandAVelocity") * m_planarVec.magnitude * 4f;
    }

    // 进入Attack层的动画节点idle时执行的方法
    // 通过PlayerController动画机中的idle节点上挂载的FSMOnEnter调用
    public void OnAttackIdleEnter()
    {
        // 打开输入模块
        pi.inputEnabled = true;
        // 修改攻击动画层权重为0
        m_anim.SetLayerWeight(m_anim.GetLayerIndex("Attack"), 0);
    }

方法依旧是熟悉的配方,通过FSMOnEnter的SendMessage来告诉PlayerController当前进入攻击动画了,进而重新设置层权重。m_anim.GetLayerIndex来让程序帮你找到LayerIndex。
接下来,攻击时我们的角色不能再按玩家的输入任意移动,否则会很奇怪(很明显了)。所以在攻击时要输入模块关闭,不用锁定平面移动(m_lockPlanar)。
如果角色在攻击时本身处于移动状态,我们就需要表现出惯性,角色需要向前冲一点,要实现这个效果,可以使用和jab类似的方案,也就是在攻击动画中加入一个曲线表示冲量的速度大小,只不过这次还要和m_planarVec.magnitude相乘,才能和游戏角色的速度相关,得到的就是看起来不错的惯性效果。至于为什么移动时攻击会在关闭输入模块之后还能得到惯性效果,是因为我们的输入模块不是直接将输入值拿来就用,而是使用了Slerp。