【バイオハザード風のゲームを作ってみよう。その20】
今回も下記動画をやっていきます。
youtu.be
今回はゾンビの攻撃のバリエーションを増やしていきます。
・攻撃モーションを準備します。mixamoから好きなアニメーションをゲットします。
・下記スクリプトを追加します。
public class AttackState : State //EP10追加 ゾンビ直下のstatesオブジェクトにアタッチ { PurseTargetState purseTargetState; //EP12追加 //EP20移動 /* [Header("Zombie Attacks")] public ZombieAttackAction[] zombieAttackActions; //EP12追加 [Header("Potential Attacks Performed Right Now")] public List<ZombieAttackAction> potentialAttacks; //EP12追加 */ [Header("Current Attack Being Performed")] public ZombieAttackAction currentAttack; //EP12追加 [Header("Stage Flags")] public bool hasPerformAttack; //EP12追加 private void Awake() { purseTargetState = GetComponent<PurseTargetState>(); //EP12追加 } public override State Tick(ZombieManager zombieManager) { zombieManager.animator.SetFloat("Vertical", 0f, 0.2f, Time.deltaTime); //EP20追加 If when in the attack state, our target gets too far away, we switch back to the purse target state if (zombieManager.distanceFromCurrentTarget > zombieManager.maximumAttackDistance) { return purseTargetState; } //If our target is dead, return here and nothing. //プレイヤーが死んでいる場合、以降の処理を行わない if (zombieManager.currentTarget.isDead) //EP18追加 { return this; } //if the zombie is being hurt, or is in some action, pause the state if (zombieManager.isPerformingAction) //EP11追加 { zombieManager.animator.SetFloat("Vertical", 0f, 0.2f, Time.deltaTime); return this; } if(!hasPerformAttack && zombieManager.attackCoolDownTimer <= 0) //EP12追加 { //EP20削除 /* if(currentAttack == null) { //GET A NEW ATTACK BASED ON DISTANCE AND ANGLE FROM CURRENT TARGET GetNewAttack(zombieManager); } */ // else // { //ATTACK THE CURRENT TARGET AttackTarget(zombieManager); // } } if (hasPerformAttack) //EP12追加 { ResetStateFlags(); return purseTargetState; } else if (currentAttack == null) //EP20追加 { return purseTargetState; } else { return this; } } /* //EP20移動 EP12追加 private void GetNewAttack(ZombieManager zombieManager) { for(int i = 0; i < zombieAttackActions.Length; i++) { ZombieAttackAction zombieAttack = zombieAttackActions[i]; //Check for attackdistanced needed to perform the potential attack if(zombieManager.distanceFromCurrentTarget <= zombieAttack.maxmumAttackDistance && zombieManager.distanceFromCurrentTarget >= zombieAttack.minimumAttackDistance) { //Check for attack angles, needed to perform the potential attack if(zombieManager.viewAngleFromCurrentTarget<=zombieAttack.maxmumAttackAngle && zombieManager.viewAngleFromCurrentTarget >= zombieAttack.minimumAttackAngle) { //if the attack passes the distance and angle, add it to the list of attacks we MAY preform right now potentialAttacks.Add(zombieAttack); } } } int randomValue = Random.Range(0, potentialAttacks.Count); //EP12追加 if (potentialAttacks.Count > 0) //EP12追加 { currentAttack = potentialAttacks[randomValue]; potentialAttacks.Clear(); //List全ての要素を削除 } } */ //EP12追加 private void AttackTarget(ZombieManager zombieManager) { if (currentAttack != null) { hasPerformAttack = true; zombieManager.zombieCombatManager.SetAttackType(currentAttack.attackType); //EP20追加 zombieManager.attackCoolDownTimer = currentAttack.attackCooldown; zombieManager.ZombieAnimationManager.PlayTargetAttackAnimation(currentAttack.attackanimation); } else { //Debug.Log の派生で警告メッセージをコンソール出力 Debug.LogWarning("WARNING: ZoMbie is attempting to perform an attack,but has no attack"); } } //EP12追加 private void ResetStateFlags() { hasPerformAttack = false; //We clear the attack when leaving the state, so the same attack does not always respect currentAttack = null; //EP20追加 } }
public class ZombieDamageCollider : MonoBehaviour //EP17追加 ゾンビ右手/左手直下のRight Hnad grapple Collider/Left Hnad grapple Colliderにアタッチ { ZombieManager zombie; [Header("Collider")] public Collider grappleCollider; [Header("Damage Collider Hand")] public bool isRightHandDamageCollider; [Header("Current Attack Type")] public ZombieAttackType attackType; //EP20追加 [Header("Damage")] public int damageAmount = 25; //EP20追加 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) { DecideAttackAction(player); } } } //EP20追加 private void DecideAttackAction(PlayerManager player) { if (attackType == ZombieAttackType.grapple) { //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); player.playerStatManager.pendingDamage = zombie.zombieCombatManager.grappleBiteDamage; //EP18追加 //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 } } else if (attackType == ZombieAttackType.swipe) { //We make the player a damage animation player.animatorManager.PlayAnimation("Hit Reaction", true); //We damage the player player.playerStatManager.TakeDamage(damageAmount); } } }
public class ZombieCombatManager : MonoBehaviour //EP17追加 ゾンビにアタッチ { (省略) //EP20追加 public void SetAttackType(ZombieAttackType attackType) { rightHandDamageCollider.attackType = attackType; leftHandDamageCollider.attackType = attackType; } }
public class Enums : MonoBehaviour //EP15追加 { } public enum AmmoType { pistal, shotgun } //EP20追加 public enum ZombieAttackType { grapple, swipe }
public class PlayerStatManager : MonoBehaviour //EP18追加 プレイヤーにアタッチ { (省略) //EP20追加 Called via code when the player takes damage from a damage collider public void TakeDamage(int damageTaken) { playerHealth = playerHealth - damageTaken; if (playerHealth == 0) { KillPlayer(); } else { player.playerUIManager.DisplayHealthPopUp(); } } }
public class PurseTargetState : State //EP8追加 ゾンビ直下のstatesオブジェクトにアタッチ { AttackState attackState; //EP10追加 [Header("Zombie Attacks")] public ZombieAttackAction[] zombieAttackActions; //EP20追加 [Header("Potential Attacks Performed Right Now")] public List<ZombieAttackAction> potentialAttacks; //EP20追加 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追加 { if (attackState.currentAttack == null) //EP20追加 { //GET A NEW ATTACK BASED ON DISTANCE AND ANGLE FROM CURRENT TARGET GetNewAttack(zombieManager); return this; } else //EP20追加 { //We disable the nav mesh agent WHEN attacking, so the attack rotation can be handled via code zombieManager.zombieNavmeshAgent.enabled = false; return attackState; } } else { return this; } } //EP20追加 private void GetNewAttack(ZombieManager zombieManager) { for (int i = 0; i < zombieAttackActions.Length; i++) { ZombieAttackAction zombieAttack = zombieAttackActions[i]; //Check for attackdistanced needed to perform the potential attack if (zombieManager.distanceFromCurrentTarget <= zombieAttack.maxmumAttackDistance && zombieManager.distanceFromCurrentTarget >= zombieAttack.minimumAttackDistance) { //Check for attack angles, needed to perform the potential attack if (zombieManager.viewAngleFromCurrentTarget <= zombieAttack.maxmumAttackAngle && zombieManager.viewAngleFromCurrentTarget >= zombieAttack.minimumAttackAngle) { //if the attack passes the distance and angle, add it to the list of attacks we MAY preform right now potentialAttacks.Add(zombieAttack); } } } int randomValue = Random.Range(0, potentialAttacks.Count); //EP12追加 if (potentialAttacks.Count > 0) //EP12追加 { Debug.Log("Distance:" + zombieManager.distanceFromCurrentTarget); attackState.currentAttack = potentialAttacks[randomValue]; potentialAttacks.Clear(); //List全ての要素を削除 } } }
3パターンの攻撃を実装しました。
youtu.be
今回は以上です。