Unityに関連する記事です

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

手順16 ーロジックの学習,肇好リプタブル・オブジェクトの作成ー
28.防衛拠点用のゲームオブジェクトを作成し、仕様と運用のロジックを考えて実装する
29.防衛用の味方キャラのデータベースを作成し、仕様と運用のロジックを考えて実装する



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

 ・ロジックの考え方
 ・enum だけのスクリプト・ファイルの作成
 ・スクリプタブル・オブジェクトの作成とデータの登録
 ・[System.Serializable(シリアライザブル)]属性
 ・List の初期化



28.防衛拠点用のゲームオブジェクトを作成し、仕様と運用のロジックを考えて実装する

1.設計を考える


 ゲームのシステムの1つである、防衛拠点用のゲームオブジェクトを作成します。
このゲームオブジェクトは TileMap 内ではなく、MainMap ゲームオブジェクトの子オブジェクトとして配置して問題ありません。

 Unity で作成するゲームオブジェクトは、アタッチされているコンポーネントやスクリプトによって、そのゲームオブジェクトの役割が決定します。
 今回作成するゲームオブジェクトに「どのような役割」を持たせることができれば、防衛拠点として機能するかを考えてみましょう。
役割を持たせるという観点でクラス(スクリプト)を作成していくと、どのような機能が必要になるのかという設計部分も考えやすいと思います。

 防衛拠点としては、敵キャラの侵入判定、防衛拠点の耐久力の設定があれば、最低限の機能を有しています。
よってそれらの情報を管理するクラスを作成して、防衛拠点のゲームオブジェクトにアタッチすることで、ゲームオブジェクトの役割を明確化します。
そちらの仕様が完成したら、敵の侵入判定に合わせてエフェクトを生成したり、演出を入れたいタイミングも検討しておきます。

 ロジックとしては、以下のようなものが想定されます。

<ロジックの考え方>
 敵キャラが移動し、防衛拠点に侵入する => どのような判定方法を使うのか? スクリプトは?
    ↓
 防衛拠点の耐久力を定数で減算する => 定数で実装できたら、敵キャラの攻撃力分で減算できるようにする
    ↓
 敵キャラを破壊する
    ↓
 防衛拠点に対する被ダメージの演出を入れる。後でも問題なし
    ↓
 Debug.Log メソッドを活用し、耐久力が 0 以下になった場合にはゲームオーバーと Console ビューに表示する => その後、ゲームオーバー判定を作る際に役立つ

 侵入判定には OnTriggerEnter2D メソッドを防衛拠点側のスクリプトに実装する方法が一番わかりやすい設計になります。
その中で、耐久力の減算処理をしたり、敵キャラを破壊したりといった処理を実装するイメージです。

 OnTriggerEnter2D メソッドを利用する場合、スクリプトを作成して侵入判定の処理を作成していくことになりますが、
侵入判定はスクリプトだけでは実装出来ません。いままで学習してきたことを振り返っていただくとわかるように
ゲームオブジェクト側にも侵入判定を実現するための設定が必要になります。

 侵入判定とはどんな処理なのか、どんなコンポーネントや設定が必要になるのかを考え、復習も兼ねて実装にチャレンジしましょう。

 敵キャラからのダメージを定数ではなくて敵キャラの攻撃力から参照するようにするためには、
敵キャラ側のスクリプト(EnemyController) に情報を持たせる必要があります。こちらを参照して利用することを考えてみてください。


2.参考コード


 自分でロジックを設計して動作確認をしてみましょう。
もしも上手く実装出来ない場合のため、サンプルコードを提示しておきます。

 問題なく実装出来た場合にも一度目を通しておくと学習になります。
この内容に書き換える必要はありません。自分の書いた処理に自信を持ちましょう。



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



3.画面に耐久力を表示する方法を考える


 現在の所、耐久力は減算できるようになりましたが、それがゲーム画面に表示されていないため、
