Menu
CoddyTech

単調にしない

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

レンガの角で跳ね返ると、ボールがほぼ水平のままになり、プレイヤーが待つ間、フィールドを何度も横切ることがあります。修正は速度の補正部分に加えます。方向の y 成分が小さすぎる場合は、最小値に設定して、もう一度正規化します。

Mathf.Abs は符号を除いた数値の大きさを返すため、上向きと下向きのボールを1つの判定で捉えられます。Mathf.Sign は負の数に対して -1、それ以外には 1 を返すため、下向きのボールは下向きのままです。

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

最小値を 0.4 にすると、方向 (0.99, -0.12, 0) の y は -0.4 になり、正規化後はおよそ (0.93, -0.37, 0) になります。つまり、右下向きのまま、戻ってこられるだけの傾きがつきます。これより傾きの大きい方向には手を加えません。

challenge icon

チャレンジ

中級

ボールがフィールドをほぼ水平に横切らないようにします。

  1. Ballで、public float minVertical = 0.3f;を追加します。
  2. FixedUpdateで、Vector3 direction = rb.linearVelocity.normalized;を取得します。Mathf.Abs(direction.y) < minVerticalの場合、direction.y = Mathf.Sign(direction.y) * minVertical;を設定し、もう一度正規化します。
  3. rb.linearVelocity = direction * speed;を設定します。

「プレイ」を押します。ボールが長時間、フィールドを水平に横切ることはありません。その後、「確認」を押します。

自分で試してみよう

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);
    }
}

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

自分で練習してみよう: Unityプレイグラウンド