You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity复刻Breakout游戏时Ball对象NaN位置错误原因求助

问题

我正在Unity中复刻Atari Breakout游戏(一款通过底部可控paddle反弹移动小球来击破方块的游戏)。我在Ball脚本中添加了一段代码,用于根据小球与paddle碰撞时的相对位置控制小球的反弹角度,代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Ball : MonoBehaviour
{
    public float speed = 5;
    float deltaX;
    float deltaY;
    double angleConst = 7 * System.Math.PI / 8 / 1.8;

    // Start is called before the first frame update
    void Start()
    {
        transform.position = new Vector2(Random.Range(-6, 6), -1);
        deltaX = Random.Range(-1 * (float)System.Math.Sqrt(speed), (float)System.Math.Sqrt(speed));
        deltaY = (float)System.Math.Sqrt(speed - System.Math.Pow(deltaX, 2));
    }

    void OnTriggerEnter2D(Collider2D col)
    {
        if(col.gameObject.tag == "Player")
        {
            // ball's horizontal position on the paddle
            double positionDiff = GameObject.Find("Paddle").transform.position.x - transform.position.x + 0.9;

            deltaX = (float)System.Math.Sqrt(speed * System.Math.Cos(positionDiff * angleConst));
            deltaY = (float)System.Math.Sqrt(-1 * speed * System.Math.Sin(positionDiff * angleConst));
        }
        else if(col.gameObject.tag == "Horizontal")
        {
            deltaY = -1 * deltaY;
        }
        else if(col.gameObject.tag == "Vertical")
        {
            deltaX = -1 * deltaX;
        }
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        transform.Translate(Vector2.right * deltaX * Time.fixedDeltaTime);
        transform.Translate(Vector2.down * deltaY * Time.fixedDeltaTime);
    }
}

当我在paddle碰撞时的deltaX、deltaY重新赋值中添加Math.Sqrt函数后,小球接触paddle就会持续弹出错误:“transform.position assign attempt for 'Ball' is not valid. Input position is { NaN, NaN, NaN }”,直至程序停止。未添加平方根函数前脚本运行正常,我查阅资料得知该错误可能与除零有关,但未发现脚本中有除零场景,请问是什么导致输入返回NaN?

分析与解决

出现NaN的核心原因是你给Math.Sqrt()传入了负数——平方根函数对负数计算会直接返回NaN,后续用这个NaN值更新小球位置,就会触发Unity的位置无效错误。

具体看代码里的两处问题:

  • 计算deltaX时:speed * System.Math.Cos(positionDiff * angleConst),当Cos返回负数时,整个表达式结果为负,传给Sqrt就会得到NaN。
  • 计算deltaY时:-1 * speed * System.Math.Sin(positionDiff * angleConst),当Sin返回正数时,整个表达式结果为负,同样会让Sqrt返回NaN。

另外还有几个潜在问题需要注意:

  1. GameObject.Find("Paddle")每次碰撞都执行查找,效率极低,建议提前用[SerializeField] private GameObject paddle;在编辑器中赋值,避免运行时重复查找。
  2. 你试图用平方根拆分速度分量的逻辑本身就有问题,正确的做法是先计算角度对应的方向向量,再乘以速度值,而非对三角函数结果开平方。

修正后的代码示例:

void OnTriggerEnter2D(Collider2D col)
{
    if(col.gameObject.tag == "Player")
    {
        // 计算小球相对于 paddle 中心的水平偏移比例(假设 paddle 宽度为1.8)
        float paddleCenterX = GameObject.Find("Paddle").transform.position.x;
        float relativeOffset = (transform.position.x - paddleCenterX) / 1.8f;
        // 限制反弹角度范围,避免垂直或接近垂直的反弹
        double bounceAngle = relativeOffset * (7 * System.Math.PI / 8);
        // 根据角度计算速度分量
        deltaX = (float)(speed * System.Math.Cos(bounceAngle));
        // 碰撞 paddle 后小球必须向上反弹,确保Y分量为正
        deltaY = Mathf.Abs((float)(speed * System.Math.Sin(bounceAngle)));
    }
    else if(col.gameObject.tag == "Horizontal")
    {
        deltaY = -1 * deltaY;
    }
    else if(col.gameObject.tag == "Vertical")
    {
        deltaX = -1 * deltaX;
    }
}

内容的提问来源于stack exchange,提问作者JustAnotherMusician7

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 11:49:58