Unityに関連する記事です

 2回に分けて、敵キャラの移動処理を実装します。
この手順では敵キャラの経路に沿った移動を行う機能を実装し、敵の移動処理を完成させます。

 以下の内容で順番に実装を進めていきます。

手順8 ー敵キャラの移動処理の実装ー
13.EnemyController スクリプトを作成し、敵キャラを PathData スクリプトで指定した経路に沿って移動させる



 新しい学習内容は、以下の通りです。

 ・メソッド・チェーン
 ・DOTweenの補間機能と実装例  DOPath メソッド、SetEase メソッドー
 ・リファクタリング
 ・Linqの機能の実装例  Select メソッド、ToArray メソッド〜



13.EnemyController スクリプトを作成し、敵キャラを PathData スクリプトで指定した経路に沿って移動させる

1.設計


 経路の設定が完了しましたので、続いては、この情報をゲーム内に反映して、敵キャラの移動する経路として運用できる機能を考えます。

 敵キャラのゲームオブジェクトには、このゲームオブジェクトの制御を行うためのスクリプトがありませんので、
まずは敵キャラ用のスクリプトを作成し、それに制御をさせることを設計として検討します。

 スクリプトがアタッチされているゲームオブジェクトは、Transform コンポーネントを参照することによって
Position の情報を制御することが出来るようになりますので、この設計により、敵キャラの移動処理が実装できるようになるためです。

 移動方法ですが、手順2でインポートしてある DOTween の機能を利用します。
DOTween には移動関連の便利なメソッドが複数用意されており、その中の DOPath メソッドを利用することによって
配列で指定した位置情報を順番に移動していく、という処理が実装出来ます。
よって、経路に沿って移動する処理はこの DOPath メソッドを実装すれば実現できます。


2.EnemyController スクリプトを作成する


 敵キャラの制御を行うために、新しくスクリプトを作成して制御させます。

 今回はまず、先ほど作成した PathTranSet ゲームオブジェクトの PathData スクリプト内に設定した経路に沿って移動を行うための機能を実装します。


EnemyController.cs


 スクリプトを作成したらセーブをします。


3.<メソッド・チェーン>


 DOTween の機能について説明する前に、メソッド・チェーンについて説明します。

 // 各地点に向けて移動
  transform.DOPath(paths, 1000 / moveSpeed).SetEase(Ease.Linear);

 今回の処理は、複数のメソッドをつなげて1行で記述している処理になります。
ピリオドの位置までが1つの処理になっており、合計で2つの DOTween のメソッドが実行されています

 このようにピリオドを利用して、前のメソッドの処理に続けて次のメソッドの処理を書くと、前のメソッドの処理を受けて次のメソッドの処理が実行されます。
この記述方法をメソッド・チェーンといいます。これは C# が持つ機能です。DOTween だけのものではありません。

 今回の場合、transform に対して DOPath メソッドが最初に実行されて、その処理結果を受けて SetEase メソッド実行されるようになっています。

 プログラムは上から下に実行されていきますが、1行に書かれた処理の場合は、手前(左側)より、ピリオド単位で区切って実行されることになります。
これは他の処理と同様です。しっかりと処理の順番を追えるように、読み解いてみましょう。


4.<DOTweenの補間機能と実装例  DOPath メソッド、SetEase メソッドー>


 新しく実装を行った DOTween の機能について、各メソッドを順番に説明します。

 // 各地点に向けて移動
  transform.DOPath(paths, 1000 / moveSpeed).SetEase(Ease.Linear);


1.DOPath (Vector3[] path, float duration)

 DOPath メソッドは、他の移動系の DOTween のメソッドと同じで、制御したいゲームオブジェクトの Transform コンポーネントに対して命令を出すメソッドです。
Transform コンポーネント、あるいは RectTransform コンポーネントの Transform の Position 値に対して補間処理を行って、アニメしているように演出してくれる機能です。

 ゲームオブジェクトの位置を現在値から第1引数で指定した値を目的地として第2引数に指定した時間をかけて移動を行います。