どの位の耐久力が残っているのかがユーザーは把握できません。

 色々なゲームを参考にしながら、どういった手法でゲーム画面に表示すればいいかを設計して、ロジックを考えていきましょう。

 数字表示でもよいですし、ゲージ表示でもいいです。どちらも DOTween の機能を利用することでアニメ演出が可能です。


<ゲージ表示の場合>



 これらをゲーム画面上に表示するためにはどのようなゲームオブジェクトが必要になるのかを考えます。
その後、そのゲームオブジェクトを制御する方法を考えます。
例えば、数字を表示するのであれば Text コンポーネントの制御が必要になりますし、
ゲージ表示であれば Image コンポーネントや Slider コンポーネントの制御が必要になります。

 全体の流れをイメージし、必要な情報が分かってから、ゲームオブジェクトの作成や
スクリプトの作成と修正を行うようにしていきます。


29.防衛用の味方キャラのデータベースを作成し、仕様と運用のロジックを考えて実装する

1.設計を考える


 防衛用の味方キャラは現在1種類です。タワーディフェンスゲームには性能が異なる多くのキャラが登場し、
それをコストなどの状態をみながら戦略を考えて配置することが楽しみの1つになっています。

 今後のために、防衛用の味方キャラについてはデータ化を行い、それをまとめたデータベースを作成しておくことで
ゲーム内で味方キャラの情報を利用できるような設計を考えてみてください。

 味方キャラのデータはスクリプトを作成し、それを List でまとめてスクリプタブル・オブジェクトを作成して
データベースを作成するようなものがおすすめです。

 インスペクターから設定もできるため、視覚的かつ管理しやすく、情報の追加・修正も容易です。


2.スクリプタブル・オブジェクトとは


 キャラやエネミー、アイテムなどのデータなどを扱う場合に、複数のデータを1つのまとまりとして管理できるデータベースのようなものがあると扱いが楽になります。

 Unityにはスクリプタブル・オブジェクトという機能(こちらはアセットとしてデータベースを扱う方法)がありますので、今回はこの機能を利用していきます。

 スクリプタブル・オブジェクトを作成するためには、専用のスクリプトを作成する必要があります。その作成方法を学習します。

 今回作成するスクリプタブル・オブジェクトはキャラのデータを管理する目的で作成を行います。
そのため、スクリプト内には、キャラの1体分のデータをまとめるための CharaData クラスを用意します。

 CharaData クラスは、キャラ1体分のデータを1つにまとめている情報群です。
現在は攻撃範囲や攻撃力といった値を個別に用意していますが、これを1つのデータ群としてまとめて管理するためのクラスになります。
CharaData クラス内に用意した変数がキャラのデータになりますので、どういった情報を持たせたいのか、何が必要かを考えてみましょう。
エネミーを攻撃するための攻撃範囲や、攻撃力といった情報がそれらに当たります。

 この CharaData クラスは、ゲームに登場するキャラの数だけ用意することになりますので、それを管理するために List 機能を利用します。

 この2つの情報を管理して完成するのがスクリプタブル・オブジェクトになります。
どのような構成になっているかはスクリプト作成後に説明をしていますので、そちらをしっかりと学習してください。

 まずは最初に CharaData クラスを作成するために必要なキャラの攻撃範囲のタイプを登録する AttackRangeType を enum にて作成してから、
スクリプタブル・オブジェクト用のスクリプトを順番に作成していきます。


3.AttackRangeType を作成する


 今回は攻撃範囲については、新しく enum を作成しておくと管理が容易になります。

 enum (イーナム) を利用して、キャラの攻撃範囲の種類を事前に登録し、この情報をキャラの持つ情報として CharaData クラス内に設定できるようにします。
enum のみでスクリプトを作成する場合、using の宣言や、MonoBehaviour(モノビヘイビア) クラスの継承は不要です
そしてどのスクリプトからでも変数の代入なしで利用可能になります。

 enum ではゲーム内に登場させたい種類の情報を、列挙子(れっきょし)という形で種類を作成できます。
