土鍋的ハッカソンチーム開発ルーティン【Unity】
はじめに
どうも、土鍋です。
私は現在までいくつかのハッカソンに出場し、賞を頂いたりもしました。
つい先日もHackUに参加し、最優秀賞をいただくことができました。
こちら最優秀賞頂きました!!#hacku #openhacku2024 https://t.co/jie7uL1T2o
— 土鍋 (@donadonadonabe) 2024年11月16日
そこで、今までのチーム開発経験をもとにいつもやっているルーティン的なことをまとめてみようと思い、この記事を書きました。
前提として、多くの人の影響を受けて現在のスタイルになったので、Unityでの開発経験がたくさんある人には当たり前のことが多いかもしれません。
ですのでこの記事のターゲットは
今後ハッカソンでUnityを使いたい方々やUnityでのチーム開発経験が少ない方々です。
また、チームリーダーがやるべきことも随所にあるので初めてエンジニアチームリーダーになるという人にも参考になると思います。
注意点として、Gitの使い方などの話は省きます。
チーム構成
まず初めに考えたいのはチームメンバーの開発経験です。
当たり前ですが、全員がUnity開発経験が豊富な場合は安心して開発やコミュニケーションを行うことができます。
が、初心者・チーム開発経験が少ないメンバーがいる場合は、経験のある人がある程度気を使ってあげる必要があります。
ドキュメント化できるところはドキュメント化し、慣れた者同士なら伝わるコミュニケーションやコーディングの仕方は避け、誰でも理解できる形であると良いと思います。 そういった心理的安全性の高いチームであれば、初心者の人が開発を嫌いにならずに、今後も楽しんでくれるようになると思います。
もちろん誰もが最初は初心者です。初心者は存分に開発経験ある人に甘えちゃいましょう。
プロジェクト構成
ここでは主にハッカソン期間が始まったら真っ先にやるであろうリポジトリやUnityプロジェクトの作成周りに関して話します。
フォルダ階層
自分はチームの開発経験と開発期間を踏まえて二通りを使い分けています。
開発経験がそこそこある or 開発期間が長い

Assets直下に自分たちのProject名のフォルダを作り、その下にScriptsやScenesといった各種要素フォルダを作る形式です。
これは次に紹介する方法と比べると長期運用向けです。
必要なものがどこにあるか明確になり、各スクリプトやPrefabの責任の範囲も分かりやすくなります。namespaceと一致するようフォルダを作ることでさらにそれを向上できます。
ただそれなりに気を使う必要があるので、開発経験がそこそこある場合か、開発期間が二ヶ月以上あるような場合のときにこのようにするべきかなーと思います。
開発経験があまりない or 開発経験が短い

