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

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

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

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

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

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

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

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

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

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

【低コスト】Wasabi オブジェクトストレージ 構築・運用サービス

【低コスト】Wasabi オブジェクトストレージ 構築・運用サービス

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

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

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

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

【中国現地企業に対応】中国クラウド / サーバー構築・運用保守

【中国現地企業に対応】中国クラウド / サーバー構築・運用保守

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

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

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

「週間 ダ○サバ○バーを作る」、創刊!
唐突にディ○ゴス○ィーニ風に始めてみました。
ご無沙汰しております。
システム開発部、松山さんです。

スマホで Facebook やマンガアプリなんかを見ていると、
「ダ○サバ○バー」の広告をよく目にします。
ああいったわちゃわちゃした感じのゲームは嫌いではないので
どんな感じで作れるのか、とりあえず作ってみることにしました。
ちなみに、体験版以外プレイしたことはないので、
本来の作品とは大きく異なる部分があるかもしれませんが、ご容赦ください。

ざっくり要件

いつも通り、要件を洗い出していきます。
① 2D のフィールドがある(グリッドを敷き詰めるタイプ)
② 自キャラ(プレイヤー)を画面タッチで上下左右に操作できる
③ 複数の武器、レベルがある
④ エネミーが自然発生して、プレイヤーに向かって移動する
⑤ プレイヤー、エネミー、武器、それぞれで当たり判定がある
⑥ エネミーを倒すと経験値が表示され、集めることで新しい武器や武器のレベルアップができる

一旦、これくらい。
それぞれ細かな部分は作りながら考えます。
時間計測、SE などの細かな演出要素は将来的な課題としましょう(やらないかもね、という空気を醸しつつ)

プロジェクト作成

長いこと Unity 触っていなかったので、最新版をインストールしておきましょう。
2022.3.18f1 が LTS の最新みたいなので、これをインストールします。
Unity Hub 便利です。
プロジェクト名を「BeBeSurvivor」にして、2D アプリとして作成しておきます。

リソース

作るにあたって必要なリソース(スプライト)を Asset Store で見繕います。
2D 無料で物色していると、そのものずばりなアセットがありました。

◾️アンデッドサバイーアセットパック
プレイヤー、エネミー、武器など必要な素材が揃っています。
何なら動くので、これで全部終わりにできる勢いですが、
スプライトだけ、ありがたく使わせてもらうことにします。

◾️Lowpoly Textures Pack
フィールドに敷き詰める画像パックです。
アンデッドサバイバーアセットパックにもついていますが、こちらも使ってみましょう。

この2つを Package Manager を使ってインポートしておきます。

フィールドを敷き詰める

Lowpoly Texture Pack に大量の素材があるので、適当に使いそうなものを選びます。
ちょっとサイズが大きかったので、スケール調整しておいてみます。
こんな感じ。

これを Prefab化して、スクリプトで適当な範囲に敷き詰めます。
こんなイメージ。

で、スクリプトはこんな感じ。

// フィールドを生成(-50, -50, 0.5)から(50, 50, 0.5)までの範囲(5 x 5 のマス目)
Vector2 pos = new Vector2(-50, 50);
float size = 5f;
int cnt = 20;
for(int x = 0; x <= cnt; x++)
{
    for(int y = 0; y <= cnt; y++)
    {
        // x, y が 0 と cnt の時は filed[1] を、それ以外は filed[0] を生成
        int idx = (x == 0 || x == cnt || y == 0 || y == cnt) ? 1 : 0;
        // 中央の 1マスはfield[2]を生成
        if(x == cnt / 2 && y == cnt / 2)
        {
            idx = 2;
        }
        // 生成
        GameObject obj = Instantiate(field[idx], new Vector3(pos.x + (x * size), pos.y - (y * size), 0.5f), Quaternion.identity);
        obj.transform.parent = parent.transform;
    }
}

外周と中央だけ、違う画像を置いてみます。
今回は有限なフィールドでお茶を濁しますが、本来は無限スクロールの方が良い気はしています。
(しかし我ながら、これは大分適当なコードですね…)

プレイヤー

サバイバーアセットパックに良さげな素材が入っているので使わせていただきます。

アニメーションパターンもあるので、待機・移動・死亡のアニメーションを作成して、
Animator に設定しておきます。

動かすとこんな感じ(待機モーション)

移動(Input System)

プレイヤーキャラを移動させます。
画面中心を原点として、タップしている方向へキャラを移動させるイメージです。
暫く Unity から離れている間に Input System なるものがリリースされていたようです。
こちらも Package Manager からインポートしておきます。

