動画のような、周囲の敵を狙撃する一般的な全方位STGのひな型システムを作成しました。
この手のゲームは沢山あるので、googleで検索するとコガネブログさんの記事とか、わりと参考記事は出てくると思います。
ここでは、情報の一つとしてここに書き残しておきます。
環境
Unity2020.3.16f1
最終的なプレイヤーの構成
図のような親子関係で構成しました。
プレイヤーや武器の画像を回転させるためにオブジェクトを回転させることが想定されるため、それら個別の変更の影響を全体が受けないように親にルートオブジェクトを作成し、全体の移動、回転はルートオブジェクトで行うようにします。
- player_root
プレイヤーのゲームオブジェクト全体の座標を扱うためのルートオブジェクト
- player_sprite
プレイヤーのスプライトを描画する用
- weapon
武器の画像を描画する用
- muzzle
弾の発射口の座標を管理する用
プレイヤーの移動
おそらく大体の人がInput.GetAxisを使って上下左右の入力を取得して、移動を実装していると思います。
なんとなく今回はGetAxisを使用したくなかったため、下記のように実装しました。
using UnityEngine; public class PlayerMove : MonoBehaviour { [SerializeField] float speed = 0.5f; [SerializeField] Rigidbody2D rigidBody2D; Vector3 moveVector; void Update() { moveVector = Vector3.zero; if(Input.GetKey(KeyCode.A)){ moveVector += Vector3.left; } if(Input.GetKey(KeyCode.D)){ moveVector += Vector3.right; } if(Input.GetKey(KeyCode.W)){ moveVector += Vector3.up; } if(Input.GetKey(KeyCode.S)){ moveVector += Vector3.down; } } void FixedUpdate() { rigidBody2D.velocity = speed * moveVector; } }
今回は移動キーをWASDにし、それぞれの入力を移動用のベクトルに加算しています。
で、Updateの最初に移動用ベクトルを0にリセットすることで、一定速度を保って移動させています。
移動にはrigidbody2Dのvelocityを直接書き換える実装をしています。あまりスマートな実装ではありませんが、後々の衝突処理を考え、等速運動させるならこれで良いと思いました。
※rigidbody2Dはルートオブジェクトにアタッチしてます
マウスポイントの方向へ武器を向ける
using UnityEngine; public class MousePointAim : MonoBehaviour { [SerializeField] Transform rootTransform; [SerializeField] Transform weaponTransform; void Update() { var mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition); mousePos = Vector3.Scale(mousePos,new Vector3(1,1,0)); //Z成分を0に var dirVector = (mousePos - rootTransform.position); //マウス座標へ向くベクトルを取得 var quat = Quaternion.FromToRotation(Vector3.right,dirVector); //基準とした右方向ベクトルから目標への回転取得 weaponTransform.rotation = quat; } }
クォータニオンを使っていてちょっと難しい実装ですが、内容としては
マウスのワールド座標を取得し、プレイヤーの座標と差分を取得してマウスポインタ方向へのベクトル(dirVector)を取得。
Quaternion.FromToRotationで基準とした右方向ベクトル(Vector3.right)からdirVectorへ向けるように回転を取得して、適用しています。
基準を右方向にしている理由ですが、今回用意した武器の素材を右向きで作成していたからです。
上方向を向いた素材を作成している場合は、上方向ベクトル(Vector3.up)を基準にすると良いと思います。
弾の生成と移動
「弾の生成」と「弾の移動」を別々のスクリプトに分け、球の生成時に球の移動側のスクリプトに移動方向を渡す実装にしました。
移動方向を渡す際、武器に直接弾の生成用のスクリプトをアタッチすることで、武器の回転を取得して、その方向自体を渡しています。
弾の移動でも後々の衝突判定を考慮して、RigidBody2Dのvelocityを書き換えて速度を与えるようにしています。
using UnityEngine; //弾の生成 public class BulletInstance : MonoBehaviour { [SerializeField] GameObject bullet; [SerializeField] Transform muzzleTransform; void Update() { if(Input.GetMouseButtonDown(0)){ //マウスの左クリックを取得 InstanceBullet(); } } void InstanceBullet() { var obj = Instantiate(bullet,muzzleTransform.position,Quaternion.identity); //muzzleの位置に弾を生成 var dirVector = transform.rotation * Vector3.right; //武器の方向を取得 obj.GetComponent<BulletMove>().setMoveDir(dirVector); } }
using UnityEngine; //弾の移動 public class BulletMove : MonoBehaviour { [SerializeField] float speed; Vector3 moveDir; [SerializeField] Rigidbody2D rigidBody2D; public void setMoveDir(Vector3 moveDir) { this.moveDir = moveDir; rigidBody2D.velocity = moveDir * speed; } }
おわりに
これで、「キャラの移動」「武器をマウスカーソルへ向ける」「弾の生成、移動」の実装ができました。
シューティングとしては、的が無いといけませんが、当たり判定の実装については今回は省略しています。
気が向いたら、続きの記事を書くかもしれませんし、他の雛型を作成するかもです。
一週間以上更新してなかったら、何を更新したらいいか分からなくなったので、こんな感じの記事を書きました。
あとunity1weekの開催が予定されているようなので、コピペ用記事みたいなのを作っておいて時短になるといいな、という考えがあります。あと、全方位STGを適当に作ろうとしたら、予想より苦労したので書き残しておこうと思いました。