【タマあんどヒシガタ】 タマをゴールまで導くゲームです。 ゲーム画面をクリックするとジャンプ台を押すようにしてタマが動きます。 タマは壁、もしくはジャンプ台にぶつかると停止し、 ヒシガタとトゲにぶつかるとミスとなります。 ヒシガタはタマと同じようにジャンプ台を利用して動き、トゲとぶつかると両方消滅します。 トゲとヒシガタにぶつからないよう、タマをゴールまで導いてください。 身に付くこと :基本操作 対象 :初心者、Unityに初めて触る方 使用バージョン:Unity 2021.3 時間 :1.5時間
目次
■キャラクター作成
●タマ作成
最初にプレイヤーとして動かすタマを作成します。
タマの隣にジャンプ台がある時に画面をクリックすると、ジャンプ台に押したように動き出します。
まずは単純に、いきなり進み続けるように作成します。
▷タマ作成
球を作成してTransformをリセットし、青色設定したマテリアルを割り当てます。

・球作成
- ヒエラルキー左上の「+」をクリック
- 「3Dオブジェクト>スフィア」をクリック
- 「Tama」と入力し、ENTERキーを押す
・位置、回転、スケールリセット
- インスペクターのTransformにある縦の三点リーダーをクリック
- リセットをクリック
・マテリアル作成
- プロジェクトの「Assets」を右クリックし、「作成>マテリアル」をクリック
- 「matTama」と入力し、ENTERキーを押す
・マテリアル色設定
- インスペクターの「Main Maps>アルベド>カラーバー」をクリックし、色ダイアログの16進数を「0000FF」に設定
- 色ダイアログの右上にある「×」をクリック
・マテリアル割り当て
- プロジェクトの「matTama」をヒエラルキーの「Tama」にドラッグ&ドロップ
▷タマ制御
スクリプトを作成してタマに割り当て、「タマを30フレームに1メートルの速さで動く」ようにスクリプトを記述します。

・スクリプト作成
- プロジェクトの「Assets」を右クリックし、「作成>C# スクリプト」をクリック
- 「CharCtrl」と入力し、ENTERキーを押す
・スクリプト割り当て
- プロジェクトの「Assets>CharCtrl」をヒエラルキーの「Tama」にドラッグ&ドロップ
・スクリプト編集開始
- プロジェクトの「Assets>CharCtrl」をダブルクリック
・スクリプト変数定義
- クラス定義内の先頭に以下の変数を追加
float moveFrame = 30; // 1歩に何フレームかけるか
float frameCount = 0; // 経過フレーム数
・スクリプト関数定義
- クラス定義内の最後に以下の関数を追加
// 物理フレーム更新時に処理
void FixedUpdate()
{
}
・スクリプト処理
- FixedFrameに以下の処理を記述
// 1歩分のフレームが経過するまで何もしない
frameCount++;
if (frameCount < moveFrame) return;
// 経過フレーム数を0初期化する
frameCount = 0;
// 向いている方向に1メートル進む
transform.position += transform.forward;
・スクリプト保存
- CTRLを押しながらSキーを押す
・Unityに戻ってスクリプトをビルド
- Unityエディタのタイトルをクリック
▷タマ認識
▷動作確認
▷スクリプト記述例
「タマを30フレームに1メートルの速さで動く」スクリプトの記述例です。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharCtrl : MonoBehaviour
{
float moveFrame = 30; // 1歩に何フレームかけるか
float frameCount = 0; // 経過フレーム数
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
// 物理フレーム更新時に処理
void FixedUpdate()
{
// 1歩分のフレームが経過するまで何もしない
frameCount++;
if (frameCount < moveFrame) return;
// 経過フレーム数を0初期化する
frameCount = 0;
// 向いている方向に1メートル進む
transform.position += transform.forward;
}
}
●ヒシガタ作成
次に、同じように動くキャラクター「ヒシガタ」を作成します。
タマと衝突すると両方とも消滅します。
▷ヒシガタ作成
箱を作成してTransformを「位置(0, 0, 0)、回転(0, 45, 0)、スケール(0.7, 1, 0.7)」とし、赤色設定したマテリアルを割り当てます。

・箱作成
- ヒエラルキー左上の「+」をクリック
- 「3Dオブジェクト>キューブ」をクリック
・位置、回転、スケール設定
- インスペクターのTransformにある位置を(0, 0, 0)とする
- インスペクターのTransformにある回転を(0, 45, 0)とする
- インスペクターのTransformにあるスケールを(0.7, 1, 0.7)とする
・マテリアル作成
- プロジェクトの「Assets」を右クリックし、「作成>マテリアル」をクリック
- 「matHisi」と入力し、ENTERキーを押す
・マテリアル色設定
- インスペクターの「MainMaps>アルベド>カラーバー」をクリックし、色ダイアログの16進数を「FF0000」に設定
- 色ダイアログの右上にある「×」をクリック
・マテリアル割り当て
- プロジェクトの「matHisi」をヒエラルキーの「Cube」にドラッグ&ドロップ
▷ヒシガタ階層化
空オブジェクトを作成してTransformを「位置(1, 0, 0)、回転(0, 270, 0)、スケール(1, 1, 1)」とし、赤色の箱を子供とします。

・空オブジェクト作成
- ヒエラルキー左上の「+」をクリック
- 「空のオブジェクトを作成」をクリック
- 「Hisi」と入力し、ENTERキーを押す
・階層設定
- ヒエラルキーの「Cube」をヒエラルキーの「Hisi」にドラッグ&ドロップ
・位置、回転、スケール設定
- ヒエラルキーの「Hisi」をクリック
- インスペクターのTransformにある位置を(3, 0, 3)とする
- インスペクターのTransformにある回転を(0, 270, 0)とする
- インスペクターのTransformにあるスケールを(1, 1, 1)とする
▷ヒシガタ認識
▷ヒシガタ制御
ヒシガタにCharCtrlを割り当て、CharCtrlを「タマとヒシガタが衝突したら両方削除する」ように修正します。