軽く触ってみたのですが、Input Action からうまく情報を取ることができません。
一旦、Action はスルーして昔ながらのスクリプト制御で実装を進めてしまいます。
(Input Action はまた時間があるときにいじってみようと思います)

var mouse = Mouse.current;
if(mouse.press.ReadValue() == 1)    // press 状態
{
    if(holdTime > 0.0f)                 // 待機
    {
        holdTime -= Time.deltaTime;
        if(holdTime < 0.0f) holdTime = 0.0f;
    }
    else                                // 移動
    {
        Vector2 pos = mouse.position.ReadValue();
    }
}
else                                // release 状態
{
    holdTime = HoldTime;
}

操作情報は Mouse.current で取得できました。
タップ状態は mouse.press.ReadValue() で取得でき、タッチしていれば 1 、離していれば 0 が返ります。
タッチ状態が一定時間経過したら、タッチ座標を mouse.position.ReadValue() で取得し、
移動方向(ベクトル)を算出します。こんなイメージ。

2点の座標が判明しているのでシンプルに引き算でベクトルは算出できます。
長さは関係ないので normalized で正規化しておきます。

Vector2 vec = (pos - center).normalized;

これをプレイヤーの座標(loacalPosition)に設定してあげれば、キャラが移動します。
こんな感じ。(待機↔︎移動)

ついでに移動する方向の X成分だけ抜き出して、キャラの向きを切り替えておきます。( y軸に 180° 回す)

カメラ追従

この状態だとキャラがカメラ外へ行ってしまうので、常にキャラが画面中央に来るようにカメラをキャラに追従させます。
MainCamera の Transform の localPosition をキャラ座標と合わせるだけで OK です。

コード的にはこんな感じ。z 座標はカメラ固定なので注意。

// カメラを追従させる
Vector3 pos = playerController.GetPosition();
pos.z = -1.0f;
mainCamera.gameObject.transform.localPosition = pos;

実装後はこんな感じに。

エネミー

ひとまずは案山子で良いのでエネミーを適当に配置します。
これもサバイバーアセットパックに良さげな素材が入っているので使わせていただきます。(ありがたい)

アニメーションパターンもあるので、移動・死亡のアニメーションを作成して、
Animator に設定しておきます。

とりあえず、こんな感じで置いておきます。

コリジョン判定

当たり判定をとります。
Unity 標準で用意されている Collider2D を利用します。

まずは準備。
プレイヤー、エネミーそれぞれに Circle Collider 2D を追加します。
IsTrigger にチェックを入れておきます。

また、プレイヤーには Rigidbody 2D も追加しておきます。(エネミーには不要です)
今回、物理演算は不要のため、Body Type は Kinematic にします。

スクリプトで当たり判定を取得します。
以下の関数をプレイヤー、エネミーのコードに追加します。(Trigger 系)

private void OnTriggerEnter2D(Collider2D collision)
{
    // コライダーが当たった瞬間に呼ばれる
    // Enemy に当たったらダメージ
    if (collision.tag == "Enemy")
    {
        Debug.Log("Player Hit : " + collision.name);
    }        
}

private void OnTriggerStay2D(Collider2D collision)
{
    //コライダーが当たっていると継続して呼ばれる
}

private void OnTriggerExit2D(Collider2D collision)
{
    //コライダーがが離れた時に呼ばれる
}

当たった対象が何であるかは tag で判定することにします。
Player タグはデフォルトで登録されているので、エネミー(Enemy)タグと、武器(Weapon)タグを追加しておきます。
各オブジェクトの Tag に設定を行っておきます。

接触したらログ出力するようにして動かすとこんな感じになります。

今月はここまで

といったところで、今回はここまで。
基本的な仕組み的については、概ね実装できていると思います。
Unity には機能が揃っているので、比較的簡単に実装できます。

次号では
・武器の実装(3種類 x 3レベル)
あたりを実装できればと思います。

Unity プロジェクトについては、後ほど GitHub にアップする予定です。
暫くお待ちいただければと思います。
アップしたら記事を更新しておきます。

Unity プロジェクトを GitHub にアップしました。
何かの参考になれば幸いです。
BeBe Survivor

それでは、今回のお話はこれまでになります。
※ 冒頭で「週間」と書いていますが次号のアップは3月になる予定です。

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

【2024.6.30 CentOS サポート終了】CentOS サーバー移行ソリューション

【大阪 / 横浜】インフラエンジニア・サーバーサイドエンジニア 積極採用中!

【大阪 / 横浜】インフラエンジニア・サーバーサイドエンジニア 積極採用中!

この記事をかいた人

About the author

松山賢勝

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