Menu
CoddyTech

単調にしない

Coddyの「Unityゲーム:ブロック崩し」コースのレッスン 10/16。

ボールはほとんど横向きに飛ぶことがあります。レンガの角に当たって跳ね返ったり、別のボールに押されたりすると、ほぼ水平になり、落ちてこないまま何度もフィールドを横切ることがあります。プレイヤーがボールが落ちてくるのを待つ間、ゲームは停滞します。この問題は速度の修正で解決します。方向の長さを1単位にした後、y成分が小さすぎる場合は、yを最小値に設定して、もう一度正規化します。

この処理には2つのMathfメソッドを使います。Mathf.Absは符号を無視して数値の大きさを返すため、1つのテストで上昇中のボールと下降中のボールの両方を判定できます。Mathf.Signは負の数に対して-1、それ以外には1を返すため、新しいyもボールの進行方向を保ちます。つまり、下降中のボールはそのまま下降します。

float drift = -0.12f;
Debug.Log(Mathf.Abs(drift));    // 0.12
Debug.Log(Mathf.Sign(drift));   // -1

最小値を0.3、方向を(0.99, -0.12, 0)として考えてみましょう。yの大きさである0.12は0.3未満なので、yは-0.3になり、(0.99, -0.3, 0)を正規化すると、およそ(0.96, -0.29, 0)になります。右下へ向かう方向は保たれ、今度は戻ってこられるほど急になります。正規化するとyは再び最小値を少し下回るため、次のステップで補われ、0.3を少し下回る状態が保たれます。より急な方向には手を加えません。

challenge icon

チャレンジ

中級
  1. Ballにpublic float minVertical = 0.3fを追加します。
  2. FixedUpdate()で、速度を正規化したものを方向として取得します。そのyの大きさがminVertical未満の場合、yの符号を保ったままyをminVerticalに設定し、方向を再度正規化します。
  3. その後、前と同じように、方向にspeedを掛けた値を速度に設定します。

Playを押してから、Checkを押します。

自分で試してみよう

using UnityEngine;
using UnityEngine.InputSystem;

public class Ball : MonoBehaviour
{
    public Transform paddle;
    public float speed = 9f;

    Rigidbody rb;
    bool launched;

    void Awake()
    {
        rb = GetComponent<Rigidbody>();
        PhysicsMaterial bouncy = new PhysicsMaterial("Bouncy");
        bouncy.bounciness = 1f;
        bouncy.dynamicFriction = 0f;
        bouncy.staticFriction = 0f;
        bouncy.bounceCombine = PhysicsMaterialCombine.Maximum;
        bouncy.frictionCombine = PhysicsMaterialCombine.Minimum;
        GetComponent<Collider>().material = bouncy;
    }

    void Update()
    {
        if (launched) return;
        transform.position = paddle.position + Vector3.up * 0.5f;
        if (Keyboard.current.spaceKey.wasPressedThisFrame) Launch(new Vector3(0.3f, 1f, 0f));
    }

    public void Launch(Vector3 direction)
    {
        launched = true;
        rb.linearVelocity = direction.normalized * speed;
    }

    public void ResetToPaddle()
    {
        launched = false;
        rb.linearVelocity = Vector3.zero;
    }

    void FixedUpdate()
    {
        if (!launched) return;
        rb.linearVelocity = rb.linearVelocity.normalized * speed;
    }

    void OnCollisionEnter(Collision collision)
    {
        if (!collision.gameObject.CompareTag("Paddle")) return;
        float offset = transform.position.x - collision.transform.position.x;
        float halfWidth = collision.collider.bounds.size.x / 2f;
        float x = Mathf.Clamp(offset / halfWidth, -1f, 1f);
        rb.linearVelocity = new Vector3(x, 1f, 0f).normalized * speed;
    }

    void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("DeathZone")) GameManager.Instance.BallLost(this);
    }
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

Unityゲーム:ブロック崩しのすべてのレッスン