Assets直下に自分たちのProject名のフォルダを作るとこまでは同じですが、その下にメンバーそれぞれのフォルダを作ります。これは一番の目的はコンフリクトを避けるためです。
開発経験があまりない場合、やはりスクリプトやシーン、3Dモデルなどががちゃがちゃになってしまうことがあります。もしフォルダを共有して使っていると余計なファイルまで変更してコンフリクトしてしまったり、ほしいものがどこにあるか見つけにくくなってしまいます。
そのため、メンバーそれぞれが好き勝手に使っていいフォルダを作ることで、開発初心者でもコンフリクトなどのことを意識せずに自由に気持ちよく開発することができます。
余計なことを考えすぎて迷惑かけたらどうしようとなったしまうと開発が楽しいということを経験できずに終わってしまいます。
これとは別で開発経験に関わらず、開発期間が短い場合も名前ごとにすると良いと思います。
これは短い期間でガッと開発するとなると他の人の作業がマージされるのを待っている時間はありません。そのため、それぞれがそれぞれのタスクを各自の責任下で開発を進め、統合が必要になったときに統合するという形が、開発者体験的に自分はいいなと思っています。
Githubリポジトリ作成
Unityは自動生成のものが多いからGit管理むずくないですか?という話をいただきますが、.gitignoreをちゃんと書けば問題なく利用できます。
Gitクライアント
Unityで開発する場合はGitクライアントアプリを使用したほうが良いと思います。(もちろんRider, VisualStudio, VSCodeのGit機能でも良い)
と、いいますのもUnityは自動的に変更されるファイルが多すぎて、CLIで管理するのが、かなり手間です。もちろん細かな状況に対応するときはCLIを使用しますが、基本的にはGUIで自動変更された差分をパッパと確認していったほうがいいです。
Github desktopは公式のものなのでWebから直接クライアントアプリに飛ぶことができます。
SourceTreeやForkではブランチをきれいに図示してくれるので俯瞰的にプロジェクトの状況を確認しやすいです。
ちなみに自分はFork派です。(SourceTreeはアカウント周りでめんどかった記憶)
ローカル
Unityのプロジェクトを作成したら、プロジェクトの最上位階層で「git init」を行ってGitを使用できるようにしてください。
この時点では数万ファイルの変更が生まれてしまいますが、.gitignoreを適用することで60ファイル程度に減ります。
リモート
Githubのサイトから新規でリポジトリを作り、リモートリポジトリとして設定し、ローカルのUnityプロジェクトをPushします。
Unityは外部アセットを使用するケースが多いので、むやみやたらにpublicリポジトリにすることができません。 そのため、publicにする可能性がある場合はExternalAssetsフォルダを作りその中に外部アセットを格納しておくことで、それを除いて公開することが容易になります。
プロジェクトマネジメント
これに関してはプロジェクトの規模と期間よると思いますが、今回はハッカソンなどの短期間向けの話に特化します。
タスク管理・スケジュール管理
これに関してはUnity固有の話はそんなにないですね。ここらへんは好みの問題な気がします。
私はNotionかGithubのProjectsを使ってました。
未着手、進行中、完了の3つのステータスによるカンバンや、それらをタイムライン形式で見れるようにしたり、Viewを切り替えられるタスク管理ツールを使うと便利です。
Gitに関して
Github Flowで進めることが一番多いと思います。リリースや運用を考える場合はGit Flowが良いと思います。
Git経験が少ない人がいる場合はここらへんのGitを使った作業の流れをドキュメント化しておくと良いと思います。
コーディング・設計関連
Scene
シーンは一番コンフリクトが懸念されるものです。ゲームで実際に使用するシーンはチームメンバー全員触りたくなるでしょうが、なるべく避けなければいけません。
ですので、それぞれのタスク用のシーンを作るのがベストです。そうすれば他人と同じシーンを触ることはめったに無いです。
どうしてもメインのビルドするシーンを触る場合は誰が触っているか明確化しておくと、コンフリクトが発生しません。
方法的には普段やり取りするチャットで宣言したり、スプシなどで管理するなどです。
Prefab
シーンの話にも繋がりますが、Prefab化するゲームオブジェクトは
複数シーンにまたがって使用することが想定されるものとインスタンス化(スクリプトからオブジェクトを生成すること)するものが一般的です。
ですので、タスク用シーンと実際にビルドシーンにまたがる時点で多くのものがPrefab化した方が自分はいいかなーと思っています。 Prefab化しないものはゲームのワールド環境オブジェクトやそれに関連するロジックのスクリプト、ライティングなど、そのシーンでしか存在しないものくらいです。
SceneとPrefabのコミット
SceneやPrefabをコミットする場合は差分を必ず確認した方が良いと思います。
Prefabに対してScene内で変更を行うと以下のように青くなります。これはそのシーンだけではそのような変更がされているということなのでPrefabには直接影響しません。そのためSceneファイルに変更が生じます。
ですが、その変更がそのPrefabを使うときはいつもそのようにしたいという場合は、ApplyしてあげるとPrefabにその変更が適用され、Sceneには変更は発生しません。

