Unityに関連する記事です

 マウスクリックではなく、キーボード(ゲームパッド)での操作が可能なインベントリウインドウの実装例です。
2回の手順に分けて学習します。

 ここでは正方形、あるいは長方形のインベントリを採用し、全グリッド(マス)を移動できず、特定のグリッドを制限するケースです。
見た目上は不安定なグリッドですが、それを多次元配列を利用した座標に置き換えることで管理します。


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




 キー入力操作については UniRx を利用し、インベントリ内のグリッド(マス目)とアイテムアイコンについては自動生成を行います。
グリッドについては多次元配列を利用して座標として管理し、アイテムアイコンの画像などの情報についてはデータベースより参照して設定します。

 現在制作しているインベントリを改良してみたい、処理の自動化・効率化を図りたい、というニーズに対応する教材となります。

 2回の手順において学習後、その後の学習としましては、ここでは1つのクラスですべてを完結していますので、
そちらを MVC(Model-View-Controller)アーキテクチャを活用した、クラスの分割(責務分け)を行うためのリファクタリング手法を学習します。



事前知識


 少なくとも、下記の機能については理解をしておく方が望ましいでしょう。

・スクリプタブル・オブジェクト
・シングルトン・デザインパターン
・多次元配列
・List
・三項演算子(すなわち、戻り値のある処理の読み解き方)

 新しく学習する内容としては、UniRx を利用したキー入力機能になります。


設計


 多次元配列を利用して、XとYの座標で管理しています。





多次元配列時のメニュー1内の座標 [x-y]


[0-0, 1-0]
[0-1, 1-1, 2-1, 3-1]
[0-2, 1-2, 2-2, 3-2]

 上記の画像を元にした配列は、このようになっています。左上が[0-0]、右下が [3-2]です。

カーソルの上下左右移動(動けない場所にはいかないようにする制限付き)
インベントリ内のメニュー切り替え(1との切り替え。カーソルの位置は1のみ対応)

 あとは、アイテムのアイコン用のゲームオブジェクトのプレハブを作成していただき、
そこに新しいクラスをアタッチして、所持しているアイテムのデータを渡すことで画像を差し替えるようにしてください。



アセットのインポート


 必要なアセットをインポートしておきます。


<1.UniRx>


 Unity のアセットストアからインポートお願いします。

UniRx - Reactive Extensions for Unity
https://assetstore.unity.com/packages/tools/integr...


<2.InputAsObservable>


 こちらは UniRx の拡張アセットとして Github に公開されています。

 下記の URL より Download Zip を選択してダウンロードしてください。
解凍したフォルダをそのままUnity へインポートしてください。

 これにより InputAsRx.Triggers の名前空間が利用できるようになります。


Github
https://github.com/euglenach/InputAsObservable


 制作様のサイトです。
アセットの利用方法も解説されています。目を通しておきましょう。

Qiita @Euglenach 様
キー入力、マウス入力をIObservable T に変換して使う


UI の作成


 インベントリ用のウインドウを UI で制作します。

 背景、フレームなどは任意です。自分の好きな形状で作成してみましょう。

 下記はサンプルです。この通りである必要はありません。


完成図



1.Canvas の作成


 Canvas Scaler コンポーネントの設定を忘れずに行うようにしてください。


2.メニュー用のゲームオブジェクトの作成


 背景画像の設定用オブジェクトや、カーソル移動先のオブジェクトの配置位置の設定用のオブジェクトを配置しておきます。
カーソルの移動先については、各マス目(Grid)を生成しますが、それを配置するためのオブジェクトを事前に配置しておくと生成しやすいです。


3.カーソル用のゲームオブジェクトの作成


 プレイヤーの操作するカーソルについてもオブジェクトを用意します。
こちらを操作する形になります。


InventoryManager スクリプトの作成


 using ではインポートした外部アセットより UniRx と InputAsRx.Triggers の名前空間を利用しています。
またカーソルをフォーカスしてキー入力操作に対応させるため、UnityEngine.EventSystems の名前空間も追加しています。


InventoryManager.cs

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



解説


 UI でキーボードやジョイパッドによるカーソル移動を実現するには、以下の手順が必要になります。

