この記事はシミュレーションRPGの作り方講座の第三回です。
前回の記事では、シミュレーションRPGのフィールドになる足場ブロックと移動不可能な水辺ブロックを作成しました。マテリアルやシェーダーの設定も無事完了です。
前回の記事:
前回に引き続きマップを3Dで実装します。マップは自動生成で作成していきます
用意した2種類のブロックプレハブを用いて実際のフィールドマップを構成していきましょう。
マップの作成方法
シミュレーションRPGのマップを作るにあたって、どのような方法でマス目となるブロックを配置するかについては先に考えておかなければいけません。
今回は以下の2パターンで考えてみます。
- シーン内にあらかじめブロックを並べておく方法
- ゲーム開始時にランダムでマップを生成する方法
ひとつひとつ順番にどのような特徴があるかを考えてみます。
1.シーン内にあらかじめブロックを並べておく方法
手作業で1つ1つブロックのインスタンスを配置してマップを構成しておくというものです。
長所
- ステージのイメージに合った地形を作る事ができる
- ゲームに戦略性を持たせやすい
- キャラクターの初期位置を決めやすい
短所
- ブロックを手作業で並べるのはそこそこ時間がかかる
一般的な手法ではありますが、マップが大きかったり数が多いと作成するのがやや大変になります。
そのゲーム用のマップエディタのような物を自前で用意できれば以降の負担は減少しますが、それを用意する事は初心者には難しいです。
2.ゲーム開始時にランダムでマップを生成する方法
長所
- ブロックのプレハブさえ用意しておけば自動的に並べてくれる
- ランダム性が生まれるのでプレイする度に違った楽しみ方ができる
短所
- 場合によっては遊びにくい地形が出来上がってしまう
- ゲーム開始までマップが存在しないのでキャラクターを配置しにくい
こちらはスクリプト作成が必須になりますが、手作業で並べる必要がありません。
今回の講座ではこちらの手法を使います。
(1の方法での作成を希望される方はそうしていただいても大丈夫ですが、配置したブロックの並びをゲーム開始時に読み込む処理を書く必要があります。)
マップ管理クラス(マップマネージャ)の作成
マップのランダム生成を行うためのスクリプトの作成を開始します。
ここで作成するスクリプトは後々マップに関するさまざまな処理を追加していくので、マップマネージャとして用意する事になります。
まずはScriptsフォルダにMapManagerスクリプトを作成します。
スクリプトは基本的にシーン内の何らかオブジェクトにアタッチされていないと動作しませんので、アタッチするためのオブジェクトも一緒に用意します。
この先GameManagerスクリプトなど〇〇Manager系を追加する予定がありますので、それらをひとまとめにする意味で「Managers」オブジェクトを新規作成します。
このManagersオブジェクトはゲーム中において表示させないので3Dオブジェクト等にする必要はありません。作成時に[Create Empty]をクリックすれば最小限の機能のみを持った空のオブジェクトを作成できます。
オブジェクトの作成を終えたら先ほどのMapManagerスクリプトをドラッグアンドドロップでアタッチしましょう。
また、既にシーン上に存在するブロックインスタンスは全て削除してしまいましょう。プレハブから生成するためシーン内に残しておく必要がないからです。
このような状態になっていればOKです。
マップをランダムに作成するスクリプト
それではスクリプトに処理を書き込んでいきます。
ProjectビューからMapManagerスクリプトをダブルクリックする等の方法でスクリプトエディタを立ち上げましょう。
ゲーム開始時にブロックをランダムに生成して配置するコードは以下になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class MapManager : MonoBehaviour { // オブジェクト・プレハブ(インスペクタから指定) public Transform blockParent; // マップブロックの親オブジェクトのTransform public GameObject blockPrefab_Grass; // 草ブロック public GameObject blockPrefab_Water; // 水場ブロック // 定数定義 public const int MAP_WIDTH = 9; // マップの横幅 public const int MAP_HEIGHT = 9; // マップの縦(奥行)の幅 private const int GENERATE_RATIO_GRASS = 90; // 草ブロックが生成される確率 void Start() { // ブロック生成位置の基点となる座標を設定 Vector3 defaultPos = new Vector3 (0.0f, 0.0f, 0.0f); // x:0.0f y:0.0f z:0.0f のVector3変数defaultPosを宣言 defaultPos.x = -(MAP_WIDTH / 2); // x座標の基点 defaultPos.z = -(MAP_HEIGHT / 2); // z座標の基点 // ブロック生成処理 for (int i = 0; i < MAP_WIDTH; i++) {// マップの横幅分繰り返し処理 for (int j = 0; j < MAP_HEIGHT; j++) {// マップの縦幅分繰り返し処理 // ブロックの場所を決定 Vector3 pos = defaultPos; // 基点の座標を元に変数posを宣言 pos.x += i; // 1個目のfor分の繰り返し回数分x座標をずらす pos.z += j; // 2個目のfor分の繰り返し回数分z座標をずらす // ブロックの種類を決定 int rand = Random.Range (0, 100); // 0~99の中から1つランダムな数字を取得 bool isGrass = false; // 草ブロック生成フラグ(初期状態はfalse) // 乱数値が草ブロック確率値より低ければ草ブロックを生成する if (rand < GENERATE_RATIO_GRASS) isGrass = true; // オブジェクトを生成 GameObject obj; // 生成するオブジェクトの参照 if (isGrass) {// 草ブロック生成フラグ:ON obj = Instantiate (blockPrefab_Grass, blockParent); // blockParentの子に草ブロックを生成 } else {// 草ブロック生成フラグ:OFF obj = Instantiate (blockPrefab_Water, blockParent); // blockParentの子に水場ブロックを生成 } // オブジェクトの座標を適用 obj.transform.position = pos; } } } void Update() { } } |
このスクリプトのどの部分がどのような働きをしているかについて大まかに解説します。
番号 | 名称・処理内容 | 説明 |
1 | usingディレクティブ | 簡単に言えば、Unityがデフォルトで用意している各種機能を呼び出すのに必要になります。 |
2 | クラス宣言部 | MapManagerという名前のクラスを宣言する部分です。{}で囲われた部分にこのクラスの処理を書いていきます。 |
3 | メンバ変数宣言 |
そのクラス内全体で使用できる変数を宣言します。publicで宣言するとUnityのInspectorビューから値をセットできます。 |
4 | 定数宣言 | constで変数を宣言すると値が変化しない定数として扱えます。ゲーム中に変化しない数値(ここではマップの大きさ)は定数で用意します。 |
5 | Startメソッド(関数) |
Startメソッドはこのクラスがゲーム内で有効になった時に1度だけ{}内の処理を実行します。 このMapManagerはシーン内に最初から存在するオブジェクト(Managers)にアタッチされている為、ゲーム開始時に一度呼び出されるという事になります。 |
6 | Updateメソッド(関数) | ゲーム中1フレームごとに中身の処理が実行されるメソッドです。このスクリプトでは使用しないので消去しても大丈夫です。 |
7 | ブロックの基点を設定 |
ブロック作成処理を始める前に、作成したブロックを配置する為の基点となる位置を用意しています。 マップ中央のブロックがちょうどposition値が全て0の位置になるようにしたいので、ブロック1個目の生成位置は端にずらします。 |
8 | 生成するブロック個数分の繰り返し処理 | マップの大きさが9×9なので、ブロック生成処理を9×9回繰り返す為のfor文です。 |
9 | ブロックの生成位置決定 | 7で用意した基点座標をもとにこれから生成するブロックの位置を決めます。 |
10 | ブロックの種類を決める |
草ブロックか水場ブロックのどちらを生成するかをランダムで決定する処理です。 Random.Rangeメソッドを使用する事で指定した範囲内でランダムな数値を得る事ができます。この乱数をもとに草ブロックが選ばれる確率と照らし合わせます。 |
11 | オブジェクト生成 |
Instantiateメソッドでプレハブからオブジェクトをシーン内に生成します。10で決定した種類のブロックを対象とします。 生成時に親となるオブジェクトのTransformを指定しています(ここではblockParent)。指定しなくても生成はできますが、沢山生成するとHierarchyビューを圧迫するので親は用意しておく事にします。 |
変数やメソッド、for文の詳細な仕組みについてはここでは説明を省略します。
以下の記事に分かりやすい解説がありますのでそちらを参照してください。
Inspectorでオブジェクト・プレハブの参照を用意する
次に上記スクリプトを動作させる為に必要なパラメータをセットしていきます。
シーン内のManagersオブジェクトを選択し、Inspectorビューを確認してください。
まずは2種類のブロックプレハブの参照を与えましょう。Projectビューからのドラッグアンドドロップで可能です。
BlockPrefab_Grassに草ブロック(Block_Grass)を、BlockPrefab_Waterに水場ブロック(Block_Water)をセットすればOKです。
ブロックの親オブジェクトを用意する
スクリプト解説の11番で触れましたが、スクリプトからブロックを生成する際管理しやすくするためにそれぞれの親となるオブジェクトを1つシーン内に用意しておきましょう。
[Create Empty]で空のオブジェクトを作成し、名前をMapBlocksとします。Transformのpositon値が全て0である事を確認してください。そしてこのオブジェクトのTransformを先ほどのMapManagerのBlockParentに渡します。
こちらはHierarchyビューからのドラッグアンドドロップになります。オブジェクトごと渡せばその中のTransfromコンポーネントが自動的にセットされます。
実行!
これでゲームを実行すれば9×9の大きさでマップが自動生成されるようになっていると思います。
まだカメラが固定なので、Sceneビューを動かしてマップを確認してみましょう。
サンプルの設定では草ブロックと水場ブロックがそれぞれ9:1の割合でランダムに作られています。
実行をやりなおすと毎回違った地形になる事が確認できます。
まとめ
これでこのゲームの舞台となるマップを用意する事が出来ました。
- スクリプト(クラス)は主にメンバ変数とメソッドで構成され、メソッドの部分に処理を書いていきます。
- Startメソッドはシーン開始時に一度だけ呼び出されるメソッドです。(スクリプトがアタッチされているオブジェクトが途中から生成される場合、その生成時に呼び出されます。)
- Updateメソッドは毎フレーム常に実行されるメソッドです。60fpsの場合1秒間に60回実行されます。
- クラス内のpublicなメンバ変数はInspectorから値・参照をセットできます。
次回はキャラクターを作成して配置するところまで行います。また、3Dマップ上で2D画像を取り込んでゲーム化するための方法も学びます。
次の記事:
コメント