【Unity】RPGを作るチュートリアルその91 決定ボタンによるイベントの起動

【Unity】RPGを作るチュートリアルその91 決定ボタンによるイベントの起動

シンプルなRPGをUnityで作るチュートリアルシリーズの91回目です。

第90回ではイベント機能の繋ぎ合わせと、メッセージ表示のイベントの作成を行いました。

今回は決定ボタンによるイベントの起動の仕組みを実装していきます。

 

 

制作環境

MacBook Pro 2023 Apple M2 Max

Unity6 (6000.0.30f1) Silicon

 

作業内容と順序

シンプルなRPGを作る上でどんな作業が必要か、どんな順番で作っていくと良さそうか、別ページで検討しました。基本的にこの流れに沿って進めていきます。

 

チュートリアルの一覧

このシリーズ全体の一覧は以下のページにまとめています。

 

前回の内容

前回はイベント機能の繋ぎ合わせと、メッセージ表示のイベントの作成を行いました。

 

イベントの起動

イベントを起動する方法としては、Enumで定義した以下の5通りを考えています。

  • なし
  • 決定ボタン
  • タイルに乗った時
  • マップに入った時に自動で実行
  • システム側で起動

前回作成したメッセージ表示のイベントでは、起動方法を決定ボタンにしました。まずは決定ボタンで起動する方法を実装していきましょう。

決定ボタンの入力は操作キャラクターの移動とも関連します。移動できる状態や決定ボタンを検知するタイミングなども考慮しつつ実装したいので、「PlayerMover」の状態を確認しつつイベント機能を独立させるため、「Player」のゲームオブジェクトの子オブジェクトの立ち位置で実装していきたいと思います。

盛り込みたい機能は、大枠としては以下の通りです。

  • 移動中の決定ボタン入力の検知
  • 1歩前のマスのイベントを確認
  • NPCに話しかけた場合は操作キャラクターの向きにする
  • イベントの起動を通知

これらを実現するために、「PlayerMover」やベースクラスの「CharacterMover」にも機能を追加します。

 

決定ボタンの入力によるイベント起動のクラスを作成

まずは決定ボタンを入力してイベントを起動するためのクラスを作成していきます。Projectウィンドウから「Assets/Scripts」のフォルダを開き、MonoBehaviourのスクリプトファイルを作成します。名前は [PlayerEventChecker] にしました。

決定ボタンでイベントを起動するクラス
決定ボタンでイベントを起動するクラス

 

作成した「PlayerEventChecker」の中身は以下のように記載しました。

この時点ではコンパイルエラーが表示されますが、後ほど既存クラスを変更する過程で修正されます。

イベントの完了を検知するため、「IEventCallback」のインタフェースを実装しています。イベント完了時に呼ばれるOnFinishedEvent()のメソッドではイベント開始時に向きを変えたNPCの向きを元に戻すようにしています。

参照関係は処理に必要なものをフィールドとして保持するようにしています。「_eventTargetMover」のフィールドに関しては、上記のイベント完了後に向きを戻すために、イベントの対象となっているキャラクターの「CharacterMover」への参照を保持するようにしています。

SetUpReference()では参照のアサインを行なっていて、「PlayerMover」から取得できる部分についてはそのまま取得し、その他のクラスについてはFindAnyObjectByTypeを使って取得します。

CheckEventInput()のメソッドは「PlayerMover」から呼び出すようにします。これは移動のためのキー入力が行われるタイミングと揃えたいためです。決定ボタンはキャラクターが移動できるタイミングで押せるようにしたいので、移動中フラグや、全体としての移動ポーズフラグを確認しています。

CheckEvent()は決定ボタンが押された時に呼ばれるイベント確認のメソッドで、操作キャラクターの位置から向いている方向に対してRayを飛ばしてNPCやイベントのゲームオブジェクトがあるかどうかを確認します。「PlayerMover」のメソッドを呼び出すようにしたいので、後で追加しましょう。

CheckEvent()の中で呼んでいるGetGameObjectFromRaycastHits()のメソッドは、引数のRaycastHit2Dのリストから先頭のゲームオブジェクトを取得する処理です。一番最初にRayが当たったゲームオブジェクトを実行対象として取得するようにしています。

SetEventMoverDirectionToPlayer()はイベントの実行対象となるキャラクターの向きを操作キャラクターの方向に変更するメソッドです。外部から向きを変更するメソッドについては「CharacterMover」に後ほど追加しましょう。メソッド内の「oppsiteDirection」では操作キャラクターと逆向きを取得する際に2を足して4で割った余りを取得しています。「MoveAnimationDirection」のEnumでは、正面、右、背面、左の順番に向きを定義しているので、操作キャラクターの向きに2を足すと反対向き(操作キャラクターに向く形)になるようになっています。値は0から3の範囲となるので、4で割った余りを使うことで、範囲内に収まるようにしています。

StartEvent()のメソッドではイベントを開始します。イベントを処理するクラスに情報を渡す際にはキューに情報をセットしてから開始の通知を行います。今回は決定ボタンによるイベント起動を行いたいので、RpgEventTrigger.ConfirmButtonが渡されるようになっています。

 

既存のクラスの変更

イベントの起動に関連する処理を追加するため、以下のクラスを変更します。

  • CharacterMover
  • NpcMover
  • PlayerMover

 

CharacterMoverの変更

キャラクターの向きを変更する機能などを追加するため、「CharacterMover」のクラスを変更します。

