1プレイ画面
#
ゲームの流れと状態を、どこに持たせるか。チームで作るときは、どこに境界を置いて、どう進めるか。
ドロップロップ
PS> Get-Item .\CreatorKousien | Format-List *
集めて、溜めて、まとめて敵に叩き込む見下ろし型3Dアクション。

担当10人チームで開発中。リポジトリの立ち上げと開発の進め方、ゲームの仕組みの一部を担当しています。
見どころ
ドロップロップの技術的な詳細を読む(設計・つまずき・コード)
2作品の詳細
遊びの核は「ためる」と「叩き込む」
ただ全部を集めればいいのではなく、どれだけ抱えるか、いつ、どの敵に放つかを考えさせます。ステージごとの強化の選び方も、その判断に効いてきます。
自分が作ってきたもの
最初のコミットから、半年間でやったことを時期順に並べました。前半はチームで作るための土台、後半はゲームの仕組みと仕上げが中心。
| 時期 | やったこと |
|---|---|
| 2026.04 | リポジトリを立ち上げ、開発ガイド・命名規則・PRテンプレートを用意。CIで .meta ファイルの抜けを検査するようにした |
| 2026.04 | Issueを作ると、GitHub Projectsのボードに自動で載るようにした |
| 2026.05 | 敵の仕組みの基盤と、UIとのつなぎ込み |
| 2026.07 | 敵の状態異常のエフェクト、ボスと蔦のギミックの作り直し、タイトルとロード画面 |
| 2026.07 | サウンドミドルウェアのWwiseを導入 |
| 2026.08 | ローグライクの強化と、バランスを調整するためのエディタ |
| 2026.08 | 落下物を500個動かしても重くならないようにする最適化 |
| 2026.09 | 常設のショップ、効果音とBGMをWwise経由に。ビルドからGitHub Releaseの作成、Discordへの通知までを1つのバッチにした |
バランスを一か所で調整する
強化の値、Waveごとの報酬、ショップの商品、収集物の出現率が別々の場所にあり、調整するたびに探し回っていました。これをUnityのエディタ上の1つの窓にまとめました。
「出現率アップを買っても、そのアイテムが出てこない」という不具合がありました。調べると、出現率の表とは別に、シーン上のスポナーが「出せるアイテム」の一覧を個別に持っていて、そこに無い種類には効果がありませんでした。
そこで、出現を決める処理を1か所にまとめて直しました。そのうえで、スポナーの一覧と出現率の表の食い違いを「検証」タブで見つけられるようにしました。あとから新しいスポナーを置いて登録を忘れても、次からは道具が教えてくれます。
3こだわったこと
500個落としても、重くしない
- やりたかったこと
-
画面いっぱいに収集物が降ってきて、それを抱えて放ちます。数を減らして軽くするのではなく、数はそのままで動きを軽くしたいと考えました。
- ぶつかった壁
-
落下物は、止まったあとも毎フレーム物理の計算と、重力をかける処理が走り続けていました。表示中の物を管理する処理も、物が増えるほど1回の追加や削除が遅くなる作りでした。
- どう作ったか
-
物理・探索・描画の3か所で、それぞれ無駄を削りました。いちばん効いたのは、止まった物を物理の計算から外すこと。
一瞬遅くなっただけで眠らせると、転がっている途中で止まってしまいます。そこで遅い状態が15ステップ続いたときだけ眠らせ、途中で少しでも動いたら数え直すようにしました。
止まった物は、物理から外す
上の図のとおり
Rigidbody.Sleep()で眠らせます。眠っている間は、重力をかける処理も飛ばします。探索を1回で済ませる
表示中の物の管理を
ListからHashSetに変え、追加や削除のたびに全体をなめていた確認をなくしました。まとめて描く
落下物のマテリアルでGPUインスタンシングを有効にし、同じ見た目の物を一度に描けるようにしました。
- どうなったか
-
止まっている落下物は、計算に入らなくなりました。500個が画面にあっても、実際に計算するのは動いている物だけで済みます。
C-6収集物が数百個、ステージに散らばっている画面静止画 PNG → images/works/creatorkousien/06-many.png数百個が散らばっていても、止まった物は眠っています
つまずいたところ壁ぎわで、収集物が震え続ける
- 起きたこと
フィールドの壁ぎわにたまった収集物が、細かく震え続けていました。
- 原因
壁に触れるたびに、スクリプトで位置を内側へ書き換えていました。物理エンジンが壁で止めている物を、その上からさらに動かしていたことになり、2つの処理がぶつかっていました。
しかも速度は外向きのまま残るので、次の物理の計算でまた壁へ押しつけられ、また戻されます。これを繰り返していました。
- 直し方
壁で止まっている物は、物理エンジンに任せます。スクリプトで戻すのは、壁を越えて、本当にフィールドの外へ出た物だけにしました。
戻すときは、速度のうち外向きの成分だけを取り除きます。速度と、壁の外向きの法線との内積をとり、外へ向かっていれば、その分を引きます。壁に沿った動きや、内側へ向かう動きは残ります。
Assets/Scripts/Gameplay/Collectibles/Entities/CollectibleObject.cs(314〜320行目)
float outwardSpeed = Vector3.Dot(
_rigidbody.linearVelocity,
outwardDirection);
if (outwardSpeed > 0f)
{
_rigidbody.linearVelocity -= outwardDirection * outwardSpeed;
}
要になったコード15ステップ続けて遅かったら、眠らせる
Assets/Scripts/Gameplay/Collectibles/Entities/CollectableGravity.cs(83〜96行目)
if (_rb.linearVelocity.sqrMagnitude <= SleepSpeedThresholdSqr)
{
_stableFixedStepCount++;
if (_stableFixedStepCount >= StableFixedStepsBeforeSleep)
{
_stableFixedStepCount = 0;
_rb.Sleep();
return;
}
}
else
{
_stableFixedStepCount = 0;
}
遅い状態が15ステップ続いたときだけ、Sleep() で物理の計算から外します。途中で少しでも速くなったら、数え直します。
一瞬止まっただけで眠らせると、転がっている途中の物まで止まってしまいます。「続いたら」を条件にしたのは、そのためです。
コレクライト
PS> Get-Item .\UnityJam | Format-List *
カラスになってライトを頼りに財宝を集め、電池が切れる前に脱出する探索アクション。

