解説

Unityでブロック崩しを作成する:その4

前回に引き続き、ブロック崩しの作成を行います。今回はボールの挙動を改善します。止まったり速くなりすぎないようにするために、スピードに制限を書けます。また、プレイヤーとの衝突時に跳ね返る方向をプレイヤーとの衝突位置によって変えるようにします。

Unityプログラミング講座一覧はこちら

Unityプログラミング講座
ゲームエンジンUnityを使ってゲーム制作を始めようと思っている人へ向けた解説記事です。初心者がUnityをインストールするところから始めてゲームを制作するのに必要な知識をできる限りステップバイステップでまとめるつもりです。

スピードに制限をかける

現在、ボールの動きは物理演算に完全に頼っており、プレイヤーとのぶつかり方によっては速度が上がったり、あるいは、止まってしまいそうなぐらい遅くなってしまうことがあります。物理学的には正しくても、ゲームとして遊べなければ意味がありません。そのため、ボールの動きはある程度、制御できるようにしたほうがよいでしょう。

そこで、ボールの動く速さに最大値と最小値を設定します。以下のようにBallスクリプトを変更してください。

using UnityEngine;
class Ball : MonoBehaviour
{
    public float speed = 5f;
    // 速さの最大値を指定する変数を追加
    public float minSpeed = 5f;
    // 速さの最小値を指定する変数を追加
    public float maxSpeed = 10f;

    Rigidbody myRigidbody;

    void Start()
    {
        myRigidbody = GetComponent<Rigidbody>();
        myRigidbody.velocity = new Vector3(speed, speed, 0f);
    }

    // 毎フレーム速度をチェックする
    void Update()
    {
        // 現在の速度を取得
        Vector3 velocity = myRigidbody.velocity;
        // 速さを計算
        float clampedSpeed = Mathf.Clamp(velocity.magnitude, minSpeed, maxSpeed);
        // 速度を変更
        myRigidbody.velocity = velocity.normalized * clampedSpeed;
    }
}

このスクリプトを理解するには、少しばかりベクトルの知識が必要になります。ややこしくならないように、2次元で話をします。

ベクトルには方向と大きさという概念があります。例えば、ベクトルの値が(3, 0)の場合、ベクトルの方向はxの正方向で、大きさは3となります。同様に、ベクトル(0, 3)は方向がyの正方向、大きさが3となります。

では、ベクトルが(3, 3)の場合はどうなるでしょうか。方向は斜め45度になり、大きさは三平方の定理から3√2になります。

ベクトル方向大きさ
(3, 0)(1, 0)3
(0, 3)(0, 1)3
(3, 3)(1/√2, 1/√2)3√2

ベクトルの大きさはmagnitudeで計算できます。例えば、ベクトルvelocityの大きさを取得するには以下のように書くことができます。

velocity.magnitude

一方、ベクトルの方向を表す場合には大きさ1のベクトルが良く用いられます。ベクトル(3, 0)の場合には方向は(1, 0)が該当します。(3, 3)の場合、大きさが3√2なので、x成分とy成分をそれぞれ3√2で割り算した(1/√2, 1/√2)が長さ1のベクトルとなります。

大きさ1のベクトルが使われるのは、計算が楽になるからです。方向を変えずに大きさだけを変更したい場合には、大きさ1のベクトルに掛け算してやればいいだけです。方向(1, 0)、大きさ5のベクトルを計算したかったら、(1, 0)にそのまま5をかけて(5, 0)が得られます。

方向をそのままにして大きさを1にしたベクトルを取得するにはnormalizedが利用できます。例えば、ベクトルvelocityの方向を表す大きさ1のベクトルは以下のように書くことができます。

velocity.normalized

それでは、Ballスクリプトに戻って確認してみましょう。

void Update()
{
    // 現在の速度を取得
    Vector3 velocity = myRigidbody.velocity;
    // 速さを計算
    float clampedSpeed = Mathf.Clamp(velocity.magnitude, minSpeed, maxSpeed);
    // 速度を変更
    myRigidbody.velocity = velocity.normalized * clampedSpeed;
}