第1引数は Vector3 型の配列を指定します。これが移動を行い際に経由する座標になります。つまり、この配列の中の座標を順番に目的地として、次々に移動を繰り返していく処理になります。
この機能を使うことにより、経路用のゲームオブジェクトの Transform コンポーネントの Position を順番に目的地として移動していくようになります。
なお、2D の場合であっても経路移動時には Position の Z 軸の情報が考慮されて計算されます。

 見た目上は X 軸と Y 軸での移動に見えますが、プログラムの内部では Z 軸も参照されて移動を行っています(DOPath メソッドの第1引数のデータ型は Vector3 型の配列です)。
そのため、移動用のゲームオブジェクト(今回であれば敵キャラ)の Z 軸、および経路用のゲームオブジェクトの Z 軸がすべて同じ値 (0) になっているかを確認しておきましょう。
仮にいずれかの Z 軸に 0 以外の値が代入されているとその地点へ移動する際の速度が極端に遅くなったり早くなったりする不具合が発生します。
これは Z 軸の分を計算し、奥行を加味して移動を行うためです(ただし画面上でのグラフィックの変化はなく、移動速度のみ不具合が発生するため、Z 軸に問題があることに気づきにくいです。)

 移動するゲームオブジェクトと移動経路のゲームオブジェクトの Z 軸はすべて 0 とし、移動時の計算値には含まれないように注意して設定しましょう

 第2引数は 1000 / moveSpeed ですので、この計算結果の値の時間をかけて、すべての目的地を通過して、ゴール地点(防衛拠点)に着くまでの時間になります。
1つの地点に移動するまでの時間ではありませんので、間違えないようにしてください。

 DOPath メソッドには他にも便利な使い方があります。例えば、ベジェ曲線を作成して移動を行いたい場合や、
同じ範囲を周回移動させたい場合にも対応できますので学習しておくといいでしょう。


参考サイト
Qiita @BEATnonanka 様
DOTween完全に理解するその4 DOPath編
https://qiita.com/BEATnonanka/items/50cacac803f88f...


2.SetEase (EaseType easeType)

 SetEase メソッドは他の DOTween のメソッドに付随する処理です。このように DOTween では DOTween のメソッド同士を1つの処理の塊として続けて処理を行うことができます。
処理を続ける場合には、前のメソッドの後にピリオド(ドット)を書くことで、次のメソッドを書くことが可能です。(最初に説明した、メソッド・チェーンの記述方法です)

 SetEase メソッドでは Ease というアニメーションさせる際のパターンを変更することができます。Ease は enum で設定されており、Ease.タイプ の書式で記述します。
非常に多くの種類があります。今回は Ease.Linear を使用しています。

 今回は、DOPath メソッドが実行されてゲームオブジェクトの Position が変更されている処理を実行している部分に、この SetEase メソッドが実行されますので
位置情報を変更する際の補間処理に対して、どのようなタイプのアニメーションのパターンを設定して移動を行わせるかを指定しています

 Position を変更する際に Linear を指定すると、最初の位置から目標となる位置までの移動速度を等速に指定することが出来ます。
そのため、初速で早くなったり、終了間際に遅くなったり、といったような補間処理ではなくなり、最初からゴール地点に到着するまで等速で座標の変更、つまり移動を行います。

参考サイト
ゲームUIネット 様
DOTweenのイージング一覧を世界一詳しく&分かりやすく説明する
https://game-ui.net/?p=835
 
 以上が今回実装している、DOTweenの処理になります。しっかりと読み解いていって使い方を覚えてください。


5.Enemy ゲームオブジェクトに EnemyController スクリプトをアタッチし、設定を行う


 ヒエラルキーにある Enemy ゲームオブジェクトに EnemyControler スクリプトをドラッグアンドドロップしてアタッチします。
アタッチしたら必ず、ゲームオブジェクトを選択してインスペクターを確認して、正常にアタッチされているかを目視でチェックします。

 EnemyController スクリプトにはインスペクターよりアサインする情報が2つ表示されていますので、こちらを設定します。

 PathData 変数には、ヒエラルキーにある PathTranSet ゲームオブジェクトをドラッグアンドドロップしてアサインします。
