Unityでゆるっと「8番ライク」を組んでみたメモ
最近また「8番出口」系のスクショをXで見かけることが増えて、「これUnityなら最低限どこまで縮められるかな?」という実験をしてみました。
ちゃんとしたチュートリアルというより、「8番ライク 作り方」を調べながら自分が試した最小構成メモです。
断定じゃなくて「たぶんこうすると楽だった」というログくらいの温度感で読んでもらえれば。
ターゲットとしては「Unityは触ったことある」「でも8番出口っぽいループゲームをどう分解したらいいかピンと来てない」くらいの人を想定しています。
C#は最低限書ける前提です。
まず「8番ライク」をざっくり分解してみる
最初にやったのは、8番出口そのものを再現しようとするのをやめることでした。
あれを丸ごとUnityでやろうとすると、グラもライトもUIも全部フルコースになってしまうので、個人開発だとまず挫折しそうだなと…。
なので、「8 番 出口 作り方 unity」っぽいものを考えるときに、ルールをかなりざっくり削りました。
自分なりに最低限必要そうだなと思ったのは、この3つだけです。
- 同じ場所を何度もループしているように見えること
- ループごとに“何か変”な異変が起こることがある
- プレイヤーが「進む/戻る」を選ぶだけでゲームが進むこと
ここまで削ると、いわゆる「8 番 出口 ゲーム 作り方」というより、「ループ+ランダムイベントのシンプルなゲーム」のイメージに寄ってきます。
今回はそのくらいの軽さを目標にしました。
ゲームエンジンとしてはUE5でもいいと思うんですが、わたしは普段Unityを触っているので、そのままUnityを使っています。
「8 番 出口 ゲーム エンジン」で迷っている人は、すでに慣れている方を選ぶのがいちばん幸せかもです。
プロジェクト最小構成:シーン1枚+数個のスクリプト
構成を盛り始めるとキリがないので、「駅or地下通路の1フロアだけ作る」という前提でプロジェクトを組みました。
シーンは基本1枚です。
タイトルやリザルトはあってもなくてもOK。
フォルダ構成もかなり雑にこんな感じにしています。
Assets/
Scenes/
LoopScene.unity
Scripts/
LoopController.cs // ループ処理
AnomalyManager.cs // 異変のON/OFF
JudgeController.cs // 進む/戻るの判定
Prefabs/
Chunk.prefab // 廊下のブロック
Player.prefab
「8 番 出口 制作 ツール」を調べると、DCCツールやらUIツールやらいろいろ出てきますが、今回はBlenderもFigmaも一旦忘れて、Unity Editor内で完結させました。
モデルはCubeを伸ばしただけとかでも、とりあえず雰囲気は確認できます。
Zenn向けなので、「きれいなアートを目指す」というより「ループと判定だけ動く最小コード」の方に全振りしています。
あとから真面目に作る場合も、このミニマム版を先に通しておくと、デバッグがだいぶ楽になりました。
ループ部分:3つの廊下ブロックをぐるぐる回すだけ
「8 番 出口 ループ」を再現しようとすると、「無限に続く廊下どうする?」が最初の壁になりがちです。
個人的には、Prefabをランタイム生成するより「最初から3枚並べておいて位置だけ動かす」が一番シンプルでした。
やっていることはよくあるエンドレスランナーとほぼ同じで、プレイヤーが十分進んだら一番後ろのブロックを先頭に持ってくるだけです。
コードはこんな感じにしています。
public class LoopController : MonoBehaviour
{
public Transform player;
public Transform[] chunks; // 3〜4個くらい
public float chunkLength = 20f;
void Update()
{
foreach (var c in chunks)
{
// プレイヤーから見て一定距離後ろに行ったら前に回す
if (player.position.z - c.position.z > chunkLength)
{
c.position += Vector3.forward * chunkLength * chunks.Length;
}
}
}
}
これで「進んでも進んでも同じ廊下が続いている」感じはとりあえず出ます。
ガチで作るならライトのベイクとかNavMeshとかも考える必要がありますが、最小構成だと一旦全部リアルタイムライト+簡単なColliderだけに割り切ってしまっても動きます。
最初はPrefabをInstantiateしていたんですが、ライトベイク周りがちょっと面倒だったので、今のところは「すべてシーンに直置き」方式に落ち着いてます。
もしちゃんとベイクしたいなら、動かすのはブロックじゃなくてプレイヤー側をワープさせるスタイルの方が綺麗かもです。
異変のON/OFFをランダムで切り替える
次は「異変どうする?」の話です。
ここもあまり作り込みすぎると大変なので、最初は「単にオブジェクトのON/OFFをランダムに切り替える」だけにしました。
例えば“変なポスター”と“普通のポスター”を差し替えるくらいのイメージです。
Unity側では、異変ごとにGameObjectを1つ用意しておいて、SetActive(true/false)を叩くだけにしています。
雰囲気だけ見るなら、このくらいの雑さでも十分ゲームっぽくなります。
public class AnomalyManager : MonoBehaviour
{
[System.Serializable]
public class Anomaly
{
public string id;
public GameObject obj;
}
public Anomaly[] anomalies;
public float probability = 0.5f;
Anomaly current;
public void Roll()
{
// いったん全部OFF
foreach (var a in anomalies) a.obj.SetActive(false);
current = null;
// そもそも異変なしパターン
if (Random.value > probability) return;
// ランダムで1つだけON
var idx = Random.Range(0, anomalies.Length);
current = anomalies[idx];
current.obj.SetActive(true);
}
public bool HasAnomaly()
{
return current != null;
}
}
ループごとにRoll()を呼ぶと、「今回は異変あり/なし」と「どの異変が出るか」が決まります。
8 番 出口 個人 開発系の記事を見ていると、もっと細かく確率を調整している例もありますが、個人的には0.3〜0.5くらいのざっくり確率でも「たまに起きる」感じは出ました。
慣れてきたら、「異変」と「ただの変化(人数が増えたとか)」を分けて、別の乱数で動かすとゲーム性が増します。
最初からそこまでやると自分が疲れるので、このあたりは後から足していくのが良さそうです。
「進む」「戻る」の判定ロジックを最低限だけ書く
ルール部分はかなりシンプルにしました。
ざっくり書くと、
- 異変がある状態で「戻る」 → 正解(ゴールに一歩近づく)
- 異変がある状態で「進む」 → ミス(最初に戻る)
- 異変がない状態で「進む」 → 正解
- 異変がない状態で「戻る」 → ミス
これをC#に落とすと、こんな感じの最低限コードになりました。
public class JudgeController : MonoBehaviour
{
public AnomalyManager anomaly;
public int currentLoop = 1;
public int goalLoop = 8;
public void OnPressForward()
{
var has = anomaly.HasAnomaly();
if (has) ResetLoop();
else NextLoop();
}
public void OnPressBack()
{
var has = anomaly.HasAnomaly();
if (has) NextLoop();
else ResetLoop();
}
void NextLoop()
{
currentLoop++;
if (currentLoop > goalLoop)
{
// クリア処理
Debug.Log("Clear!");
// シーン遷移など
}
else
{
// 次のループへ
anomaly.Roll();
// プレイヤー位置リセットなど
}
}
void ResetLoop()
{
currentLoop = 1;
anomaly.Roll();
// スタート位置に戻す
Debug.Log("Reset");
}
}
UIはボタンでもキー入力でも何でもいいんですが、わたしは一旦「キーボードのWで進む/Sで戻る」を仮で割り当てて、のちほどボタンに差し替えました。
Zennの記事としてはキー入力の方が早く試せるので、こっちの方が向いているかもしれません。
ループ数は8にしていますが、ここはお好みで。
短くしたければ3〜4でもいいですし、長くして「だんだん異変の確率が上がる」みたいな調整もアリだと思います。
ロジック的にはgoalLoopを変えるだけなので、あとから決めてしまってもそんなに困りませんでした。
簡単なUIと演出を足して“それっぽさ”を出す
ここまでで、見た目は雑でも「ループ+異変+判定」は一応動きます。
とはいえ真っ暗な廊下にキューブだけだとテンションが上がらないので、最低限のUIと演出だけ軽く足しておきました。
やったことはこのくらいです。
- Canvas上に「出口番号」っぽいTextを1つ置く
- ループごとにcurrentLoopを表示して“出口に近づいている感”を出す
- Directional Lightの色を少しだけ緑寄りにして、地下っぽさを足す
UIの更新は本当に1行で済むので、Judge側から投げてしまっています。
public TMPro.TextMeshProUGUI loopLabel;
void UpdateLoopUI()
{
loopLabel.text = $"出口 {currentLoop}";
}
「8番出口 着想」をそのまま真似るのも良いんですが、個人的には“舞台の設定”だけ少しオリジナル寄りにしておくと、モチベーションが保ちやすかったです。
自分の場合は「使われていない地下商店街」みたいなテーマにして、看板やポスターを異変にしています。
音回りはまだ何もやっていなくて、環境音だけ後で足そうかな、くらいの気持ちです。
8番出口 UE5系の動画を見るとサウンドの重要度が高そうなんですが、まずはゲームルール部分の検証を優先しました。
作ってみての反省と、次やるなら足したいところ
ここまでが「Zennに投げるにはギリ動いている最小構成」くらいのラインです。
実際に触ってみて思った反省点もいくつか出てきました。
ひとつは、「異変のパターンが少ないとすぐ飽きる」という当たり前の話です。
コード側はシンプルに済ませたので、今度時間が取れたら“異変だけを増やす回”みたいな作業をしようかなと思っています。
エンジン側はもう触らず、アセットとパラメータだけ足していく感じですね。
もうひとつは、「プレイヤーの行動に応じて確率が変わる仕組み」は今回は入れていないので、本家のような“ジワジワやばくなる感じ”はそこまで出ていません。
状態を1つ持たせて、ミスが続いたら異変が出やすくなる…みたいな調整も、そのうち試してみたいところです。
とはいえ、「8番ライク 作り方」で検索して最初に触るサンプルとしては、このくらいの軽さでも十分かなという感触がありました。
ループ処理とランダムイベントの組み合わせは、別ジャンルのゲームでも割と使い回せそうなので、個人的には良い練習題材だったなと。
もっと腰を据えて作り込みたい場合は、Unity以外の制作ツールや本家の作り込みも研究した方が良さそうですが、まずはこのミニマム版を手元で動かしてみてから、次の一歩を考えるのがおすすめかもしれません。
ゲーム制作を体系的に学びたいタイミングが来たら、わたしは動画講座を併用することが多くて、最近だとUnity入門の森の【Unity6対応】Unity ホラー風3D脱出ゲーム講座【全10回】 【雰囲気あふれる8番ライクゲーを作ろう】あたりを眺めつつ「次はどのジャンルで遊ぼうかな」と考えることが多いです。
Discussion