今回は、キャラの攻撃範囲の種類、という情報を AttackRangeTypeType という名前で作成し、その中に攻撃範囲の種類を登録しておきます。
これは追加可能な情報ですので、先々に'攻撃範囲の種類が増えても対応できます''。

 enum に登録した列挙子に応じて、キャラの攻撃範囲である AttackRangeArea ゲームオブジェクトの
BoxCollider コンポーネントの制御を行うように考えてみてください。



AttackRangeType.cs

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


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

 3つ以上の情報を管理する場合には、enum でその種類を登録しておくことをおすすめします
enum を利用する場合、その登録してある列挙子からしか情報を指定できませんので、
例えば、文字列と異なり、指定に際して打ち間違えが発生しませんので、不備の値が入ることも防ぐことが出来ます。

 ゲームの内容に応じた enum を考えて作成して運用します
プレイヤーの状態用(毒、混乱、痺れとか)、アイテムの種類(消耗品、武器、防具、など)、
ゲームの状態管理(ゲーム開始前、ゲーム中、ゲーム終了)など、非常に応用が利く機能です。



 なお enum では各列挙子に自動的に整数の番号が与えられます一番上から 0 で連番になっています
今回の場合であれば、Normal_0 には 0、Boss には 2 の数字が与えられています。

 この番号は見えない情報ですが、列挙子を int 型にキャストを行うことで取得して利用出来ます
下記の例の場合、enumValue には 1 が代入されます。

<enum の列挙子のキャスト>
int  enumValue = (int)AttackRangeType.Short;

 また、列挙子の宣言時に数字を指定して代入することも可能です。その場合には連番ではなく、指定した数値を取得出来ます。

<数字の代入の例(今回この方式は利用しません)>
EnemyType.cs
public enum EnemyType {
    Short = 10,
    Middle = 5,
    Long = 100
}

 上記のように代入されている場合には、列挙子を int 型にキャストすると、代入してある値が取得出来ます。
今回は数字の代入は行っていませんので一番上の列挙子には 0 から順番に採番されています。


4.CharaData を作成する


 味方キャラのデータを考える際には、1つのキャラごとに、どのような情報を持っていれば
ゲーム内で活用できるのかを考えてそれをスクリプトの変数として落とし込んでいきましょう。

 現在 CharaController クラスにある情報を元に、必要な情報を書き出してみてください。
それらはどのような型であればゲーム内で利用がしやすいか、一緒に考えておきます。


<CharaData クラスの参考イメージ>
 通し番号  =>  それぞれの型は?
 名前
 攻撃力
 攻撃までの待機時間
 コスト
 攻撃範囲
 攻撃回数
 詳細情報
 
 (可能なら)
 画像やアニメーションクリップ




CharaData.cs

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


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


5.[System.Serializable(シリアライザブル)]属性


 CharaData クラスの1行上には上記の宣言があります。[ ]で宣言された設定値は「属性」と呼ばれる情報になり、特別な意味を持ちます。

 今回利用している[System.Serializable]属性は、Systemに含まれている設定値であり、こちらを宣言することでクラスの情報をインスペクターに表示することが出来ます。
これを書き忘れてしまうと、インスペクターに CharaData の内容が表示されず、データをインスペクターから登録することが出来ません
[Serializable]属性を宣言する際に、using System; を宣言している場合には [Serializable] とだけ記述すれば適用されます。
using に宣言していない場合には [System.Serializable] と記述する必要があります。


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


 スクリプタブル・オブジェクトを作成するために必要な CharaDataSO スクリプトを作成します。
スクリプタブル・オブジェクト専用の ScriptableObject クラスを継承し、[CreateAssetMenu] 属性を記述することで作成可能になります。

 なお、CreateAssetMenu 属性は、エディターの拡張機能を付与するもので、スクリプタブル・オブジェクトに限ったものではありません。
この属性を付与すると、右クリックをしてメニューをした際に、新しく自分で作ったメニューを追加することができる機能です。
今回はこの機能を活用して、スクリプタブル・オブジェクトを作成する拡張機能を付与していることになります。
 


 スクリプタブル・オブジェクトでは、指定したデータを複数のデータとしてまとめて管理することが出来ます。