キャラクターの移動フラグを外部から取得できるように、「IsMoving」や「IsMovingPaused」のプロパティを追加しました。また、現在向いている方向を取得するために「AnimationDirection」のプロパティも追加しています。

「_isCheckPostMove」のフラグは、移動後の処理を行うかどうかのフラグで、基本的にはtrueのまま使います。もしイベントプロセスとしてキャラクターの移動が追加される時に、エンカウント処理などが呼ばれてしまうと大変なので、そうした場合にfalseにしてイベント内で移動処理を完結させることを考えています。

また、Start()については継承先でoverrideできるようにvirtualに変更しています。さらに、マップの切り替え時に初期化処理を行えるようにOnEnable()のイベント関数も追加して、初期化処理を行うようにしています。

 

既存のMoveCharacter()のメソッドの前に、GetMoveDirection()のメソッドを追加しました。このメソッドでは、引数のアニメーションの方向からVector2Intの向きを取得するようにしています。元々「NpcMover」や「PlayerMover」の中で直書きでswitch文を書いていたものをメソッド化した形になります。

 

MoveCharacter()の内部では、アニメーターに直接値をセットしていた部分を、この後作成するSetCharacterDirection()のメソッドを使う形に変更しています。向きを変更する動作を外部から呼び出したかったので、publicなメソッドとして切り出しました。

 

ExistsOtherCharacter()は既存のメソッドで、Raycastを使ってゲームオブジェクトを取得していた部分をメソッドとして切り出した関係で変更を行なっています。

GetOtherCharacter()は引数の向きにRaycastして、当たったゲームオブジェクト(正確にはRaycastHit2D)をリストとして追加して返しています。

IsCollisionTargetTag()は既存のメソッドで変更なしで、SetCharacterDirection()は外部から向きを変更するために追加したpublicなメソッドです。

 

NpcMoverの変更

「CharacterMover」の変更に伴い、「NpcMover」も一部変更します。

変更を行うのはMoveNpc()で、ベースクラスの「CharacterMover」で向きに応じたVector2Intを取得するためのメソッドとしてGetMoveDirection()を追加したので、それを使うようにしています。また、アニメーションの方向についてはフィールドの「_animationDirection」に保持するようにしています。

 

PlayerMoverの変更

「PlayerMover」も「PlayerEventChecker」と連動して動作するように変更していきます。変更箇所が多いので、漏れを防ぐために全文を掲載します。

上で作成した「PlayerEventChecker」への参照を保持するフィールドとして「_playerEventChecker」を用意しています。

Start()では「PlayerEventChecker」側のセットアップ用メソッドに自身の参照を渡すようにしています。また、Update()では決定ボタンの入力検知機能を呼び出すようにしています。

CheckMoveInput()についてはVector2Intの取得でGetMoveDirection()を使うように変更しました。

PostMove()では、メソッド内で直接エンカウントの確認を行うようにしていましたが、エンカウントの確認部分を別メソッドに切り出しています。これはエンカウントの確認の前に、移動後のタイル上のイベント確認を入れたいためです。この部分は次回以降に実装予定です。

 

スクリプトのアタッチ

作成したスクリプトをアタッチしていきましょう。

 

PlayerEventCheckerのアタッチ

Hierarchyウィンドウから「Player」のゲームオブジェクトを選択し、子オブジェクトとして空のゲームオブジェクトを作成します。名前は [PlayerEventChecker] にしました。

子オブジェクトの作成
子オブジェクトの作成

 

作成した「PlayerEventChecker」では同名のスクリプトファイルをアタッチします。

スクリプトのアタッチ
スクリプトのアタッチ

 

「Player」のゲームオブジェクトにアタッチした「PlayerMover」のスクリプトでは「PlayerEventChecker」への参照をアサインします。

参照のアサイン
参照のアサイン

 

動作確認

やっと動作確認ができるようになりました。ここでの目的は、ゴブリンに向かって決定ボタンを押した時に、

  • メッセージが表示されること
  • ゴブリンが主人公の方を向くこと
  • メッセージのページ送りができること
  • イベント終了後に動けるようになること

を確認することです。ゴブリンのゲームオブジェクトでは、移動の頻度を「Rarely」などに変えておくと話しかけやすくなります。

ゲームを実行して、ゴブリンに向かって決定ボタンを押します。画面上にメッセージウィンドウが表示され、Inspectorウィンドウで設定したテキストが表示されることを確認します。

イベントが実行されることを確認
イベントが実行されることを確認

 

もしエラー等が出る場合は、これまで作成したイベント関連のスクリプトのアタッチができているかも確認してみてください。

 

今回のブランチ

 

まとめ

今回は決定ボタンによるイベントの起動の仕組みを実装しました。イベントが動くようになっていよいよゲームらしくなってきました。

次回はカウンターなどの特定のタイル越しにイベントを起動する機能を実装します。

     

ゲーム開発の攻略チャートを作りました!

CTA-IMAGE

「ゲームを作ってみたいけど、何から手を付けていいか分からない!」


そんなお悩みをお持ちの方向けに、todoがアプリをリリースした経験を中心に、ゲーム作りの手順や考慮すべき点をまとめたe-bookを作成しました。ゲーム作りはそれ自体がゲームのように楽しいプロセスなので、「攻略チャート」と名付けています。


ゲームを作り始めた時にぶつかる壁である「何をしたら良いのか分からない」という悩みを吹き飛ばしましょう!