ハイパーカジュアルゲーム「Hoppy Japan」をUnityで1ヶ月で作った話

はじめに

こんにちは!Unityエンジニアの佐藤です。
この記事はカヤックUnityアドベントカレンダー2018の4日目の記事になります。
今年の10月にリリースしたKAYACの新作ハイパーカジュアルゲーム、「Hoppy Japan」の開発についてお話します。
f:id:takashicompany:20181203102625p:plain

AppStore
Google Play

Hoppy Japanとは

「Hoppy Japan」は、弊社のソーシャルゲーム事業部の「天下一企画会」という制度から生まれたゲームです。

f:id:takashicompany:20181203102932p:plain

Hoppy Japanは手軽にスピード感と高揚感を体験できるワンタップアクションゲームです。


Hoppy Japan

開発メンバーは

  • プロデューサー
  • ディクレター
  • プランナー / Unityエンジニア (私)
  • 3Dアーティスト / デザイナー
  • サウンドアーティスト

の5人です。

製品版の制作は1ヶ月で行いました。

技術的なコンセプト

  • 全世界での配信を想定しているので、低スペックな端末でも動作するように
  • 短期間で確実に開発を進めるために、シンプルな構造や設計を意識する
  • アーティストがUnityで作業・確認をしやすいように

ということを念頭に置きました。

背景オブジェクトの再利用

「Hoppy Japan」は町並みを楽しむゲームです。
キャラが進むにつれて、「背景オブジェクトを配置する・後方で映らなくなった背景オブジェクトを消す」という実装をしています。
その際に、Instantiate関数やDestroy関数を用いるとCPU処理に負荷がかかりカクつく原因になり、ゲームの体験を損ねてしまいます。

f:id:takashicompany:20181203102654p:plain

上の図のようにカメラから描画されなくなった街の3DモデルをDestoryせず、前方へ再配置し直すという仕組みにしています。

f:id:takashicompany:20181203102843g:plain

上の図のようにキャラが進むにあわせて、非表示化と配置(再利用)を逐次実行しています。

シーンを分割 / 一括ロード

Hoppy Japanではタイトル画面・ゲームプレイ画面・リザルト画面・スキン一覧画面の4つのシーンでゲームを構成しています。

1つのシーンファイルでも作れそうなコンパクトなゲームですが、シーンを分割することで

  • 機能の切り分けがしやすい
  • 機能の確認を単独でできる
  • 複数人で作業した時に変更がぶつからない

といったことを実現する狙いがあります。

加えて、アプリ起動時に全てシーンをロードし、シームレスに遊べるように努めました。 f:id:takashicompany:20181203102829p:plain

これは「Hoppy Japan」の総メモリ使用量が約60MBだったため採用できた...という側面もあります。

作ったものを確認して調整するサイクルを早くするために、各シーンは単体でエディタ再生しても動作するようにしました。

独自のタイムスケール

このゲームには「スーパースロー」という機能が入っています。
長押しをするとキャラの移動速度がスローになって着地点が狙いやすくなる...というものです。
UnityにはTime.timeScaleというゲーム全体のスピードを調整する機能がありますが、今回は使いませんでした。
「キャラの移動やモーションだけスローにして、UIは通常のスピードで動かす」といったように要素毎に調整できる方が柔軟に対応できると考えたためです。
「Hoppy Japan」ではTime.timeScaleは使用せず、独自のtimeScaleを定義し、アニメーションインスタンス(DOTween)や、Animator、パーティクルなどスーパースローを適用したい要素に対して同期させる処理を書きました。

void Update()
{
    // GameManager.timeScaleという変数を定義して、同期させたいアニメーションに適用する
    
    // Animatorの場合
    _animator.speed = _gameManager.timeScale;

    // DOTweenのSequenceインスタンスの場合
    _doTweenSequence.timeScale = _gameManager.timeScale;

}

void LateUpdate()
{
    // ParticleSystemのみ、LateUpdateで同期させる(Updateだと動きが荒くなったので)
    _particleSystem.Simulate(
        Time.deltaTime * _gameManager.timeScale,
        true,
        false
    );
}

アーティストとの連携

アーティストが自ら作ったアートをゲームに組み込んで確認できるようにいくつか工夫をしました。

まず、「Header属性でアートの反映方法を書いておく」というものです。
Header属性はシリアライズ変数に定義することにより、UnityのInspectorビューに任意のテキストを表示することができます。
主にInspectrビューでの変数の説明に用いられます。

[Header("アートをゲームに組み込む手順")]
[Header("1. 作ったBlockにGameBlockコンポーネントを設定")]
[Header("2. StageCheckSceneを開く")]
[Header("3. GameStageコンポーネントのプレハブを修正する")]
[Header("- フィールドを修正する場合はField階層以下と同じように設定する")]
[Header("- ステップを追加する場合はStepPoolにプレハブを追加する")]
[Header("- ブロックを追加する場合はBlockPoolsにプレハブを追加")]
[Header("4. Stageのプレハブを保存したら「StageBundleData」を検索")]
[Header("5. StageBundleDataにプレハブを設定したらSaveProjectとシーン保存")]
[Header("6. GameSceneを再生するとゲームに登場する")]

f:id:takashicompany:20181203102629p:plain

やや力技な感じですが、プログラマは別途wiki等を用意せずに説明が書けるので手軽かと思います。

また、開発初期はシーン内のオブジェクトにInspectorで作ったアートを設定してもらう という方式でしたが シーンファイルをお互いに触る場合コンフリクトが生じてしまうので ScriptableObjectを用いてアーティストが簡単にアートを設定できるようにしました。

f:id:takashicompany:20181203102639p:plain

パフォーマンス・チューニング

一部の端末では、GPUの負荷が高くFPSの低下を招いていたため、 特定の条件を満たした端末(FPSが一定数を連続で下回り続けた場合など)では、解像度を調整して描画負荷を軽減するという手法をとりました。

Screen.SetResolution(Screen.width / 2, Screen.height / 2, true);

終わりに

以上のようにして「Hoppy Japan」は作られました。
ハイパーカジュアルゲームはUnityと相性が良く、既存の機能を適切に使うだけでも良いゲームが作れます。
ぜひ、チャレンジしてみてください!

明日は宮野有史の「BluetoothでUnityと接続するコントローラをESP32マイコンで作る」になります!