そのため、データベースとしての役割を果たすことが可能になっています。

 今回指定して管理したいデータはキャラのデータ群です。
そのため、スクリプタブル・オブジェクト内に必要な情報は以下の2つです。

 1.キャラ1体単位でのデータ(攻撃範囲、攻撃力、画像などのデータ群)を扱うクラス
 2.キャラのデータをまとめる List(リスト)

 1については、すでに CharaData クラスを作成済ですので、この情報を活用して、2の List を作成する部分がスクリプタブル・オブジェクトが必要になります。

 まずは最初にスクリプトを記述してから、内容を確認しましょう。



CharaDataSO.cs

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


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


7.CharaDataSO スクリプトの構造について


 処理の内容について、順番に確認していきます。

 1.キャラ1体単位でのデータ(攻撃範囲、攻撃力、画像などのデータ群)
 2.キャラのデータをまとめる List(リスト)


1.キャラ1体単位でのデータ(攻撃範囲、攻撃力、画像などのデータ群)

 CharaController スクリプトにて管理していたキャラの攻撃範囲や攻撃力を個別の変数ではなく、
1つのデータ単位として管理できるように、 CharaData クラスとして作成して、こちらにて管理を行うようにします。
利点は、1つの CharaData クラス内には1体分のキャラの全データが登録できることです。
そのため、Hp 用、攻撃力用というように変数を個別に作る必要はなく、CharaData の攻撃範囲、CharaData の攻撃力という形で CharaData を参照して利用できる部分です。
例えば、CharaData.attackPower と記述すれば、それはその CharaData クラスに登録されている attackPower の値を参照することになります。

 ここからはピリオドによる参照処理が増えていきますので、しっかりと処理を読み解いていきましょう

[System.Serializable]
public class CharaData
{
    public int charaNo;
    public int cost;
    public Sprite charaSprite;
    public string charaName;

    public int attackPower;
    public AttackRangeType attackRange;
    public float intervalAttackTime;
    public int maxAttackCount;

    [Multiline]
    public string discription;
    
    // TODO 他にもあれば追加

}

 このようにキャラ1体分に必要になるデータをクラスとしてまとめておくことで管理と利用が容易になります
また、キャラの情報を増やしたい(獲得できる Exp や属性など)場合には、この CharaData クラス内に 型と変数を追記すれば、好きなだけ増やすことも出来ます。
この教材でも順番に CharaData クラスに追記していきますので、それを確認するとわかりやすいでしょう。


2.キャラのデータをまとめる List(リスト)

 CharaData クラスにはキャラ1体分に必要な情報をまとめて登録できるようにしました。
このデータはキャラ1体分ですので、もしも複数のキャラのデータを用意して登録したい場合、
この CharaData クラスを複数用意して、それを管理するための変数が必要になります。

 大切なのは、CharaData 1つにつき、1つに変数を作っていない部分です。
List 型にして、1つの変数内に複数の CharaData が管理されていることによって、1つのデータ群として利用できるようにしています。

 こういった1つの同じデータ群をまとまったものをコレクションといいます。
C# にはコレクションを管理する方法として、Dictinary(ディクショナリー)List(リスト) があります。



 List クラスは <T> にジェネリック型(任意の型)を指定して、同じデータ型をまとめて管理するコレクション機能を持つクラスです。
配列と異なり、要素を自由に追加・削除できます。(要素数が可変する)
List はサイズ(長さ)が可変可能な配列のイメージです。

 List を利用する場合には配列と同様に初期化が可能ですが、Listでは初期化時に要素数の宣言が不要です

<配列の初期化>
  CharaData[] charaDatas = new CharaData[3];       // <=  要素数の宣言が必要

<List の初期化>
  List<CharaData> charaDatasList = new List<CharaData>();   // <=  要素数の宣言が不要

 そのため基本的には、予め要素数の確定しているデータを扱う場合には配列を、要素数が未確定であったり可変長であるデータについてはListを利用するように考えてください。