1.UI が開いたとき、1つのゲームオブジェクトにフォーカスする。このゲームオブジェクトがカーソルの初期位置になるようにする
2.キー入力とカーソルの動きを連動させる


1.UI が開いたとき、1つのゲームオブジェクトにフォーカスする。このゲームオブジェクトがカーソルの初期位置になるようにする


 1については、Unity が用意している処理があります。

''EventSystem.current.SetSelectedGameObject() メソッド''

 このメソッドを利用し、引数には GameObject 型を指定することで、フォーカスします。
これは UnityEngine.EventSystems 名前空間(namespace)に含まれていますので、using で宣言しています。


2.キー入力とカーソルの動きを連動させる


 2については、キー入力部分と、カーソルへの情報を渡す部分をそれぞれ作ります。
 キー入力には、UniRx.Triggers を拡張している先ほどの InputAsObservable アセットを利用します。

this.OnAxisRawAsObservable("Horizontal")      // GetAxisRaw メソッドと同じ
    .Where(horizontal => horizontal != 0)     // キー入力があるか確認
    .ThrottleFirst(TimeSpan.FromSeconds(inputDelay))     // 連続入力を制御。inputDelay 時間だけ連続入力を受け付けない
    .Subscribe(horizontal => MoveCursor((int)horizontal, 0));   // キー入力の値を MoveCursor に渡して移動先を決める

 これがキー入力部分です。
 this とあるのは、このクラスと、このクラスが継承している MonoBehaviour クラスを指しています。
その中に用意されている Input.GetAxisRaw メソッドを UniRx の形で書いた書式になります。

 UniRx は、一度処理を作成すれば、自動的に監視処理が実行されます。

 このケースであれば、Horizontal 方向(X 軸)のキー入力を監視し、Where 部分で条件を確認します。これは if 文と同じ機能です。
つまり、Horizontal 方向(左右矢印、あるいはAWキー)の入力を OnAxisRawAsObservable("Horizontal") で受付、
実際に入力値があるかどうかを.Where(horizontal => horizontal != 0)部分でチェックしています。キー入力があれば値は 0 以外になるためです。

 その下にある .ThrottleFirst(TimeSpan.FromSeconds(inputDelay)) の処理は、重複入力を防ぐ機能です。
GetAxisRaw による命令はキャラの移動にも利用できるように連続して判定します。
そのため、1秒押しただけでも画面端までカーソルが一気に移動してしまうので、
それを引数で指定した時間だけ、キー入力を受け付けないようにすることで
キー入力をし続けても、一定の間隔でカーソルを移動できるように調整しています。

 最後の .Subscribe(horizontal => MoveCursor( (int)horizontal, 0)) がキー入力があったときに実行される処理です。
horizontal 変数に、今回の分のキー入力の値が入るので、1 か -1 の値が入っており、それを MoveCorsor メソッドに渡しています。



 ここで利用されている処理の記述は UniRx のものですが、メソッドを複数つなげていくメソッドチェーンという書式は C# 自体の機能です。



 MoveCorsor メソッドでは、キー入力の値が X 軸と Y 軸について利用し、それをカーソルの位置に落とし込んでいます。
また、カーソルが移動できるグリッド(枠)を超えていないかを判定し、移動できない場合には前の位置に再移動させています。
 カーソルは移動時に親子関係を使って移動していますが、単純にカーソルの座標だけを動かしてもいいです。
ただし、親子関係にしておくと、親のゲームオブジェクトが「アイテムアイコン」のゲームオブジェクトになりますので、
カーソルの位置からアイテムアイコンの情報を参照しやすくなるので、このようにしていますが、この辺りは任意だと思います。

 メニュー1については、フラグを使うことでカーソルの位置を記憶させるケースとさせないケースの切り替えが出来るようにしてあります。

 ボタンを押すたびに1と2を切り替えます。


スクリプトをアタッチして設定を行う


 作成したスクリプトは、どのゲームオブジェクトにアタッチしても問題ありません。(なぜ、どのゲームオブジェクトでも問題ないのか、理由を考えてみましょう。)
新規作成したゲームオブジェクトでもよいですし、Canvas でも問題ありません。

 GridList 変数には Grid(移動可能なマス目)の位置用のゲームオブジェクトをアサインしてください。
