【UnityC#講座】ユニティちゃんに殴り合いをさせる【NavMeshAgent】

殴り合い/トップ

Unity2019.2.2f1
Windows10

殴り合い/トップ

【UnityC#講座】3D人型モデルを三人称視点で動かす操作を改良

【UnityC#講座】ユニティちゃんにパンチさせる

これらの記事の続きです。

今回は自分で操作するユニティちゃんとNavMeshAgentで動くユニティちゃんが互いにパンチし合うようにします。
より具体的に言うとNavMeshAgentの方は殴られると動き出してプレイヤーを追いかけてColliderに接触したら攻撃し、追いかける時間が決められただけ経つとスタート地点に戻るようにしました。

■ユニティちゃんをコピーする

Duplicateする

前回作ったユニティちゃんをHierarchyで右クリック、Duplicateでコピーして敵にします。

殴り合い/Duplicate

Tagを2つ替える

その際、気を付けないといけないのはコピーユニティちゃんとそのAttackPointのTagを直すことです。
コピーユニティちゃんのTagをUntaggedにします。

殴り合い/Untagged

コピーユニティちゃんのAttackPointをEnemyAttackにします。

殴り合い/Tag/EnemyAttack

簡単に階層が下のオブジェクトにアクセスするにはHierarchyの検索欄を使います。
あるいはInspectorからPlayerスクリプトに入っているAttackPointをクリックすると簡単にアクセスできます。

Playerスクリプトを外す

そしてコピーの方のPlayerスクリプトを外します。
右上の歯車マークをクリックし、Remove Componentを選んでください。

殴り合い/Playerスクリプト/AttackPoint

殴り合い/Playerスクリプト/Remove Component

それを残しておくとアニメーションイベント絡みのエラーが出ます。
別に機能に問題はないですが鬱陶しいので消しておきましょう。

参考
【Unity】SetBoolでNull???【エラー】

■NavigationウィンドウでBakeする

Navigation Static

次はNavMeshAgentを動かすための土台を作ります。
まずはPlanないしはユニティちゃんが移動する床のオブジェクトをNavigation Staticにしましょう。
Inspectorからの右上にあるStaticをクリックし、Navigation Staticを選択します。

殴り合い/Static

殴り合い/Static/Navigation Static

Navigationウィンドウ

次はNavigationウィンドウを出します。
画面上部のメニューバーのWindowから選択します。

Window > AI > Navigation

Bake

次はNavigationウィンドウでBakeします。

Navigation > Bakeタブ > Bakeボタン

殴り合い/Navigationウィンドウ/Bake

するとSceneビューのNavigation Staticにしたオブジェクトに青い面ができますが、それがNavMeshAgentが移動できる範囲です。

殴り合い/NavMesh

そのオブジェクトを移動したり、形を変えたりするとBakeし直さなければなりません。

殴り合い/NavMesh/移動

スポンサーリンク

■スクリプトを設定する

