ダ○サバ○バーをつくる(第3号)
社内勉強会などもあり、ちょっと間が空いてしまいました。
お疲れ様です。
システム開発部、松山さんです。
前々回から唐突に始まった、「ダ○サバ○バーをつくる」の第3号をお届けします。
創刊号はこちら↓
第2号はこちら↓
第2号では、プレイヤーが扱う武器3種(最大レベル3)の実装まで行いました。
第3号では、ゾンビの生態(生成と移動)、集めた経験値でのレベルアップを実装します。
ゾンビ生成
ゲームシステム的には一定時間生き残る系だと思うので、時間経過とともにゾンビの登場数を増やすことが要件だと思います。
なので、2種類の生成処理を用意してみました。
① 一定時間(短)ごとに1体発生
② 一定時間(長)ごとに大量発生(ウェーブ)
① は初期の武器レベルが低い状態で、ちょこちょこ経験値を稼ぎ易くするイメージ
② は大量発生して逃げならが武器のレベル上げをするのと、武器レベルが上がってジェノサイドモードに入った時の爽快感用なイメージです。
どちらもプレイヤーの位置から一定距離離れた場所に発生するようにします。
また、一定時間の待ち合わせはコルーチンを使って対応しましょう。
一定時間ごとに1体発生する処理はこんな感じ。
private IEnumerator spawnBase() { const int MAX_BASE_ENEMYS = 50; const float spawnRadiusMin = 8.0f; const float spawnRadiusMax = 10.0f; const float spawnInterval = 1f; var waitTime = new WaitForSeconds(spawnInterval); while(true) { // 常に MAX体のエネミーが存在するように生成する if(enemies.Count < MAX_BASE_ENEMYS) { // プレイヤー位置 Vector2 playerPos = playerController.GetPlayer().Position; // 配置半径を決定 float r = UnityEngine.Random.Range(spawnRadiusMin, spawnRadiusMax); // 配置角度を決定 float angle = UnityEngine.Random.Range(0, 360); // 生成 spawn(playerPos, r, angle); } yield return waitTime; } else { yield return null; } }
・1秒ごと
・最大50体まで
・プレイヤーから 8.0 〜 10.0 離れた位置
・プレイヤーを中心に 0.0 〜 360.0 の角度でランダム
・生成(Instantiate)は spawn 関数で行う
こんな感じで生成していきます。
ウェーブ(②)は基本的な実装は ①と同じです。
座標・角度・生成を発生されるゾンビ数分繰り返す形になります。
1回目の発生より、2回目を多くする感じで段々発生数を増やしていきます。
その部分だけ抜粋。
// 生成数を wave から決定(最大100体) float spawnNum = Mathf.Min(wave * 10, MaxSpawnNum); // 等間隔に配置 float angleRange = 360 / spawnNum; // プレイヤー位置 Vector2 playerPos = playerController.GetPlayer().Position; // 生成 for(int i = 0; i < spawnNum; i++) { // 配置半径を決定 float r = spawnRadiusMax + UnityEngine.Random.Range(-1.0f, 1.0f); // 配置角度を決定 float angle = angleRange * i; // 生成 spawn(playerPos, r, angle); }
こんな感じ。
ゾンビ移動
本家を見ているとこんな印象でした。
・プレイヤーより遅い
・プレイヤーに向かって移動
・追従性は低そう
なので、処理としては
① プレイヤーに向けたベクトルを求める
② その方向へ一定時間移動
③ ①に戻る
これくらいで良さそうです。
private void walk() { // 歩く this.transform.localPosition += new Vector3(walkInfo.course.x * WalkSpeed * Time.deltaTime, walkInfo.course.y * WalkSpeed * Time.deltaTime, 0); // タイマー更新 walkInfo.timer -= Time.deltaTime; // タイマーが0以下になったら再設定 if(walkInfo.timer < 0) { walkInfo.timer = walkInfo.interval; // タイマー walkInfo.course = (walkInfo.player.Position - this.transform.localPosition).normalized; // 歩く方向 } }
立ち止まるとかもないので、Update() から毎フレーム実行するだけです。
こんな感じ。
経験値
経験値という呼び名が正しいかは分かりませんが、集めて武器を取得・レベルアップするアレ。
ゾンビを倒すと配置されて、プレイヤーが近づくと回収されます。
Enemy クラスで死亡アニメーション終了時に、ExpController クラスを通じて Exp オブジェクトを生成します。
生成処理
public void OnAnimationEnd() { // 死亡アニメーションが終わったら消滅 if (status == Status.Dead) { expController.Spawn(this.transform.localPosition); Destroy(this.gameObject); } }
回収はプレイヤーとの距離を Vector2.Distance 関数で求めて、閾値内のオブジェクトをプレイヤー座標に向けて移動させます。これもコルーチンで処理します。
回収処理
private IEnumerator move() { var elapsedTime = 0f; var startPosition = transform.localPosition; while (elapsedTime < 1f) { var targetPosition = playerController.GetPosition(); elapsedTime += Time.deltaTime * MoveSpeed; transform.localPosition = Vector2.Lerp(startPosition, targetPosition, elapsedTime); yield return null; } Destroy(gameObject); }
こんな感じ。
武器レベル用 UI
一定数経験値を集めたら武器ゲット or レベル上げできるアレ。
各武器のレベル制御は第2号で実装しているので、UI で操作できるように用意します。
・グレーの下敷き:Panel
・武器アイコン:Button
・文字と★☆:Text
これらを組み合わせると、それっぽくできます。
出来上がりはこちら
今回の実装と、創刊号・第2号と合わせるとこんな感じになります。
大分それっぽくなったのではないでしょうか?
あとは、
・プレイヤーの HP 管理
・制限時間
・マグネット
あたりを加えて完成にしたいと思います。
プロジェクト全体については、例によって GitHub に上がっています。
今回のブログはソース多めなので、全体を見たい方はこちらからご確認いただければと思います。
何かの参考になれば幸いです。
BeBe Survivor
それでは、今回のお話はこれまでになります。
創刊号はこちら↓
https://beyondjapan.com/blog/2024/02/survivor/
第2号はこちら↓
https://beyondjapan.com/blog/2024/03/survivor2/