【バイオハザード風のゲームを作ってみよう。その17】

今回も下記動画をやっていきます。
youtu.be

今回はゾンビがプレイヤーを掴む動作を実装していきます。

・下記のアニメーションをmixamoのアニメーションをベースにvery animationで作成しました。
 ・ゾンビがプレイヤーを掴む動作→mixamoのzombieのhit reactionを改変。
 ・プレイヤーがゾンビに掴まれ、振り払う動作→mixamoのhit reactionをそのまま使用。

また、2つのアニメーション動作のタイミングを合わせます。

参考動画
youtu.be

・ゾンビの両手にコライダーをアタッチします。

・ゾンビがプレイヤーを追いかけ、距離が近い場合は掴むようにします。掴まれたプレイヤーはこれを振り払う動作をします。

public class ZombieCombatManager : MonoBehaviour //EP17追加 ゾンビにアタッチ
{
    ZombieManager zombie;
    ZombieGrappleCollider rightHandGrappleCollider;
    ZombieGrappleCollider leftHandGrappleCollider;

    private void Awake()
    {
        zombie = GetComponent<ZombieManager>();
        LoadGrappleColliders();
    }

    //変数rightHandGrappleCollider/leftHandGrappleColliderにゾンビの右手/左手のcolliderを設定
    private void LoadGrappleColliders()
    {
        ZombieGrappleCollider[] grappleColliders = GetComponentsInChildren<ZombieGrappleCollider>();

        foreach(var grappleCollider in grappleColliders)
        {
            if (grappleCollider.isRightHandGrappleCollider)
            {
                rightHandGrappleCollider = grappleCollider;
            }
            else
            {
                leftHandGrappleCollider = grappleCollider;
            }
        }
    }

    //ゾンビ右手/左手のcolliderをONする
    public void OpenGrappleColliders()
    {
        rightHandGrappleCollider.grappleCollider.enabled = true;
        leftHandGrappleCollider.grappleCollider.enabled = true;
        //when/if collider contacts player, player is locked into grapple animation
        //zombie is locked into grapple animation
        //zombie and player face one another
    }

    //ゾンビ右手/左手のcolliderをOFFする
    public void CloseGrappleColliders()
    {
        rightHandGrappleCollider.grappleCollider.enabled = false;
        leftHandGrappleCollider.grappleCollider.enabled = false;
    }

    public void EnableRotationDuringAttack()
    {
        zombie.canRotate = true;
    }

    public void DisableRotationDuringAttack()
    {
        zombie.canRotate = false;
    }
}
public class ZombieGrappleCollider : MonoBehaviour //EP17追加 ゾンビ右手/左手直下のRight Hnad grapple Collider/Left Hnad grapple Colliderにアタッチ
{
    ZombieManager zombie;
    public Collider grappleCollider;
    public bool isRightHandGrappleCollider;

    private void Awake()
    {
        zombie = GetComponentInParent<ZombieManager>();
        grappleCollider = GetComponent<Collider>();
    }

    //右手/左手にコライダーが接触した場合、実行
    private void OnTriggerEnter(Collider other)
    {
        //If we grab a collider on the "Player" layer,we proceed
        if (other.gameObject.layer == LayerMask.NameToLayer("Player"))
        {
            PlayerManager player = other.GetComponent<PlayerManager>();

            if (player != null)
            {
                //if the player is not already performing an action, we proceed
                if (!player.isPreformingAction)
                {
                    //Play the zombie's grapple animation, and set their walking movement at 0
                    zombie.ZombieAnimationManager.PlayGrappleAnimation("Zombie Grapple", true);
                    zombie.animator.SetFloat("Vertical", 0f);

                    //Play the player's grapple animation
                    player.animatorManager.ClearHandIKWeights();
                    player.animatorManager.PlayAnimation("Player Grapple", true);

                    //Mask the Zombie face it's target ゾンビを掴むターゲット(プレイヤー)方向に向ける
                    Quaternion targetZombieRotation = Quaternion.LookRotation(player.transform.position - zombie.transform.position);
                    zombie.transform.rotation = targetZombieRotation;

                    //Mask the target face Zombie プレイヤーを掴まれたターゲット(ゾンビ)方向に向ける
                    Quaternion targetPlayerRotation = Quaternion.LookRotation(zombie.transform.position - player.transform.position);
                    player.transform.rotation = targetPlayerRotation;

                    //FUTURE TO DO
                    //PLAY DIFFRENT GRABS DEPENDING ON ANGLE OF TARGET
                    //EX:If Zombie is behind the player, allow the zombie to grab player from behind
                    //ROTATE SMOOTHILY OVER TIME IF PLAYER HAS TO ROATE

                }
            }
        }
    }
}
public class ZombieAnimationManager : MonoBehaviour //EP12追加 ゾンビにアタッチ
{
   (省略)
    //EP17追加 掴む動作を実行
    public void PlayGrappleAnimation(string grappleAnimation, bool useRootMotion)
    {
        zombie.animator.applyRootMotion = useRootMotion;
        zombie.isPerformingAction = true;
        zombie.animator.CrossFade(grappleAnimation, 0.2f);
    }
}
public class ZombieManager : MonoBehaviour //EP8追加 ゾンビオブジェクトにアタッチ
{
   (省略)
    public bool canRotate; //EP17追加 trueの場合、ゾンビがプレイヤー方向に向くことができる。
}
public class ResetAnimatorBoolZombie : StateMachineBehaviour //EP11追加 ゾンビアニメータのoverrideレイヤーEmptyにアタッチ
{
    // OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
    override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        ZombieManager zombie = animator.GetComponent<ZombieManager>();