・スクリプト割り当て
- プロジェクトの「Assets>CharCtrl」をヒエラルキーの「Hisi」にドラッグ&ドロップ
・スクリプト編集開始
- プロジェクトの「Assets>CharCtrl」をダブルクリック
・タグでオブジェクトを検索
- FixedUpdate関数の処理先頭に「タグ検索でタマとヒシガタの一覧を取得する」処理を追加
// タグ検索でタマとヒシガタの一覧を取得する
GameObject[] tamas = GameObject.FindGameObjectsWithTag("Player");
GameObject[] hisis = GameObject.FindGameObjectsWithTag("Enemy");
・衝突時にオブジェクトを削除
- 移動処理の後に「ヒシガタとタマが衝突したら両方削除」する処理を追加
// ヒシガタとタマが衝突したら両方削除
if (tamas.Length != 0 && hisis.Length != 0)
{
// タマとヒシガタの位置が同じだった場合
if (tamas[0].transform.position == hisis[0].transform.position)
{
// タマとヒシガタを削除
Destroy(tamas[0]);
Destroy(hisis[0]);
}
}
・スクリプト保存
- CTRLを押しながらSキーを押す
・Unityに戻ってスクリプトをビルド
- Unityエディタのタイトルをクリック
・プロジェクト保存
- CTRLを押しながらSキーを押す
▷動作確認
▷スクリプト記述例
「タマとヒシガタが衝突したら両方削除する」スクリプトの記述例です。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharCtrl : MonoBehaviour
{
float moveFrame = 30; // 1歩に何フレームかけるか
float frameCount = 0; // 経過フレーム数
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
// 物理フレーム更新時に処理
void FixedUpdate()
{
// タグ検索でタマとヒシガタの一覧を取得する
GameObject[] tamas = GameObject.FindGameObjectsWithTag("Player");
GameObject[] hisis = GameObject.FindGameObjectsWithTag("Enemy");
// 1歩分のフレームが経過するまで何もしない
frameCount++;
if (frameCount < moveFrame) return;
// 経過フレーム数を0初期化する
frameCount = 0;
// 向いている方向に1メートル進む
transform.position += transform.forward;
// ヒシガタとタマが衝突したら両方削除
if (tamas.Length != 0 && hisis.Length != 0)
{
// タマとヒシガタの位置が同じだった場合
if (tamas[0].transform.position == hisis[0].transform.position)
{
// タマとヒシガタを削除
Destroy(tamas[0]);
Destroy(hisis[0]);
}
}
}
}
●トゲ作成
動かないヒシガタである「トゲ」を作成します。
タマやヒシガタが衝突するとトゲとともに消滅します。
▷トゲ作成
同じ構成なので、ヒシガタをコピーして利用します。
名前を変更して位置を(1, 0, 3)とし、色を紫に変更します。

・オブジェクト複製
- ヒエラルキーの「Hisi」をクリック
- CTRLを押しながらDキーを押す
・オブジェクト名変更
- F2キーを押す
- 「Toge」と入力し、ENTERキーを押す
・位置設定
- インスペクターのTransformにある位置を(1, 0, 3)に設定
・アセット複製
- プロジェクトの「Assets>matHisi」をクリック
- CTRLを押しながらDキーを押す
・アセット名変更
- F2キーを押す
- 「matToge」を入力し、ENTERキーを押す
・マテリアル色設定
- インスペクターの「Main Maps>アルベド>カラーバー」をクリックし、色ダイアログの16進数を「FF00FF」に設定
- 色ダイアログの右上にある「×」をクリック
・マテリアル割り当て
- ヒエラルキーの「Toge」左にある「▶」をクリック
- プロジェクトの「Assets>matToge」をヒエラルキーの「Toge>Cube」にドラッグ&ドロップ
▷トゲ認識
▷トゲ制御
トゲは動かないのでCharCtrlスクリプトを外します。
「自分(タマかヒシガタ)がトゲと衝突した時に両方消える」ようにCharCtrlを修正します。
結果、トゲとタマ、もしくはトゲとヒシガタが衝突した時に両方が消えるようになります。

・コンポーネント削除
- ヒエラルキーの「Toge」をクリック
- インスペクターの「Char Ctrl(スクリプト)」にある縦の三点リーダーをクリック
- 「コンポーネントを削除」をクリック
・スクリプト編集開始
- プロジェクトの「Assets>CharCtrl」をダブルクリック
・スクリプト関数定義
- FixedUpdate関数の後に以下の関数を追加
// 指定位置にあるオブジェクトを取得
GameObject GetObject(Vector3 pos)
{
// 指定位置にオブジェクトが無いのでNULLを返す
return null;
}
・スクリプト関数作成
- GetObject内の「指定位置にオブジェクトが無いのでNULLを返す」前に以下の処理を追加
// 小数点誤差を補正
float posX = Mathf.Floor(pos.x + 0.5f);
float posZ = Mathf.Floor(pos.z + 0.5f);
// 処理対象とするタグの一覧を作成
string[] tags = new string[] { "Toge" };
// タグを1つ1つ処理
for (int i = 0; i < tags.Length; i++)
{
// 指定タグのオブジェクト一覧を取得
GameObject[] objs = GameObject.FindGameObjectsWithTag(tags[i]);
// オブジェクトを1つ1つ処理
for (int j = 0; j < objs.Length; j++)
{
// 指定位置に存在するオブジェクトを返す
Vector3 objPos = objs[j].transform.position;
if (objPos.x == posX && objPos.z == posZ) return objs[j];
}
}
・スクリプト関数呼び出し
- FixedUpdate関数の最後に以下の処理を記述
// 重なったオブジェクトを取得
GameObject obj = GetObject(transform.position);
// トゲと重なったら自分とトゲを削除
if (obj != null && obj.tag == "Toge")
{
Destroy(gameObject);
Destroy(obj);
}
・スクリプト保存
- CTRLを押しながらSキーを押す
・Unityに戻ってスクリプトをビルド
- Unityエディタのタイトルをクリック
・プロジェクト保存
- CTRLを押しながらSキーを押す
▷動作確認
ヒシガタがトゲと衝突したら両方消えるかどうか、プレビューして確認します。