自動的に PathData スクリプトの情報が登録されます。ここで敵キャラの移動する経路を参照する設定をしています。

 MoveSpeed 変数には、敵キャラの移動速度の値になりますので、任意の値を設定します。
最初は 30 前後に設定しておいて、調整をしていくといいでしょう。高くなるほど、移動速度も速くなります。


インスペクター画像



 設定が終了したら、Enemy ゲームオブジェクトのインスペクターの Layer 設定の下にある Overrides ボタンを押してください
先ほどプレファブにした際と今の状態では差分がありますので、それを上書きするか確認が出ます。
Apply All を選択することで、現在のプレファブの情報が Prefabs フォルダにある Enemy ゲームオブジェクトにも反映されます

 プレファブ元の情報を現在のプレファブの情報に更新したい場合には、この Overrides 機能を利用してください。


6.ゲームを実行して動作を確認する


 Enemy ゲームオブジェクトの位置を、StartTran ゲームオブジェクトの位置に移動してください。
その上で、ゲームを実行します。Enemy ゲームオブジェクトが StartTran ゲームオブジェクトの位置から
各 PathTran の位置へ順番に移動し、最後に GoalTran ゲームオブジェクトの位置で停止すれば制御成功です。


<実装動画 StartTran から移動を開始>
動画ファイルへのリンク


<実装動画◆ヽ PathTran を順番に経由して移動>
動画ファイルへのリンク


<実装動画 移動速度を早くしてみる>
動画ファイルへのリンク


7.EnemyController スクリプトを修正する


 Start メソッド内で実行している下記の処理について、1行にまとめて処理を記述する方法を学習します。

  // 移動する地点を取得するための配列の宣言と初期化
  Vector3[] paths = new Vector3[pathData.pathTranArray.Length];

 // 移動する位置情報を順番に配列に取得
  for (int i = 0; i < pathData.pathTranArray.Length; i++) {
      paths[i] = pathData.pathTranArray[i].position;
  }

 こちらの処理は、移動する地点の情報を取得するために配列の宣言と初期化を行い、
その後、for 文を利用して、PathData クラスにある pathTranArray 変数の情報を順番に取得しています。

 Linq という機能を利用することで、この処理を1行にまとめて記述することが可能になります。

 すでに実装している処理について、ゲーム上の処理(見た目の挙動)は変更なく、内部的な処理を精査して修正することをリファクタリングといいます。
この考え方は非常に重要になります。処理を実装して終わり、ではなく、常にどのようすればより簡潔に、わかりやすい処理になるかを考えていくことがスキルアップにつながります。


EnemyController.cs


 スクリプトを修正したら、セーブを行います。


8.<Linq の機能の実装例  Select メソッド、ToArray メソッド〜>


 Linq(リンク)とは、コレクション(Dictionary や List など)の要素を操作して、検索したり集計する処理を簡潔に記述することができるライブラリ(複数の機能をまとめたもの)です。

 Linqを使用するためにはusing の宣言が必要になります。今回作成したメソッド内に登場している処理ですので、復習して読み解けるようにしていきましょう。

using System.Linq;



 Linqを記述する際にはラムダ式の記述を用います。ラムダ式についてはこちらをご確認ください。
SamuraiBlog様
【C#入門】LINQの使い方総まとめ(Select、Where、GroupByなど)
https://www.sejuku.net/blog/56519

 Linqには多くの機能がありますが今回利用している機能についてまとめておきます。
そのほかの機能については記事がたくさんありますが、こちらのサイトも参考になります。
地平線に行く様
LINQの拡張メソッド一覧と、ほぼ全部のサンプルを作ってみました。
https://yujisoftware.hatenablog.com/entry/20111031...


1.Select メソッド

 射影処理と呼ばれる機能です。射影とはデータベース用の専門用語で、テーブル(実データ)から特定の列のデータのみを取り出すことを言います。

 Select メソッドでは引数にした条件を元に、照合できたデータのみを取り出す操作を行います。