まず、Rigidbodyコンポーネントにアクセスして現在の速度を取得し、変数velocityに代入します。

次に、速さに制限を書けるためにMathf.Clampメソッドを使用して制限後の速さを計算して変数clampedSpeedに代入しています。Mathf.Clampメソッドは値をある範囲内に強制的に収めるメソッドです。以下のように用います。

// Mathf.Clamp(値, 最小値, 最大値)のように用いる

// 5は1を超えているので1になる
Mathf.Clamp(5, 0, 1);

// 5は6より小さいので6になる
Mathf.Clamp(5, 6, 10);

したがって、1つめの引数に現在のボールの速さであるvelocity.magnitudeを、2つめの引数に最低速度のminSpeedを、3つめの引数に最高速度のmaxSpeedを指定すれば、制限をかけた後の速度を計算できます。

変数minSpeed, maxSpeedに関しては、インスペクタービュー上で値を変更できるようにpublicなフィールド変数として宣言しています。

最後に、Rigidbodyコンポーネントの速度velocityに大きさを変えたベクトルを設定してあげれば完了です。normalizedを用いて方向を計算し、それに速さを掛け算すれば、方向を変えずに速さだけを変更できます。

当たった位置によって跳ねる方向を変える

ボールが当たった場所によって、ボールの跳ね返る方向が変わるようにします。具体的には、プレイヤーの右側に当てればボールは右に、左側に当てればボールは左に跳ねるように変更します。これにより、ボールを当てる位置が重要になり、プレイにテクニックが必要になります。物理的には正しくないですが、ゲームとして面白くなるならこのような挙動もありです。

Ballスクリプトを以下のように変更してください。

using UnityEngine;
class Ball : MonoBehaviour
{
    public float speed = 5f;
    public float minSpeed = 5f;
    public float maxSpeed = 10f;

    Rigidbody myRigidbody;
    // Transformコンポーネントを保持しておくための変数を追加
    Transform myTransform;

    void Start()
    {
        myRigidbody = GetComponent<Rigidbody>();
        myRigidbody.velocity = new Vector3(speed, speed, 0f);
        // Transformコンポーネントを取得して保持しておく
        myTransform = transform;
    }

    void Update()
    {
        Vector3 velocity = myRigidbody.velocity;
        float clampedSpeed = Mathf.Clamp(velocity.magnitude, minSpeed, maxSpeed);
        myRigidbody.velocity = velocity.normalized * clampedSpeed;
    }

    // 衝突したときに呼ばれる
    void OnCollisionEnter(Collision collision)
    {
        // プレイヤーに当たったときに、跳ね返る方向を変える
        if (collision.gameObject.CompareTag("Player"))
        {
            // プレイヤーの位置を取得
            Vector3 playerPos = collision.transform.position;
            // ボールの位置を取得
            Vector3 ballPos = myTransform.position;
            // プレイヤーから見たボールの方向を計算
            Vector3 direction = (ballPos - playerPos).normalized;
            // 現在の速さを取得
            float speed = myRigidbody.velocity.magnitude;
            // 速度を変更
            myRigidbody.velocity = direction * speed;
        }
    }
}

跳ね返る方向を計算するために、ボールの現在位置を取得する必要があるので、Transform型の変数を用意して、Startメソッドで自身のTransformコンポーネントを取得しています。

続いて、衝突した際の跳ね返る方向を制御するために、衝突時に呼ばれるOnCollisionEnterメソッドを追加します。OnCollisionEnterメソッドの引数は衝突した相手の情報が含まれるCollision型のデータが渡されます。Blockスクリプトではこの変数は使用しませんでしたが、今回はぶつかった相手を区別するために使用します。つまり、壁やブロックに当たったときには普通に跳ね返り、プレイヤーに当たったときのみ特殊な跳ね返りを起こすようにします。

衝突した相手のゲームオブジェクトには、Collision型の変数collisionにgameObjectをつけることでアクセスできます。

// 衝突した相手のゲームオブジェクト
collision.gameObject

