//tips
//AIモンスターの挙動制御
モンスターの一度の攻撃アクションでプレイヤーに複数回の被弾が生じてしまうので、被弾回数と攻撃アクション数を合わせるよう変更する。
WaitAnimationfin()メソッドの中身にanimatorのstateの遷移状態を読み取る条件を加えることで制御できないか考える。
(!animator.IsInTransition(0) && !animator.GetCurrentAnimatorStateInfo(0).IsName("RabbitWalk3”))などのstate遷移も入れて考えていく。
EnemyState.Waitの状態にした後、animator.SetTrigger("Punch Attack”);でパンチアクションへ遷移させる際にのみコライダーを外し、アクション中は再度アクションが呼ばれないように下記のような条件にした。
animator.IsInTransition(0) && !animator.GetCurrentAnimatorStateInfo(0).IsName("PunchAttack0")
これでも複数回呼ばれてしまう。
直接ダメージを与えるGolemAttackManagerスクリプトを下記のように変更した。ゴーレムのアタックモーションに時間制限を設け、damageflagをGolemAttackManagerスクリプトで管理するようにした。MoveEnemyスクリプトからは除外。これにより、OnTriggerEnter自体が呼ばれる回数は増えたが、ダメージはしっかり接触回数と同じになっている。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GolemAttackManager : MonoBehaviour
{
public float powerEnemy = 1; //攻撃力
HPScript hPScript;
public bool damageflag=false;
[SerializeField]
GameObject ParentObject;
private void OnTriggerEnter(Collider other)
{
Debug.Log(" OnTriggerEnterGolemAttackManager");
if (other.gameObject.tag == "Player" && other.gameObject != ParentObject)
{
hPScript = other.gameObject.GetComponent<HPScript>();
if (!damageflag)
{
//hPScript.hp -= powerEnemy;
damageflag = true;
StartCoroutine("WaitDamagefin");
}
if (hPScript.hp <= 0)
{
Destroy(other.gameObject); //ゲームオブジェクトが破壊される
}
}
}
IEnumerator WaitDamagefin()
{
hPScript.hp -= powerEnemy;
yield return new WaitForSeconds(1f);
damageflag = false;
}
}
次にゴーレムにダメージを与える方を考えていく。各キャラクターのAttackManagerスクリプトのOnTriggerEnter(Collider other)メソッドにother.gameObject.tag == “Enemy”を設定する。
まずはHPUIをセットする。HPscriptを変えるか迷ったが一旦現状のスクリプトを使用。ゴーレムのtagをEnemyに変更。下記スクリプトをDeerAtackManagerスクリプトに加えた。
if (other.gameObject.tag == “Enemy”)
{
hPScript = other.gameObject.GetComponent<HPScript>();
hPScript.hp -= powerEnemy;
if (hPScript.hp <= 0)
{
Destroy(other.gameObject); //ゲームオブジェクトが破壊される
}
}
これで無事にダメージを与えることができた。
ゴーレムがdestroyされた時に下記2点のエラーが発生しているのでnullの際にはアクセスさせないように修正を加える。
"SetDestination" can only be called on an active agent that has been placed on a NavMesh.
UnityEngine.AI.NavMeshAgent:SetDestination(Vector3)
MoveEnemy:SetState(EnemyState, Transform) (at Assets/MoveEnemy.cs:217)
SearchCharacter:OnTriggerStay(Collider) (at Assets/SearchCharacter.cs:23)
"Resume" can only be called on an active agent that has been placed on a NavMesh.
UnityEngine.AI.NavMeshAgent:set_isStopped(Boolean)
MoveEnemy:SetState(EnemyState, Transform) (at Assets/MoveEnemy.cs:218)
SearchCharacter:OnTriggerStay(Collider) (at Assets/SearchCharacter.cs:23)
SearchCharacterスクリプトには下記の修正。
if(col.transform != null)
{
moveEnemy.SetState(MoveEnemy.EnemyState.Chase, col.transform);
}
MoveEnemyスクリプトには下記の修正。
if (playerTransform.position != null)
{
navMeshAgent.SetDestination(playerTransform.position);
navMeshAgent.isStopped = false;
}
上記の修正を加えても同じエラーが出てしまう。調べたところ、ナビメッシュ上にオブジェクトが存在し、経路探索可能な状態かどうか調べるにはnullではなく、
navMeshAgent.pathStatus != NavMeshPathStatus.PathInvalid
とする必要があるとのこと。
その際にはusing UnityEngine.AI;を追加し、[SerializeField] NavMeshAgent navMeshAgent;で参照をとる必要がある。
これによりエラーが生じなくなった。
この後はゴーレム消滅後にアイテムをドロップさせる仕組みを作成する。
ゴーレムの子要素にアイテムを入れたり、ゴーレム内のスクリプトでinstantiateさせることも考えたが、ここは安全に別の空オブジェクトでゴーレムを追跡し、そのオブジェクトの子要素に非表示の形でアイテムを格納、ゴーレムdestroyの処理と同時にアイテムの表示処理を指示させて対処する。
下記のスクリプトを空オブジェクトにアタッチし、オブジェクト子要素の球を表示させてみた。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Golemfollow : MonoBehaviour
{
[SerializeField]
GameObject golem;
// Update is called once per frame
void Update()
{
Vector3 offset = new Vector3(0, 3, 0);
this.transform.position = golem.transform.position + offset;
}
}
きちんと機能している。子要素にrigidbodyを付加して自由落下も確認でき、非表示から表示に直した際にも非表示中にきちんとゴーレムを追跡し、表示のタイミングで真上から球を落とすことができた。
DeerAtackManagerスクリプトのif (other.gameObject.tag == "Enemy”)の(hPScript.hp <= 0)ダメージ計算時にGolemfollowスクリプトを呼ぶ必要がある。
OnTriggerでゴーレムにアタッチさせているスクリプトを容易にとれるので、ここは
public void Dropgolemitem()
{
dropitem.SetActive(true);
}
のようなシンプルなコードをアタッチして対応できそう。ゴーレムとアイテムはここでは一対一での対応とするので問題ない。DeerAtackManagerスクリプトも修正。
if (other.gameObject.tag == "Enemy")
{
hPScript = other.gameObject.GetComponent<HPScript>();
hPScript.hp -= powerEnemy;
golemfollowdrop = other.gameObject.GetComponent<Golemfollowdrop>();
if (hPScript.hp <= 0)
{
golemfollowdrop.Dropgolemitem();
Destroy(other.gameObject); //ゲームオブジェクトが破壊される
}
}
きちんと対応できた。dropitemをキャラクターが拾えるようにし、インベントリーUIへの反映を行わせる。