・プレビュー開始
- エディタ上中央の「▶」をクリック
・動作確認
- ヒシガタとトゲがぶつかったら両方消えることを確認
・プレビュー終了
- エディタ上中央の「▶」をクリック
・プレビュー開始
- エディタ上中央の「▶」をクリック
・プレビュー一時停止
- エディタ上中央の「||」をクリック
・仮編集
- ヒエラルキーの「Toge」をクリック
- インスペクターのTransformにある位置を(0, 0, 2)に設定
・プレビュー再開
- エディタ上中央の「||」をクリック
・動作確認
- タマとトゲがぶつかったら両方消えることを確認
・プレビュー終了
- エディタ上中央の「▶」をクリック
▷スクリプト記述例
「トゲと重なったら自分とトゲを削除する」スクリプトの記述例です。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharCtrl : MonoBehaviour
{
float moveFrame = 30; // 1歩に何フレームかけるか
float frameCount = 0; // 経過フレーム数
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
// 物理フレーム更新時に処理
void FixedUpdate()
{
// タグ検索でタマとヒシガタの一覧を取得する
GameObject[] tamas = GameObject.FindGameObjectsWithTag("Player");
GameObject[] hisis = GameObject.FindGameObjectsWithTag("Enemy");
// 1歩分のフレームが経過するまで何もしない
frameCount++;
if (frameCount < moveFrame) return;
// 経過フレーム数を0初期化する
frameCount = 0;
// 向いている方向に1メートル進む
transform.position += transform.forward;
// ヒシガタとタマが衝突したら両方削除
if (tamas.Length != 0 && hisis.Length != 0)
{
// タマとヒシガタの位置が同じだった場合
if (tamas[0].transform.position == hisis[0].transform.position)
{
// タマとヒシガタを削除
Destroy(tamas[0]);
Destroy(hisis[0]);
}
}
// 重なったオブジェクトを取得
GameObject obj = GetObject(transform.position);
// トゲと重なったら自分とトゲを削除
if (obj != null && obj.tag == "Toge")
{
Destroy(gameObject);
Destroy(obj);
}
}
// 指定位置にあるオブジェクトを取得
GameObject GetObject(Vector3 pos)
{
// 小数点誤差を補正
float posX = Mathf.Floor(pos.x + 0.5f);
float posZ = Mathf.Floor(pos.z + 0.5f);
// 処理対象とするタグの一覧を作成
string[] tags = new string[] { "Toge" };
// タグを1つ1つ処理
for (int i = 0; i < tags.Length; i++)
{
// 指定タグのオブジェクト一覧を取得
GameObject[] objs = GameObject.FindGameObjectsWithTag(tags[i]);
// オブジェクトを1つ1つ処理
for (int j = 0; j < objs.Length; j++)
{
// 指定位置に存在するオブジェクトを返す
Vector3 objPos = objs[j].transform.position;
if (objPos.x == posX && objPos.z == posZ) return objs[j];
}
}
// 指定位置にオブジェクトが無いのでNULLを返す
return null;
}
}
■オブジェクト作成
●床作成
タマやヒシガタが動き回る「地面」を作成します。
▷床作成
平面を作成してTransformの位置を(4.5, -0.5, 4.5)とし、グレーのチェッカー模様に設定したマテリアルを割り当てます。

・平面作成
- ヒエラルキー左上の「+」をクリック
- 「3Dオブジェクト>平面」をクリック
- 「Floor」と入力し、ENTERキーを押す
・位置設定
- インスペクターのTransformにある位置を(4.5, -0.5, 4.5)とする
・マテリアル作成
- プロジェクトの「Assets」を右クリックし、「作成>マテリアル」をクリック
- 「matFloor」と入力し、ENTERキーを押す
・マテリアルテクスチャ設定
- インスペクターの「Main Maps>アルベド」左の◎をクリックし、「Default-Checker-Gray」をダブルクリック
・マテリアル色設定
- インスペクターの「Main Maps>アルベド>カラーバー」をクリックし、色ダイアログの16進数を「CCCCCC」に設定
- 色ダイアログの右上にある「×」をクリック
・マテリアルタイリング設定
- インスペクターの「Main Maps>タイリング」を(5, 5)に設定
・マテリアル割り当て
- プロジェクトの「matFloor」をヒエラルキーの「Floor」にドラッグ&ドロップ
・プロジェクト保存
- CTRLを押しながらSキーを押す
●壁作成
タマとヒシガタの移動を停止させる壁を作成します。
移動先に壁がある場合、推進力を失って停止します。
▷壁作成
箱を作成してTransformの位置を(0, 0, 5)とし、オレンジ色の「Background」に設定したマテリアルを割り当てます。

