このパートで行うこと
いよいよこのパートからローグライクゲームの作成を開始しよう。
さっそくこのパートでは、プレイヤーや敵が動き回るためのフィールドの作成に入る。
フィールドにはさまざま物体があるが、その中でもまずは床・外壁・出口をフィールドに配置していく。
これらは必ず決まった位置に配置されるもので、もちろん動いたりもしない。シンプルな物体だ。
壁には外壁と内壁の二種類あるが、ここではフィールドの外周に必ず表示する壁のことを外壁と呼ぶ。
このシンプルな物体を配置することでこのゲームにおける物体を配置するまでの流れをおさえよう。
前回の記事はこちら。
⇒ パート①:イントロダクション@2Dローグライク公式tutorial解説【Unity2018】
※この記事は、unity公式チュートリアル「2D Roguelike tutorial」を解説した連載記事です。
この連載記事共通の事項(実施している環境等)は以下の記事を参照ください。
⇒ 表紙:2Dローグライク公式tutorial解説【Unity2018】
床のプレハブを作成する
まずはじめにフィールドに配置される床を作成する。
プレハブを作るために適当なSceneを作成しよう。
(Projectビューの)Sceneなど任意のフォルダで右クリック -> Create -> Scene
次に床のゲームオブジェクトを作成する。
新規にゲームオブジェクトを作成し、「Floor1」という名前をつけておこう。
(Hierarchyで)右クリック -> Create Empty
画像を表示する
いま作成した「Floor1」ゲームオブジェクトで画像を表示するには、
SpriteRenderer
コンポーネントをアタッチ*1し、 SpriteRenderer#Sprite
に床の画像を設定しよう。
画像はすでに「Sprites」フォルダの下に「Scavengers_SpriteSheet」というスプライトシートで用意されている。
スプライトシートというのは、一つの画像を分割して複数のスプライトとして扱うものだ。スプライトアトラスとかそういった名前で呼ばれることもある。
「Scavengers_SpriteSheet」スプライトシートが複数のスプライトに分割されていることを確認しよう。
床画像は 「Scavengers_SpriteSheet_32」として用意されている。 SpriteRenderer#Sprite
に設定しよう。
描画順を指定する
床はフィールドに配置するどの物体よりも奥に描画したい(床の上にプレイヤーやアイテムがのるイメージ)ため、
ここでは描画順を一番奥に設定していく。
描画順はSpriteRenderer#SortingLayer
を使って設定することができる。
SortingLayerにはデフォルトで設定されている「Default」の他に「Floor」「Items」「Units」の3つの値が設定されており*2、このゲームではこの3つのみ使う。
SortingLayerはプルダウンの上に表示されている値ほど奥に、下に表示されている値ほど手前に描画される。
このゲームでは「Default」は使用しないので一番奥に描画される「Floor」を設定しよう。
プレハブにする
作成した「Floor1」ゲームオブジェクトはプレハブにする。
プレハブとはゲームオブジェクトのひな形・テンプレートのようなもので、今設定したコンポーネントやその設定値をそのまま保存し、複製して使うことができるというものだ。
ゲームオブジェクトをプレハブにするには、Projectビューにゲームオブジェクトをドラックすればよい。
床に限らずこの先フィールドに配置する物体はすべてプレハブにしてから使うことになるので、この手順はおさえておこう。
プレハブもいくつか作るので「Prefabs」フォルダにまとめておくと整理しやすいゾ。
複数の床を作成する
実は床用の画像は全部で8種類用意されており、「Scavengers_SpriteSheet_32 〜 39」まであるので、 先ほど作成した「Floor1」を元に画像だけを変えたプレハブも作成しよう。
「Floor1」はすでにプレハブとして保存されているので、ヒエラルキーに残った「Floo1」というゲームオブジェクトを「Floor2」へリネームし流用する。
先程と同じように画像の指定を SpriteRenderer#Sprite
で行おう。
今度は「Scavengers_SpriteSheet_33」というスプライトを指定する。
設定が終わったら「Floor2」も忘れずにプレハブにしておくこと。
同じ要領で残りの画像分、床プレハブを作成してしまおう。
プレハブ名 | Sprite |
---|---|
Floor1 | Scavengers_SpriteSheet_32 |
Floor2 | Scavengers_SpriteSheet_33 |
Floor3 | Scavengers_SpriteSheet_34 |
Floor4 | Scavengers_SpriteSheet_35 |
Floor5 | Scavengers_SpriteSheet_36 |
Floor6 | Scavengers_SpriteSheet_37 |
Floor7 | Scavengers_SpriteSheet_38 |
Floor8 | Scavengers_SpriteSheet_39 |
作成が完了するとこのように8種類の床プレハブが出来ているはずだ。
外壁のプレハブを作成する
今度は外壁のプレハブを作成する。
外壁は3種類の画像が用意されているので、床の時と同様に3つのプレハブを作成する。
床のプレハブを元に外壁用の画像を指定してプレハブ「OuterWall1」「OuterWall2」「OuterWall3」を作成しよう。
プレハブ名 | Sprite |
---|---|
OuterWall1 | Scavengers_SpriteSheet_25 |
OuterWall2 | Scavengers_SpriteSheet_26 |
OuterWall3 | Scavengers_SpriteSheet_28 |
床と外壁を配置する
ここからはScriptの実装にも入っていく。
BoardManager.cs
新規にスクリプトを作成して、「BoardManager」と名前をつけよう。
(Projectビューの任意のフォルダで)右クリック -> Create -> C# Scriot
このクラスは、その名の通りフィールドを管理するクラスとなる。
このパートでは、ゲーム起動時に床・外壁などの物体を配置する処理を追加する。
using UnityEngine; public class BoardManager : MonoBehaviour { public int columns = 8; public int rows = 8; public GameObject[] floorTiles; //床プレハブ public GameObject[] outerWallTiles; //外壁プレハブ private Transform boardHolder; private void BoardSetup() { this.boardHolder = new GameObject("Board").transform; //外壁 -1 からスタートする for (int x = -1; x < columns + 1; x++) { for (int y = -1; y < rows + 1; y++) { //床の配置 GameObject toInstantiate = this.floorTiles[Random.Range(0, this.floorTiles.Length)]; if (x == -1 || x == columns || y == -1 || y == rows) //外壁 { toInstantiate = this.outerWallTiles[Random.Range(0, this.outerWallTiles.Length)]; } GameObject instance = Instantiate(toInstantiate, new Vector3(x, y, 0), Quaternion.identity, this.boardHolder) as GameObject; } } } public void SetupScene() { BoardSetup(); } }
上記のコードは、先ほど作成した床と外壁のプレハブを必要な数だけ複製しフィールドに配置している。
このゲームではフィールド全体のサイズは固定であり、10×10マスである。
このパートの最初に記載した通り、壁には内壁と外壁の2種類存在するが、 ここでいう外壁は必ずフィールド全体を囲むように配置される。
よってフィールド全体のサイズは10×10だが外壁分減らした8×8の範囲、プレイヤーが動き回れることになる。
コードのはじめでフィールドのサイズは8×8と定義されているが、これはプレイヤーが動き回れる範囲のサイズであることに注意しよう。
public int columns = 8; public int rows = 8;
BoardSetup()
はフィールドに床と外壁の配置を行っている。
これまでに床のプレハブは8種類、外壁プレハブは3種類作成していた。
画像違いのプレハブの中からランダムに1つのプレハブ決め、そのプレハブを複製=Instantiate
してできたゲームオブジェクトをBoardゲームオブジェクトの子ゲームオブジェクトにすることで、配置を行っている。
フィールドの外周に配置される外壁は8×8のプレイヤー移動可能エリアの外側に1マス分囲むように配置される。
プレイヤー移動可能エリアの一番左下のマスを(0,0)として考えると、外壁はxかy座標が-1か8のマスに配置されることになる。
床はフィールド全体に配置されるものだが、外壁を配置するところには必要はない。
なぜなら、外壁の画像は床も含んだ不透過な画像となっているからだ。
よって床は外壁を配置した以外のマスに配置するようになっている。
最後に SetupScene()
を用意している。
これは、BoardSetup()
がprivateアクセスなのに対してpublicなメソッドとなっており、
外部からはこの SetupScene()
のみ呼べば良いようにしておく。
ゲームを起動して確認する
配置がうまくいっているか実際にゲームを起動してテストしたいが、現在はスクリプトを書いただけで BoardManager
はどこからも呼び出されていない。
ここでは BoardManager
をテストするための準備を行う。
GameManager
新規に「GameManager」というスクリプトを作成する。
using UnityEngine; public class GameManager : MonoBehaviour { public static GameManager instance; private BoardManager boardScript; void Awake() { if (instance == null) { instance = this; } else if (instance != this) { Destroy(gameObject); } DontDestroyOnLoad(gameObject); boardScript = GetComponent<BoardManager>(); InitGame(); } void InitGame() { boardScript.SetupScene(); } }
GameManager
はその名の通りこのゲーム全体を管理する役目を担う。
このクラスは複数存在しない方が都合がよいため、このクラスのインスタンスを保持するstaticフィールドを用意して唯一のインスタンスにしておく。
public static GameManager instance;
外部からこのクラスを使う場合は、
GameManager.instance.InitGame();
というように静的にアクセスすることができる。
また、ゲーム起動中「GameManager」ゲームオブジェクトは常に存在していてほしいため、DontDestroyOnLoad
にして破棄されないようにしている。
Awake()
の最後でInitGame()
を呼び、その中で先ほど作成したBoardManager#SetupScene()
を呼ぶ。
今度はunityエディタ側に戻り、「GameManager」というゲームオブジェクトを新規で作成しよう。
そしてここまでで作成した、「GameManager」、「BoardManager」スクリプトをアタッチする。
「BoardManager」は、複数種類ある床と外壁プレハブからランダムに1つのプレハブを選び配置する動きだった。
「BoardManager」には選ばされる対象の床と壁のプレハブがまだ伝わっていないのでここで設定しよう。
インスペクターの右上にある錠前アイコンをクリックすると表示が切り替わらなくなり、選択しやすい。
「GameManager」ゲームオブジェクトもこれまでと同様にプレハブにして保存しておき、ヒエラルキーに残ったゲームオブジェクトは削除しておこう。
Loader
最後に「Loader」というスクリプトを新規に作成する。 このクラスは「GameManager」プレハブが存在しないならば作成する、という役割を担う。
using UnityEngine; public class Loader : MonoBehaviour { public GameObject gameManager; void Awake() { if (GameManager.instance == null) { Instantiate(gameManager); } } }
「Loader」スクリプトを使うために、シーンに自動で作成されている「Main Camera」ゲームオブジェクトに「Loader」スクリプトをアタッチしておこう。
Loader#GameManager
に「GameManager」プレハブを設定する。
カメラを調整する
ここまでで「BoardManager」をテストするための準備が整ったので、ゲームを起動してみよう。
フィールドが表示されているようだがやけに右上に表示されてしまっているので、カメラの位置を調整しよう。
カメラの位置は、「Main Camera」ゲームオブジェクトの Transform#position
で設定できる。x,y = 3.5 にするとちょうどよいだろう。
また、背景色が青くなっていてこのゲームのイメージにはあわないのでCamera#Background
で黒色に変更しておく。
このように画面中央にフィールドが配置されればOKだ。
出口のプレハブを作成する
次はステージを切り替える出口のプレハブを作成しよう。
出口についても床と同じように静止画像を表示するので、先ほど作成した床プレハブを元に「Exit」ゲームオブジェクトを作成する。
スプライトは、「Scavengers_SpriteSheet_20」と言う出口用の画像を設定しておこう。
また床よりも手前に出口の画像を表示するため、SortingLayerは床プレハブに設定した「Floor」よりも手前に描画される「Items」をしよう。
ここまでの設定を載せておこう。床プレハブと同じ要領なので特に難しいところはない。
確認ができたらいつものようにプレハブにしてヒエラルキーからは削除しておくこと。
出口を配置する
今作成した出口プレハブを配置するコードを書いていこう。
using UnityEngine; public class BoardManager : MonoBehaviour { public int columns = 8; public int rows = 8; public GameObject[] floorTiles; //床プレハブ public GameObject[] outerWallTiles; //外壁プレハブ public GameObject exit; //出口プレハブ private Transform boardHolder; private void BoardSetup() { this.boardHolder = new GameObject("Board").transform; //外壁 -1 からスタートする for (int x = -1; x < columns + 1; x++) { for (int y = -1; y < rows + 1; y++) { //床の配置 GameObject toInstantiate = this.floorTiles[Random.Range(0, this.floorTiles.Length)]; if (x == -1 || x == columns || y == -1 || y == rows) //外壁 { toInstantiate = this.outerWallTiles[Random.Range(0, this.outerWallTiles.Length)]; } GameObject instance = Instantiate(toInstantiate, new Vector3(x, y, 0), Quaternion.identity, this.boardHolder) as GameObject; } } } public void SetupScene() { BoardSetup(); //出口配置 Instantiate (exit, new Vector3 (columns - 1, rows - 1, 0f), Quaternion.identity); } }
追加した部分はBoardManager#SetupScene()
で出口プレハブを複製してできたゲームオブジェクトを配置する処理のみだ。
出口はプレイヤーが動きまわれるエリアの一番右上のマスに固定で配置される。 このマスは0始まりであるので、一番右上のマス(7,7)に出口が配置される。
「GameManager」プレハブのBoardManager#exit
に作成した出口プレハブを設定する。
ゲーム起動して出口が追加されたことを確認しよう。
次回予告
今回は、もともと位置が決まっていて、動かない物体の床・外壁・出口の配置までを行った。
次のパートでは、フィールド内のランダムな位置に配置される、アイテムや内壁の配置を行う。
スクリプトも含めて配置の流れは今回でつかめたと思うので、違いはランダムな位置に配置することだけだ。
⇒ 続きのパートはこちら。