【ゲームノウハウ】誰でも簡単!ゲートギミックの作り方と活用方法【Unity編】
みなさん、こんにちは!
個人ゲームクリエイターの黒菱優山です。
みなさんは、ゲームを作るときに、どんなギミックを考えて作っていますか?
今回は、既存のゲームでもよく見る”ゲートギミック”の作り方と、その活用例をいくつかご紹介していきたいと思います。
というのも、このギミックはシンプルで汎用性が高いため、とてもコスパが良いんです。
ゲームを作っていると、何だかんだで複雑になってしまう…なんてことがよくあったりします。
複雑になるにつれて、バグ発生の原因になることや、エラーの原因が特定しづらいなど、余計な手間が増えることにも繋がるので、シンプルであるに越したことはありません。
私のようにプログラムを書くことが苦手な人や、ギミックを考えることが得意ではない人に、特にオススメの内容になっていると思います。
というわけで、今回は誰でも簡単に出来る、汎用性の高いゲートギミックの作り方と、それでどんな”遊び”が生まれるのか、既存ゲームでの実例を交えて、この3つのパターンに分けて紹介していきます。
”一度きり”しか発動しないギミック
”条件を満たすとき”だけ発動するギミック
条件を満たすと”元に戻る”ギミック
また、今回はUnity(バージョン:2020.3.24f1)を使用していますが、ギミックの考え方や原理は、他のバージョンやUE4でも変わらないので、同じように考えてもらえれば、特に問題ないと思います。
正直、内容がとても長くなってしまったので、読むのが面倒くさいという方は、目次を活用するか、もしくは本日投稿したこちらの動画を見ることをオススメします。
※なお、作り方とは書いていますが、Unityの基本的な操作方法に関しては、大分省略しています。もし、操作方法で分からない部分がある場合は、色んなサイトに情報が掲載されておりますので、ご自身で調べてみてください。
ゲートギミックについて
そもそも、ゲートギミックって何?と思う方が多いのではないでしょうか?
それもそのはず。このゲートギミックという言葉は、正式名称ではなく、私が勝手に使っている言葉です。
ということで、今回は物体の当たり判定を切り替えて、ルートの開放と遮断を制御するギミックのこと、としておきましょう。
既存ゲームであるギミックをいくつか例に挙げてみると、
・攻撃すると、崩れて通れるようになる壁
・片側からのみ開けられるショートカット用の扉
・何回か踏むと、崩れて消える床
なんかが、このギミックを活用すれば作ることができます。
ギミックを作るときのポイント
ゲートギミックに関わらず、ギミックを作る前に、以下の4つのポイントを明確にしておくと、よりスムーズにギミックを実装することができます。
ギミックのゲームでの役割
例:プレイヤーの妨害、新アクションのチュートリアルギミックの発動条件
例:ギミックに近づいたとき、特定のアイテムを持っているときギミック発動による状況変化
例:ルートの開通、妨害トラップの除去ギミック処理の動作手順
例:センサーの無効化→当たり判定の有効化
これらのポイントを押さえておくと、情報の整理や見える化ができるので、汎用性が高い、もしくは需要のあるギミックの実装や、他の類似したゲームとの差別化など、ゲームデザイン面でも役に立つと思います。
と、前置きが長くなりましたが、早速本題に入っていきましょう。
”一度きり”しか発動しないギミック
一度だけ発動可能なギミックの例としては、”Bloodborneのショートカット扉”の①短縮ルートの開通や、”Dead by Daylightのパレット(板)”の②障害物でのルート遮断、などが挙げられます。
ここでは、この2つのギミックの作り方について紹介していきます。
まず、先ほど述べた4つのポイントに当てはめると、次のようになります。
ポイント
ギミックのゲームでの役割
①ルートの短縮
②敵行動の妨害ギミックの発動条件
①②近くで指定のアクションボタンを押すギミック発動による状況変化
①ルートの開通
②ルートの遮断ギミック処理の動作手順
①②センサーに入ると、アクション用フラグが切り替わる(false→true)
→センサーから出ると、再度フラグが切り替わる(true→false)
→センサーに入っている状態で、アクションボタンを押す。
→管理用スクリプトでフラグが切り替わる(true→false)
→センサーの当たり判定が無効になる。
①ゲートの外見表示が無効になり、視覚的に消える。
→ゲートと衝突しなくなり、ルートが開通する。
②ゲートの外見表示が有効になり、視覚的に現れる。
→ゲートと衝突するようになり、ルートが遮断する。
次に、今回の場合は、ゲートとセンサー、2つのオブジェクトを主に使用したやり方です。
ゲートは、外見表示の有効化/無効化と、衝突の有効化/無効化で、ルートの開通と遮断の役割を果たします。
センサーは、ゲートによる”ルートの開通と遮断”や”ギミックのリセット”など、ギミック発動のタイミングや条件を制御する役割があります。
この2つの関係性は、親オブジェクト:ゲート、子オブジェクト:センサーであり、この関係性は他のゲートギミックでも変わらないので、覚えておくとよいかもしれません。
また、ギミックによっては、ギミック間で共有もしくは利用するためのパラメータやフラグを活用する必要があるため、何でもいいので、ギミック管理用のオブジェクトを用意しておきましょう。
ギミックの作り方:ショートカット
まず、ゲートとセンサーを設置し、センサーとギミック管理用オブジェクトに、それぞれ下記のスクリプトを作成し、アタッチします。
ここで、ゲートコライダーのisTriggerのチェックが外れていることを確認し、センサーコライダーのisTriggerにチェックを入れます。
そして、センサーにアタッチしたスクリプトに、ギミック管理用スクリプトとゲートオブジェクトをアタッチします。
これだけで、ショートカットギミックの出来上がりです!
なお、動画ではボタンUIやメッセージテキストの表示切替も行っていますが、ギミックとは関係ない部分なので、ここでは省略しています。
センサーにアタッチするスクリプト(新規)
using UnityEngine;
public class SensorControl : MonoBehaviour
{
[SerializeField]
private GimicMaster gimicControl; // ギミック管理用スクリプト
[SerializeField]
private GameObject gate; // ゲート
private BoxCollider collider; // ゲートのコライダー
private MeshRenderer mesh; // ゲートの外見
void Start()
{
mesh = gate.GetComponent<MeshRenderer>();
collider = gate.GetComponent<BoxCollider>();
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Player") // 操作キャラのオブジェクトタグ
{
gimicControl.actionFlug = true; // アクションによるギミック発動の有効化
}
}
private void OnTriggerStay(Collider other)
{
if (other.gameObject.tag == "Player") // 操作キャラのオブジェクトタグ
{
if (!gimicControl.actionFlug) //アクションボタンを押したとき
{
mesh.enabled = false; // ゲートの外見表示を無効化
collider.isTrigger = true; // ゲート開通
gate.transform.GetChild(0).GetComponent<BoxCollider>().enabled = false; // センサーを無効化
Debug.Log("ショートカット開通!");
}
}
}
private void OnTriggerExit(Collider other)
{
if (other.gameObject.tag == "Player") // 操作キャラのオブジェクトタグ
{
gimicControl.actionFlug = false; // アクションによるギミック発動の無効化
}
}
}
ギミック管理用オブジェクトにアタッチするスクリプト(新規)
using UnityEngine;
using UnityEngine.InputSystem;
public class GimicMaster : MonoBehaviour
{
public bool actionFlug;
void Start()
{
actionFlug = false;
}
public void OnAction(InputAction.CallbackContext context) // Bボタン
{
if (context.started)
{
actionFlug = false;
}
}
}
上記ではルート短縮のためのショートカットと書いていますが、他にも、未開拓エリアへ侵入する際のゲートや、新アクションを使った分岐ルートの開放で使えるギミックです。
ギミックの作り方:障害物での遮断
前述したショートカットのやり方と基本的には同じことをしています。
違う点は両側にセンサーがあることと、ゲートを遮断→開通から開通→遮断に変えていることだけです。
なので、センサーを反対側にも設置し、そのセンサーを無効化する処理を追加するだけで作ることが出来ます。
もし、スクリプトの書き方が分からない場合は、下記のスクリプトのOnTriggerStayの中身を、上記で書いたに部分に上書きするだけでも問題ないです。
センサーにアタッチするスクリプト(変更箇所のみ)
private void OnTriggerStay(Collider other)
{
if (other.gameObject.tag == "Player") // 操作キャラのオブジェクトタグ
{
if (!gimicControl.actionFlug) //アクションボタンを押したとき
{
mesh.enabled = true;
collider.isTrigger = false;
gate.transform.GetChild(0).GetComponent<BoxCollider>().enabled = false; // センサーを無効化
gate.transform.GetChild(1).GetComponent<BoxCollider>().enabled = false; // センサーを無効化
Debug.Log("ルート封鎖!");
}
}
}
他の使い方としては、防御スキルによる壁の生成や、空中でキャラの足元に足場を作りだすなど、戦闘や移動アクションにも転用できるギミックだと、個人的に思います。
”条件を満たすとき”だけ発動するギミック
次に、特定の条件を満たすときだけ発動するギミックを紹介します。
ギミックの内容は、前回と同様に、ルートの開通と遮断なので、どんな条件でギミックが発動するか、ということに注目して作れば、問題ないと思います。
既存のゲームで例えると、”フォールガイズ”の何回か踏むと崩れる床や、”SCP:Containment Breach”のカードキーで開けられる扉などが挙げられます。
今回は、①一定回数攻撃すると破壊できる障害物、②プレイヤーのレベルを制限するためのゲート、③キーアイテムを所持していると通れるゲートの3つの作り方を紹介していきます。
先ほどと同様に、ポイントをまとめると、次のようになります。
ポイント
ギミックのゲームでの役割
①破壊による経験値の入手
②キャラレベルの制限
③必須アイテムの獲得ギミックの発動条件
①一定回数、攻撃を当てる
②一定レベルへの到達
③特定のアイテムの所持ギミック発動による状況変化
①ルートの開通
②③ルートの開通・遮断ギミック処理の動作手順
①攻撃用コライダーがセンサーに入ると、カウントが1つ増える。
→カウントが指定回数に達すると、カウントが0にリセットされる。
→センサーの当たり判定が無効になる。
→ゲートの外見表示が無効になり、視覚的に消える。
→ゲートと衝突しなくなり、ルートが開通する。
②③任意の条件を満たす場合、センサーに入ったときに、ゲートと衝突しなくなり、ルートが開通する。
→ 任意の条件を満たす場合、センサーから出たとき、ゲートと衝突するようになり、ルートが遮断する。
ギミックの作り方:破壊できる障害物
基本的に追加、変更する内容は、以下の通りです。
破壊可能な攻撃回数と現在の攻撃回数のパラメータの定義
条件となるタグを攻撃用コライダーに変更する
OnTriggerEnter()のみを使用し、内容を変更する
これで、攻撃用コライダーが範囲に入ると、カウントが1増えて、指定の回数だけ繰り返すと、ギミックが発動するようになります。
また、この後紹介する”レベル制限ゲート”のために、今回は破壊するとレベルが1上がるようにしており、レベルの条件を満たすようにしています。
センサーにアタッチするスクリプト(新規)
using UnityEngine;
public class SensorControl : MonoBehaviour
{
[SerializeField]
private GimicMaster gimicControl; // ギミック管理用スクリプト
[SerializeField]
private GameObject gate; // ゲート
private BoxCollider collider; // ゲートのコライダー
private MeshRenderer mesh; // ゲートの外見
private int count = 0; // 攻撃した回数
[SerializeField]
private int maxCount = 3; // 破壊できる攻撃回数
void Start()
{
mesh = gate.GetComponent<MeshRenderer>();
collider = gate.GetComponent<BoxCollider>();
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Counter-P") // 攻撃用のオブジェクトタグ
{
if (count >= maxCount - 1)
{
count = 0; // カウントをリセットする
mesh.enabled = false; // ゲートの外見表示を無効化
collider.isTrigger = true; // ゲート開通
gate.transform.GetChild(2).GetComponent<BoxCollider>().enabled = false; // センサーを無効化
gimicControl.level += 1 //破壊によるレベルアップ(今回のみ)
}
else
{
count += 1; // カウントを1増やす
}
}
}
}
このギミックは、敵から逃げながら、障害物への攻撃を繰り返し、破壊して逃げ道を確保するゲームや、敵からの攻撃を何度か受けると消える防御盾などでも活用できると思います。
ギミックの作り方:ゲートによるキャラレベル制限
今回は、OnTriggerEnter()とOnTriggerExit()を使用しています。
流れとしては、管理用スクリプトで定義したパラメータを参照して、条件を満たす場合、センサーに入るときはゲートと衝突しなくなるようにし、逆に出るときは、ゲートと衝突するように、元に戻します。
センサーにアタッチするスクリプト(新規)
using UnityEngine;
public class SensorControl : MonoBehaviour
{
[SerializeField]
private GimicMaster gimicControl; // ギミック管理用スクリプト
[SerializeField]
private GameObject gate; // ゲート
private BoxCollider collider; // ゲートのコライダー
void Start()
{
collider = gate.GetComponent<BoxCollider>();
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Player") // 操作キャラのオブジェクトタグ
{
if (gimicControl.level >= 2) // レベル2以上であるとき
{
collider.isTrigger = true; // ゲート開通
}
}
}
private void OnTriggerExit(Collider other)
{
if (other.gameObject.tag == "Player") // 操作キャラのオブジェクトタグ
{
if (gimicControl.level >= 2) // レベル2以上であるとき
{
collider.isTrigger = false; // ゲート遮断
}
}
}
}
ギミック管理用オブジェクトにアタッチするスクリプト(新規)
using UnityEngine;
public class GimicMaster : MonoBehaviour
{
public int level = 1;
void Start()
{
level = 1;
}
}
このギミックは、序盤では進んで欲しくないルートや、クリアに必要な最低限のレベルをプレイヤーに教える場合など、プレイヤーを意図したルートに誘導したいときに活用できると思います。
ギミックの作り方:キーアイテムによるゲート通過
こちらもほぼ同じなので、条件だけ変えるだけで問題ありません。
ただし、”ゲートを通るためのアイテム”をゲットする必要があるので、何でもいいですが、キャラに触れるとゲットできるアイテムを用意しておきましょう。
※今回はアイテムゲット時に管理用スクリプトのフラグをfalse→trueに切り替わるようにしています。
センサーにアタッチするスクリプト(条件のみ変更)
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Player") // 操作キャラのオブジェクトタグ
{
if (gimicControl.item) // キーアイテムを所持しているとき
{
collider.isTrigger = true; // ゲート開通
}
}
}
private void OnTriggerExit(Collider other)
{
if (other.gameObject.tag == "Player") // 操作キャラのオブジェクトタグ
{
if (gimicControl.item) // キーアイテムを所持しているとき
{
collider.isTrigger = false; // ゲート遮断
}
}
}
ギミック管理用オブジェクトにアタッチするスクリプト(関数のみ変更)
using UnityEngine;
using UnityEngine.InputSystem;
public class GimicMaster : MonoBehaviour
{
public bool item;
void Start()
{
item = false;
}
}
キーアイテムオブジェクトにアタッチするスクリプト(新規)
using UnityEngine;
public class ItemControl : MonoBehaviour
{
[SerializeField]
private GimicMaster gimicControl; // ギミック管理用スクリプト
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Player") // 操作キャラのオブジェクトタグ
{
gimicControl.item = true;
this.gameObject.GetComponent<MeshRenderer>().enabled = false; // アイテムの外見表示を無効化
this.gameObject.GetComponent<SphereCollider>().enabled = false; // アイテムの当たり判定を無効化
}
}
}
このギミックは、プレイヤーに必ずゲットさせたいアイテムがあるときや、消費させたいアイテムがあるときに、使えるギミックだと思います。
条件を満たすと”元に戻る”ギミック
時間制限付きのギミックを作るときなどに使うことが出来ます。
”Kena”の破壊しても時間経過で元に戻る岩や、”ポケットモンスター”のエリアを出ると技で切った木や動かした岩が戻る、といったように、条件を満たすことで復活するギミックもあります。
今回は、一番最初に紹介したショートカットを活用した、①時間制限付きのショートカットと、②センサーに触れることでリセットされるギミック、の2つの作り方を紹介していきます。
ポイント
ギミックのゲームでの役割
①②ギミックの再利用、ゲームの再プレイなどギミックの発動条件
①一定時間の経過
②センサーへの侵入ギミック発動による状況変化
①自動的にギミック復活
②特定のタイミングでギミック復活ギミック処理の動作手順
①ギミック発動時にカウントダウン用フラグを切り替える。
→カウントダウンを始める。
→カウントダウン後にフラグを切り替える。
→ギミックのリセット処理を実行する。
②管理スクリプトでセンサーオブジェクトを取得する。
→センサースクリプトでリセット処理を実行する。
ギミックの作り方:時間経過で復活するギミック
ギミック復活のプログラム処理の書き方は、どのギミックに適用するかで少し変わると思いますが、やっていることは大して難しくありません。
カウントダウンを始めるタイミングで、countDownをtrueにし、Update()の処理が始まり、時間経過後にギミックをリセットされます。
なので、下記のスクリプトでは、ショートカットを例に、センサー用のスクリプトのみ、載せています。
また、長いように見えますが、制限時間用の関数の追加、Update()内容の追加、ギミック発動の部分に"countDown = true;" を追加するだけなので、見た目に騙されないように注意してください!
センサーにアタッチするスクリプト(ギミック内容例:ショートカット)
using UnityEngine;
public class SensorControl : MonoBehaviour
{
[SerializeField]
private GimicMaster gimicControl; // ギミック管理用スクリプト
[SerializeField]
private GameObject gate; // ゲート
private BoxCollider collider; // ゲートのコライダー
private MeshRenderer mesh; // ゲートの外見
private bool countDown = false; // カウントダウン用のフラグ
[SerializeField]
private float timeLimit = 15.0f; // ギミック復活までの時間
private float seconds; // 復活までの残りの時間
void Start()
{
mesh = gate.GetComponent<MeshRenderer>();
collider = gate.GetComponent<BoxCollider>();
seconds = timeLimit;
}
void Update()
{
if (countDown)
{
if (seconds > 0)
{
seconds -= Time.deltaTime;
}
else if (seconds <= 0)
{
countDown = false; // カウントダウンの無効化
seconds = timeLimit; //制限時間の再設定
// ギミックを元の状態に戻す(リセットする内容)
mesh.enabled = true; // ゲートの外見表示を有効化
collider.isTrigger = false; // ゲート遮断
gate.transform.GetChild(0).GetComponent<BoxCollider>().enabled = true; // センサーを有効化
Debug.Log("ショートカット開通!");
}
}
}
// ここからは、だいたいショートカットと同じ内容
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Player") // 操作キャラのオブジェクトタグ
{
gimicControl.actionFlug = true; // アクションによるギミック発動の有効化
}
}
private void OnTriggerStay(Collider other)
{
if (other.gameObject.tag == "Player") // 操作キャラのオブジェクトタグ
{
if (!gimicControl.actionFlug) //アクションボタンを押したとき
{
countDown = true; // カウントダウンの有効化 ← 唯一追加する内容
mesh.enabled = false; // ゲートの外見表示を無効化
collider.isTrigger = true; // ゲート開通
gate.transform.GetChild(0).GetComponent<BoxCollider>().enabled = false; // センサーを無効化
Debug.Log("ショートカット開通!");
}
}
}
private void OnTriggerExit(Collider other)
{
if (other.gameObject.tag == "Player") // 操作キャラのオブジェクトタグ
{
gimicControl.actionFlug = false; // アクションによるギミック発動の無効化
}
}
}
このギミックは、時間の長さによって色んな使い方をすることが出来ます。
時間を短く設定すると、自動で閉まる扉のように、プレイヤーから”閉める”行動を取り除くことができます。
時間を長く設定すると、「ルートが閉鎖してしまう前に、何かのアイテムを取りに行く」みたいな、ミッションの制限時間としての使い方をすることもできます。
ギミックの作り方:エリア通過でのギミックリセット
最後に、エリア通過でのギミックリセットです。
やっていることは、先ほどの”時間経過での復活”と同じようで、エリア通過という条件に代わるだけです。
今回は、センサーのオブジェクトを管理用オブジェクトにアタッチすることで、センサーとゲート、親子関係の2つのオブジェクト情報を参照して、以下のスクリプトではショートカットを例に載せています。
ただし、ステージのギミック全てをリセットするような使い方をする場合は、リセットするギミック全ての情報を参照するため、どれだけ簡単な処理でギミックの情報を参照できるか、どれだけ工夫して手間を減らせるかなど、考えて作る必要が出てきます。
また、リセットする数や規模によっては、処理が重くなることもあるので、これを防ぐために各エリアごとで実行したり、タイミングをズらすなど、リセットの範囲やタイミングを考慮しないといけません。
センサーにアタッチするスクリプト(リセット内容例:ショートカット)
using UnityEngine;
public class SensorControl : MonoBehaviour
{
[SerializeField]
private GimicMaster gimicControl; // ギミック管理用スクリプト
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Player") // 操作キャラのオブジェクトタグ
{
gimicControl.sensor[0].GetComponent<MeshRenderer>().enabled = true;
gimicControl.sensor[0].GetComponent<BoxCollider>().enabled = true;
gimicControl.sensor[0].transform.parent.gameObject.GetComponent<MeshRenderer>().enabled = true;
gimicControl.sensor[0].transform.parent.gameObject.GetComponent<BoxCollider>().isTrigger = false;
}
}
ギミック管理用オブジェクトにアタッチするスクリプト(新規)
using UnityEngine;
public class GimicMaster : MonoBehaviour
{
public GameObject[] sensor; // ギミックのセンサーをアタッチ
}
センサーを使った活用法としては、エリアのギミックだけでなく、トラップセンサーのように、プレイヤーの行動をトリガーにギミックをリセットしたい場合にも使える方法です。
まとめ
今回は、誰でも簡単に出来る、様々なゲートギミックの作り方とその活用方法について取り上げて紹介しました。
しかし、今回の記事だけでは、実際にどんな風に動くのかや、どんな場所に設置すれば良いのか、あまり想像が付かなかったところも多かったと思います。
本日投稿したこちらの動画では、今回ご紹介したギミックを使用して作った簡易ステージを用いて、今回紹介した内容に触れておりますので、興味のある方は、是非見てみてください!
また、今回ご紹介したギミックは、あくまで一例に過ぎず、コードの書き方も、これが正しいという訳ではございません。
ぜひご自身の手で、より面白いギミックを作ってみていただければと思います。
少しでも誰かのゲーム制作の参考や助けになっていれば幸いです。