Unityに関連する記事です

 2回に分けて、プレイヤーキャラと NPC(ノン・プレイヤー・キャラクター)との会話イベントの実装を行います。
まずは最初に NPC 用のゲームオブジェクトを作成し、特定のボタンを押したら、Console ビューに会話イベントの状態を表示する制御を実装します。

 実装する処理、および処理の考え方についてはかなり深い部分での学習になりますので、1回の学習で難しくても復習しながら進めていってください。


<実装動画>
動画ファイルへのリンク


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

手順10 ーNPC との会話イベントの実装ー
17.NPC 用のゲームオブジェクトの作成と NonPlayerCharacter スクリプトの作成をし、設定を行う
18.プレイヤーの向いている方向に NPC がいる場合に会話イベントが発生する処理を実装する



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

 ・RaycastHit2D 型と Physics2D.Raycast メソッド
 ・Debug.DrawLine メソッド
 ・LayerMask.GetMask メソッド
 ・TryGetComponent メソッドと out キーワード宣言



17.NPC 用のゲームオブジェクトの作成と NonPlayerCharacter スクリプトの作成をし、設定を行う

1.設計


 すべての手順を1回の実装で行う必要はありません。
処理の全体図を俯瞰的に管理して、その中に必要となる処理を1つずつ組み込んでいきます。

 今回は最初に NPC 用のゲームオブジェクトを作成します。
こちらの準備をおこなった上でプレイヤーキャラが NPC の方向を向いて特定のボタンを押したら、Console ビューに会話イベントの状態を Debug 表示する制御を実装します。

 Console ビューに Debug が表示できたら、処理が正常に動いていることが確認できます。
その上で、この Debug の部分にウインドウを表示する処理を実装していくようにします。

 実装したい処理のゴール地点を設定し、そのゴールにたどり着くにはどのような処理を積み重ねていけばよいかを考えてみてください。
そして処理を逆算していく中で、どんな制御があればいいのか、という設計ロジックを組み立ていきます。


2.NPC 用の画像ファイルを用意する


 NPC キャラとして利用したい画像ファイルを Unity へインポートしてください。
Textures フォルダ内に格納しておくと管理がしやすくなります。


フォルダ画像 Textures/NPC 内に格納




 インポートした画像が1つのファイル内に複数まとまっている場合には、以前と同じように
Sprite Editor の機能を利用して、画像ファイルを分割してください。
分割する画像ファイルを選択し、インスペクターの設定のうち、Texture Type を Spriteに、Sprite Mode を Multiple に変更して、
Apply してから Sprite Editor を起動して分割します。


インスペクター画像(分割済)



 この画像の分割に関しては常に同じ処理の流れになりますので、何回かやって覚えてしまいましょう。


3.ヒエラルキーに NPC 用のゲームオブジェクトを作成する


 インスペクターした画像ファイルより、NPC 用のゲームオブジェクトを作成します。
 画像ファイルを1つ、あるいは、同じ方向を向いている画像が複数ある場合にはそれらをまとめて選択してから、ヒエラルキーの上にドラッグアンドドロップしてください。
まとめて選択する方法は Ctrl キーを押しながら左クリックです。
どの方向を向いている画像ファイルでも構いません。今回は正面を向いている画像ファイルを3つ選択しています。


画像ファイルの選択



選択時のインスペクター画像



 ヒエラルキーにドラッグアンドドロップすると、Create New Animation というウインドウが開きます。


Create New Animation ウインドウ



 ファイル名を npc の名称_front.anim に変更して保存を選択してください。
例えば今回は犬のゲームオブジェクトを NPC としているので、dog_front.anim にしています。
正面以外のアニメの場合には、その方向の名称にしてください。

 保存先には Animations フォルダを指定しています。その中にさらにフォルダ分けをして保存するようにすると管理しやすくなります。
このとき、anim ファイルと一緒に、Animator Controler ファイルも一緒に作成されて保存されます。


保存先フォルダ



 ヒエラルキーに画像ファイルの最初のファイル名と同じ名称のゲームオブジェクトが作成されます。
(複数の画像の場合、自動的に先頭のファイル名を持つゲームオブジェクトになります)

 ヒエラルキーでNPC 用のゲームオブジェクトを選択し、名前を画像に見合う名前に変更してください。
