第10回 タイトルを付ける
ゲームを開始する前にタイトル画面を表示します。
空のゲームオブジェクトを作成し、名前をTitleとします。Titleゲームオブジェクトの位置はX 0 Y 0 Z 0とします。
次に、もう一つ空のゲームオブジェクトを作成し、名前をShooting Gameとして、Titleの子要素とします。名前を変更したら、メニューのComponent -> Rendering -> GUI Text
を選択し、GUIテキストを作成します。
Titleの子要素としたShooting Gameのパラメータを設定していきます。
Shooting Gameの位置はX 0.5 Y 0.65 Z 0とします。
Textを「Shooting Game」、Anchorを「upper
center」、Fonts
フォルダのSAM_5C_27TRG_をFontへ格納、**Font
Sizeを「60」**とします。
図10.1のように表示されているはずです。
図10.1:
次にShooting GameゲームオブジェクトをDuplicateして、名前をPress Xとします。 Shooting Gameゲームオブジェクトと変更する部分は位置のYを0.45、**Textを「Press X」**になります。
図10.2のように表示されれば問題ありません。
図10.2:
タイトル表示中はプレイヤーやWaveの作成を行わないようにします。
まずはPlayerの位置を X 0 Y -2.5 Z 0にしてプレハブを更新して、Playerゲームオブジェクトを削除してください。
空のゲームオブジェクトを作成し、名前をManagerとします。
次にManager.csをScripts
フォルダの中に作成します。
Manager.cs
using UnityEngine;
public class Manager : MonoBehaviour
{
// Playerプレハブ
public GameObject player;
// タイトル
private GameObject title;
void Start ()
{
// Titleゲームオブジェクトを検索し取得する
title = GameObject.Find ("Title");
}
void Update ()
{
// ゲーム中ではなく、Xキーが押されたらtrueを返す。
if (IsPlaying () == false && Input.GetKeyDown (KeyCode.X)) {
GameStart ();
}
}
void GameStart ()
{
// ゲームスタート時に、タイトルを非表示にしてプレイヤーを作成する
title.SetActive (false);
Instantiate (player, player.transform.position, player.transform.rotation);
}
public void GameOver ()
{
// ゲームオーバー時に、タイトルを表示する
title.SetActive (true);
}
public bool IsPlaying ()
{
// ゲーム中かどうかはタイトルの表示/非表示で判断する
return title.activeSelf == false;
}
}
スクリプトの実行順によっては Manager.cs で参照するはずの title を取得する前に Emitter.cs などで title にアクセスしようとしてしまいます。この時に NullReferenceException が発生してしまうかもしれません。この解決方法としてスクリプトの実行順を管理する「Script Execution Order」を利用します。
メニューの Edit -> Project Settings -> Script Execution Order を選択することで MonoManager がインスペクターに表示されます。元々スクリプトの実行順は保証されているわけではなく、ユーザーは実行順を把握することは出来ません。その状態が「Default Time」と表記されている部分です。
MonoManagerでは「Default Time」から特定のスクリプトを抜き出して実行タイミングを指定することが出来ます。
「+」ボタンを押して Manager を選択してみましょう。
すると、Managerの実行タイミングを表すフィールドが追加されるので、そのフィールドを摘んで「Default Time」の上に移動させます。
これで全スクリプトで一番最初に実行されるのは Manager.cs となりました。
Player.csを修正します。
Player.cs
using UnityEngine;
using System.Collections;
public class Player : MonoBehaviour
{
// Spaceshipコンポーネント
Spaceship spaceship;
IEnumerator Start ()
{
// Spaceshipコンポーネントを取得
spaceship = GetComponent<Spaceship> ();
while (true) {
// 弾をプレイヤーと同じ位置/角度で作成
spaceship.Shot (transform);
// ショット音を鳴らす
GetComponent<AudioSource>().Play();
// shotDelay秒待つ
yield return new WaitForSeconds (spaceship.shotDelay);
}
}
void Update ()
{
// 右・左
float x = Input.GetAxisRaw ("Horizontal");
// 上・下
float y = Input.GetAxisRaw ("Vertical");
// 移動する向きを求める
Vector2 direction = new Vector2 (x, y).normalized;
// 移動の制限
Move (direction);
}
// 機体の移動
void Move (Vector2 direction)
{
// 画面左下のワールド座標をビューポートから取得
Vector2 min = Camera.main.ViewportToWorldPoint(new Vector2(0, 0));
// 画面右上のワールド座標をビューポートから取得
Vector2 max = Camera.main.ViewportToWorldPoint(new Vector2(1, 1));
// プレイヤーの座標を取得
Vector2 pos = transform.position;
// 移動量を加える
pos += direction * spaceship.speed * Time.deltaTime;
// プレイヤーの位置が画面内に収まるように制限をかける
pos.x = Mathf.Clamp (pos.x, min.x, max.x);
pos.y = Mathf.Clamp (pos.y, min.y, max.y);
// 制限をかけた値をプレイヤーの位置とする
transform.position = pos;
}
// ぶつかった瞬間に呼び出される
void OnTriggerEnter2D (Collider2D c)
{
// レイヤー名を取得
string layerName = LayerMask.LayerToName(c.gameObject.layer);
// レイヤー名がBullet (Enemy)の時は弾を削除
if( layerName == "Bullet (Enemy)")
{
// 弾の削除
Destroy(c.gameObject);
}
// レイヤー名がBullet (Enemy)またはEnemyの場合は爆発
if( layerName == "Bullet (Enemy)" || layerName == "Enemy")
{
// Managerコンポーネントをシーン内から探して取得し、GameOverメソッドを呼び出す
FindObjectOfType<Manager>().GameOver();
// 爆発する
spaceship.Explosion();
// プレイヤーを削除
Destroy (gameObject);
}
}
}
Emitter.csを修正します。
Emitter.cs
using UnityEngine;
using System.Collections;
public class Emitter : MonoBehaviour
{
// Waveプレハブを格納する
public GameObject[] waves;
// 現在のWave
private int currentWave;
// Managerコンポーネント
private Manager manager;
IEnumerator Start ()
{
// Waveが存在しなければコルーチンを終了する
if (waves.Length == 0) {
yield break;
}
// Managerコンポーネントをシーン内から探して取得する
manager = FindObjectOfType<Manager>();
while (true) {
// タイトル表示中は待機
while(manager.IsPlaying() == false) {
yield return new WaitForEndOfFrame ();
}
// Waveを作成する
GameObject g = (GameObject)Instantiate (waves [currentWave], transform.position, Quaternion.identity);
// WaveをEmitterの子要素にする
g.transform.parent = transform;
// Waveの子要素のEnemyが全て削除されるまで待機する
while (g.transform.childCount != 0) {
yield return new WaitForEndOfFrame ();
}
// Waveの削除
Destroy (g);
// 格納されているWaveを全て実行したらcurrentWaveを0にする(最初から -> ループ)
if (waves.Length <= ++currentWave) {
currentWave = 0;
}
}
}
}
さて、ゲームを再生してみてください。タイトル表示中はプレイヤーも敵も出てませんか?Xキーを押すとゲームが開始されますか?
今回はここで終了です。つまずいてしまった方はプロジェクトファイルをダウンロードして新たな気持ちで次の回へ進みましょう。
第01回 スプライトとスプライトアニメーションの作成
第02回 プレイヤーの移動
第03回 プレイヤーから弾を撃つ
第04回 敵を作成しよう
第05回 当たり判定とアニメーションイベントとレイヤー
第06回 背景を作る
第07回 Wave型の仕組み作り
第08回 音をつける
第09回 プレイヤーの移動制限と様々な修正
第10回 タイトルを付ける
第11回 エネミーのHP、弾の攻撃力、アニメーションの追加
第12回 Waveを5個にする、スコアの実装
第01回 複数の解像度に対応する(黒帯を追加する)
第02回 複数の解像度に対応する(引き伸ばす)
第03回 タッチパネル対応
第04回 バーチャルジョイスティック対応