今回は大人気ゲーム、Vampire Survivors(以下バンサバ)などで登場する武器の1つ、聖書の様に自機の周りを回転するオブジェクトの作り方を紹介したいと思います。おまけとして途中で逆回転したり、中心方向を向いた状態で回転するような機能も合わせて紹介したいと思います。
聖書のようなものを作る
まずはバンサバっぽいものを作ります。要素の確認とそれに伴って必要な実装を行っていきます。
作るものの確認
バンサバの聖書には、次のような要素があります。
- 自機を中心に回転する
- 複数のオブジェクトが位相をずらして回転する
また、プログラムをする上で、次のようなものを加えたいと思います。
- 360度を1周として、位相をずらす
- スピード調整が行える
これらを満たすことで聖書が作れるとしましょう。
プログラムで気をつけるポイント
今回の回転プログラムでは、sin/cosを使って回転させます。RotateAroundなどもありますが、他エンジンなどでの回転処理を考慮すると、ある程度プログラムで仕上げた方が汎用性も高くなるためです。
- X軸方向がコサイン、Y軸方向がサイン
- target(自機)を中心に座標変換
- radius(半径)分だけ離す
- スピードの分だけ毎フレーム角度を変化させる
このあたりに注意しながらプログラムを読んで見てください。
プログラム作成
プログラムは次のようになります。スクリプト名はRotateAroundTargetとします。
using UnityEngine;
public class RotateAroundTarget : MonoBehaviour
{
public Transform target; // 中心となるTransformをインスペクターでセット
public float rotationSpeed = 30f; // 回転速度
public float phaseOffset = 0f; // 位相オフセット
public float radius = 3f; // 中心との距離
private float angle = 0f;
public float Angle { get { return angle; } }
void Update()
{
float direction = reverseRotation ? 1f : -1f;
angle += direction * rotationSpeed * Time.deltaTime;
PositionUpdate(angle);
}
private void PositionUpdate(float angle)
{
float x = Mathf.Cos(Mathf.Deg2Rad * (angle + phaseOffset)) * radius;
float y = Mathf.Sin(Mathf.Deg2Rad * (angle + phaseOffset)) * radius;
Vector3 newPosition = target.position + new Vector3(x, y, 0f);
transform.position = newPosition;
}
// 後で使うので先に入れておく
public void SetOffset(float angle, float offset)
{
this.angle = angle;
phaseOffset = offset;
PositionUpdate(angle);
}
}
動かしてみる
プログラムが出来たら、実際に利用してみましょう。
- キャラクターを配置
- 好きな画像でOK。ない場合は2D Object>Circleとかでも良し!
- 聖書の代わりになるものを配置
- 四角いほうがわかりやすいので2D Object>Squareを追加
- コンポーネントの設定
- SquareにRotateAroundTargetコンポーネントを追加
- インスペクターにキャラクターにしたいTransformをセット(GameObjectセットでOK)
- 他の設定は一旦デフォルトのままでOK
セット出来たら動かして見てください。私の方ではキャラクターを動かす処理も入れています。
回転するスピードが物足りないなと感じた方はRotationSpeedを増やしてみてください。360にすると1回転するのに1秒かかります。
ゲームっぽい要素を追加していく
一応この要素を利用して、当たり判定何かを追加することで聖書っぽく振る舞えますが、簡単にゲーム実装時に必要そうな処理も追加しておきます。
- 聖書の個数が増えたときに角度を等間隔に空ける
- テスト機能としてスペースキーで聖書を増やす処理を実装
以上を満たすような機能として、次のようなプログラムを作成します。スクリプト名はSeishoManager。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SeishoManager : MonoBehaviour
{
[SerializeField] private Transform target;
public RotateAroundTarget rotateAroundPrefab;
private List<RotateAroundTarget> rotateAroundList = new List<RotateAroundTarget>();
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
RotateAroundTarget rotateAround = Instantiate(rotateAroundPrefab);
rotateAround.target = target;
rotateAroundList.Add(rotateAround);
float baseAngle = rotateAroundList[0].Angle;
for (int i = 0; i < rotateAroundList.Count; i++)
{
rotateAroundList[i].SetOffset(baseAngle, 360f / rotateAroundList.Count * i);
}
}
}
}
プログラムの勘所としては
- 新しい聖書を追加するたびに、オフセットの量が変化するので再セットを行う
- またベースのangleをすべての聖書が共有する必要があるので、オフセットと同時に先頭の聖書のangleを引き継ぐようにしている。
このプログラムを見て、回転させる処理はManager側でやったほうが良いのでは?と思ったあなた。非常にグッドです。今回はまず回してみるというのが前提だったので、完全に理解した人は、独自に書き換えてみてください。
マネージャーを使って回転させてみる
スクリプトが作成出来たら次は回転させてみましょう。
- Square(聖書代わり)をプレファブ化します
- プレファブ化が出来たらヒエラルキーからは削除しましょう。
- 空のGameObjectにSeishoManagerコンポーネントをアタッチ
- インスペクターにセット
- Targetr:キャラクター
- Rotate Around Prefab:先程プレファブ化した聖書(Square)
- インスペクターにセット
セットできたら動かして確認してみましょう。スペースキーを押すたびに、聖書が増えます!(私の方では雰囲気出すために画像を本に変えてます)
おまけ:Dizzy Roguesのような軌道
ここからはおまけ。聖書の動きと似ているようでちょっと違う、Dizzy Roguesというゲームの武器の軌道について。
回転処理の違う部分
自機を中心に回転するのは同じですが、次のような点が異なります。
- 武器の柄部分が中心を向いている
- 何かに当たると反対方向に回転する
実は回転させるだけの処理であれば、Time.timeとかを使えば、先程の回転角度を一致させる処理とかいらなかったんですが、こっちの対応のためにあえてTime.deltaTimeで実装しました。
スクリプトの更新
スクリプトはこれが全てになります。変更になるポイントは変数の追加とPositionUpdateメソッドの中になります。
using UnityEngine;
public class RotateAroundTarget : MonoBehaviour
{
public Transform target; // 中心となるTransformをインスペクターでセット
public float rotationSpeed = 30f; // 回転速度
public bool reverseRotation = false; // 逆回転を許可
public float phaseOffset = 0f; // 位相オフセット
public float radius = 1f; // 中心との距離
private float angle = 0f;
public float Angle { get { return angle; } }
void Update()
{
float direction = reverseRotation ? 1f : -1f;
angle += direction * rotationSpeed * Time.deltaTime;
PositionUpdate(angle);
}
private void PositionUpdate(float angle)
{
float x = Mathf.Cos(Mathf.Deg2Rad * (angle + phaseOffset)) * radius;
float y = Mathf.Sin(Mathf.Deg2Rad * (angle + phaseOffset)) * radius;
Vector3 newPosition = target.position + new Vector3(x, y, 0f);
transform.position = newPosition;
// オブジェクトが中心を向くように回転を調整
float angleToCenter = Mathf.Atan2(y, x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0f, 0f, angleToCenter - 90f);
}
public void SetOffset(float angle, float offset)
{
this.angle = angle;
phaseOffset = offset;
PositionUpdate(angle);
}
}
動かしてみる
先程のプログラムをそのまま流用するのであれば、次の手順で確認が出来ます
- 1つだけ聖書を出現させる
- 聖書のインスペクターを表示
- Reverse Rotationのチェックを入れたり外したりする
私の方では上下方向がわかりやすい様に画像を変更してますが、Squareでも十分に変化が確認できると思います。
コメント