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

Unity中如何实现类似乒乓球的射线反射轨迹?

问题与解决方案

我的代码如下:

using System.Collections.Generic;
using UnityEngine;

public class Test : MonoBehaviour
{
    [SerializeField] private Vector2 _vector;
    [SerializeField] private int _reflectionCount;
    [SerializeField] private float _distance;

    private void Update()
    {
        Vector2[] points = GetReflectionPoints(transform.position, _vector, _reflectionCount, _distance, Physics2D.AllLayers);

        for (int i = 0; i < points.Length; i++)
        {
            if (i + 1 == points.Length)
                break;

            Debug.DrawLine(points[i], points[i + 1], Color.red);
        }
    }

    public static Vector2[] GetReflectionPoints(Vector2 currentPosition, Vector2 vector, int reflectionCount, float distance, int layerMask)
    {
        List<Vector2> points = new(reflectionCount);

        vector.Normalize();
        points.Add(currentPosition);
        
        for (int i = 0; i < reflectionCount; i++)
        {
            RaycastHit2D hit = Physics2D.Raycast(currentPosition, vector, distance, layerMask);
            points.Add(hit.point);
            vector = Vector2.Reflect(vector, -hit.normal);
        }

        return points.ToArray();
    }
}

运行结果:
轨迹对比图

图中红色是当前得到的轨迹,绿色是期望的轨迹。我看过很多相关视频,它们要么使用Rigidbody2D.velocity要么使用循环实现,请问是否有其他方法(比如通过点积)来实现该效果?


问题根源与修复

你的代码核心问题是每次射线都从初始位置发射,没有更新当前位置到上一次的碰撞点,导致所有反射线段起点重合,出现错误轨迹。先修正这个问题,再看用点积实现反射的方法。

用点积手动实现反射向量

Vector2.Reflect本质就是基于点积推导的公式,你可以手动实现这个逻辑,公式如下:

反射向量 = 入射向量 - 2 * (入射向量 · 单位法线) * 单位法线

Unity中hit.normal是指向碰撞体外的单位向量,需注意入射方向与法线方向的对应关系。

修正后的代码(含点积实现反射)

using System.Collections.Generic;
using UnityEngine;

public class Test : MonoBehaviour
{
    [SerializeField] private Vector2 _vector;
    [SerializeField] private int _reflectionCount;
    [SerializeField] private float _distance;

    private void Update()
    {
        Vector2[] points = GetReflectionPoints(transform.position, _vector, _reflectionCount, _distance, Physics2D.AllLayers);

        for (int i = 0; i < points.Length - 1; i++)
        {
            Debug.DrawLine(points[i], points[i + 1], Color.red);
        }
    }

    public static Vector2[] GetReflectionPoints(Vector2 currentPosition, Vector2 vector, int reflectionCount, float distance, int layerMask)
    {
        List<Vector2> points = new(reflectionCount + 1); // 初始点+反射点,总数量需+1
        Vector2 direction = vector.normalized;
        points.Add(currentPosition);
        
        for (int i = 0; i < reflectionCount; i++)
        {
            RaycastHit2D hit = Physics2D.Raycast(currentPosition, direction, distance, layerMask);
            
            // 未命中时停止循环,避免添加无效点
            if (!hit) break;
            
            points.Add(hit.point);
            
            // 用点积手动计算反射向量
            float dotProduct = Vector2.Dot(direction, hit.normal);
            direction = direction - 2 * dotProduct * hit.normal;
            
            // 更新当前位置到碰撞点,下一次射线从这里发射
            currentPosition = hit.point;
        }

        return points.ToArray();
    }
}

关键改动说明

  1. 更新当前位置:每次碰撞后将currentPosition设为hit.point,保证下一次射线从碰撞点出发,轨迹连贯。
  2. 手动计算反射向量:用点积实现与Vector2.Reflect完全一致的逻辑,本质是同一数学原理的手动展开。
  3. 修正列表容量:初始点加反射点总数量为reflectionCount + 1,避免列表容量不足。
  4. 增加命中判断:未命中时终止循环,防止生成无效轨迹点。

修改后红色轨迹会与绿色期望轨迹一致,同时通过点积实现了反射计算,无需依赖Vector2.Reflect或Rigidbody组件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 22:17:18