・箱作成
- ヒエラルキー左上の「+」をクリック
- 「3Dオブジェクト>キューブ」をクリック
- 「Block」と入力し、ENTERキーを押す
・位置、回転、スケール設定
- インスペクターのTransformにある位置を(0, 0, 5)とする
- インスペクターのTransformにある回転を(0, 0, 0)とする
- インスペクターのTransformにあるスケールを(1, 1, 1)とする
・マテリアル作成
- プロジェクトの「Assets」を右クリックし、「作成>マテリアル」をクリック
- 「matBlock」と入力し、ENTERキーを押す
・マテリアルテクスチャ設定
- インスペクターの「Main Maps>アルベド」左の◎をクリックし、「Background」をダブルクリック
・マテリアル色設定
- インスペクターの「Main Maps>アルベド>カラーバー」をクリックし、色ダイアログの16進数を「FF8000」に設定
- 色ダイアログの右上にある「×」をクリック
・マテリアル割り当て
- プロジェクトの「matBlock」をヒエラルキーの「Block」にドラッグ&ドロップ
▷壁認識
▷壁衝突
移動先に壁かジャンプ台があったら移動停止するようにCharCtrlを修正します。

・スクリプト編集開始
- プロジェクトの「Assets>CharCtrl」をダブルクリック
・スクリプト関数修正
- FixedUpdate関数内の「経過フレーム数を0初期化する」の後に以下のスクリプトを追加
// 移動先に壁かジャンプ台があったら移動終了
GameObject moveObj = GetObject(transform.position + transform.forward);
if (moveObj != null && (moveObj.tag == "Block" || moveObj.tag == "Jump"))
{
return;
}
・スクリプト関数修正
- GetObject関数内の「処理対象とするタグの一覧を作成」を以下のスクリプトのように修正
// 処理対象とするタグの一覧を作成
string[] tags = new string[] { "Toge", "Block" };
・スクリプト保存
- CTRLを押しながらSキーを押す
・Unityに戻ってスクリプトをビルド
- Unityエディタのタイトルをクリック
・プロジェクト保存
- CTRLを押しながらSキーを押す
▷動作確認
●ジャンプ台作成
画面をクリックするとジャンプ台の隣にあるタマとヒシガタを押すようにして移動を開始させます。
また、壁と同じように移動先にジャンプ台があると移動を停止します。
▷ジャンプ台作成
箱を作成してTransformの位置を(0, 0, -1)とし、緑色の「Knob」に設定したマテリアルを割り当てます。

・箱作成
- ヒエラルキー左上の「+」をクリック
- 「3Dオブジェクト>キューブ」をクリック
- 「Jump」と入力し、ENTERキーを押す
・位置、回転、スケール設定
- インスペクターのTransformにある位置を(0, 0, -1)とする
- インスペクターのTransformにある回転を(0, 0, 0)とする
- インスペクターのTransformにあるスケールを(1, 1, 1)とする
・マテリアル作成
- プロジェクトの「Assets」を右クリックし、「作成>マテリアル」をクリック
- 「matJump」と入力し、ENTERキーを押す
・マテリアルテクスチャ設定
- インスペクターの「Main Maps>アルベド」左の◎をクリックし、「Knob」をダブルクリック
・マテリアル色設定
- インスペクターの「Main Maps>アルベド>カラーバー」をクリックし、色ダイアログの16進数を「008000」に設定
- 色ダイアログの右上にある「×」をクリック
・マテリアル割り当て
- プロジェクトの「matJump」をヒエラルキーの「Jump」にドラッグ&ドロップ
▷ジャンプ台認識
▷ジャンプ台制御
ゲーム画面をクリックした時にタマかヒシガタが隣にあったら移動を開始するようCharCtrlを修正します。

