【大阪 / 横浜 / 徳島】インフラ / サーバーサイドエンジニア募集中!

【大阪 / 横浜 / 徳島】インフラ / サーバーサイドエンジニア募集中!

【導入実績 500社以上】AWS 構築・運用保守・監視サービス

【導入実績 500社以上】AWS 構築・運用保守・監視サービス

【CentOS 後継】AlmaLinux OS サーバー構築・移行サービス

【CentOS 後継】AlmaLinux OS サーバー構築・移行サービス

【WordPress 専用】クラウドサーバー『ウェブスピード』

【WordPress 専用】クラウドサーバー『ウェブスピード』

【格安】Webサイト セキュリティ自動診断「クイックスキャナー」

【格安】Webサイト セキュリティ自動診断「クイックスキャナー」

【予約システム開発】EDISONE カスタマイズ開発サービス

【予約システム開発】EDISONE カスタマイズ開発サービス

【100URLの登録が0円】Webサイト監視サービス『Appmill』

【100URLの登録が0円】Webサイト監視サービス『Appmill』

【200ヶ国以上に対応】グローバル eSIM「ビヨンドSIM」

【200ヶ国以上に対応】グローバル eSIM「ビヨンドSIM」

【中国への旅行・出張・駐在なら】中国SIMサービス「チョコSIM」

【中国への旅行・出張・駐在なら】中国SIMサービス「チョコSIM」

【グローバル専用サービス】北米・中国でも、ビヨンドのMSP

【グローバル専用サービス】北米・中国でも、ビヨンドのMSP

【YouTube】ビヨンド公式チャンネル「びよまるチャンネル」

【YouTube】ビヨンド公式チャンネル「びよまるチャンネル」

ダ○サバ○バーをつくる(第2号)

今回はそれほどお待たせしなかったかと思います。
システム開発部、松山さんです。

前回から唐突に始まった、「ダ○サバ○バーをつくる」の第2号をお届けします。

創刊号はこちら↓

ダ○サバ○バーをつくる(創刊号)

創刊号では、プレイヤー・エネミー・フィールドを表示して、プレイヤーを移動できるところまで実装しました。

「ダ○サバ○バー」の魅力は、大量のゾンビを多種多様な武器で蹴散らす爽快感にあると思いますので、第2号では武器を実装していこうと思います。

武器の種類

といっても、大量の武器種を全て実装するには工数が掛かり過ぎるので、とりあえず3種類くらいに絞ろうと思います。

① ショットガン(デフォルト武器)
② レンガ
③ ガーディアン

このあたり。

例によって、リソース的に使えそうな画像をアンデッドサバイバーさんから探して使います。

① ショットガンはそのものズバリがありました
② レンガはないです。農具が揃っているので「すき」を投げようと思います
③ ガーディアンも円盤上のものはないです。「かま」かぶん回しましょう

ショットガン

要件を洗い出します。

① プレイヤーの向き(進行方向)に発射
② レベルに応じて、発射される弾数が増える
③ レベルに応じて、発射間隔を短くする
④ レベルに応じて、射程距離を伸ばす
⑤ ゾンビが近くにいる場合、ゾンビに向けて発射(オートエイム)
⑥ 弾は①の方向に一定時間飛んで消える
⑦ ゾンビに当たった場合は貫通はしない

意外とあった。

① 〜 ⑤までは発射までのプロセス(Weapon_1)
⑥ 〜 ⑦ は弾のプロセス(Bullet)

になるので、それぞれクラスを分けます。
Weapon_1 で Bullet を生成して、打ち出された Bullet は自身で移動して消えるイメージ。

基本的には、プレイヤーの向きを Player クラスからもらい、基準となる射線を決定します。
あとは弾数に応じて始点(プレイヤー座標)を基準に ±n° 射線を回転してあげれば、扇状に弾を散らすことができます。

制御用にこういったパラメータを用意しておきました。

// 弾の発射間隔(3レベル)
private readonly float[] interval = { 1.5f, 1.25f, 1.0f };
// 弾の射程(3レベル)
private readonly float[] range = { 4.0f, 6.0f, 8.0f };
// 同時発射数(3レベル)
private readonly int[] simultaneous = { 1, 3, 5 };
// 発射角度(5方向:simultaneous の数に依存する)
private readonly float[] angle = { 0, 10, -10, 20, -20 };

⑤ については、
EnemyController クラスで近くにいるゾンビさんを探してもらいます。
いたらプレイヤー座標とゾンビ座標のベクトルが射線となります。
弾を散らすのは、上記と同じです。

弾を発射したあとは、Bullet クラスが自律的に動きます。
移動してゾンビにあたればコリジョン判定が取れるので、自身のオブジェクトを削除することで貫通しない状態が作れます。
当たらない場合も、一定時間後にやはり自身で削除です。

そんなこんなでショットガン完成。
出来上がりはこちら↓
・レベル1

・レベル2

・レベル3

レンガ(すき)

① 上に放り投げる
② ある程度の高さで弧を描いて落下
③ 落下し切ったところ(画面外)で削除
④ ゾンビに当たっても貫通する(ただし3体まで)
⑤ レベルに応じて、投げる すき の数を増やします。(時間差をつけて投げる)

ショットガンと同様なクラス構成にします。
Weapon_2 で Plow(すき)を生成。あとは Plow 自身が放物線を描いて飛びます。
特にエネミーの位置などは関係ありません。