NPC_1 などでも構いません。今回は Dog にしています。

 Sprite Renderer コンポーネントの Sorting Layer の設定を Collision に設定してください。
描画する際に、プレイヤーキャラよりも優先順位は低く、歩けるタイルよりは優先順位が高くなるようにするためです。


ヒエラルキー画像



NPC 用ゲームオブジェクト インスペクター画像



 そのままゲームを実行してみてください。NPC がアニメーションを繰り返すはずです。(位置はどこでも構いません。)


4.NPC 用のゲームオブジェクトの設定を行う


 ヒエラルキーでNPC 用のゲームオブジェクトを選択してインスペクターを確認します。

 Transform コンポーネントの Position を Player と重ならない位置に変更してください。
あまり離れすぎるとデバッグが大変になりますので、適宜な位置にしてください。


Sceneビュー と Gameビュー画像




 続いて、インスペクターの一番下にある Add Component ボタンを押して、Box Collider 2D コンポーネントを探して追加してください。

 コライダーのサイズは (1, 1) のままでも問題ありませんが、調整していただいても構いません。


インスペクター画像



Sceneビュー画像



 以上で設定は完了です。


5.NPC 用のレイヤーを作成して、設定を行う


 NPC 用の新しい Layer を作成して、NPC 用のゲームオブジェクトに設定します。

 ヒエラルキーの NPC 用のゲームオブジェクトを選択して、インスペクター上部にある Layer を選択します。
プルダウンメニューが開きますので、一番下にある Add Layer… を選択します。インスペクターの表示が Tags & Layers に切り替わり、Layer の編集が可能になります。

 Layers を選択し、Layers 8 以降に NPC を登録してください。


Tags & Layers インスペクター画像(ここでは Layers 8 に登録しています)




 再度、NPC 用のゲームオブジェクトを選択して、インスペクターの Layer を選択します。
プルダウンメニューに NPC が追加されていますので、そちらを選択します。


NPC 用ゲームオブジェクト インスペクター画像



 以上でレイヤーの設定は完了です。


6.NonPlayerCharacter スクリプトを作成する


 NPC 用ゲームオブジェクトを制御するためのスクリプトを作成し、NPC 用ゲームオブジェクトにアタッチします。
今後はこのスクリプトを利用して、会話イベントのウインドウを操作するようにしていきます。


NonPlayerCharacter.cs

 <= クリックすると開きます。


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


7.NPC 用ゲームオブジェクトに NonPlayerCharacter スクリプトをアタッチする


 ヒエラルキーにある NPC 用のゲームオブジェクトに対して、NonPlayerCharacter スクリプトをドラッグアンドドロップしてアタッチしてください。
アタッチしたあとには必ず、対象となったゲームオブジェクトのインスペクターを確認して、アタッチされているかを確認してください。


インスペクター画像



 以上で NPC 用のゲームオブジェクトは完成です。


18.プレイヤーの向いている方向に NPC がいる場合に会話イベントが発生する処理を実装する

1.設計


 プレイヤーキャラには向いている方向の情報が設定されています。
これは移動の際のキー入力より取得していますので、現在の最新の方向を取得出来ています

 この情報を活用して、プレイヤーキャラの向いている方向に対して NPC が存在している場合には
会話イベントの Debug.Log メソッドが実行されるように処理を組み立てていきます。



 処理の実装方法には色々な方法がありますが、今回はキャラの向いている方向の情報を利用して、Ray という見えない光線を投射する機能を活用します。

 Ray はゲームオブジェクトの持つコライダーに反応をします。
この処理を活用し、特定のボタンを押したら、キャラの向いている方向に Ray を投射し、その一定方向にコライダーを持つゲームオブジェクトが存在していたら
Ray がそのゲームオブジェクトにぶつかって情報を取得します。

 この時点では「どの」ゲームオブジェクトであるかは判明していませんので、Ray がぶつかったゲームオブジェクトのレイヤーを基準に判定を行います
Ray がぶつかったコライダーを持つゲームオブジェクトのうち、レイヤーが NPC に設定されているゲームオブジェクトである場合には、
そこで初めて「会話する対象のゲームオブジェクト」として認識するように制御を行うようにします。

 これが今回の会話イベントを発生させるための設計ロジックです。

 レイヤーで判定を行っているため、今回は NPC にのみ反応をしていますが、この部分を例えば「宝箱」のレイヤーや、「障害物」のレイヤーというように