・スクリプト編集開始
- プロジェクトの「Assets>CharCtrl」をダブルクリック
・スクリプト変数追加
- 「1歩に何フレームかけるか」の変数の前に以下の変数を追加
bool moveFlag = false; // 移動中かどうか
・スクリプト関数修正
- Update関数内に以下のスクリプトを記述
// マウスクリックされた場合
if (Input.GetMouseButtonUp(0) == true)
{
}
・スクリプト関数修正
- Update関数内の「マウスクリックされた場合」の条件内に以下のスクリプトを記述
// 現在の回転値を保存
Quaternion rot = transform.rotation;
// 上下左右のオブジェクトを取得
transform.rotation = Quaternion.Euler(0, 0, 0);
GameObject objU = GetObject(transform.position + transform.forward);
GameObject objD = GetObject(transform.position - transform.forward);
GameObject objL = GetObject(transform.position - transform.right);
GameObject objR = GetObject(transform.position + transform.right);
・スクリプト関数修正
- Update関数内の「上下左右のオブジェクトを取得」処理の後に以下のスクリプトを記述
// 指定方向がジャンプ台かつ逆側がなにも無いかトゲの場合、オブジェクトをジャンプ台の逆側に向ける
float dir = -1;
if (objU != null && objU.tag == "Jump" && (objD == null || objD.tag == "Toge")) dir = 180;
if (objD != null && objD.tag == "Jump" && (objU == null || objU.tag == "Toge")) dir = 0;
if (objL != null && objL.tag == "Jump" && (objR == null || objR.tag == "Toge")) dir = 90;
if (objR != null && objR.tag == "Jump" && (objL == null || objL.tag == "Toge")) dir = 270;
if (dir != -1)
{
moveFlag = true;
rot = Quaternion.Euler(0, dir, 0);
}
// 回転値を設定
transform.rotation = rot;
・スクリプト関数修正
- FixedUpdate関数内の「経過フレーム数を0初期化する」処理の後に以下のスクリプトを記述
// 移動していない場合は終了
if (moveFlag == false) return;
・スクリプト関数修正
- FixedUpdate関数内の「移動先に壁かジャンプ台があったら移動終了」条件内のreturn前に以下のスクリプトを記述
moveFlag = false;
・スクリプト関数修正
- GetObject関数内の「処理対象とするタグの一覧を作成」を以下のスクリプトのように修正
// 処理対象とするタグの一覧を作成
string[] tags = new string[] { "Toge", "Block", "Jump" };
・スクリプト保存
- CTRLを押しながらSキーを押す
・Unityに戻ってスクリプトをビルド
- Unityエディタのタイトルをクリック
・プロジェクト保存
- CTRLを押しながらSキーを押す
▷動作確認
▷スクリプト記述例
「クリックしたらタマが動き出す」スクリプトの記述例です。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharCtrl : MonoBehaviour
{
bool moveFlag = false; // 移動中かどうか
float moveFrame = 30; // 1歩に何フレームかけるか
float frameCount = 0; // 経過フレーム数
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
// マウスクリックされた場合
if (Input.GetMouseButtonUp(0) == true)
{
// 現在の回転値を保存
Quaternion rot = transform.rotation;
// 上下左右のオブジェクトを取得
transform.rotation = Quaternion.Euler(0, 0, 0);
GameObject objU = GetObject(transform.position + transform.forward);
GameObject objD = GetObject(transform.position - transform.forward);
GameObject objL = GetObject(transform.position - transform.right);
GameObject objR = GetObject(transform.position + transform.right);
// 指定方向がジャンプ台かつ逆側がなにも無いかトゲの場合、オブジェクトをジャンプ台の逆側に向ける
float dir = -1;
if (objU != null && objU.tag == "Jump" && (objD == null || objD.tag == "Toge")) dir = 180;
if (objD != null && objD.tag == "Jump" && (objU == null || objU.tag == "Toge")) dir = 0;
if (objL != null && objL.tag == "Jump" && (objR == null || objR.tag == "Toge")) dir = 90;
if (objR != null && objR.tag == "Jump" && (objL == null || objL.tag == "Toge")) dir = 270;
if (dir != -1)
{
moveFlag = true;
rot = Quaternion.Euler(0, dir, 0);
}
// 回転値を設定
transform.rotation = rot;
}
}
// 物理フレーム更新時に処理
void FixedUpdate()
{
// タグ検索でタマとヒシガタの一覧を取得する
GameObject[] tamas = GameObject.FindGameObjectsWithTag("Player");
GameObject[] hisis = GameObject.FindGameObjectsWithTag("Enemy");
// 1歩分のフレームが経過するまで何もしない
frameCount++;
if (frameCount < moveFrame) return;
// 経過フレーム数を0初期化する
frameCount = 0;
// 移動していない場合は終了
if (moveFlag == false) return;
// 移動先に壁かジャンプ台があったら移動終了
GameObject moveObj = GetObject(transform.position + transform.forward);
if (moveObj != null && (moveObj.tag == "Block" || moveObj.tag == "Jump"))
{
moveFlag = false;
return;
}
// 向いている方向に1メートル進む
transform.position += transform.forward;
// ヒシガタとタマが衝突したら両方削除
if (tamas.Length != 0 && hisis.Length != 0)
{
// タマとヒシガタの位置が同じだった場合
if (tamas[0].transform.position == hisis[0].transform.position)
{
// タマとヒシガタを削除
Destroy(tamas[0]);
Destroy(hisis[0]);
}
}
// 重なったオブジェクトを取得
GameObject obj = GetObject(transform.position);
// トゲと重なったら自分とトゲを削除
if (obj != null && obj.tag == "Toge")
{
Destroy(gameObject);
Destroy(obj);
}
}
// 指定位置にあるオブジェクトを取得
GameObject GetObject(Vector3 pos)
{
// 小数点誤差を補正
float posX = Mathf.Floor(pos.x + 0.5f);
float posZ = Mathf.Floor(pos.z + 0.5f);
// 処理対象とするタグの一覧を作成
string[] tags = new string[] { "Toge", "Block", "Jump" };
// タグを1つ1つ処理
for (int i = 0; i < tags.Length; i++)
{
// 指定タグのオブジェクト一覧を取得
GameObject[] objs = GameObject.FindGameObjectsWithTag(tags[i]);
// オブジェクトを1つ1つ処理
for (int j = 0; j < objs.Length; j++)
{
// 指定位置に存在するオブジェクトを返す
Vector3 objPos = objs[j].transform.position;
if (objPos.x == posX && objPos.z == posZ) return objs[j];
}
}
// 指定位置にオブジェクトが無いのでNULLを返す
return null;
}
}
●ゴール作成
タマと衝突するとクリアになる、「ゴール」を作成します。
▷ゴール作成
パーティクルシステムを作成して位置を(0, 0, 3)とし、その場に留まって揺らめくエフェクトとします。

・パーティクルシステム作成
- ヒエラルキー左上の「+」をクリック
- 「エフェクト>パーティクルシステム」をクリック
- 「Goal」と入力し、ENTERキーを押す
・位置、回転、スケール設定
- インスペクターのTransformにある位置を(0, 0, 3)とする
- インスペクターのTransformにある回転を(-90, 0, 0)とする
- インスペクターのTransformにあるスケールを(1, 1, 1)とする
・メインモジュール設定
メインモジュールを以下のように設定します。
- インスペクターにあるParticle Systemの「メインモジュール>継続時間」を(1)に設定
- インスペクターにあるParticle Systemの「メインモジュール>開始時の生存期間」を(1)に設定
- インスペクターにあるParticle Systemの「メインモジュール>開始時の速度」を(0)に設定
- インスペクターにあるParticle Systemの「メインモジュール>開始時の色」右にあるカラーバーをクリックし、色ダイアログの16進数を(FFFF00)に設定
- 色ダイアログの右上にある「×」をクリック
- インスペクターにあるParticle Systemの「メインモジュール>重力モディファイア」を(-0.1)に設定
・形状モジュール設定
形状モジュールを以下のように設定します。
- 形状モジュールの「形状」を(スフィア)に設定
- 形状モジュールの「半径」を(0.3)に設定
- 形状モジュールの「半径の厚さ」を(0)に設定
▷ゴール認識
▷ゴール制御
タマとゴールが衝突した時にタマとヒシガタの動きが止まるようにCharCtrlを修正します。