このメソッドの戻り値は IEnumerable<TResult> であり、その情報を取得して利用したい場合には、メソッドチェーンを利用して、ToList メソッドや ToArray メソッドを利用して扱える状態にします。
これは Where メソッドと同じ処理の手順になります。

  // 同じ希少度の合計値を算出して、ランダムな値を抽出
  int randomRewardValue = UnityEngine.Random.Range(0, rewardDatas.Select(x => x.rarityRate).ToArray().Sum());

 こちらの実装例では、Range メソッドの第2引数の部分で Select メソッドを利用しています。
rewardDatas.Select(x => x.rarityRate).ToArray().Sum() ここまでが、第2引数の処理です。

 rewardDatas 配列変数の各要素より、rarityRate 変数の情報をそれぞれ取り出します。
rewardDatas.Select(x => x.rarityRate) ここまでが Select メソッドです。


参考サイト
MicroSoft
Enumerable.Select メソッド
https://docs.microsoft.com/ja-jp/dotnet/api/system...
.NET Column 様
【LINQのSelectメソッドの書き方3選|LINQについてなどを紹介
https://www.fenet.jp/dotnet/column/language/1454/
Qiita @t_takahari 様
LINQのそのForEach、実はSelectで書き換えられるかも
https://qiita.com/t_takahari/items/6dc72f48b1ebdfe...


2.ToArray メソッド

 IEnumerable<T> から配列を作成します。

 IEnumerable<T> は Linq の処理を実行した際の戻り値の情報です。例えば、フィルタリングして算出された結果を 配列型にして利用したい場合に利用します。

  // 同じ希少度の合計値を算出して、ランダムな値を抽出
  int randomRewardValue = UnityEngine.Random.Range(0, rewardDatas.Select(x => x.rarityRate).ToArray().Sum());
 
 Select メソッドによって抽出された情報を配列にしています。そしてその後に Sum メソッドを実行することにより、
配列の要素をすべて合計した値を算出し、それを Range メソッドの最大値として利用しています。

  // 同じ希少度の合計値を算出して、ランダムな値を抽出
  int randomRewardValue = UnityEngine.Random.Range(0, [ 配列である rewardDatas クラスの持つ各 rarityRate 変数の値のみ取り出してそれを配列にし、その配列の要素の値の合計値 ]);


参考サイト
MicroSoft
Enumerable.ToArray<TSource>(IEnumerable<TSource>) メソッド
https://docs.microsoft.com/ja-jp/dotnet/api/system...
Qiita @Marimoiro 様
LINQチートシート的なもの
https://qiita.com/Marimoiro/items/0e119b47d65bf138...
Qiita @mounntainn 様
LINQについての備忘録
https://qiita.com/mounntainn/items/e8b8f94a15ec4fe...


9.ゲームを実行して動作を確認する


 リファクタリングを行っているので、ゲーム上の処理には変化はありません。
そのため、いままでと同じように敵が経路に沿って移動を行っていれば制御成功です。

 新しい処理を学習する際には、その処理が本来どのような処理になっているかを把握していることが前提になります。
そういったことも含めて、自分の書いている処理を理解し、それを簡潔にまとめていくようにすると、新しい処理の理解も深まります

 最初から新しい処理を書いてもよいですが、まずは基本的な記述をして処理の内容を理解した上で書き替えていくようにしてみてください。
そうすることによってコーディングのスキルが上達します



 以上でこの手順は終了です。

 次は 手順9 −敵キャラの移動方向と移動アニメの同期処理の実装− です。

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

Menu



技術/知識(実装例)

2Dおはじきゲーム(発展編)

2D強制横スクロールアクション(発展編)

3Dダイビングアクション(発展編)

2Dタップシューティング(拡張編)

レースゲーム(抜粋)

2D放置ゲーム(発展編)

3Dレールガンシューティング(応用編)

3D脱出ゲーム(抜粋)

2Dリアルタイムストラテジー

2Dトップビューアドベンチャー(宴アセット使用)

3Dタップアクション(NavMeshAgent 使用)

2Dトップビューアクション(カエルの為に〜、ボコスカウォーズ風)

VideoPlayer イベント連動の実装例

VideoPlayer リスト内からムービー再生の実装例(発展)

AR 画像付きオブジェクト生成の実装例

AR リスト内から生成の実装例(発展)

private



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

管理人/副管理人のみ編集できます