【Unity7】敵キャラクターのAnimator Controllerを設定したい!【2Dローグライク11】
Unity5.0.0f4 Personal(2015年6月)
前回の続きです〜
今回は敵キャラクターのアニメーションを管理するアニメーターを完成させたいと思います〜
また、GameManagerスクリプトとEnemyスクリプトに加筆修正を加え、①Playerが動く②Enemyが動く③アイテムを取る④壁を破壊する⑤Enemyからのダメージを受けると、5つのテストを行います〜
【目標】敵キャラクターのアニメーターを設定する
2D Roguelike 11 of 14 : Enemy Animator Controller ...
①Enemy1アニメーターの設定
それではEnemy1のアニメーターの設定をしていきます〜ProjectビューのAnimations>AnimatorControllers>Enemy1を選択し、アニメータービューを開いて下さい〜
Enemy1のアニメータービュー
まずはパラメーターを設定します〜画面左のparametersをクリックし、プラスボタンを押してTrigger型の変数を作成します〜名前はenemyAttackにしましょう〜
Trigger型の変数作成
続いてMakeTransitionを行い、Enemy1IdleとEnemy1Attackを矢印でお互い結び合って下さい〜
右クリック→Make Transitionでお互いを結ぶ
それではまずEnemy1IdleからEnemy1Attackへ伸びている矢印のほうから設定していきましょう〜矢印をクリックし、Inspectorビューを見て下さい〜
Idle状態はループさせるため、Has Exit Timeのチェックを外し、Transition Durationを0に変更します〜最後に、Conditionsにあるプラスボタンを押し、enemyAttackを選択して下さい〜
こんな感じに設定
次にEnemy1AttackからEnemy1Idleへ伸びているの矢印をクリックし、Inspectorビューの設定をしていきます〜
1回アニメーションしたらEnemy1Idleへ戻したいため、Has Exit Timeはチェックしたままにし、Exit Timeを1、Transition Durationを0に変更します〜
こんな感じ
これでEnemy1のアニメーター設定は終了しましたが、実はもうEnemy2のアニメーター設定も終了しています〜
Enemy2のアニメーターはAnimator Override Controllerで作成したため、Enemy1で設定した内容は自動的に反映されるからです〜
②GameManagerスクリプト加筆修正
まずはGameManagerスクリプトに修正を加えていきます〜以下ソースです〜
using UnityEngine; using System.Collections; //********** 開始 **********// using System.Collections.Generic; //Listを使う時に宣言 //********** 終了 **********// public class GameManager : MonoBehaviour { //********** 開始 **********// public float turnDelay = .1f; //Enemyの動作時間(0.1秒) //********** 終了 **********// public static GameManager instance = null; public BoardManager boardScript; public int playerFoodPoints = 100; [HideInInspector] public bool playersTurn = true; private int level = 3; //********** 開始 **********// private List<Enemy> enemies; //Enemyクラスの配列 private bool enemiesMoving; //Enemyのターン中true //********** 終了 **********// void Awake () { if (instance == null) { instance = this; } else if (instance != this) { Destroy(gameObject); } DontDestroyOnLoad(gameObject); //********** 開始 **********// //Enemyを格納する配列の作成 enemies = new List <Enemy>(); //********** 終了 **********// boardScript = GetComponent<BoardManager>(); InitGame(); } void InitGame () { //********** 開始 **********// enemies.Clear(); //EnemyのList(配列)を初期化 //********** 終了 **********// boardScript.SetupScene(level); } public void GameOver () { enabled = false; } //********** 開始 **********// void Update () { //プレイヤーのターンかEnemyが動いた後ならUpdateしない if (playersTurn || enemiesMoving) { return; } StartCoroutine(MoveEnemies()); } public void AddEnemyToList (Enemy script) { enemies.Add (script); } // IEnumerator MoveEnemies () { enemiesMoving = true; yield return new WaitForSeconds (turnDelay); if (enemies.Count == 0) { yield return new WaitForSeconds (turnDelay); } //Enemyの数だけEnemyスクリプトのMoveEnemyを実行 for (int i = 0; i < enemies.Count; i++) { enemies[i].MoveEnemy(); yield return new WaitForSeconds(enemies[i].moveTime); } playersTurn = true; enemiesMoving = false; } //********** 終了 **********// }
GameManager.cs
ListでEnemyの配列を作成
C#のList機能を使って、複数のEnemyを格納するenemies配列を作成しています〜
まずはAwakeメソッドでnewを行い配列を作成し、InitGameメソッドで配列の初期化を行っています〜
Updateメソッド
Updateメソッドではまず、playersTurnとenemiesMovingがtrueである場合にreturnを行い、Updateメソッドを終了しています〜
どちらかがfalseであった場合、コルーチンであるMoveEnemiesメソッドを実行し、Enemyを動かしていきます〜
コルーチンについては以下エッセンスをご参照下さい〜
MoveEnemiesメソッド
まずはじめにenemiesMovingをtrueにします〜
コルーチンを実行すると何秒間か継続的に実行状態となりますが、その間もUpdateメソッドは何度も実行されます〜
ですので、MoveEnemiesメソッド実行中はUpdateメソッドを実行しなくするため、enemiesMoving=trueの時はUpdateメソッドを終了させる処理をUpdateメソッド先頭に書いていたというわけです〜
次に0.1秒待機し、Enemyの数が0の場合はもう0.1秒待ちます〜
次にfor文を使い、Enemyの数だけ処理をループさせています〜処理の内容は、List配列に格納しておいたEnemyクラスのMoveEnemyメソッドを実行し、EnemyクラスのmoveTIme分だけ待機し、そしてfor文の頭に戻っています〜
Enemyを全て動かしfor文を抜けると、playerTurnをtrueにしPlayerへ順番を譲り、enemiesMovingをfalseにしています〜
AddEnemyToListメソッド
このメソッドでは、List配列にEnemyを格納しています〜このあとEnemyスクリプトのStartメソッドに、AddEnemyToListを呼び出す処理を追記していきます〜
③Enemyスクリプト加筆修正
続いてEnemyスクリプトを修正していきます〜以下ソースです〜
using UnityEngine; using System.Collections; public class Enemy : MovingObject { public int playerDamage; private Animator animator; private Transform target; private bool skipMove; protected override void Start () { //********** 開始 **********// //GameManagerスクリプトのEnemyの配列に格納 GameManager.instance.AddEnemyToList(this); //********** 終了 **********// animator = GetComponent<Animator>(); target = GameObject.FindGameObjectWithTag("Player").transform; base.Start(); } protected override void AttemptMove <T> (int xDir, int yDir) { if (skipMove) { skipMove = false; return; } base.AttemptMove <T> (xDir, yDir); skipMove = true; } public void MoveEnemy () { int xDir = 0; int yDir = 0; if (Mathf.Abs (target.position.x - transform.position.x) < float.Epsilon) { yDir = target.position.y > transform.position.y ? 1 : -1; } else { xDir = target.position.x > transform.position.x ? 1 : -1; } AttemptMove <Player> (xDir, yDir); } protected override void OnCantMove <T> (T component) { Player hitPlayer = component as Player; //********** 開始 **********// animator.SetTrigger("enemyAttack"); //攻撃アニメーション実行 //********** 終了 **********// hitPlayer.LoseFood(playerDamage); } }
Enemy.cs
修正は2箇所のみです〜1箇所目はStartメソッドにて、先ほどのAddEnemyToListメソッドを呼び出しています〜引数thisは、このEnemyクラス自体を表しています〜
2箇所目はOnCantMoveメソッドにて、アニメーターで設定したTrigger型のenemyAttackをtrueにし、攻撃アニメーションを行うようにします〜
④テスト
それではテストをしてみましょう〜
ゲーム実行
プレイヤーを2回動かすとEnemyが動く
壁を攻撃し破壊する
アイテムを取る
Enemyが攻撃し、Playerがダメージを受ける
うまくいきました〜今回はここまでです〜
ありがとうございました〜
【Unity開発7】Unity公式チュートリアル2Dローグライクの導入【2Dローグライク1】
【Unity開発7】アニメーションを作りたい!【2Dローグライク2】
【Unity開発7】Floorやアイテムを作成したい!【2Dローグライク3】
【Unity開発7】床や敵キャラ、アイテム等を自動生成させたい!【2Dローグライク4】
【Unity開発7】BoardManagerを呼び出すGameManagerを作成する【2Dローグライク5】
【Unity開発7】キャラクターを動かすための抽象クラス作成【2Dローグライク6】
【Unity開発7】破壊可能な壁(Wall)を作成したい!【ローグライク7】
【Unity開発7】プレイヤーのアニメーターを設定したい!【ローグライク8】
【Unity開発7】プレイヤー用のスクリプトを設定したい!【ローグライク9】
【Unity開発7】敵キャラクターのスクリプトを作成したい!【2Dローグライク10】
【Unity開発7】敵キャラクターのアニメーターを設定したい!【2Dローグライク11】
【Unity開発7】uGUIでUIの表示とレベル機能を実装したい!【2Dローグライク12】