はじめに
お盆休みにunity本格入門(賀好昭仁さま著)を読んで勉強しておりました。Unityを触るのが初めてでしたので、大変興味深い内容で、とても分かりやすく説明されている本でした。
まだ読んでいる途中ではありますが、学んだ内容を整理するために、記事にさせていただこうと思います。こちらの記事が気になった方は、ぜひ実際にこちらの本をお手にとっていただくことをお勧めいたします。
目次
UML
C# to PlantUMLでUMLを作成してみたのですが、private系の変数やメソッドを表示してくれませんので、想像していたより、関係性を表せませんでした。自動生成されたUMLが正しいのは承知しているのですけれども、勉強のためには全部表示してみました。
また、Unityでのクラスの基底となるMonobehaviourクラスは、UMLから省くことで、より見易いように致しました。
各機能実現方法
ここから先は、各機能をどのようにクラス間で処理し、実現しているのかを説明させていただきます。
近くにいるプレイヤーを追いかける
敵がプレイヤーを追いかけるという動作には、三つのステップがあります。
一つ目はプレイヤーが近くに来たということを検知するものです。
二つ目はプレイヤーと敵の間に障害物がないかを確認します。
三つ目はプレイヤーの位置に敵を向かわせることです。
1.プレイヤーを検知する仕組み
一つ目の近くに来たことを検知するには、Collisionで検知させます。
CollisionDetectorを作成し、こちらで衝突を検知した場合の動作を指定させます。
ColliderコンポーネントをIs Triggerをオンにしておくと、OnTriggerXXというメソッドが実行されます。
そのため、CollisionDetectorではこのOnTriggerXXを実装します。
ここではOnTriggerStayが検知用のメソッドとなっております。OnTriggerStayで実行される内容は、SerializeFieldとして記載することで、Inspectorウィンドウで設定できる仕組みです。
本ではInspectorウィンドウで、OnTriggerStayで実行されるものに、EnemyMove.OnDetectObjectを指定しております。
[RequireComponent(typeof(Collider))]
public class CollisionDetector : MonoBehaviour
{
[SerializeField] private TriggerEvent onTriggerStay = new TriggerEvent();
private void OnTriggerStay(Collider other)
{
onTriggerStay.Invoke(other);
}
}
Inspectorウィンドウで表示できるようにするため、UnityEventを継承したTriggerEventというクラスを作成します。
上記コードをご参照いただきますと、OnTriggerStayメソッドと同じ名前のTriggerEventを作成しております。
これにより、InspectorウィンドウにOnTriggerStayメソッドが、CollisionDetectorスクリプトの中に存在し、選択できるようになります。
[Serializable] public class TriggerEvent : UnityEvent<Collider>
{
}
これで検知する仕組みの実装は完了です。
2.障害物の有無を検知する仕組み
二つ目の障害物の有無を検知する仕組みは、Raycastを使います。
判定はEnemyMoveのクラスで行いますので、まずはEnemyMoveのクラスで、RaycastHitの配列を保持します。
EnemyMoveクラスには、先ほどOnTriggerStayにて指定した、EnemyMove.OnDetectObjectをメソッドを作成しておきます。
こちらのメソッドに障害物の検知や、プレイヤーの位置へ移動する処理など、近くにいるプレイヤーを追いかけるための処理を記載してまいります。
次にcolliderで受け取ったオブジェクトの座標を取得し、自身との差分から距離差を求めていきます。
var positionDiff = collider.transform.position - transform.position;
var distance = positionDiff.magnitude;
他にもこのpositionDiffを用いて、プレイヤーへの方向も求めることができます。
var direction = positionDiff.normalized;
これらを求めたところで、RayCastを使って、障害物の有無を検知してまいります。
var hitCount = Physics.RaycastNonAlloc(transform.position, direction, _raycastHits, distance, raycastLayerMask);
これにより、_raycastHitsの配列に検知したオブジェクトの情報が格納されております。
ここで、raycastLayerMaskは、プレイヤー側のColliderを障害物として検知しないようにするための仕組みとなります。プレイヤー側のColliderに対し、各種layerを設定し、Edit -> Project Settings -> Physicsで衝突するレイヤーを設定します。
raycastLayerMask自体はSerializeFieldとしてInspectorウィンドウで設定しています。
これで、hitCountが0であれば、プレイヤーとの間に障害物が無いことが分かりますので、敵を移動させる準備が整ったことになります。
3.プレイヤーの位置に向かう仕組み
三つ目のプレイヤーの位置に向かう仕組みは、Unityの探索機能であるNavMeshを用います。
この時、NavMeshAgentが敵オブジェクトに付与する必要があることと、あらかじめBakeしておく必要があるのでお忘れずに。
NavMeshAgentはStartメソッド内でインスタンス化しておきます。
private NavMeshAgent _agent;
private void Start()
{
_agent = GetComponent<NavMeshAgent>();
}
上記の下準備が整いましたら、先ほどのhitCountが0の場合に移動させることになります。
_agent.isStopped = false;
_agent.destination = collider.transform.position;
近くにいるプレイヤーを攻撃する
敵キャラクターがプレイヤーの近くに寄った場合、攻撃を仕掛けるように致します。
上で
本日のまとめはここまでです!少しずつ加筆してまいります。