ゲームオブジェクト単位で目的に応じたレイヤーの設定を行っておくことにより、プレイヤーキャラは特定のボタンを押すだけで、
その対象物のレイヤーを判別する制御処理があることによって、1つのボタンで複数のイベントを発生させることも可能です。

 これは、ドラクエの便利ボタンや FF のアクションボタンのように、対象物によって自動的に処理を分岐するようにする設計方法です。


2.PlayerController スクリプトを修正する


 設計で考えた内容とロジックを元にして、キャラの現在の方向の取得と、
その方向に合わせた移動アニメの再生の制御処理を実装します。

 また会話イベント発生時用に判定用の変数を用意しています。
この情報を利用することで、会話イベント時には移動のキー入力を受け付けずに移動を出来ないように制御しています。

 このように書くと非常に簡単に見えますが、そこに至るまでの過程を説明します。
この部分を考えて実装を行うのがプログラマーの仕事になるためです。



 すべての分岐制御処理には、その判断基準となるための情報があります。
すでにある情報を活用することで分岐を作成できるものもあれば、新しく変数を用意して、分岐のために運用する必要があるものもあります。

 今回のケースでは「会話イベント中であるかどうか」という判断が行うことができれば、その情報を元に分岐を作成し、
「会話イベント中であれば移動のキー操作を行えないようにする」という制御処理を実装することが可能になります。

 ですが現在はまだ、この「会話イベント中であるかどうか」を判断できる情報はどこにもありません
どこにもない場合、その判断するための情報としての意味を持たせて、判断用に利用する変数を新しく用意する必要があります。

 新しく変数を用意する、ということは、その変数には明確な目的や意味が必要になります。
そうしないと、変数を利用したり、運用したりということが行えません。この意味付けをするのがエンジニアの役割でもあります。
つまり、この変数の持つ情報とは、どのようなときに利用するのか何の判断要素となりえるのかという部分を考えて運用していく必要があるということになります。
 


 今回の場合であれば、「会話イベント中であるかどうか」という判定に利用したい、という目的があります。
この時点で新しく作成する変数には明確な運用目的がありますので、あとは、その情報をどのように活用していくかに着目します。

 例えば bool 型の変数を作成するとした場合、値には真偽のいずれかの情報が代入されますので、
真(true)の場合にはどのような状態偽(false)の場合にはどのような状態、という設定を自分で考えて、その考えに沿った分岐を作成することで
プログラム内での運用が行えるようになります



 そういった部分に注目しながら、今回のプログラムを見ていってください。
そして、どの部分にどのような変数が使われていることで分岐が発生して処理が制御されているのかを、1つずつ確認してみてください。


PlayerController.cs

 <= クリックすると開きます。


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


3.InputManager の設定を行う


 Unity Editor の左上のメニューより、Edit => Project Settings を選択し、Project Settings ウインドウを開きます。
左側の項目より、Input Manager を選択します。

 Vertical の下にある設定内容である Fire1 の Name 内の文字列を "Action" に変更してください。
この文字列を PlayerController スクリプト内のキー入力の情報として利用しています。
Fire1 の部分も Action に変更になります。
もしも Fire1 のまま利用するのであれば、同スクリプトの文字列を "Action" ではなく "Fire1" に修正してください。


InputManager



 以上で設定は完了です。


4.<RaycastHit2D 型と Physics2D.Raycast メソッド>


 新しく学習した処理について順番に説明を行います。
基本的にはこの説明を読んで終わりではなく、Unity の公式マニュアルなどを読んだり、
他にも参考になるサイトがありますので、ぜひ自分で調べていただいて、より深い知識として吸収できるよう復習に努めてください。



 Physics2D クラスの持つ Raycast メソッドは、空間のある地点から特定の方向にめがけて、見えない光線(レイ)を投射する処理です。
この光線はコライダーを持つオブジェクトにヒットをして、そのヒットしたオブジェクトの情報を、RaycastHit2Dという型として戻り値を返してくれます
そのため、左辺に RaycastHit2D 型の hit 変数を用意しています。