最後はスクリプトを設定して終わりです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//NavMeshAgent使うとき必要
using UnityEngine.AI;
[RequireComponent(typeof(CapsuleCollider))]
[RequireComponent(typeof(Rigidbody))]
//オブジェクトにNavMeshAgentコンポーネントを設置
[RequireComponent(typeof(NavMeshAgent))]
public class Opponent : MonoBehaviour
{
private NavMeshAgent agent;
private Vector3 startPoint;
Rigidbody rb;
CapsuleCollider caps;
private Animator animator;
GameObject player;
//追跡モードになってからの時間
[SerializeField] float time;
//追跡モードを続ける時間
[SerializeField] float chaseTime = 5.0f;
//攻撃にかける時間
[SerializeField] float attackTime = 0.5f;
//追跡のスピード
[SerializeField] float chaseSpeed = 3.5f;
//スタート地点に戻るスピード
[SerializeField] float returnSpeed = 1.0f;
enum Action
{
Wait,
Attack,
Chase,
Damage,
Return
}
[SerializeField] Action action = Action.Wait;
//攻撃判定のオブジェクトを入れる
public GameObject attackJudgment;
void Start()
{
//Animatorコンポーネントを取得
animator = GetComponent<Animator>();
//Rigidbodyコンポーネントを取得
rb = GetComponent<Rigidbody>();
//RigidbodyのConstraintsを3つともチェック入れて
//勝手に回転しないようにする
rb.constraints = RigidbodyConstraints.FreezeRotation;
//CapsuleColliderコンポーネントを取得
caps = GetComponent<CapsuleCollider>();
//CapsuleColliderの中心の位置を決める
caps.center = new Vector3(0, 0.8f, 0);
//CapsuleColliderの半径を決める
caps.radius = 0.23f;
//CapsuleColliderの高さを決める
caps.height = 1.6f;
//NavMeshAgentコンポーネントを取得
agent = GetComponent<NavMeshAgent>();
// autoBraking を無効にすると、目標地点の間を継続的に移動します
//(つまり、エージェントは目標地点に近づいても
// 速度をおとしません)
agent.autoBraking = false;
//スタート地点を取得
startPoint = transform.position;
//Playerタグのオブジェクトを取得
player = GameObject.FindGameObjectWithTag("Player");
//攻撃判定のオブジェクトを隠す
attackJudgment.SetActive(false);
}
void Update()
{
//殴られていない時は動かす待機モーション出す
if (action == Action.Wait)
{
//Agentを止める
agent.isStopped = true;
//スピードによりモーションが変わる
animator.SetFloat("Y", agent.velocity.sqrMagnitude);
}
//殴られるとPlayerを追跡し出す
else if (action == Action.Chase)
{
//NavMeshAgentが動き出す
agent.isStopped = false;
//目的地をPlayerにする
agent.destination = player.transform.position;
agent.speed = chaseSpeed;
animator.SetFloat("Y", agent.velocity.sqrMagnitude);
time += Time.deltaTime;
//chaseTimeの時間が過ぎるとReturnモードになる
if (time > chaseTime)
{
action = Action.Return;
time = 0;
}
}
//攻撃モード
else if (action == Action.Attack)
{
animator.SetBool("Attack", true);
agent.speed = 0;
attackJudgment.SetActive(true);
}
//ダメージモード
else if (action == Action.Damage)
{
animator.SetBool("Damage", true);
agent.speed = 0;
}
//スタート地点に帰る
else if (action == Action.Return)
{
animator.SetFloat("Y", agent.velocity.sqrMagnitude);
agent.speed = returnSpeed;
//目的地をスタート地点に
agent.destination = startPoint;
//経路探索の準備ができておらず、
//目標地点との距離から0.1m未満になったら起動
if (!agent.pathPending && agent.remainingDistance < 0.1f)
{
//待機モードに戻る
action = Action.Wait;
}
}
}
//攻撃受けた時の処理
//IsTriggerにチェックしたオブジェクトが
//別オブジェクトに触れると起動
private void OnTriggerEnter(Collider other)
{
//PlayerAttackタグに触れると、ダメージモードなら何も起きない
//それ以外のモードならダメージモードになる
if (other.gameObject.tag == "PlayerAttack")
{
if (action == Action.Damage)
{
return;
}
else
{
action = Action.Damage;
}
}
}
//攻撃する条件
//本体のColliderが別オブジェクトに触れたら起動
private void OnCollisionEnter(UnityEngine.Collision collision)
{
//相手がPlayerタグでこちらが追跡モードの時に起動
//攻撃モードになる
if (collision.gameObject.tag == "Player" && action == Action.Chase)
{
action = Action.Attack;
}
}
//DAMAGED00のアニメーションイベントで呼ばれる
//Action.Moveに戻す
//ダメージアニメーションから移動アニメーションへ
void DamageEnd()
{
animator.SetBool("Damage", false);
action = Action.Chase;
}
//POSE30のアニメーションイベントで呼ばれる
//Invokeで指定した秒数の後にAttackEndAnimが呼ばれる
void AttackEnd()
{
//attackTime秒後にAttackEndAnimを呼ぶ
Invoke("AttackEndAnim", attackTime);
}
//追跡モードに戻す
//パンチアニメーションから移動アニメーションへ
//攻撃判定のオブジェクトをしまう
void AttackEndAnim()
{
animator.SetBool("Attack", false);
attackJudgment.SetActive(false);
action = Action.Chase;
}
}
view raw 46.cs hosted with ❤ by GitHub

列挙型(Enum)のWateが待機モード。
プレイヤーのAttackPointオブジェクトに触れるとChaseになりプレイヤーを追跡します。
そしてプレイヤーのColliderに触れると攻撃し、決められた時間が経つとReturnになりスタート地点に戻ります。

コピーしたユニティちゃんにスクリプトをアタッチしたら、InspectorからAttackPointを入れてください。

殴り合い/Opponentスクリプト/AttackPoint

これで終わりですがちゃんと動いたでしょうか?

■関連記事

【UnityC#講座】ユニティちゃんにキソラちゃんがついてくるようにする【NavMeshAgent、Blend Tree】

【UnityC#講座】ユニティちゃんたちをドラクエっぽく一列に移動や順番変更させる【List、for文、NavMeshAgent】

【UnityC#講座】近づいたら追いかけてくる巡回ユニティちゃん【NavMeshAgent】

【UnityC#講座】見つけたら追いかけてくる監視ユニティちゃん【NavMeshAgent】

【UnityC#講座】ユニティちゃんをモブキャラのごとくランダム移動させる【NavMeshAgent】

ユニティちゃんライセンス

この作品はユニティちゃんライセンス条項の元に提供されています

スポンサーリンク

目次に戻る