最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c# - Why the object doesn't rotate smoothly? - Stack Overflow

programmeradmin4浏览0评论

I'm trying to rotate the object, that were detected by my raycast. I use the Lerp function, but it doesn't work as I want and I don't know why. I saw the same question, but the problem was that the lerp function was used in the start function, instead of the update function. My function seemed to be running in an update, so I don't understand what the problem is. I'm relatively new to unity, so maybe the answer is obvious, but not for me.

using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;
using static UnityEngine.GraphicsBuffer;

public class PlayerInteraction : MonoBehaviour
{
    [Header("Camera settings")]
    public Camera playerCamera;


    [Header("Raycast")]
    private Ray cameraRay;
    private Vector3 rayStartPosition = new Vector3(Screen.width / 2, Screen.height / 2, 0);
    private float lengthOfRay = 5f;
    private RaycastHit raycastedObject;
    private string objectTag;
    private GameObject hittedObject;


    [Header("Interaction")]
    private KeyCode interactionKey = KeyCode.E;

    [Header("Door settings")]
    private Quaternion startRotation;
    private Quaternion targetOpen;

    void Start()
    {

    }

    void Update()
    {
        RotationOfObject();

    }




    public void RotationOfObject()
    {
        cameraRay = playerCamera.ScreenPointToRay(rayStartPosition);

        if (Physics.Raycast(cameraRay, out raycastedObject, lengthOfRay) && Input.GetKeyDown(interactionKey))
        {
            hittedObject = raycastedObject.collider.gameObject;

            startRotation = hittedObject.transform.rotation;
            targetOpen = new Quaternion(hittedObject.transform.rotation.x, hittedObject.transform.rotation.y + 90f, hittedObject.transform.rotation.z, 0);

            hittedObject.transform.rotation = Quaternion.Lerp(startRotation, targetOpen, 0.01f);
        }
    }
}

I'm trying to rotate the object, that were detected by my raycast. I use the Lerp function, but it doesn't work as I want and I don't know why. I saw the same question, but the problem was that the lerp function was used in the start function, instead of the update function. My function seemed to be running in an update, so I don't understand what the problem is. I'm relatively new to unity, so maybe the answer is obvious, but not for me.

using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;
using static UnityEngine.GraphicsBuffer;

public class PlayerInteraction : MonoBehaviour
{
    [Header("Camera settings")]
    public Camera playerCamera;


    [Header("Raycast")]
    private Ray cameraRay;
    private Vector3 rayStartPosition = new Vector3(Screen.width / 2, Screen.height / 2, 0);
    private float lengthOfRay = 5f;
    private RaycastHit raycastedObject;
    private string objectTag;
    private GameObject hittedObject;


    [Header("Interaction")]
    private KeyCode interactionKey = KeyCode.E;

    [Header("Door settings")]
    private Quaternion startRotation;
    private Quaternion targetOpen;

    void Start()
    {

    }

    void Update()
    {
        RotationOfObject();

    }




    public void RotationOfObject()
    {
        cameraRay = playerCamera.ScreenPointToRay(rayStartPosition);

        if (Physics.Raycast(cameraRay, out raycastedObject, lengthOfRay) && Input.GetKeyDown(interactionKey))
        {
            hittedObject = raycastedObject.collider.gameObject;

            startRotation = hittedObject.transform.rotation;
            targetOpen = new Quaternion(hittedObject.transform.rotation.x, hittedObject.transform.rotation.y + 90f, hittedObject.transform.rotation.z, 0);

            hittedObject.transform.rotation = Quaternion.Lerp(startRotation, targetOpen, 0.01f);
        }
    }
}

Share Improve this question edited Mar 17 at 17:12 ipodtouch0218 3,3709 gold badges14 silver badges29 bronze badges asked Mar 17 at 16:51 dmitry rubanovdmitry rubanov 251 silver badge5 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

Your rotation logic is indeed inside of Update, but it's also inside the Input.GetKeyDown(interactionKey) and Physics.Raycast checks- rotation is only possible on the single frame where you pressed the interaction key.

What you'd want to do instead is keep track of where the door should be, probably through the use of a boolean isOpen; variable, and move it towards that destination inside of Update (without it being inside of the if statement that contains the raycast/key checks). Quaternion.RotateTowards is great for this.

As well, we should also move the door-opening logic to its own behaviour attached to the door itself, instead of having the PlayerInteraction class also be responsible for rotating the door.

For example:

public class PlayerInteraction : MonoBehaviour
{
    // ... your other variables here

    public void Update() {
        // Check for interaction
        if (Input.GetKeyDown(interactionKey))
        {
            TryInteractWithObject();
        }
    }
    
    public void TryInteractWithObject()
    {
        Ray cameraRay = playerCamera.ScreenPointToRay(rayStartPosition);

        if (Physics.Raycast(cameraRay, out raycastedObject, lengthOfRay))
        {
            GameObject hitObject = raycastedObject.collider.gameObject;

            // Try to find the "interactable" attached to the object hit
            // by the raycast. If the object isn't "interactable", this
            // if statement will not run.
            if (hitObject.TryGetComponent(out IInteractableObject interactable))
            {
                interactable.Interact();
            }
        }
    }
}

And for the door, we can make a new Door MonoBehaviour, along with an IInteractableObject interface. This interface will allow any new behaviour to be "interactable" by simply inheriting from the interface (and implementing Interact()), not just the door:

public interface IInteractableObject
{
    // This function will be called when a player interacts with this object.
    void Interact();
}

public class Door : MonoBehaviour, IInteractableObject
{
    [SerializeField] private float doorRotationSpeed = 500f; // In degrees per second
    private Quaternion initialRotation, targetRotation;
    private bool isOpen;

    public void Start()
    {
        initialRotation = transform.rotation;
        targetRotation = transform.rotation;
    }

    public void Update()
    {
        // Slowly rotate towards our desired rotation
        transform.rotation = Quaternion.RotateTowards(
            transform.rotation,
            targetRotation,
            doorRotationSpeed * Time.deltaTime);
    }

    public void Interact()
    {
        // Invert the "isOpen" boolean
        isOpen = !isOpen;
        
        if (isOpen)
        {
            // Door is now open- add 90 degrees on the Y axis.
            targetRotation = initialRotation * Quaternion.AngleAxis(90, Vector3.up);
        }
        else
        {
            // Door is now closed- reset to starting rotation
            targetRotation = initialRotation;
        }
    }
}

An alternative to putting the rotation logic in Update would be putting it in a Coroutine, but I recommend doing it this way first, so you can learn the pattern of starting something once and continuing it across Update calls.

发布评论

评论列表(0)

  1. 暂无评论