参考
Unity公式スクリプトリファレンス
Physics2D.Raycast
https://docs.unity3d.com/ja/current/ScriptReferenc...
Unity公式スクリプトリファレンスRaycastHit2D
https://docs.unity3d.com/ja/current/ScriptReferenc...

 今回の実装例です。

 // Player の位置を起点とし、Player の向いている方向に 1.0f 分だけ Ray を発射し、NPC レイヤーを持つゲームオブジェクトに接触するか判定し、その情報を hit 変数に代入
  RaycastHit2D hit = Physics2D.Raycast(rb.position, lookDirection, 1.0f, LayerMask.GetMask("NPC"));

 今回の処理では、Player ゲームオブジェクトの位置を起点(第1引数 = rb.position)とし、Player の向いている方向(第2引数 = lookDirection)に 1.0f 分(第3引数 = 1.0f)だけ Ray を発射しています。
そのため、あまりに離れているゲームオブジェクトのコライダーとは接触しないようになっています。

 このとき、NPC レイヤーを持つゲームオブジェクトに接触するか判定し(第4引数 = LayerMask.GetMask("NPC") メソッドの戻り値)、取得した情報を hit 変数に代入しています。

 hit 変数の情報は RaycastHit2D 型ですので、この情報からは、Collider 型の情報を取得することが出来ます。

参考
うら干物書き様
【Unity】マウスのある場所にオブジェクトを配置したい
https://www.urablog.xyz/entry/2017/04/28/213010


5.<Debug.DrawLine>


 この機能は、Sceneビューにて機能します。

 上記の Raycast メソッドや、Linecast メソッドといった、見えない光線を可視化するためのDebug機能です。
引数には、Raycasy メソッドや Linecast メソッドで設定した内容と同じ内容で設定を行うことで、
どの位置からどの方向に向かって、どの位の距離まで Ray が投射されているのかを確認することが出来ます。


 // Scene ビューにて Ray の可視化
  Debug.DrawRay(rb.position, lookDirection, Color.blue, 1.0f);


Scene ビュー画像




 キャラの中央から縦に青い線が見えると思います。これが Raycast メソッドの実行内容であり、それを可視化しています。
青い線がキャラの向いている方向に約 1 マス分( 1.0f )だけ投射されていることがわかります。
この部分が「Ray とぶつかる」位置として判定されます。そのため、この青い線とコライダーを持つゲームオブジェクトがぶつかる
その情報が hit 変数に戻り値として取得出来るようになっています。Debug.DrawLine メソッドは、それを可視化する処理です。

 このように処理を可視化することで、どのような処理が実行されているかを確認できます。
非常に重要なことですので、積極的に利用してデバッグをスムースにして捗るようにしましょう。

参考サイト
Unity公式スクリプトリファレンス
Debug.DrawLine
https://docs.unity3d.com/ja/current/ScriptReferenc...


6.<LayerMask.GetMask メソッド>


 引数に指定した LayerMask の文字列を int 型の値に変換してくれるメソッドです。

  LayerMask.GetMask("NPC")

 今回の場合、NPC を引数に指定していますので、NPC を登録している Layer の番号である 8 の値が戻り値として取得出来ています。
これは、Physics.Raycast メソッドの第4引数の指定値が int 型で Layer を指定するように要求されているためで、それに情報を合わせるためです。

Layer



参考サイト
Unity公式スクリプトリファレンス
LayerMask.GetMask
https://docs.unity3d.com/jp/current/ScriptReferenc...


7.<TryGetComponent メソッドと out キーワード宣言>


 Unity2019.2 以降に追加されたメソッドです。処理結果として bool 型で戻り値を返してくれます。
このときの処理結果というのは、指定したコンポーネントの型の取得を行い、それが取得できればtrue、取得できなければfalseが戻ります

 また out キーワードによる宣言があります。
 out キーワード宣言を行うと、out を付けた引数で指定した変数はメソッド内で必ず結果が入ることが保証されるものです。