参考サイト
.net column様
【初期化の方法】C#で配列やリストを初期化するには?
https://www.fenet.jp/dotnet/column/language/713/



 public 修飾子にて List を宣言することで、インスペクター上でサイズの変更が可能です
例えばキャラのデータを3体分作って登録したい場合には、インスペクターで List のサイズを 3 に設定すれば
CharaData クラスが 3 つ、Element 0 〜 Element 2 として作成されますので、ここにキャラのデータを1体ずつ、合計3体分登録することが出来ます。

<CharaData クラスを扱う List>
    public List<CharaData> charaDatasList = new List<CharaData>();


8.CharaDataSO スクリプタブル・オブジェクトを作成する


 CharaDataSO スクリプトを元に CharaDataSO スクリプタブル・オブジェクトを作成します。
CharaDataSO スクリプトに用意してある CharaData 型の List である charaDatasList 変数がデータベースの役割を持っています。



 CharaData クラスは1つのデータ情報群を扱うことが出来ます。今回であればキャラの1体分の情報です。
そのため複数のキャラの情報を扱う必要がある今回のような場合には、その分だけ CharaData クラスを追加して作成しなければなりません

 それらを管理するために CharaData 型の List を作り、まとめて管理を出来るようにしています。
ここで大切なことは、1つ1つの別の変数に個別に CharaData が存在していたのではまとめて管理していることにはなりません
CharaDataのリストとはすなわち、CharaDataをまとめて扱っているデータの集合体になりますので、ここにデータベースとして役割を成立させることが出来ます



 最初に、スクリプタブル・オブジェクトを管理するためのフォルダを作成しておきます。
Project 内で右クリックをしてメニューを開き、Datas フォルダを作成してください。
この中に作成されたスクリプタブル・オブジェクトを入れて管理します。



 Unity の左上のメニューより、Assets => Create => Create CharaDataSO を選択します。
この機能が CharaDataSO クラスに宣言していただいた CreateAssetMenu 属性の機能になります。
新しく CharaDataSO というファイルが作成されます。名前はそのままで構いません。

 このアイコンの形が違うファイルがスクリプタブル・オブジェクトになります。
これはアセットとして取り扱われるようになる情報です。

 CharaDataSO スクリプタブル・オブジェクトを Datas フォルダへ移動してください。
今後もスクリプタブル・オブジェクトを作成したら、Datas フォルダ内で管理するようにします。


<フォルダ管理>



 早速スクリプタブル・オブジェクトを活用して、キャラのデータを登録していきましょう。



 作成された CharaDataSO スクリプタブル・オブジェクトを選択してインスペクターを確認します。
CharaDataSO スクリプトにて宣言した charaDatasList 変数がインスペクターに表示されて、 Sizeが 0 になっています。
これがスクリプタブル・オブジェクトの中身です。

CharaDataSO スクリプタブル・オブジェクト インスペクター画像


 Size に任意の数を入力すると、同数の Element が作成されます。これが List で管理する CharaData クラスの情報群になります。


CharaDataSO スクリプタブル・オブジェクト インスペクター画像




 まずはキャラのデータを5体分登録しておきたいと思います。
charaDatasList 変数の Size を 5 に変更してください。Element 0 〜 4 が下に作成されます。

 Element とは List の要素(中身)のことです。
そのため、Element 1つが CharaData 1つになります。Element の番号は 0 から始まります。これは List 内での要素番号(index)も表しています。

 以上のことから、1つの Element には1つの CharaData クラスの内容を設定できるようになっています。
このとき、CharaData クラスの上に [Serializable] 属性を宣言しているので、CharaData クラスの内容がインスペクターに表示されています。
[Serializable] 属性を活用することによって、インスペクターからキャラ用の情報を1体ずつ、CharaData 単位で登録出来るようになっています。



 インスペクター画像を参考に自由に設定を行ってみてください。
