就活向けゲーム企画書のコツ/Unityコルーチンとasync/awaitについて(第1回ゲー制ザツダン定例)
就活向けゲーム企画書のコツ/Unityコルーチンとasync/awaitについて(第1回ゲー制ザツダン定例)
2025年4月19日 ゲーム制作サークル定例ログ
はじめに
ゲーム制作サークルのスタジオカブキでは、
隔週(月2回)ペースで、学生とゲーム企業のプログラマーが就活やゲー制の相談会をしてます。
今回は 就活向けゲーム企画書のコツや、Unity初心者あるあるトラップを回避するモダンな実装方法について話をしました。
ゲーム就活に関する情報ってまだまだ少ないので、困ってる学生さんたちの参考になればと思い
文書化・公開を始めました。
nottaで文字起こし+geminiで要約してるので若干変なところがあるかもですがご容赦くださいー。
もし興味持っていただけましたら、学生さん・業界の方ぜひ一緒にお話ししましょうー。X等でご連絡ください。
冒頭挨拶と近況
AKI(プログラマー)とSOBORO(学生)が1週間ぶりに会話を開始。
SOBOROはUnityでのブロック崩し開発に毎日取り組んでいるものの、思うように進まず苦戦している様子を語る。
SOBORO: まあ毎日触るようにはしてます。Unityとか。すごい何かしらは。ただまあ思うようにやっぱすすまなくて。
AKI: いやね、ちょっともう慣れるまでむずいっすよね。やっぱ初めて使うツールだと思うし、多分ゲーム開発も初めてですよね。
Unityブロック崩し開発の進捗と課題
SOBOROが開発中のブロック崩しゲーム画面を共有し、進捗と課題を説明。
基本機能(ボールの反射、ブロック破壊、ライフ減少、ゲームオーバー)は実装できているが、一点大きな問題が。
- 課題: ゲームオーバー後もボールが無限にスポーン(生成)してしまう。
SOBORO: すぐにゲームが終わってほしいんですけど、なんか(ボールが)ずっとスポーンし続けちゃって。ボールが。あああ、まあね。で、なんか結構頑張ってるんですけど、チャットGPTとか使って、なんかいや、合ってるはずなんだけどなぁっていうのがずっと。
デバッグの試行錯誤とAIアシスタントの導入
ボールが無限スポーンする原因について議論を開始。
スポーン条件を管理するフラグ (IsSpawn) の挙動が怪しいと推測。
AKI: これ、スポーンする条件って何なんですか? 画面クリックかな?
SOBORO: いや、えっと、ライフが減った時にTrueにして、ボールがスポーンしたらFalseにするっていう…
Unityエディタ上でIsSpawnの値を確認するも、デストロイされた瞬間に一瞬だけTrueになる(ように見える)だけで、なぜスポーンし続けるのか原因特定に至らない。
SOBORO: ここでフォールスになってもう戻らないんですよ。ここからトゥルーになることもなく、なんかもうわけがわからなくて。
AKIはVisual Studioのデバッグ機能(ブレークポイント)を使って変数の中身を直接確認しようと試みる。
AKI: あとはブレークポイント使うとその行が呼ばれた瞬間に一時停止するんで、本当にそのif文の中に入ってるのか?とかがチェックできるんですよね。
SOBORO: えー!すごい!
さらにAKIは開発効率化のため、AIコードアシスタント(Cursor, Windsurfなど)の導入を提案。
AKI: これマジで、ファイル編集とかも全部やってくれるんですよ。ChatGPTからコピペしなくていい。
このUnityプロジェクトのフォルダーを指定すると、そのフォルダーから必要なファイルを検索してみて、「まあ、こんな感じで」って言って書き換えて。
SOBORO: ええ、すごいマジで終わってる...
AKI: いい意味で終わってる(笑)。
AKIは、AIアシスタントを使えば、コードの検索、編集、リファクタリング、さらにはデバッグ環境のセットアップなども効率化できると説明する。
AKI: Unity向けのプラグイン入れてセットアップしてみてください。
デバッガーもプラグイン入れたらできる。非公式になってたかちょっと忘れたけど。
Unity初心者トラップを回避:イベント駆動と非同期処理
デバッグと並行して、AKIは現在のコード実装の問題点を指摘し、よりモダンで効率的な実装方法を提案する。
-
ポーリング (
Updateでの監視) の問題点:-
Update関数内で毎フレーム状態を監視(ポーリング)する方法は、常に処理が走り続けるためパフォーマンスに影響を与える可能性があり、特にフラグを用いた状態管理はコードを複雑にしがち。 -
IsSpawnのようなフラグが増え、しかもグローバル(public)だと、「いつ、どこで、どのフラグが、なぜ変わったのか?」を追うのが困難になり、バグの温床になりやすい。また変数名も適切ではない(今回は「スポーンすべき」をIsSpawnで管理していた。)
-
-
イベント駆動 (
Action) への移行:- C#の
Action(イベント)を用いることを推奨。 - ボールが破壊された、ライフが減ったなどの「イベント」が発生したタイミングで、関連する処理(ボールの再スポーンなど)を実行する方式。
Actionに変数を定義し、イベント発生元でInvoke()を呼び出し、受け取り側で+=でメソッドを登録する。 - これにより、「ボールが破壊されたらスポナーに通知する」のように、各コンポーネントの責務が明確になり、コードの見通しが良くなる。
- C#の
-
コルーチンの代替:
- 時間のかかる処理や待機処理には、コルーチンではなく
async/awaitを用いた非同期処理を推奨。 -
Task.Delayを使うことで、「N秒待つ」といった処理を簡潔かつ直感的に記述できる。
- 時間のかかる処理や待機処理には、コルーチンではなく
-
コルーチンの代替 (
async/await):- 時間のかかる処理や待機処理には、Unity特有のコルーチンではなく、C#標準の
async/awaitを用いた非同期処理を推奨。 -
Task.Delayを使うことで、「N秒待つ」といった処理をStartCoroutineなどを使わずに、メソッド呼び出しのように簡潔かつ直感的に記述できる。
AKI: 例えばさっきの処理(2秒待機してスポーン)、どうやって書けるかっていうと、[...]こうするとめっちゃ楽じゃないですか?
SOBORO: えっ、すごい!なんか便利で!えっちょっとじゃあもうコード書き直します! - 時間のかかる処理や待機処理には、Unity特有のコルーチンではなく、C#標準の
AKIはActionとasync/awaitを使ったボールスポーン処理の具体的な改善コード例を提示する。
AKI: この辺で書き直すように(AIに)指示すると、それでもわかりやすいコードにだんだんなっていくと思いますね。
コード修正と実行確認
AKIの提案に基づき、イベント駆動と非同期処理を用いたコードに修正する。
-
ボールスポーナークラス (
BallSpawner.cs) の修正:-
Update関数内のポーリング処理とIsSpawnフラグを削除。 - ボール破壊イベントを受け取るための
Action(public static event Action OnBallDestroyed;) を定義(staticにしてどこからでもアクセス可能に)。 - イベント購読/購読解除処理を
OnEnable/OnDisableメソッドに追加 (OnBallDestroyed += HandleBallDestroyed;,OnBallDestroyed -= HandleBallDestroyed;)。 - イベント発生時に呼び出される非同期メソッド (
async void HandleBallDestroyed()) を実装。この中でawait Task.Delay(2000)で2秒待機し、新しいボールをスポーンさせる。
-
-
ボールクラス (
BallStatus.cs) の修正:-
IsSpawnフラグ関連の処理をすべて削除。 - ボールが破壊される箇所(画面下に落ちた時など)で、
BallSpawner.OnBallDestroyed?.Invoke();を呼び出し、イベントを発行するように変更。
-
修正中の問題点:
-
命名の混乱: 修正過程で、SOBOROが過去にChatGPTの助言?でファイル名を
Ball.csからBallStatus.csに変更していたことが発覚。しかし古いBall.csが残り、クラス名とファイル名が一致しない状態に。AKI: これね、あのUnityプロジェクトって、
.csのファイル名と、このpublic class Ball: MonoBehaviourの名前が一致した方が分かりやすいです。歴史的には同じじゃないと動かなこともあって[...] 本当にプログラミングは命名が重要。不要な
Ball.csを削除し、クラス名をBallStatusに統一する。 -
エディタ連携問題: Visual StudioとUnityの連携が不安定で、コード編集中にエラー箇所(
IsSpawnを使っている箇所など)がリアルタイムで表示されず、デバッグに手間取る場面も。AKI: VS Code(Visual Studio)側でエラー出てこないね… なんか連携できてない気がしますね。[...] これだともう開発のしようがないから、エディター上でエラーが見えねえっていうね。
最終的にはUnity側のコンソールエラーを頼りに修正を進める。
動作確認:
修正後、ゲームを実行。
- ボールが下に落ちて破壊されると、2秒後に新しいボールが正しくスポーンする。
- ライフがゼロになると、ボールはスポーンされず、ゲームオーバー状態で停止する。
- 無限スポーンの問題が綺麗に解決したことを確認。
SOBORO: うわー、すごいで!2回目の時も!(ちゃんと動く)[...] これやっぱポーリングの問題ですよね、解決して良かったです。え、なんかもうコードもめっちゃ短くなってる!
AKI: 書きやすいは正義ですよね。バグが防げて品質が上がり、ゲームが面白くなる。
ゲーム企画書の作成について
SOBOROがAKIの過去のエントリーシートにあったゲーム企画書について質問したことをきっかけに、企画書作成に関する議論が展開される。
SOBORO: 企画書ってなんかどうやって作ってんのかなって思って。(AKIさんの企画書は)画像とかすごい完成度高かったんで。
-
企画書作成ツール:
- AKIはMacのKeynote、Googleスライド、Canvaなどを利用。プレゼンツール上で画像編集も行うことが多い。
- クラウドベースのツール(Googleスライドなど)は、デバイスを選ばずアクセスでき、共同編集もしやすいため便利。
-
画像の準備:
- 最優先は意図を伝えること: 必ずしも綺麗な絵である必要はない。手書きや簡単な図形でも、ゲームの内容や面白さが伝わればOK。
- 素材: フリー素材、既存ゲームの画像(著作権に配慮しつつ、個人利用の範囲内で参考にする程度ならOKな場合も)、図形ツールでの自作、AI画像生成など、様々な方法がある。
- AI画像の注意点: AKIは、AI生成画像は便利だが、一見綺麗に見えてもかえって意図が伝わりにくくなる可能性もあるため、頼りすぎないようにしていると語る。
-
他の学生の例: AKIは、コンセプトと構成が秀逸な別の学生の企画書を例示。「きっとわざと気合いの入ってない絵で、こんな面白い企画書いてくるんだ。」と評しつつ、コンセプトの重要性を強調。
AKI: (例の企画書について)面白いし論理の立て方がしっかりしてるんだよな。ホラー好きな人も嫌いな人も楽しめるホラーゲームって言ってて。[...] ホラーが苦手でも、脅かす側になれば楽しめるんじゃないかっていう視点が面白い。
-
企画書で最も重要なこと:
- コンセプト:「なぜこのゲームを作るのか」「誰に何を楽しんでほしいのか」という核となる考え。これが全ての要素(ゲームシステム、キャラクターデザイン、UI、操作方法など)の判断基準となる。コンセプトがないと企画がぶれる。
- 分かりやすさ: ゲームの内容、面白さ、ターゲット層などが明確に伝わる構成・表現を心がける。
- 読み手への配慮: 相手(選考担当者など)が何を知りたいかを想定し、アピールしたい点(自分の強み)やゲームのボリューム感(将来性)が伝わる情報を盛り込む。
-
AKI自身の企画書解説:
- ロボット合体ゲームの企画書を例に、各ページで伝えたかった意図を説明。
- 1ページ目: バカゲー感、パーティーゲーム感を伝える雰囲気作り(イラスト選択、色彩)。コアシステムの「合体」を視覚的に強調。
- 2ページ目: ゲームの具体的な面白さ(役割分担、協力プレイ)を説明。合体の仕組みを先に提示。
- 3ページ目: ゲームのコンセプト(協力して動かす面白さ、そこから生まれるコミュニケーション)を提示。協力マルチゲームであることを明確化。
- 構成: コンセプトを最初に持ってくるか、ゲームシステムを先に見せるか、構成には様々な考え方がある。AKIの企画書ではシステム説明が先になっている。どちらが良いかは一概に言えないが、SOBOROは「コンセプト先の方が分かりやすいかも」とコメント。AKIも「ぼくが考えたさいきょうのゲームシステム言いたい病だった」と振り返る。
- ロボット合体ゲームの企画書を例に、各ページで伝えたかった意図を説明。
プログラマーのキャリアと就職活動
ゲームプログラマーの就職活動やキャリアについて、より広い視点での議論。
-
企画書の必要性:
- プログラマー志望の場合、企画書提出が必須でないことが多い。ポートフォリオ(実際に作ったゲーム)の方が重視される。
- ただし、企画経験があると企画担当者との意思疎通がスムーズになったり、仕様への提案(「この仕様はコンセプトに合ってる/合わない」など)ができたりするメリットがある。ゲーム作りへの熱意を示す材料にもなり得る。
-
技術選考:
- 多くのゲーム会社でコーディング試験が実施される。アルゴリズム、データ構造、難易度が上がると数学(線形代数など)も含まれる。AKIは過去に受けた3Dの行列計算(プロジェクション・ビュー)を実装させる試験の経験を語る。
-
開発環境構築の重要性:
- 実際の開発現場では、コードを書く時間だけでなく、開発ツールやライブラリのセットアップ、連携、ビルドワークフロー、CI/CDなどの環境構築に多くの時間が費やされることも珍しくない。
AKI: (特にモバイル系では)エンジニアの仕事って、マジ現場でも本当に環境構築というか。[...] マイクラで言うとさ、自動化するの大変じゃないですか? あの住居を建ててかまどいっぱい建てて、経験値トラップ作ってエンチャントしまくって、ようやくドラゴン討伐に行けるみたいな。あれを割とやってますね。
-
技術的知識の深化:
- Unityのようなゲームエンジンを使う場合でも、その内部的な仕組み(レンダリングパイプライン、物理演算のアルゴリズム、メモリ管理、C#の言語仕様など)を理解していると、より高度な開発やパフォーマンスチューニング、問題解決が可能になる。AKIは参考資料として3Dゲーム開発の仕組みを解説するウェブサイトを紹介。
まとめと今後の展望
- SOBORO: イベント駆動や非同期処理といった新しいアプローチを学び、ブロック崩しゲームの課題を解決できたことに大きな手応えを感じる。「IsSpawnだらけでマジ困ってた」状況から脱却できた。今後、タイトル画面作成など、ゲームの完成度を高めていきたい。
- AKI: モダンな開発手法やAIアシスタントを活用することの有効性を改めて強調。ポーリングや安易なフラグ管理の弊害、イベント駆動のメリットを再確認。また、ゲームをビルドして実際に遊べる形にすることがモチベーション維持につながるとアドバイス。
あとがきと補足
会話上では触れなかったのですが、今回紹介した内容を少し補足します。
staticの注意点
- 会話の流れでは変数(Action)を
staticで扱っていますが、実は使用方法に注意があります - 安全なstatic利用例
-
不変データ(
readonly付き定数)や純粋関数ユーティリティはstaticでもOK。-
constでは書けない辞書の定数など。private static readonly Dictionary<string, int> ScoreTable = new() { ["Easy"] = 100, ["Hard"] = 200 };
-
-
不変データ(
- 他方、状態を持つstaticはできるだけ禁止すると良いでしょう。
- ゲーム中のスコアやイベントをstaticで持つのは避けた方が良い
- どこからでも呼べて便利な反面、変更箇所が散らばってバグの原因が特定不能になるため。
- ライフサイクル(いつ生成・破棄されるか)も曖昧になる。
- 破棄を忘れると、イベントが生き続けてメモリリークの原因になる
- コードのキレイさ以外のデメリットとしては、
EnterPlayMode設定によるPlayの高速化と相性が悪いことが挙げられます。-
https://shibuya24.info/entry/unity-enterplaymode
- これを有効にすると、Playが高速になるがstatic変数は再初期化されない。
- その結果、「開発中だけ動くがビルドでは落ちる」バグが頻発する。(逆も然り)
- とはいえ、そもそも依存してるライブラリが状態を
staticで管理していると使えません。
-
https://shibuya24.info/entry/unity-enterplaymode
非同期処理
- 非同期処理=命令を投げて「待たずに」次に進む
- ゲームでは演出にもしばしば使う
- また、「重い仕事中にゲームが固まる」のを防ぐ
- 例えばネットワーク通信やディスクアクセスは待ち時間が長いので、メインスレッドを止めず別スレッドで動作させる
- Unityでは2018年ごろからC#の
async/awaitを使えるようになった- 一方で
C#標準のTaskだけではさまざまな課題があった
- 一方で
- そこでサードパーティ製の軽量ライブラリUniTaskが開発され、スタンダードに。
- Unityパッケージで配布されており導入も簡単。慣れてきたらぜひ導入してみましょう
- https://github.com/Cysharp/UniTask
- パフォーマンスだけでなく、さまざまな処理をより記述しやすくなりました。
- またasync/awaitは単純に演出や通信の待機だけでなく、重い処理をワーカースレッドで動作させる(マルチスレッド)ことにも使える
- これによりメインスレッドを止めずに、1フレームの限られた時間内でより多くの処理を行えるように。
- 下記サイト(Valkanのドキュメント)では、ゲームにおけるマルチスレッド化の経緯が詳しく書かれています。
- https://vkguide.dev/docs/extra-chapter/multithreading/#:~:text=While this approach is very,work in 1 core%2C and
- 初期のゲームエンジンはシングルスレッドで走り続け、コア増加分の性能をまったく活かせなかった。(ボトルネック化(
- Unreal Engineは「ゲームスレッド+レンダースレッド」モデルにより、役割で専門のスレッドを持たせる手法を導入。
- が、スレッドごとの負荷偏りや同期待ちで4コア以上にスケールしにくい欠点があった。
- PS4世代以降はタスクベースへ移行し、小さな独立タスクを依存グラフで管理して全コアに分散実行することで、Doom Eternalのように4→16コアまで滑らかなスケーリングを実現している。
- なお必ずしも非同期処理==マルチスレッドではありません。
- 他にもキャンセルや排他制御(ロック)などさまざまなトピックがあります。
Discussion