・スクリプト編集開始
- プロジェクトの「Assets>CharCtrl」をダブルクリック
・スクリプト変数追加
- 「移動中かどうか」の変数の前に「プレイ中かどうか」の変数を作成
bool playFlag = true; // プレイ中かどうか
・スクリプト関数修正
- FixedUpdate関数の先頭に以下の処理を追加
// プレイ中でない場合は即関数を終了
if (playFlag == false) return;
・スクリプト関数修正
- FixedUpdate関数の最後に以下の処理を追加
// 自分がタマで、かつゴールと重なった場合
else if (tag == "Player" && obj != null && obj.tag == "Goal")
{
// タマのplayFlagをfalseに設定
playFlag = false;
}
・スクリプト関数修正
- GetObject関数内の「処理対象とするタグの一覧を作成」を以下のスクリプトのように修正
// 処理対象とするタグの一覧を作成
string[] tags = new string[] { "Toge", "Block", "Jump", "Goal" };
・スクリプト保存
- CTRLを押しながらSキーを押す
・Unityに戻ってスクリプトをビルド
- Unityエディタのタイトルをクリック
・プロジェクト保存
- CTRLを押しながらSキーを押す
▷動作確認
▷スクリプト記述例
「タマとゴールが衝突したらタマの動きが止まる」スクリプトの記述例です。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharCtrl : MonoBehaviour
{
bool playFlag = true; // プレイ中かどうか
bool moveFlag = false; // 移動中かどうか
float moveFrame = 30; // 1歩に何フレームかけるか
float frameCount = 0; // 経過フレーム数
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
// マウスクリックされた場合
if (Input.GetMouseButtonUp(0) == true)
{
// 現在の回転値を保存
Quaternion rot = transform.rotation;
// 上下左右のオブジェクトを取得
transform.rotation = Quaternion.Euler(0, 0, 0);
GameObject objU = GetObject(transform.position + transform.forward);
GameObject objD = GetObject(transform.position - transform.forward);
GameObject objL = GetObject(transform.position - transform.right);
GameObject objR = GetObject(transform.position + transform.right);
// 指定方向がジャンプ台かつ逆側がなにも無いかトゲの場合、オブジェクトをジャンプ台の逆側に向ける
float dir = -1;
if (objU != null && objU.tag == "Jump" && (objD == null || objD.tag == "Toge")) dir = 180;
if (objD != null && objD.tag == "Jump" && (objU == null || objU.tag == "Toge")) dir = 0;
if (objL != null && objL.tag == "Jump" && (objR == null || objR.tag == "Toge")) dir = 90;
if (objR != null && objR.tag == "Jump" && (objL == null || objL.tag == "Toge")) dir = 270;
if (dir != -1)
{
moveFlag = true;
rot = Quaternion.Euler(0, dir, 0);
}
// 回転値を設定
transform.rotation = rot;
}
}
// 物理フレーム更新時に処理
void FixedUpdate()
{
// プレイ中でない場合は即関数を終了
if (playFlag == false) return;
// タグ検索でタマとヒシガタの一覧を取得する
GameObject[] tamas = GameObject.FindGameObjectsWithTag("Player");
GameObject[] hisis = GameObject.FindGameObjectsWithTag("Enemy");
// 1歩分のフレームが経過するまで何もしない
frameCount++;
if (frameCount < moveFrame) return;
// 経過フレーム数を0初期化する
frameCount = 0;
// 移動していない場合は終了
if (moveFlag == false) return;
// 移動先に壁かジャンプ台があったら移動終了
GameObject moveObj = GetObject(transform.position + transform.forward);
if (moveObj != null && (moveObj.tag == "Block" || moveObj.tag == "Jump"))
{
moveFlag = false;
return;
}
// 向いている方向に1メートル進む
transform.position += transform.forward;
// ヒシガタとタマが衝突したら両方削除
if (tamas.Length != 0 && hisis.Length != 0)
{
// タマとヒシガタの位置が同じだった場合
if (tamas[0].transform.position == hisis[0].transform.position)
{
// タマとヒシガタを削除
Destroy(tamas[0]);
Destroy(hisis[0]);
}
}
// 重なったオブジェクトを取得
GameObject obj = GetObject(transform.position);
// トゲと重なったら自分とトゲを削除
if (obj != null && obj.tag == "Toge")
{
Destroy(gameObject);
Destroy(obj);
}
// 自分がタマで、かつゴールと重なった場合
else if (tag == "Player" && obj != null && obj.tag == "Goal")
{
// タマのplayFlagをfalseに設定
playFlag = false;
}
}
// 指定位置にあるオブジェクトを取得
GameObject GetObject(Vector3 pos)
{
// 小数点誤差を補正
float posX = Mathf.Floor(pos.x + 0.5f);
float posZ = Mathf.Floor(pos.z + 0.5f);
// 処理対象とするタグの一覧を作成
string[] tags = new string[] { "Toge", "Block", "Jump", "Goal" };
// タグを1つ1つ処理
for (int i = 0; i < tags.Length; i++)
{
// 指定タグのオブジェクト一覧を取得
GameObject[] objs = GameObject.FindGameObjectsWithTag(tags[i]);
// オブジェクトを1つ1つ処理
for (int j = 0; j < objs.Length; j++)
{
// 指定位置に存在するオブジェクトを返す
Vector3 objPos = objs[j].transform.position;
if (objPos.x == posX && objPos.z == posZ) return objs[j];
}
}
// 指定位置にオブジェクトが無いのでNULLを返す
return null;
}
}
■ゲーム仕上げ
●ステージ作成
▷カメラ設定
ステージを見渡せるようカメラの姿勢を調整します。