そのため、処理結果の戻り値が true の場合には必ず、この out の後に宣言した変数内に型が代入されます

 // そのゲームオブジェクトにアタッチされている NonPlayerCharacter クラスが取得できた場合
  if (hit.collider.TryGetComponent(out NonPlayerCharacter npc)) {

 今回はこのような処理として利用しています。hit 変数の持つ collider 情報から NPC のゲームオブジェクト へとアクセスし、
NPC のゲームオブジェクトに対して TryGetComponent メソッドを実行しています。

 out 以降には、そのゲームオブジェクトから取得したい型と変数を宣言します。今回は、 NonPlayerCharacter 型npc 変数を用意しておきます。

 もしもこの TryGetComponent メソッドの処理結果が true であるならば、つまり、ゲームオブジェクトに NonPlayerCharacter スクリプトがアタッチされているのであれば、
out として用意した npc 変数に NonPlayerCharacter スクリプトの情報が代入されて、さらに、if 文内に処理が実行されます
また if 文内の間はこの npc 変数が使用できることになります。(スコープがif文ブロック内であるためです

 TryGetComponent メソッドの処理結果が false の場合には NonPlayerCharacter スクリプトの取得ができなかったため、npc 変数は null のままで、false が結果として戻り、
このif文内の処理は実行されないままで終了します

 なお TryGetComponent メソッドには複数の書式があります。こちらは下記のリファレンスを参照してください。

参考
Unity公式スクリプトリファレンス
Component.TryGetComponent
https://docs.unity3d.com/ScriptReference/Component...


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


 Action のボタンに割り当てられているキーを押してみてください。
Sceneビュー 上に、Ray が可視化されていれば Ray の制御は成功です。


<実装動画 Ray の可視化>
動画ファイルへのリンク



 この状態で、Game ビューに設置してある NPC のゲームオブジェクトに Ray が当たるようにキーを押してください。
確認すべき点が複数ありますので、順番に確認します。

 まずは、Console ビューに Debug.Log で設定した "会話ウインドウを開く" の文字列が表示されれば制御成功です。

 この確認ができたら、Player ゲームオブジェクトの PlayerController スクリプトの isTalking 変数も同じようにキー操作に合わせて切り替わっているかを確認しましょう。
この時点ではスイッチが入って true の状態になっているはずです。
この変数を元に、プレイヤーキャラの移動が制御されるようになっていますので、
会話ウインドウが開いてる状態のときにはプレイヤーキャラの移動が出来ないように制御されていれば、こちらも制御成功です。

 最後に、NPC ゲームオブジェクトのインスペクターを確認しながら、NonPlayerCharacter スクリプトの isTalking 変数のスイッチが入って true になっているかも一緒に確認します。
ここまでが会話ウインドウが開いたときに確認すべき項目になります。

 すべて問題がなければ、次の確認に進みます。



 会話ウインドウが開いている状態(isTalking 変数が true)のときに、もう一度、キーを押すと、
Console ビューに Debug.Log で設定した "会話ウインドウを閉じる" の文字列が表示されれば制御成功です。
この状態になると、プレイヤーキャラが再度移動できる状態になります。この処理がどの部分で制御されているかを、しっかりと把握しておいてください。

 NPC ゲームオブジェクトのインスペクターを確認しながら、NonPlayerCharacter スクリプトの isTalking 変数のスイッチが切り替わって false になっているかも一緒に確認します。


<実装動画◆Console ビューで動作を確認>
動画ファイルへのリンク



 デバッグを行う際に複数のインスペクターを確認したい場合には、インスペクターを複数個同時に表示しておくと便利です。

 インスペクタータブの上で右クリックをしてメニューを開き、Add Tab => Inspector を選択すると増やすことが出来ます。
インスペクター右上部に錠のアイコンがありますので、常に表示していたいゲームオブジェクトのインスペクターの内容にしてからこのアイコンを押すと
ヒエラルキーのゲームオブジェクトを切り替えてもインスペクター表示を選択しているゲームオブジェクトに固定することが出来ます。


右上の錠のアイコン インスペクターに表示するゲームオブジェクトを固定する機能



表示内容固定中



 例えば、NPC 用のゲームオブジェクトを選択した状態でインスペクターの錠のアイコンを押すと、このインスペクターでは常に NPC 用のゲームオブジェクトの情報を表示します。

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

 次は 手順11 −NPC との会話イベント用ウインドウの実装− です。

コメントをかく


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

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

Menu



プログラムの基礎学習

コード練習

技術/知識(実装例)

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

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

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

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

レースゲーム(抜粋)

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

3D脱出ゲーム(抜粋)

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

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

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

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

3Dトップビューアクション(白猫風)

VideoPlayer イベント連動の実装例

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

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

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

private



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

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