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

Unity开发Portal类2D游戏:Quaternion旋转速度向量遇问题

问题分析与解决方案

核心问题拆解

  1. 2D环境下轴方向误用
    Unity 2D中transform.forward指向Z轴(垂直屏幕),完全不在XY游戏平面内。你用它计算传送门朝向的旋转,等于基于错误的轴做转换,自然得不到正确的速度方向。

  2. 传送激活逻辑混乱
    Portal脚本里的activated变量触发逻辑颠倒,且Movement中每帧都会检查该状态,会导致速度被重复修改;同时硬编码指定Green/Blue传送门,无法适配动态配对的场景。

  3. 2D速度旋转方式错误
    2D平面的向量旋转无需用3D的Quaternion复杂处理,直接基于平面角度计算更准确,强行用Quaternion会因轴空间不匹配导致结果异常。


修正后代码实现

Portal脚本(优化触发逻辑)

using UnityEngine;

public class Portal : MonoBehaviour
{
    public Portal linkedPortal; // 直接在Inspector指定配对传送门
    private bool isTeleporting; // 防止连续触发传送

    void OnTriggerEnter2D(Collider2D col)
    {
        if (isTeleporting || linkedPortal == null) return;

        var playerMovement = col.GetComponent<Movement>();
        if (playerMovement == null) return;

        // 标记传送中状态
        isTeleporting = true;
        linkedPortal.isTeleporting = true;

        // 传送位置(偏移一点避免立刻再次触发触发器)
        col.transform.position = linkedPortal.transform.position + linkedPortal.transform.up * 0.1f;

        // 应用旋转后的速度
        playerMovement.ApplyPortalVelocity(this, linkedPortal);

        // 延迟重置状态,防止连续触发
        Invoke(nameof(ResetTeleportFlag), 0.2f);
        linkedPortal.Invoke(nameof(ResetTeleportFlag), 0.2f);
    }

    void ResetTeleportFlag()
    {
        isTeleporting = false;
    }
}

Movement脚本(优化速度旋转与移动逻辑)

using UnityEngine;

public class Movement : MonoBehaviour
{
    private float horizontal;
    private float speed = 8f;
    private float jumpingPower = 8f;
    public Rigidbody2D rb; 

    void Update()
    {
        horizontal = Input.GetAxisRaw("Horizontal");

        if (Input.GetButtonDown("Jump"))
        {
            rb.velocity = new Vector2(rb.velocity.x, jumpingPower);
        }
    }
    
    private void FixedUpdate()
    {
        // 只有非传送状态下处理常规移动,避免覆盖传送后的速度
        if (!IsInTeleport())
        {
            rb.velocity = new Vector2(horizontal * speed, rb.velocity.y);
        }
    }

    // 检查是否处于传送状态
    private bool IsInTeleport()
    {
        foreach (var portal in FindObjectsOfType<Portal>())
        {
            if (portal.isTeleporting) return true;
        }
        return false;
    }

    // 处理传送门速度转换
    public void ApplyPortalVelocity(Portal inPortal, Portal outPortal)
    {
        // 计算两个传送门的朝向角度(基于up轴,假设传送门up为正对方向)
        float inAngle = Mathf.Atan2(inPortal.transform.up.y, inPortal.transform.up.x) * Mathf.Rad2Deg;
        float outAngle = Mathf.Atan2(outPortal.transform.up.y, outPortal.transform.up.x) * Mathf.Rad2Deg;
        // 旋转差值:从进入方向转为穿出方向,额外加180度修正方向
        float rotationDelta = outAngle - inAngle + 180f;

        // 旋转速度向量
        rb.velocity = Rotate2DVector(rb.velocity, rotationDelta);
    }

    // 2D向量旋转工具方法
    private Vector2 Rotate2DVector(Vector2 vec, float angleDeg)
    {
        float angleRad = angleDeg * Mathf.Deg2Rad;
        float cos = Mathf.Cos(angleRad);
        float sin = Mathf.Sin(angleRad);
        return new Vector2(
            vec.x * cos - vec.y * sin,
            vec.x * sin + vec.y * cos
        );
    }
}

关键修正说明

  • 轴方向修正:改用transform.up作为传送门的朝向参考(需确保传送门Sprite的up方向与视觉朝向一致),完全基于XY平面计算角度。
  • 触发逻辑优化:用isTeleporting标记防止连续触发,传送后延迟重置状态,避免反复触发触发器。
  • 动态配对:每个传送门直接指定linkedPortal,无需硬编码固定传送门名称,适配更多场景。
  • 2D向量旋转:用三角函数直接计算平面旋转,避免3D Quaternion的轴空间混淆,结果更准确。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 20:10:32