ここに登録した位置に Grid を生成し、カーソル移動可能なオブジェクトとして利用します。
また多次元配列の座標がわかりやすいように、ゲームオブジェクトの名称がゲーム実行時に座標名に変更されます。

 Cursor 変数にアサインするゲームオブジェクトは、Canvas 内に作成したカーソル用のゲームオブジェクトをアサインしてください。

 IsMemoryCursorPos にチェックを入れると、メニュー1のみ、カーソルの位置を記憶しますので、
一度、インベントリを閉じて開き直しても、前のカーソルの位置からスタートします。




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


 ゲーム実行時のものです。
GridList 変数に登録したゲームオブジェクトの名前を、デバッグしやすいように座標位置に変更しています。





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



まとめ


 まだ完成はしていませんが、メニュー1と2への切り替えも入れてあります。
ただし、カーソルの位置がメニュー1しか対応していません。



 UniRx でキー入力の情報を作成するメリットは、Update メソッド内にキー入力の処理を書かなくて済むようになり、処理の負荷軽減が見込めます。
 
 また今回はやっていませんが、インベントリの表示/非表示などの Update 内に書いてある処理もすべて UniRx での記述に変更可能です。
そうすることで Update メソッドを書かなくて済みます。

 その場合、this.UpdateAsObservable メソッドを使う形になりますので、よろしければ調べてみてください。
あるいは、ChatGPT に Update 内の処理を書いて、UniRx への変換を指示してもらってもいいかもしれません。



 UniRx を利用すると「,海ΔいΔ海箸起きたとき、△海ΔいΔ海箸鮗孫圓垢襦廚箸いΨ舛諒儡垢出来ます。
これは普段利用しているボタンの処理を、色々なものにも対応させるイメージです。

 ボタンを押したら、登録してあったメソッドが動く、というのを、他の処理にも応用できます。
これは処理のイベント化や、イベント駆動型の処理ともいい、事前に「´◆廚鮴瀋蠅靴討くことで実現しています。

 この処理は非同期処理となるので、コルーチンメソッドと同じく、メインの処理から分離されて動き出します。
Update メソッドの場合、メインの処理内でボタンを押したかどうかを監視する形になりますが、
非同期処理の場合にはメインの処理とは別の場所で動くので、相対的に処理の負荷軽減が実装出来ます。


 例えば、A というフラグが立ったら B というメソッドを実行する」というような、フラグ監視の処理を Update に書くのではなくて
事前にセットしておくだけで、あとはずっと監視してくれるのが UniRx の実現できる内容です。

 この,鉢△隆屬砲蓮▲プション設定が可能です。
それが今回のキー入力の部分でも利用されています。

this.OnAxisRawAsObservable("Horizontal")      // GetAxisRaw メソッドと同じ
    .Where(horizontal => horizontal != 0)     // キー入力があるか確認
    .ThrottleFirst(TimeSpan.FromSeconds(inputDelay))     // 連続入力を制御。inputDelay 時間だけ連続入力を受け付けない
    .Subscribe(horizontal => MoveCursor((int)horizontal, 0));   // キー入力の値を MoveCursor に渡して移動先を決める

<キー入力を監視する>
 this.OnAxisRawAsObservable("Horizontal") // GetAxisRaw メソッドと同じ

<それに対応して、処理を実行する>
◆.Subscribe(horizontal => MoveCursor( (int)horizontal, 0)); // キー入力の値を MoveCursor に渡して移動先を決める

 この,鉢△隆屬砲△襭欧弔僚萢がオプションです。
この場合は、「キー入力があったか確認」「一定時間おきにキー入力を受け付ける」という設定をしているので、
△僚萢に行く前に色々な条件を設定することで、自分の想定している処理を実現しています。




参考サイト
ソフトライム 様
C#スキル別、Inventory(インベントリ)持ち物システム

コメントをかく


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

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

Menu



技術/知識(実装例)

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

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

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

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

レースゲーム(抜粋)

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

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

3D脱出ゲーム(抜粋)

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

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

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

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

VideoPlayer イベント連動の実装例

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

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

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

private



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

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