一番の問題は放物線を描く計算式ですね。
少し探していたら素晴らしいサイトを発見しました。
矢を放物線状に飛ばしたい(2D 重力無視)
完璧です。Hatena Blog 様に感謝。
始点(p0)、頂点(p1)、終点(p2) で指定されたベジェ曲線上にある座標を求める数式になります。
頂点付近で すき の向きを 180° 回転させる処理も加えてコルーチンを作成します。

/// <summary>
/// 放物線を描く
/// </summary>
/// <param name="p0">始点</param>
/// <param name="p1">頂点(相対座標)</param>
/// <param name="p2">終点(相対座標)</param>
/// <returns>コルーチン</returns>
IEnumerator Throw (Vector3 p0, Vector3 p1, Vector3 p2)
{
   float distance = Vector3.Distance(Vector3.zero, p2);
   float speed = 0.3f;

   float t = 0f;
    while (t <= 1 && isAlive) {
        float Vx = 2 * (1f - t) * t * p1.x + Mathf.Pow (t, 2) * p2.x + p0.x;
        float Vy = 2 * (1f - t) * t * p1.y + Mathf.Pow (t, 2) * p2.y + p0.y;
        transform.position = new Vector3 (Vx, Vy, 0);

        t += 1 / distance / speed * Time.deltaTime;

        // t が 0.4 から 0.6 の間に、徐々に180°回転させる
        if (t > 0.4f && t < 0.6f)
        {
            float angle = Mathf.Lerp(0, courseGoal[course], (t - 0.4f) / 0.2f);
            transform.rotation = Quaternion.Euler(0, 0, angle);
        }
        yield return null;
    }
    Destroy (this.gameObject);
}

※ p1, p2 は相対座標値である必要があっため修正しました(2024.3.22)

あとは Plow 生成時に p0 〜 p2 を指定。
頂点の高さや、始点から終点までの幅に乱数を入れてバラつきを持たせてあげるとそれっぽくなります。
出来上がりはこちら↓
・レベル1

・レベル2

・レベル3

ガーディアン(鎌)

プレイヤーのまわりをクルクル回るやつ。

① プレイヤーを中心に周囲を回転
② レベルが上がると個数が増える
③ ゾンビに当たっても消えない(貫通)

うん。一番要件が少ない。

作りは一緒。Weapon_3 で Sickle クラスを生成。
配置する個数に応じて等間隔に置いて、同じ速度で回してあげるだけです。
円運動は三角関数で座標(オフセット値)が算出できます。
あとはプレイヤーを追従するのでプレイヤーの座標を足してあげれば OK です。

こういった定義を用意して、

// 半径
public const float Radius = 4.0f;
// 回転速度(秒間)
private const float RotateSpeed = 270f;
// 自転速度(秒間)
private const float SpinSpeed   = 1080f;

// 角度(位置を決定するための角度)
private float rotateAngle = 0.0f;
// 角度(自転)
private float spinAngle = 0.0f;

あとはフレームワークで処理するだけ。

void Update()
{
    // 角度(公転)を増加
    rotateAngle -= RotateSpeed * Time.deltaTime;
    // 角度(自転)を増加
    spinAngle -= SpinSpeed * Time.deltaTime;

    // 角度(公転)をラジアンに変換
    float rad = rotateAngle * Mathf.Deg2Rad;
    // 角度(自転)をラジアンに変換
    float spinRad = spinAngle * Mathf.Deg2Rad;

     // 位置を更新
    Vector2 pos = player.Position;
    transform.position = new Vector3(
        pos.x + Radius * Mathf.Cos(rad),
        pos.y + Radius * Mathf.Sin(rad),
        0.0f
    );

    // 回転を更新
    transform.rotation = Quaternion.Euler(0.0f, 0.0f, spinAngle);
}

円運動のついでに、鎌自身も回転させてます。
出来上がりはこちら↓
・レベル1

・レベル2

・レベル3

デバッグ用UI

動作確認用にデバッグ用のUIを作っておきます。

・全武器共通でレベル1〜3の設定を行うことができる
・武器1はデフォルトでレベル1
・武器2、3は初期では未取得状態

Legacy な Button などを追加って、サクッと作ります。

今月はここまで

といったところで、今回はここまで。
参考にさせてもらえたサイトのおかげもあり、割りかしサクッと実装できたと思います。
ミニゲームレベルでも、Unity で何かを作るのは楽しいです。

ちなみにフルアタック状態はこんな感じ。

次回は

・ゾンビさんの生態
・経験値の回収と、武器レベル上げ

あたりを実装できればと思います。

プロジェクト全体については、例によって GitHub に上がっています。
何かの参考になれば幸いです。
BeBe Survivor

それでは、今回のお話はこれまでになります。

この記事がお役に立てば【 いいね 】のご協力をお願いいたします!
7
読み込み中...
7 票, 平均: 1.00 / 17
340
X facebook はてなブックマーク pocket
【2025.6.30 Amazon Linux 2 サポート終了】Amazon Linux サーバー移行ソリューション

【2025.6.30 Amazon Linux 2 サポート終了】Amazon Linux サーバー移行ソリューション

この記事をかいた人

About the author

松山賢勝

長くゲーム開発会社でプログラムやプロジェクトマネージメントなどの業務に従事。
2019年より株式会社ビヨンド に入社。横浜オフィスに勤務。
サーバーサイド開発業務のプロジェクトマネージメントを中心に担当。(時々、プログラミング)
趣味は自転車(ロードレーサー)と競馬観戦。