charaSprite 変数にはキャラ用のスプライト画像を1枚登録できます。


CharaDataSO スクリプタブル・オブジェクト インスペクター画像 参考



 なお、CharaData クラス内の一番最初に記述した変数が Element の部分に表示されますので、
string 型を最初に設定することで Element の表示が文字列表記に変わります。ここは好みで使い分けてください。
(string 型のときは各 Element の初期値は数字が入らずに空白になっています)


CharaDataSO スクリプタブル・オブジェクト インスペクター画像 参考



 各数値や画像は任意ですが、CharaNo の値だけは異なる番号で設定してください。できれば 0 から連番が理想です。
この番号はキャラ用の個体番号として利用する可能性がありますので、同じ番号を重複して設定してしまうと、同じ番号の個体が複数存在することになり、
番号によってキャラ用を特定することが出来なくなります。


9.攻撃範囲のデータベース用のスクリプタブル・オブジェクトを作成してみる


 攻撃範囲として AttackRangeType の enum を作成しました。
これによって種類を登録しておくことができるようになりましたが、例えば、Short という列挙子を利用するとなった場合に
実際の攻撃範囲としてどのような値を利用するかまではデータがありません。

 例えば Short や Long といった列挙子に合わせて、BoxCollider2D の大きさを変化できるようすれば、
列挙子を基準に検索することで利用できるデータがあれば便利です。

 この AttackRangeType をさらに活用して、攻撃範囲のデータだけをあつめたデータベースも作成してみましょう。

<AttackRangeSize クラスのイメージ>
 攻撃範囲の種類  AttackRangeType
 攻撃範囲の値   BoxCollider コンポーネントの Size には X と Y の情報があります。どのような型にすれば運用しやすいか考えてみましょう



 これを元にして、AttackRangeSize スクリプトを作成します。これは1つ分の攻撃範囲のデータです。
それらを List でまとめてデータベース化するための AttackRangeSizeSO スクリプトを作成して、スクリプタブル・オブジェクトを作成します。


AttackRangeSize.cs

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




 AttackRangeSize クラスを List で管理するためのスクリプタブル・オブジェクトです。


AttackRangeSizeSO.cs

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




 スクリプトが完成したら、スクリプタブル・オブジェクトの作成を行います。
Datas フォルダ内で右クリックをしてメニューを開き、Create => Create AttackRangeSizeSO を選択してください。


AttackRangeSizeSO スクリプタブル・オブジェクト

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


 attackRangeSizesList の Size は、AttackRangeType の列挙子の数と同じだけ作成し、それぞれに、各列挙子を登録します。
その上で、その種類の際の BoxCollider2D コンポーネントの Size を Size 変数に登録します。
後程、この情報を活用することで、CharaData クラスに登録された AttackRangeType を参照して、BoxCollider2D の Size を変更できるようします。
つまり、CharaData の AttackRangeType が Middle に設定されているキャラであれば、AttackRangeTypeSO 内の AttackRangeType が Middle の部分に登録されている
BoxCollider2D の Size をその設定値に変更する、というように運用します。



 ここで作成したスクリプタブル・オブジェクトは今後、選択したキャラを配置する際に利用します。
その場合には、攻撃範囲の種類(AttackRangeType)から、データベース内を検索して適応する情報を照合して使うという処理の実装も必要になります。

 データベースも作成しただけではゲーム内に運用できません。
どういった制御があればデータベースを活用していくことができるのか、そのロジックも考えていくことが大切です。

 ここではまだ自動的に反映する処理はありませんが、処理を書き換えて CharaController スクリプトに反映させることもできますので、
実際に CharaDataSO スクリプタブル・オブジェクトからデータが利用できるか試してみましょう。




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

 次は 手順17 −ロジックの学習◆ です。

コメントをかく


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

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

Menu



技術/知識(実装例)

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

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

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

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

レースゲーム(抜粋)

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

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

3D脱出ゲーム(抜粋)

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

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

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

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

VideoPlayer イベント連動の実装例

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

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

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

private



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

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