・位置、回転、スケール設定
- ヒエラルキーの「Main Camera」をクリック
- インスペクターのTransformにある位置を(4.5, 16, -2)とする
- インスペクターのTransformにある回転を(65, 0, 0)とする
- インスペクターのTransformにあるスケールを(1, 1, 1)とする
・アスペクト比設定
- ゲームビュー上メニュー左から3番目ドロップダウンをクリック
- 「9:16 Aspect」をクリック、無い場合は以降を操作
- 左下の「+」をクリック
- ドロップダウン「タイプ」をクリックし、「アスペクト比」をクリック
- 幅&高さの「X」に(9)、「Y」に(16)を入力
- 「OK」をクリック
▷プレハブ化
▷単一オブジェクト配置
▷トゲ配置
トゲ配置用の空オブジェクトにトゲを格納し、複製しながら配置していきます。

・空オブジェクト作成
- ヒエラルキー左上の「+」をクリック
- 「空のオブジェクトを作成」をクリック
- 「Toges」と入力し、ENTERキーを押す
・位置スケールリセット
- インスペクターのTransformにある縦の三点リーダーをクリック
- リセットをクリック
・階層設定
- ヒエラルキーの「Toge」をヒエラルキーの「Toges」にドラッグ&ドロップ
・位置設定
- インスペクターのTransformにある位置を(1, 0, 0)とする
・複製、位置設定
- ヒエラルキーの「Toges>Toge」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(9, 0, 1)とする
- ヒエラルキーの「Toges>Toge (1)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(8, 0, 9)とする
- ヒエラルキーの「Toges>Toge (2)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(0, 0, 8)とする
- ヒエラルキーの「Toges>Toge (3)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(5, 0, 4)とする
- ヒエラルキーの「Toges>Toge (4)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(6, 0, 2)とする
- ヒエラルキーの「Toges>Toge (5)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(5, 0, 2)とする
- ヒエラルキーの「Toges>Toge (6)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(4, 0, 2)とする
・階層を閉じる
- ヒエラルキーの「Toges」左にある「▼」をクリック
▷ジャンプ台配置
ジャンプ台配置用の空オブジェクトにジャンプ台を格納し、複製しながら配置していきます。

・空オブジェクト作成
- ヒエラルキー左上の「+」をクリック
- 「空のオブジェクトを作成」をクリック
- 「Jumps」と入力し、ENTERキーを押す
・位置スケールリセット
- インスペクターのTransformにある縦の三点リーダーをクリック
- リセットをクリック
・階層設定
- ヒエラルキーの「Jump」をヒエラルキーの「Jumps」にドラッグ&ドロップ
・位置設定
- インスペクターのTransformにある位置を(1, 0, 9)とする
・複製、位置設定
- ヒエラルキーの「Jumps>Jump」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(0, 0, 1)とする
- ヒエラルキーの「Jumps>Jump (1)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(8, 0, 0)とする
- ヒエラルキーの「Jumps>Jump (2)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(9, 0, 8)とする
- ヒエラルキーの「Jumps>Jump (3)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(6, 0, 9)とする
- ヒエラルキーの「Jumps>Jump (4)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(5, 0, 9)とする
- ヒエラルキーの「Jumps>Jump (5)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(4, 0, 9)とする
- ヒエラルキーの「Jumps>Jump (6)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(3, 0, 4)とする
・階層を閉じる
- ヒエラルキーの「Jumps」左にある「▼」をクリック
▷壁配置
壁配置用の空オブジェクトに壁を格納し、複製しながら配置していきます。

・空オブジェクト作成
- ヒエラルキー左上の「+」をクリック
- 「空のオブジェクトを作成」をクリック
- 「Blocks」と入力し、ENTERキーを押す
・位置スケールリセット
- インスペクターのTransformにある縦の三点リーダーをクリック
- リセットをクリック
・階層設定
- ヒエラルキーの「Block」をヒエラルキーの「Blocks」にドラッグ&ドロップ
・位置設定
- インスペクターのTransformにある位置を(0, 0, 9)とする
・複製、位置設定
- ヒエラルキーの「Blocks>Block」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(0, 0, 7)とする
- ヒエラルキーの「Blocks>Block (1)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(0, 0, 6)とする
- ヒエラルキーの「Blocks>Block (2)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(0, 0, 5)とする
- ヒエラルキーの「Blocks>Block (3)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(0, 0, 4)とする
- ヒエラルキーの「Blocks>Block (4)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(0, 0, 3)とする
- ヒエラルキーの「Blocks>Block (5)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(0, 0, 2)とする
- ヒエラルキーの「Blocks>Block (6)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(0, 0, 0)とする
- ヒエラルキーの「Blocks>Block (7)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(2, 0, 0)とする
- ヒエラルキーの「Blocks>Block (8)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(3, 0, 0)とする
- ヒエラルキーの「Blocks>Block (9)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(4, 0, 0)とする
- ヒエラルキーの「Blocks>Block (10)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(5, 0, 0)とする
- ヒエラルキーの「Blocks>Block (11)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(6, 0, 0)とする
- ヒエラルキーの「Blocks>Block (12)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(7, 0, 0)とする
- ヒエラルキーの「Blocks>Block (13)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(9, 0, 0)とする
- ヒエラルキーの「Blocks>Block (14)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(9, 0, 2)とする
- ヒエラルキーの「Blocks>Block (15)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(9, 0, 3)とする
- ヒエラルキーの「Blocks>Block (16)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(9, 0, 4)とする
- ヒエラルキーの「Blocks>Block (17)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(9, 0, 5)とする
- ヒエラルキーの「Blocks>Block (18)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(9, 0, 6)とする
- ヒエラルキーの「Blocks>Block (19)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(9, 0, 7)とする
- ヒエラルキーの「Blocks>Block (20)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(9, 0, 9)とする
- ヒエラルキーの「Blocks>Block (21)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(7, 0, 9)とする
- ヒエラルキーの「Blocks>Block (22)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(3, 0, 9)とする
- ヒエラルキーの「Blocks>Block (23)」をクリック
- CTRLを押しながらDキーを押す
- インスペクターのTransformにある位置を(2, 0, 9)とする
・階層を閉じる
- ヒエラルキーの「Blocks」左にある「▼」をクリック
・プロジェクト保存
- CTRLを押しながらSキーを押す
●ミスUI作成
▷ミスUI作成
タマがトゲ、もしくはヒシガタに衝突した時に「Miss!」というテキストを表示します。