シーンとPrefabのコンフリクトはUnityYAMLMergeを使用することで解消できるケースがありますが、意図しない統合のされ方をされてしまう可能性があるので、なるべくならコンフリクトしないように開発するのがベストです。
(もちろんUnityの開発になれるまでは深いことは考えずにコミットしちゃっていいです。)
(コンフリクトはチームのUnity得意な人にぶん投げちゃってください)
Game Manager
Unityで開発しているとまずぶち当たるのがスクリプト間の参照かなと思います。
いちばん簡単なのがインスペクターからその参照したいスクリプトを設定するという方法でしょう。(むしろこれがあるからUnityはプログラミング初心者がとっつきやすい)
ただ、ゲームの進行状況、今どのワールドにいるのか、ユーザーのステータスはどのような状態かなどのゲームの始まりから最後まで存在し続けるものに関してはSingletonパターンを適用したGame Managerクラスなどを用意しておくと、かなり開発が楽になります。
public static GameManager instance; private void Awake() { if (instance == null) { instance = this; DontDestroyOnLoad(gameObject); } else { Destroy(gameObject); } }
参照するときはGameManager.instance.〇〇 といった感じ。 DontDestroyOnLoadのSingletonクラスなので、どのシーンに移動しても存在し続け、複数存在することもありません。
また、ゲーム進行状況ステータスなどは変更が発生した瞬間に処理を発火したいケースが多いと思います。
その場合、EventかObserverパターンを使うと思うので一応書き方を書いておきます。(今回の主題からは外れるけども)
Event
public GameStatus NowGameStatus { get => _nowGameStatus; set { OnGameStatusChanged?.Invoke(value); _nowGameStatus = value; Debug.Log("now GameStatus: " + value); } } private GameStatus _nowGameStatus; public event Action<GameStatus> OnGameStatusChanged;
OnGameStatusChangedに実行したいメソッドを追加。
private void Start() { GameManager.instance.OnGameStatusChanged += 実行したいメソッド; } private void OnDisable() { GameManager.instance.OnGameStatusChanged -= 実行したいメソッド; }
Observerパターン
public GameStatus NowGameStatus { get => _nowGameStatus; set { statusSubject .OnNext(value); _nowGameStatus = value; Debug.Log("now GameStatus: " + value); } } private GameStatus _nowGameStatus; public IObservable<GameStatus> OnGameStatusChanged => statusSubject; private Subject<GameStatus> statusSubject = new Subject<GameStatus>();
OnGameStatusChangedを購読(UniRx使用)
GameManager.instance.OnGameStatusChanged.Subscribe(x => { 実行したいこと }).AddTo(this);
ReactivePropertyを使用すれば、もっと楽にかけますが、個人的には処理を切り分けたいので上記のような実装をしています。
設計に関して
ハッカソンにおいては正直がっちり設計固めている時間はないので、ざっくり脳内でできるレベルの設計しかしません。 ただ、循環参照やSOLID原則に配慮したり、SingletonやObserverパターンなどそこまで重くないデザインパターンは適度に使っています。
ここらへんもチーム構成によって変わります。
初心者がいるときに変にMVPでUI設計しちゃったり、DI導入しちゃったりすると初心者の人が全くもって理解できずにあまり開発に関われなくなってしまいます。
なので、そういうがっちりとした設計はせずに、「GameManagerのこれを参照したら勝手にステータスが送られてくるから使ってみてね」的な伝え方をして、ある程度のコードの循環参照をしちゃわないようにこちらで配慮するなどといった感じです。
まとめ
と、ここまで語ってきましたが、ハッカソンにおいて重要なのは"とりあえず動けばいい"なので後半の設計の話などは正直気にしなくていい気はします。
ただ、自分が制御できる範囲は制御することで、バグの原因究明が容易だったり、自分のレビューの手間が減ったり、コンフリクトが起きなかったり、etc...
メリットはたくさんあると思います。
ハッカソンは別に怖くないので、バンバン出て、作品を作るきっかけにしてください!
この記事がどなたかの参考になったら幸いです!