担当2週間のチーム開発。ステージ生成や3日間の進行、ショップ・結果画面など、ゲーム全体の骨組みを担当。
見どころ
1プレイ画面
コレクライトの技術的な詳細を読む(設計・つまずき・コード)
2作品の詳細
3日間の流れ
1日に4階まで潜ります。その日の結果の画面で稼ぎを払うと、次の日は前の日に届いた階から始められます。そのあとのショップで電池を買い足して、次の日に備えます。3日目が終わると、最終結果とエンドクレジットです。
階が変わるたびに、ステージを組み立て直す
1階ごとに、ステージとプレイヤーを、その階のプレハブから組み立てます。ゴールに着くとシーンを読み直し、次の階をまっさらな状態で組み立てます。前の階の敵や宝箱を1つずつ片付けるより、読み直すほうが取りこぼしがありません。
階をまたいで残したいものだけを、シーンを読み直しても消えないオブジェクトに置きました。何日目の何階か、その日までのスコア、電池の残りです。電池は、ライトが減るたびにそこへ書き込み、次の階のライトが最初に読み出します。
- 読み直すと消えるものステージ、敵、宝箱、プレイヤー(
StageManagerが階ごとに作る) - 残るもの日と階、日ごとのスコア(
GameSessionManager)、電池の本数と残量(PlayerDataManager)
状態が変わったら、それぞれが自分で反応する
ゲーム全体の状態は GameManager だけが持ちます。状態が変わったことをイベントで知らせ、画面・音・オブジェクトを受け持つクラスが、それぞれ受け取って動きます。GameManager はUIや音のことを知りません。
125件のPRを回すための仕組み
2週間でIssueは103件、PRは125件。管理の手間を減らすため、GitHub Actionsで次のことを自動にしました。
- Issueのフォームに書いた優先度と期日を、ボードの項目に入れるGitHub ProjectsのAPI(GraphQL)で書き込みます
- タイトルの先頭でラベルを付ける
[TASK]ならtask、[BUG]ならbug - 閉じたIssueはDoneへ動かす
- 担当が決まったらDiscordに通知する
3こだわったこと
稼ぎを使うほど、スコアは減る
- やりたかったこと
-
ダンジョンの中では、いつも「もう少し潜るか、いま帰るか」を迷います。この迷いを、ダンジョンを出たあとにも続けたいと考えました。
- ぶつかった壁
-
稼ぎで次の日を楽にできるなら、稼いだ人ほど楽になり、迷う理由がなくなります。
失敗も軽すぎました。最初は、失敗したらその日を0点にして、次の日へ進む作りでした。これだと3日目は、失うものがその日の分しかありません。
- どう作ったか
-
最終スコアは3日の稼ぎの合計です。次の日のために払った分は、そのまま最終スコアから引かれるようにしました。
- 近道は、その日の稼ぎで払う前の日に届いた階から始められます。代金は2日目が200、3日目が250です
- 電池は、買うほど高くなる1本目は10、2本目は15。そのあとは1本ごとに1.5倍になります。ショップに入るたびに10へ戻ります
- 失敗したら、そこで終わりその日に集めた分は残りますが、残りの日は0点になり、最終結果へ進みます
- どうなったか
-
1日の終わりに、稼ぎを近道に使うか、電池に使うか、残しておくかを選びます。どれを選んでもスコアは減ります。
失敗すると残りの日がなくなるので、1日目の無理は重く、3日目の無理は軽くなります。日が進むほど、深く潜る理由が増えていきます。
つまずいたところ1日の終わりが2回来て、日数が飛ぶ
- 起きたこと
ダンジョンで力尽きると、その1回の失敗で、日数が2日以上進んでしまうことがありました。
- 原因
1日を終える入り口は、帰還・ゴール・失敗の3つあります。失敗のときは、その場で0点を記録してから、結果画面の状態へ移っていました。
ところが、結果画面の状態に入るときにも、その日の結果を記録していました。1回の失敗で記録が2回走り、そのたびに日数が進んでいました。
- 直し方
進行を管理する側と、記録する側の2か所で防ぎました。進行を管理する側では、1日を終える処理を、1日に1回しか受け付けません。記録する側では、同じ日の結果が2回届いたら、2回目を捨てます。
呼び出す側の流れがあとで変わっても、記録が重ならないようにするためです。
Assets/Scripts/Core/GameSessionManager.cs(52〜59行目)
public void RegisterDayResult(int score, Dictionary<ItemMaster, int> items)
{
// 重複登録チェック: 既に今日のスコアが登録済みなら無視
if (DayScores.Count > CurrentDayIndex)
{
Debug.LogWarning(…);
return;
}
要になったコード失敗したら、残りの日を0点で埋める
Assets/Scripts/Core/GameManager.cs(473〜487行目から抜粋)
if (UnityJam.Core.GameSessionManager.Instance != null)
{
// 当日の結果登録
UnityJam.Core.GameSessionManager.Instance.RegisterDayResult(currentScore, currentItems);
// 2. 残りの日程を全て0点で埋める
while (!UnityJam.Core.GameSessionManager.Instance.IsSessionFinished())
{
UnityJam.Core.GameSessionManager.Instance.RegisterDayResult(0, new Dictionary<ItemMaster, int>());
}
// 3. FinalResultへ遷移
ChangeState(GameState.FinalResult);
}
その日の稼ぎを記録したあと、3日ぶんの記録がそろうまで0点を足していきます。最終結果の画面は、いつも3日ぶんの記録を読むだけで済みます。途中で終わったかどうかを、画面の側で気にする必要がありません。
そろそろ帰ろう
PS> Get-Item .\sorosoro-kaerou | Format-List *
背後で物音がしたら、スマホを持ったまま本当に振り返ってシャッターを切る、Android向けのホラー。

担当CyberAgentの2日間の開発実習で、3人チーム。Androidの層(ライト・ジャイロ・振動)、ゲームの流れの統合、暗さと怖さの演出を担当。
見どころ
1プレイ画面
そろそろ帰ろうの技術的な詳細を読む(設計・つまずき・コード)
2作品の詳細
実機とエディタを、同じゲームのコードで動かす
ジャイロもライトも、エディタ上にはありません。GameManager が入力と演出をインターフェース越しに扱うようにし、実機用とエディタ用の実装を差し替えられるようにしました。ゲームの流れはエディタで確かめ、手触りだけを実機で詰められます。
自分が担当したところ
Androidの層
端末のライト、ジャイロでの振り返り判定、振動、最初に向きを決めるキャリブレーション。
ゲームの流れの統合
別々に作っていた3Dのプレイ部分をメインのゲームに一本化し、シャッターの入力をイベントでロジックへつなぎました。
暗さと怖さの演出
暗い部屋のポストエフェクト、怪異が近づく表示、撮影の瞬間に画面が白く飛ぶ演出。
3こだわったこと
本当に、振り返らせる
- やりたかったこと
-
背後で物音がしたら、プレイヤーがボタンではなく、体の向きで振り返って応えます。怖さを画面の外まで持ち出したいと考えました。
- ぶつかった壁
-
遊ぶ人によって、立っている向きも、スマホの持ち方も違います。
「何度を超えたら背後」と境目を1つだけ決めると、ちょうどその角度で持っているだけで、正面と背後の判定が行ったり来たりしてしまいます。
- どう作ったか
-
始めるときに、今向いている方向を0°として登録します。そこからどれだけ回ったかを、ジャイロの姿勢(クォータニオン)から出します。オイラー角はジンバルロックで値が暴れるので、判定には使いません。
そして、「背後」になる範囲と、「正面」に戻る範囲を分けました。135°〜225°に入ったら背後、45°以内に戻ったら正面。そのあいだの角度では、今の状態を変えません。境目で判定が揺れることがなくなります。
振り返ってシャッターを切ると、AndroidのカメラのライトをUnityから直接点けます。0.7秒点けて、0.1秒消して、もう一度点けます。画面の中の光と、手元の本物の光が重なります。
- どうなったか
-
プレイヤーは本当に後ろを振り向き、手元のスマホが光ります。遊んでいる姿そのものが、ゲームの一部になりました。
R-7実機で振り返ってシャッターを押し、端末のライトが光るところ。スマホの外から撮った動画動画 MP4(無音・5〜10秒) → images/works/sorosoro/07-turn-flash.mp4振り返ってシャッターを押すと、手元のライトが光ります
要になったコード背後に入る角度と、正面に戻る角度を分ける
Assets/_sorosoro-kaerou/Scripts/Android/PhoneOrientation.cs(185〜203行目)
if (!isBack)
{
if (135 < relativeZ && relativeZ < 225)
{
isBack = true;
}
}
else
{
if (315 < relativeZ || relativeZ < 45)
{
isBack = false;
if (feedback != null && feedback.IsLightOn)
{
feedback.SetLight(false);
}
}
}
正面を向いているあいだは、135°〜225°に入ったときだけ「背後」にします。背後にいるあいだは、315°〜45°に戻ったときだけ「正面」にします。
そのあいだの角度では、状態を変えません。境目で手が少しぶれても、判定が行ったり来たりしません。
ONE MORE PLANK
PS> Get-Item .\OneMorePlank | Format-List *
使える板は、1部屋に1枚だけ。置いて、渡って、拾って、また置きます。10部屋を駆け抜けるタイムアタック。

ONE MORE PLANKの画面と技術的な詳細を読む(設計・つまずき・コード)
1プレイ画面
2作品の詳細
遊び方
- Shiftを押しながらクリックで置く画面の中心の先、6mまでに置けます。床がなくても宙に置けて、壁などに重なる場所だけは置けません。右クリックで45°ずつ回します
- Eで拾う渡り終えた板を拾って、次の足場にします
- スペースでジャンプ移動はWASD、視点はマウスで動かします
- 落ちたら、部屋の入口から板は1枚に戻ります。Rを長押しすれば、自分から部屋をやり直せます
最初は「借金」の遊びだった
はじめは、板が足りなくなったら「もう1枚」を借りられて、借りるほど霧が濃くなり、音が暗くなる設計でした。タイトルはその名残。
2日で遊びきれる形にするため、提出版では1部屋1枚のタイムアタックに絞りました。遊びの芯を「板1枚をどう使い回すか」に決め、それ以外を削りました。
3こだわったこと
音声ファイルを、1つも使わない
- やりたかったこと
-
暗く広い空間を、板1枚で渡っていくゲーム。その雰囲気に合う音を、2日のうちに用意したいと考えました。
- ぶつかった壁
-
音の素材を探して、長さや雰囲気をそろえるだけで時間がなくなります。2日のゲームジャムでは、その時間が取れません。
- どう作ったか
-
音はすべて、ゲームの中で波形を計算して作ることにしました。
音 作り方 背景の和音 少しだけ音程をずらしたサイン波を重ね、ゆっくり立ち上がって、ゆっくり消える ドローンと低音 背景でずっと鳴り続ける低い音 板を置く音・着地の音 木を叩いたような短い音。同じ音を、高さを変えて使い分ける 足音・ジャンプ・リスポーン 足音は高さの違う2種類を作っておき、鳴らすたびにどちらかを選んで、高さも少し揺らす 背景の和音は、同じ高さの音を0.1%だけ上下にずらして重ねています。わずかにずれた音どうしがゆっくりうねり、厚みが出ます。そこに2倍・3倍の高さの音を少しだけ足しています。
- どうなったか
-
音声ファイルは0個のまま、背景の和音から足音まで、すべての音が鳴っています。
要になったコード0.1% ずらした音を、重ねる
Assets/OneMorePlank/Scripts/Core/AudioManager.cs(142〜148行目)
// コーラス効果のためにわずかにデチューンされた豊かな倍音
float wave = 0f;
wave += Mathf.Sin(2 * Mathf.PI * freq * t);
wave += Mathf.Sin(2 * Mathf.PI * freq * 1.001f * t) * 0.7f;
wave += Mathf.Sin(2 * Mathf.PI * freq * 0.999f * t) * 0.7f;
wave += Mathf.Sin(2 * Mathf.PI * freq * 2f * t) * 0.3f;
wave += Mathf.Sin(2 * Mathf.PI * freq * 3f * t) * 0.1f;
基準の高さの音に、1.001 倍と 0.999 倍の音を重ねています。わずかにずれた音どうしがゆっくりうねって、1つの音より厚みが出ます。
その上に、2倍と3倍の高さの音を少しずつ足しています。音声ファイルを使わず、背景の和音はこの5行で波形を作って鳴らしています。