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

Unity中如何实现门的平滑旋转?代码问题求助

问题分析与解决方案

你的代码存在三个核心问题导致平滑旋转失效:

  • Lerp仅执行一次:DoorOpen()和DoorClose()只在按下E时被调用一次,而Quaternion.Lerp需要每一帧持续执行才能产生平滑过渡效果,单次调用只会让门旋转极小的角度,看起来没变化。
  • 目标旋转计算错误:Start()里用transform.rotation.y(四元数的分量)来构建欧拉角,这完全不符合逻辑——四元数的x/y/z/w不是欧拉角的轴角度,应该用transform.eulerAngles来获取当前门的欧拉角参数。
  • 全局开关状态混乱:Interaction里的doorOpened是全局变量,当场景中有多个门时,这个变量会导致开关逻辑混乱。

修改后的Door脚本

using UnityEngine;

public class Door : MonoBehaviour
{
    [SerializeField] float speed = 2f;
    [SerializeField] float openAngle;
    [SerializeField] float closeAngle;

    private Quaternion _targetRotation;
    private bool _isOpening;

    void Start()
    {
        // 基于当前门的欧拉角设置目标旋转(仅修改Y轴)
        _targetRotation = Quaternion.Euler(transform.eulerAngles.x, closeAngle, transform.eulerAngles.z);
        _isOpening = false;
    }

    void Update()
    {
        // 每一帧执行Lerp,实现平滑旋转
        transform.rotation = Quaternion.Lerp(transform.rotation, _targetRotation, Time.deltaTime * speed);
    }

    public void ToggleDoor()
    {
        _isOpening = !_isOpening;
        // 根据开关状态切换目标旋转
        _targetRotation = _isOpening 
            ? Quaternion.Euler(transform.eulerAngles.x, openAngle, transform.eulerAngles.z) 
            : Quaternion.Euler(transform.eulerAngles.x, closeAngle, transform.eulerAngles.z);
    }

    // 保留原方法兼容旧逻辑(可选)
    public void DoorOpen()
    {
        _isOpening = true;
        _targetRotation = Quaternion.Euler(transform.eulerAngles.x, openAngle, transform.eulerAngles.z);
    }

    public void DoorClose()
    {
        _isOpening = false;
        _targetRotation = Quaternion.Euler(transform.eulerAngles.x, closeAngle, transform.eulerAngles.z);
    }

    // 对外暴露开关状态,供Interaction脚本判断
    public bool IsOpening => _isOpening;
}

修改后的Interaction脚本

using UnityEngine;

public class Interaction : MonoBehaviour
{
    void Update()
    {
        Vector3 rayStartPosition = new Vector3(Screen.width / 2, Screen.height / 2, 0);
        Ray cameraRay = Camera.main.ScreenPointToRay(rayStartPosition);
        float maxDistance = 3f;

        if (Physics.Raycast(cameraRay, out RaycastHit hit, maxDistance))
        {
            if (hit.collider.CompareTag("Door") && Input.GetKeyDown(KeyCode.E))
            {
                var door = hit.collider.GetComponent<Door>();
                // 调用ToggleDoor直接切换状态,或者根据门自身状态判断
                door.ToggleDoor();
                // 或者用原逻辑:
                // if (door.IsOpening) door.DoorClose();
                // else door.DoorOpen();
            }
        }
    }
}

关键修改说明

  1. 持续帧更新Lerp:把Lerp逻辑移到Update()中,确保每一帧都计算旋转过渡,实现平滑动画。
  2. 修正目标旋转计算:使用transform.eulerAngles获取门的当前欧拉角,仅修改Y轴角度来构建目标旋转四元数,避免四元数分量误用。
  3. 门自身维护状态:将开关状态_isOpening移到Door脚本中,每个门独立管理自己的开关状态,解决多门场景的逻辑混乱问题。
  4. 简化交互逻辑:新增ToggleDoor()方法,一键切换门的开关状态,减少重复代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 17:05:17