Unityで敵撃破時にキャラクターの欠片が落ちる演出を設計した話
Unityで制作中のおはじきバトルゲームに、敵の撃破位置へキャラクターの欠片を表示する演出を設計しました。報酬抽選と演出を一致させる方法、重複付与の防止、ボスの確定ドロップについてまとめます。
Unityで制作しているスマートフォン向けのおはじきバトルゲームでは、敵を倒してキャラクターの欠片を集め、一定数に達すると味方として解放できる仕組みを作っています。
欠片の獲得処理は用意していましたが、画面上では敵を倒しても何が手に入ったのか分かりにくい状態でした。
リザルト画面まで進めば報酬を確認できても、戦闘中に獲得した実感がありません。
そこで今回は、敵を倒した場所に欠片を表示するドロップ演出を設計しました。
この記事では、演出の見た目だけでなく、報酬抽選と表示結果を一致させる方法、撃破位置の受け渡し、重複した撃破通知への対策、ボスの確定ドロップについてまとめます。
前回設計したキャラクター解放システム
キャラクター解放システムでは、敵から対応するキャラクターの欠片を集め、必要数に達すると解放できる仕組みを考えました。
通常キャラクターは欠片20個を基本の解放条件とし、未遭遇、未解放、解放済みの状態を分けて管理します。
詳しい設計については、前回の記事でまとめています。
Unityゲームでキャラクター解放と欠片システムを設計した話
今回は、その欠片を戦闘中に獲得したことが分かるようにするための演出を追加します。
演出を追加する理由
欠片が内部で正しく加算されていても、プレイヤーから見えなければ、敵を倒したことと報酬の獲得が結び付きません。
特に、同じクエストを繰り返し遊んで欠片を集める仕組みでは、ドロップした瞬間の分かりやすさが重要です。
今回の演出には、次の役割を持たせます。
- 欠片を獲得したことをその場で伝える
- どの敵から落ちたのか分かるようにする
- 1個と2個のドロップを区別する
- 欠片を集める楽しさを強める
- クエストを周回する目的を分かりやすくする
派手な演出を長時間再生するのではなく、戦闘の流れを止めずに獲得結果を伝えることを目指します。
共通の欠片演出を一つ用意する
登場するキャラクターごとに別の演出を作ると、キャラクター数が増えるたびに素材と設定が必要になります。
現在は多数のキャラクターを追加しているため、演出を個別に管理すると負担が大きくなります。
そこで、欠片のドロップ演出は全キャラクターで共通のものを使用する方針にしました。
演出自体は共通にし、次の情報だけを実行時に渡します。
- 欠片を落とした敵のキャラクターID
- 敵が倒れた位置
- 今回獲得した欠片の数
- 必要に応じて表示するアイコンや色
共通化することで、キャラクターが増えても同じ処理を利用できます。
将来、ボスだけ特別な演出にしたくなった場合も、基本処理を維持したまま表示部分だけ差し替えられる構成にします。
欠片の抽選は敵撃破時に一度だけ行う
今回の設計で最も重要なのは、報酬処理と演出処理で別々に抽選しないことです。
たとえば、報酬の加算時と演出の表示時にそれぞれ乱数を使うと、次のような問題が発生します。
- 演出では欠片が出たのに、実際の所持数が増えていない
- 所持数は増えたのに、演出が表示されない
- 演出では1個なのに、リザルトでは2個になっている
- 同じ敵から二度抽選され、想定より多く獲得できる
これを防ぐため、敵撃破時に一度だけドロップを判定し、その結果を報酬への加算と演出の両方で使用します。
処理の流れは次のようになります。
- 敵の撃破を確定する
- 対象キャラクターのドロップ設定を取得する
- 欠片の獲得数を一度だけ抽選する
- 獲得数をクエスト内の報酬へ加算する
- 同じ獲得数を演出処理へ渡す
- 0個なら演出を表示しない
見た目は報酬結果を伝える役割に限定し、演出側では報酬を決めません。
RecordDefeat()から今回の獲得数を返す
欠片の抽選と集計は、既存の QuestShardDropTracker.RecordDefeat() で行っています。
これまでは、抽選した数量をメソッド内で集計するだけで、今回の撃破で何個獲得したかを外側から取得できませんでした。
演出を表示するには、「今回の敵から落ちた数」が必要です。
そこで、RecordDefeat() が今回の獲得数を返すようにします。
考え方を簡略化すると、次のような形です。
int droppedAmount = shardDropTracker.RecordDefeat(enemyCharacterId);
if (droppedAmount > 0)
{
shardDropEffect.Play(
enemyCharacterId,
defeatedPosition,
droppedAmount
);
}
実際の抽選と報酬集計は RecordDefeat() の中で一度だけ行い、戻り値は表示の判断に使います。
演出のために再抽選しないため、内部の報酬と画面表示を常に一致させられます。
敵の撃破位置を演出へ渡す
欠片を画面中央へ表示するだけでも獲得は伝わりますが、どの敵が落としたのか分かりにくくなります。
今回は、倒された敵の位置から欠片が現れる演出にします。
敵撃破時には OhajikiWaveManager.NotifyEnemyDefeated() が呼ばれます。この通知に、倒された敵のキャラクター情報だけでなく、撃破時点の位置も渡します。
処理の流れは次のとおりです。
- 敵のHPが0になる
- 敵の削除前に現在位置を取得する
- 撃破通知へキャラクターIDと位置を渡す
- 欠片の獲得数を取得する
- 獲得数が1個以上なら、その位置で演出する
敵のGameObjectを先に破棄すると、演出に必要な位置を取得できなくなる場合があります。
そのため、敵を削除する前に必要な情報を保存しておきます。
ワールド座標とUI座標を分けて考える
敵はゲーム空間上のワールド座標で動いていますが、欠片の演出をUIとして表示する場合はCanvas上の座標が必要です。
敵の位置をそのままUIへ設定しても、Canvasの描画方式によっては異なる場所に表示されます。
そのため、演出の表示方法に応じて座標を変換します。
ワールド空間に表示する場合
敵と同じ空間にSpriteやParticleを生成します。
敵の位置をそのまま利用しやすい反面、カメラや他のキャラクターとの描画順を調整する必要があります。
UIとして表示する場合
敵のワールド座標を画面座標へ変換し、さらにCanvas上のローカル座標へ変換します。
UIとして扱うと、他の戦闘オブジェクトに隠れにくく、個数表示も組み合わせやすくなります。
今回のゲームは縦画面で戦闘エリアが固定されているため、端末の画面比率が変わっても表示位置がずれないことを実機で確認します。
1個と2個のドロップを表示する
通常の敵からは、欠片が1個落ちる場合と2個落ちる場合を用意する予定です。
正確な確率は、必要な周回数を確認してから調整します。現時点では、基本となる1個のドロップを比較的発生しやすくし、2個は少しうれしい結果として扱う方針です。
演出では、獲得数が分かるようにします。
たとえば、次の方法があります。
- 欠片アイコンの横に「×1」「×2」を表示する
- 2個の場合だけアイコンを二つに分ける
- 2個の場合だけ少し強い光を付ける
表示時間や動きを大きく変えすぎると戦闘のテンポを損なうため、基本演出は共通にし、個数表示だけで区別する方法が扱いやすいと考えています。
ドロップが0個なら表示しない
抽選結果が0個の場合は、欠片演出を再生しません。
「ドロップなし」と毎回表示すると、敵を倒すたびに不要な表示が発生し、戦闘画面が見にくくなります。
演出を出す条件を明確にしておきます。
if (droppedAmount <= 0)
{
return;
}
抽選結果が0個でも、キャラクターとの遭遇状態は別に更新します。
欠片を落とさなかった敵でも、一度戦ったキャラクターとして一覧へ表示できるようにするためです。
遭遇状態と欠片の所持数を分けて管理する設計が、ここでも役立ちます。
ボスは100%で欠片1個を落とす
通常の敵は確率で欠片を落としますが、ボスについては撃破時に100%で欠片1個を獲得できる方針です。
ボス戦は通常の敵より挑戦回数が少なく、戦闘時間も長くなることが想定されます。
苦労して倒したのに何も落ちなければ、報酬としての納得感が弱くなります。
そこで、ボスは最低1個を確定させます。
ただし、ボスの解放に必要な欠片数や、一日に挑戦できる回数によって適切な収集期間は変わります。
確定ドロップを維持しつつ、必要数や追加報酬を調整して入手難度を整えます。
重複した撃破通知で二重付与しない
敵の死亡処理では、HPが0になった瞬間、衝突処理、演出完了、GameObjectの削除など、複数の経路から処理が呼ばれる可能性があります。
同じ敵について NotifyEnemyDefeated() が二度呼ばれると、欠片抽選も二度行われ、報酬が重複する恐れがあります。
対策として、敵ごとに撃破処理済みかを記録します。
if (defeatProcessed)
{
return;
}
defeatProcessed = true;
より広い範囲で管理する場合は、敵インスタンスの識別子を集合へ保存し、同じ識別子の撃破通知を一度だけ受け付ける方法もあります。
重要なのは、演出の重複だけでなく、報酬の付与そのものを一度に制限することです。
見た目だけを抑えても、内部で欠片が二重に増えていれば問題は解決しません。
Wave遷移と演出の関係
最後の敵を倒した直後には、次のWaveへの切り替えが始まります。
欠片演出を敵のGameObjectへ直接ぶら下げていると、Wave切り替え時に敵と一緒に削除され、演出が途中で消える可能性があります。
そのため、演出は敵本体とは別の管理対象として生成します。
報酬の加算は敵撃破時点で確定させ、演出が途中で消えたとしても獲得結果が失われないようにします。
また、Wave遷移を演出終了まで待つか、演出を再生したまま次へ進むかは、戦闘テンポを見ながら判断します。
短い欠片演出であれば、Wave遷移を止めずに表示を継続する方が自然です。
リザルト画面との整合性
戦闘中の演出で表示した欠片は、クエスト終了後のリザルトにも同じ数量を表示します。
敵を倒すたびに獲得数をクエスト内の報酬へ集計し、リザルトではその集計結果を読み取ります。
たとえば、同じキャラクターの敵を複数倒した場合は、次のように加算します。
1体目:欠片1個
2体目:ドロップなし
3体目:欠片2個
合計:欠片3個
戦闘中には1個と2個の演出をそれぞれ表示し、リザルトでは合計3個と表示します。
この集計にも再抽選を使わず、各撃破時に確定した数量を加算します。
演出中に報酬処理を待たせない
欠片演出は報酬を決める処理ではなく、確定した結果を伝える処理です。
そのため、演出の再生に失敗したり、設定で演出を無効にしたりしても、欠片の獲得結果には影響させません。
処理の優先順位は次のようにします。
- 撃破の重複を確認する
- 欠片の抽選結果を確定する
- 報酬へ加算する
- セッション内の集計へ保存する
- 表示可能なら演出する
演出を報酬処理の必須条件にしないことで、画面表示の問題がプレイヤーデータへ影響することを防ぎます。
オブジェクトを使い回すか検討する
敵を倒すたびに演出用GameObjectを生成し、終了後に破棄する方法は分かりやすい一方、連続して多くの敵を倒す場面では生成と破棄が繰り返されます。
演出が軽量で発生頻度も低ければ、最初は単純な生成方式でも問題ありません。
今後、敵数や同時ドロップ数が増えて負荷が目立つ場合は、オブジェクトプールを使って演出を再利用します。
最初から複雑にしすぎず、実機での動作を確認して必要になった段階で改善する方針です。
実装後に確認したい項目
欠片ドロップ演出は、報酬、敵撃破、座標、Wave遷移など複数の機能と関係します。
実装後は、次の内容を確認します。
報酬抽選
- 敵1体につき抽選が一度だけ行われるか
- 0個、1個、2個の結果を正しく扱えるか
- ボスは必ず1個獲得できるか
- 対象キャラクターの欠片へ加算されるか
- 同じ敵から二重に付与されないか
演出表示
- 欠片が敵の撃破位置に表示されるか
- 0個の場合は表示されないか
- 1個と2個を見分けられるか
- 複数の敵を連続で倒しても表示できるか
- 画面端でも演出が切れないか
- 異なる画面比率のAndroid端末で位置がずれないか
Waveとクエスト終了
- 最後の敵を倒しても演出が不自然に消えないか
- Wave遷移後の敵へ処理が引き継がれないか
- 最終Waveでも報酬が保存されるか
- リタイア時に未確定報酬が残らないか
- リザルトの合計数と戦闘中の獲得数が一致するか
セーブと所持数
- クエスト終了後に所持数へ反映されるか
- アプリ再起動後も所持数が保持されるか
- 解放済みキャラクターの欠片も正しく増えるか
- 所持上限を設定した場合にあふれないか
特に、演出の表示回数ではなく、実際に加算された所持数を確認することが重要です。
設計して分かったこと
欠片を表示するだけであれば、敵の位置に画像を出すことで実現できます。
しかし、ゲームの報酬として扱う場合は、見た目よりも内部データとの一致が重要です。
今回の設計では、次の点を特に意識しました。
- 報酬を決める処理と表示する処理を分ける
- 抽選結果は一度だけ確定する
- 演出には確定済みの結果を渡す
- 敵を削除する前に位置を保存する
- 同じ撃破通知を複数回処理しない
- 演出に失敗しても報酬を失わない
- 戦闘中の表示とリザルトの合計を一致させる
プレイヤーが目にする演出と、実際に保存される報酬が同じ結果になることが、安心して遊べる仕組みにつながります。
今後追加したいもの
基本的なドロップ演出が完成した後は、次の改善も検討しています。
- 欠片アイコンが少し浮き上がる動き
- 獲得数に応じた表示の変化
- 画面端で演出が切れない位置補正
- 獲得した欠片が報酬欄へ移動する表現
- ボス専用の少し豪華な演出
- 音と振動によるフィードバック
- 設定から演出を軽量化できる機能
演出を増やす場合も、報酬抽選は変更せず、表示部分だけを交換できる構成を維持します。
まとめ
今回は、Unityで敵を倒したときにキャラクターの欠片が落ちる演出を設計しました。
主な方針は次のとおりです。
- 欠片演出は全キャラクターで共通化する
- 敵撃破時にドロップを一度だけ抽選する
RecordDefeat()から今回の獲得数を返す- 報酬処理と演出で同じ結果を使用する
- 敵を削除する前に撃破位置を取得する
- 0個の場合は演出を表示しない
- 通常敵は1個または2個、ボスは100%で1個を基本とする
- 重複した撃破通知で欠片を二重付与しない
- 戦闘中の表示とリザルトの合計を一致させる
- 演出の成否を実際の報酬へ影響させない
欠片を獲得した瞬間が見えるようになることで、キャラクターを集める目的が分かりやすくなり、敵を倒したときの手応えも強くなります。
今後は実機で表示位置とテンポを確認しながら、戦闘を邪魔せず、獲得した喜びが伝わる演出へ調整していきます。