ぶつかった相手の種類を識別する方法の1つとしてタグを利用する方法を紹介します。ゲームオブジェクトにはタグと呼ばれる文字列をそれぞれに設定できるので、プレイヤーのゲームオブジェクトにPlayerというタグを設定して、プレイヤーとそれ以外を区別することにします。

タグの設定はインスペクタービューで行います。Playerゲームオブジェクトを選択してTagの部分をクリックし、Playerを選択してTagをPlayerに変更しましょう。(Playerはよく使われるタグなので、デフォルトで用意されていますが、「Add Tag…」から自分で好きなタグを作成することもできます。)

スクリプトを用いてタグを判定するにはCompareTagメソッドを使用します。

// CompareTag(タグ)のように使う
// 衝突した相手のゲームオブジェクトのタグがPlayerだったらtrueになる
collision.gameObject.CompareTag("Player");

if文と組み合わせることで、特定の相手とぶつかった時の処理を行うことができます。

// 衝突したときに呼ばれる
void OnCollisionEnter(Collision collision)
{
    if (collision.gameObject.CompareTag("Player"))
    {
        // タグがPlayerであるゲームオブジェクトの場合のみ実行される処理をここに書く
    }
}

タグの判定方法

タグを判定する方法には、CompareTagメソッドを使用する以外にもタグを直接比較する方法もあります。今回のケースだと、if文の条件を以下のように書いても問題なく動作します。

if (collision.gameObject.tag == "Player")
{
    // Playerとの衝突処理
}

CompareTagメソッドを用いる方法との違いは計算速度です。CompareTagメソッドのほうが高速ですので、CompareTagメソッドを使用することを推奨されています。

ただ、個人的にはどちらの方法でもいいんじゃないかと思っています。「タグを用いてゲームオブジェクトを区別して処理する」という考え方が重要であって、どちらの書き方が優れているかなどの細かい話はUnityでのゲーム制作に慣れてからにしたほうが学習が捗ると思います。

ボールがプレイヤーの右側に当たったら右に、左側に当たったら左に跳ね返るようにするには、跳ね返る方向を計算する必要があります。

まず、計算に必要なプレイヤーとボールの位置をTransformコンポーネントにアクセスして取得します。

// プレイヤーの位置を取得
Vector3 playerPos = collision.transform.position;
// ボールの位置を取得
Vector3 ballPos = myTransform.position;

今回のケースでは、ボールの跳ね返り方向は、プレイヤーから見たボールの方向にしています。

プレイヤーから見たボールの方向はベクトルの引き算により求められます。例えば、プレイヤーが(3, 0)にいて、ボールが(2, 1)にいるときには、プレイヤーから見てボールは左斜め上にいるので、方向は(-1√2, 1/√2)になります。これはボールの位置からプレイヤーの位置を引き算して得られるベクトル(2, 1)-(3, 0)=(-1, 1)の方向に等しいです。

つまり、ボールの座標からプレイヤーの座標を引き算したうえでnormalizedを使えば、大きさが1の方向を表すベクトルが計算できます。

// プレイヤーから見たボールの方向を計算
Vector3 direction = (ballPos - playerPos).normalized;

方向が得られたので、あとは速さを掛け算してやれば、衝突後のボールの速度が求められます。速さは、ボールの速さをそのまま使用すればいいでしょう。

// 現在の速さを取得
float speed = myRigidbody.velocity.magnitude;

最後に、Rigidbodyコンポーネントのvelocityに求めた速度を代入すれば完了です。

// 速度を変更
myRigidbody.velocity = direction * speed;

まとめ

今回は、ボールの速さに制限をかけて、跳ね返る方向を工夫しました。次回はビルドを行い実行ファイル化することで、Unityなしでもゲームがプレイできるようにします。

Unityプログラミング講座一覧はこちら

Unityプログラミング講座
ゲームエンジンUnityを使ってゲーム制作を始めようと思っている人へ向けた解説記事です。初心者がUnityをインストールするところから始めてゲームを制作するのに必要な知識をできる限りステップバイステップでまとめるつもりです。
タイトルとURLをコピーしました