・テキスト作成
- ヒエラルキー左上の「+」をクリック
- 「UI>テキスト – TextMeshPro」をクリック
- 「TextMiss」と入力し、ENTERキーを押す
・TextMeshPro準備
- TMP Importerの「Import TMP Essentials」をクリック
- TMP Importerの右上にある「×」をクリック
・UI位置設定
- インスペクターのRectTransform左上にあるアイコンをクリック
- アンカープリセットの右下にある「Stretch – Stretch」をダブルクリック
- インスペクターのRectTransform右上にある上下左右を全て「0」に設定
・表示文字設定
- インスペクターのTextMeshProにある「Text Input>テキスト入力」を「Miss!」に設定
・文字装飾
- インスペクターのTextMeshProにある「Main Settings>Font Style」にある「B」をクリック
- インスペクターのTextMeshProにある「Main Settings>Audo Size」のチェックボックスをクリック
- インスペクターのTextMeshProにある「Main Settings>Vertex Color」右のカラーバーをクリックし、色ダイアログの16進数を「FF0000」に設定
- 色ダイアログの右上にある「×」をクリック
- インスペクターの「Shader>Outline」左にあるチェックボックスをクリック
- インスペクターの「Shader>Outline」にある「Thickness」を「0.2」に設定
・オブジェクト無効化
- インスペクター上部の左上にあるチェックボックスをクリック
▷ミス制御
タマがトゲ、もしくはヒシガタとぶつかった時にミステキストを表示するようにCharCtrlを修正します。

・スクリプト編集開始
- プロジェクトの「Assets>CharCtrl」をダブルクリック
・スクリプト変数定義
- クラス定義内の先頭に以下の変数を追加
public GameObject TextMiss; // ミスUI
・スクリプト関数修正
- 「タマとヒシガタの位置が同じだった場合」の条件内にある「タマとヒシガタを削除」前に「ミスUIを有効化」する以下の処理を追加
// ミスUIを有効化
TextMiss.SetActive(true);
・スクリプト関数修正
- 「トゲと重なったら自分とトゲを削除」の条件内の先頭に「自分がタマだった場合、ミスUIを有効化」する処理を追加
// 自分がタマだった場合、ミスUIを有効化
if (tag == "Player")
{
TextMiss.SetActive(true);
}
・スクリプト保存
- CTRLを押しながらSキーを押す
・Unityに戻ってスクリプトをビルド
- Unityエディタのタイトルをクリック
・オブジェクト割り当て
- ヒエラルキーの「Tama」をクリック
- ヒエラルキーの「Canvas>TextMiss」をインスペクターの「Char Ctrl (スクリプト)>Text Miss」右の参照ボックスにドラッグ&ドロップ
・プロジェクト保存
- CTRLを押しながらSキーを押す
▷動作確認
●クリアUI作成
▷クリアUI作成
タマがゴールに衝突した時に「Clear!」というテキストを表示します。

・オブジェクト複製
- ヒエラルキーの「Canvas>TextMiss」をクリック
- CTRLを押しながらDキーを押す
・オブジェクト名変更
- F2キーを押して「TextClear」と入力し、ENTERを押す
・オブジェクト有効化
- インスペクター上部の左上にあるチェックボックスをクリック
・表示文字設定
- インスペクターのTextMeshProにある「Text Input>テキスト入力」を「Clear!」に設定
・文字装飾
- インスペクターのTextMeshProにある「Main Settings>Vertex Color」右のカラーバーをクリックし、色ダイアログの16進数を「00FFFF」に設定
- 色ダイアログの右上にある「×」をクリック
・オブジェクト無効化
- インスペクター上部の左上にあるチェックボックスをクリック
▷クリア制御
タマがゴールと衝突した時にクリアテキストを表示するようにCharCtrlを修正します。

・スクリプト編集開始
- プロジェクトの「Assets>CharCtrl」をダブルクリック
・スクリプト変数定義
- ミスUIの定義の後に以下の変数を追加
public GameObject TextClear; // クリアUI
・スクリプト関数修正
- 「自分がタマで、かつゴールと重なった場合」の条件内処理の先頭に以下のスクリプトを追加
// クリアUIを有効化
TextClear.SetActive(true);
・スクリプト保存
- CTRLを押しながらSキーを押す
・Unityに戻ってスクリプトをビルド
- Unityエディタのタイトルをクリック
・オブジェクト割り当て
- ヒエラルキーの「Tama」をクリック
- ヒエラルキーの「Canvas>TextClear」をインスペクターの「Char Ctrl (スクリプト)>Text Clear」右の参照ボックスにドラッグ&ドロップ
・プロジェクト保存
- CTRLを押しながらSキーを押す
▷動作確認
完成!
コメント