You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Unity 2019.3.7f1 2D愤怒小鸟式发射轨迹线弯曲问题排查

嘿,我来帮你搞定这个2D抛物线路径的问题!你要的是愤怒小鸟那种拉拽蓄力后的精确轨迹,还要带碰撞检测和长度控制,现在代码只能画直线对吧?没问题,咱们一步步把它改好。

修复方案:2D抛物线路径绘制 + 碰撞检测

核心原理

咱们要模拟的是恒定重力下的抛体运动,物理公式很清晰:

  • 水平方向(X轴):因为无摩擦,所以是匀速运动,位置随时间线性变化
  • 垂直方向(Y轴):受恒定重力影响,是匀变速运动,位置会有二次曲线的变化

对应的公式是:

x = 初始X + 水平速度 * 时间
y = 初始Y + 垂直速度 * 时间 + 0.5 * 重力加速度 * 时间²

我们可以通过逐时间步计算位置点,再把这些点传给Line Renderer,就能画出完美的抛物线了。

完整修改后的TrajectoryShower脚本

using UnityEngine;

public class TrajectoryShower : MonoBehaviour
{
    [Header("轨迹设置")]
    public LineRenderer lineRenderer;
    public Transform launchPoint; // 发射起点(比如弹弓位置)
    public float maxForce = 10f; // 最大蓄力力量
    public float timeStep = 0.1f; // 每两个轨迹点的时间间隔
    public int maxPointCount = 50; // 轨迹最大点数(控制轨迹长度)
    public LayerMask collisionLayers; // 需要检测碰撞的图层

    private Vector2 initialVelocity; // 发射初始速度

    private void Awake()
    {
        // 初始化Line Renderer
        lineRenderer.positionCount = 0;
        lineRenderer.useWorldSpace = true;
        lineRenderer.loop = false;
    }

    // 调用这个方法更新轨迹(比如在拉拽蓄力时调用)
    public void UpdateTrajectory(Vector2 pullDirection, float pullDistance)
    {
        // 把拉拽的距离和方向转换成初始速度(限制最大力量)
        float force = Mathf.Clamp(pullDistance, 0f, maxForce);
        initialVelocity = -pullDirection.normalized * force; // 负号是因为拉拽方向和发射方向相反

        CalculateAndDrawTrajectory();
    }

    private void CalculateAndDrawTrajectory()
    {
        lineRenderer.positionCount = 0;
        Vector2 currentPosition = launchPoint.position;
        float elapsedTime = 0f;

        // 添加第一个点(发射起点)
        AddTrajectoryPoint(currentPosition);

        for (int i = 0; i < maxPointCount; i++)
        {
            elapsedTime += timeStep;

            // 用物理公式计算下一个位置
            Vector2 nextPosition = new Vector2(
                currentPosition.x + initialVelocity.x * elapsedTime,
                currentPosition.y + initialVelocity.y * elapsedTime + 0.5f * Physics2D.gravity.y * elapsedTime * elapsedTime
            );

            // 检测当前点到下一个点之间是否有碰撞
            RaycastHit2D hit = Physics2D.Linecast(currentPosition, nextPosition, collisionLayers);
            if (hit.collider != null)
            {
                // 碰到物体,把碰撞点作为最后一个点
                AddTrajectoryPoint(hit.point);
                break;
            }

            // 添加正常轨迹点
            AddTrajectoryPoint(nextPosition);
            currentPosition = nextPosition;
        }
    }

    private void AddTrajectoryPoint(Vector2 point)
    {
        lineRenderer.positionCount++;
        lineRenderer.SetPosition(lineRenderer.positionCount - 1, point);
    }

    // 隐藏轨迹(比如发射后调用)
    public void HideTrajectory()
    {
        lineRenderer.positionCount = 0;
    }
}

关键细节说明

  1. 蓄力转速度:拉拽方向和发射方向是相反的,所以用-pullDirection来修正,同时用Mathf.Clamp限制最大力量,避免速度超标。
  2. 碰撞检测:用Physics2D.Linecast检测每一段轨迹的线段,如果碰到目标图层的物体,就停止绘制并把碰撞点作为终点,完美解决穿透问题。
  3. 轨迹长度控制:通过maxPointCounttimeStep共同控制,比如timeStep=0.1+maxPointCount=50就是最多显示5秒的轨迹,你可以根据需求调整这两个参数。
  4. Line Renderer设置:一定要开启useWorldSpace,因为我们计算的是世界坐标下的位置。另外可以调整startWidthendWidth让轨迹线更美观。

使用方法

  1. 把这个脚本挂到任意GameObject上,比如弹弓对象。
  2. lineRenderer赋值一个Line Renderer组件,调整好材质和宽度。
  3. 设置launchPoint为发射的起点(比如弹弓的发射位置)。
  4. 在你的蓄力逻辑里(比如触摸/鼠标拉拽时),调用UpdateTrajectory(pullDirection, pullDistance)方法,传入拉拽的方向和距离。
  5. 发射后调用HideTrajectory()隐藏轨迹。

这样就能画出精确的抛物线路径,还能自动检测碰撞停止绘制,完全符合你的需求啦!

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

火山引擎 最新活动