        if (zombie != null)
        {
            zombie.isPerformingAction = false;
            zombie.canRotate = true; //EP17追加
        }
    }
}
public class PurseTargetState : State //EP8追加 ゾンビ直下のstatesオブジェクトにアタッチ
{
    AttackState attackState; //EP10追加

    private void Awake()
    {
        attackState = GetComponent<AttackState>(); //EP10追加
    }

    public override State Tick(ZombieManager zombieManager)
    {
        //if the zombie is being hurt, or is in some action, pause the state
        if (zombieManager.isPerformingAction) //EP11追加
        {
            RotateTwardsTargetWhilsAttacking(zombieManager); //EP17追加
            zombieManager.animator.SetFloat("Vertical", 0f, 0.2f, Time.deltaTime);
            return this;
        }
           

       // Debug.Log("Running pursue target state");
        MoveTowardsCurrentTarget(zombieManager); //EP10追加
        RotateTowardsTarget(zombieManager); //EP10追加

        if (zombieManager.distanceFromCurrentTarget <= zombieManager.maximumAttackDistance) //EP12追加
        {
            zombieManager.zombieNavmeshAgent.enabled = false; //EP12追加
            return attackState;
        }
        else
        {
            return this;
        }
        
    }

    private void MoveTowardsCurrentTarget(ZombieManager zombieManager) //EP10追加
    {
        //walkアニメーションに切り替え
        zombieManager.animator.SetFloat("Vertical", 1f, 0.2f, Time.deltaTime);
    }

    //This roration method uses a navmesh agent
    private void RotateTowardsTarget(ZombieManager zombieManager) //EP10追加
    {
        if (zombieManager.canRotate) //EP17追加
        {
            zombieManager.zombieNavmeshAgent.enabled = true;
            //移動先に移動開始
            zombieManager.zombieNavmeshAgent.SetDestination(zombieManager.currentTarget.transform.position);
            //ターゲット方向に回転
            zombieManager.transform.rotation = Quaternion.Slerp(zombieManager.transform.rotation,
                zombieManager.zombieNavmeshAgent.transform.rotation, zombieManager.rotationSpeed / Time.deltaTime);
        }
    }

    //This rotation method will blindly rotate towards the target, ignoring collision checks EP17追加
    //ゾンビが攻撃モーション中でも方向を変更可能にする。このメソッドが無い場合、ゾンビは攻撃時直進する。
    private void RotateTwardsTargetWhilsAttacking(ZombieManager zombieManager)
    {
        if(zombieManager.canRotate )
        {
            Vector3 direction = zombieManager.currentTarget.transform.position - zombieManager.transform.position;
            direction.y = 0;
            direction.Normalize();

            //ゾンビがプレイヤー方向を向いている場合
            if(direction== Vector3.zero)
            {
                //directionにVector3(0, 0, 1)を設定
                direction = zombieManager.transform.forward;
            }

            //ゾンビをプレイヤー方向に向ける
            Quaternion targetRotation = Quaternion.LookRotation(direction);
            zombieManager.transform.rotation = Quaternion.Slerp(zombieManager.transform.rotation, targetRotation, zombieManager.rotationSpeed * Time.deltaTime);
        }
    }
}

プレイ動画はこんな感じです。
youtu.be

今回は以上です。