简体   繁体   中英

Elliptical Rotation on touch (or click) in Unity

I'm making a camera that rotates around an egg. I can't figure out how to do that. The rotation has clamps at -90 +90 on x and y each. I made a code that changes the camera position based on the current x rotation of the pivot but it doesn't give me an accurate result. This is the code for that:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraMotionScript : MonoBehaviour {

public Transform thePivot, theCamera;
public float rotSpeed = 5f, lerpSpeed, minX, maxX, minY, maxY, maxDist, divRatio;

private float   rotX = 0f,
                rotY = 0f,
                dX = 0f,
                dY = 0f,
                rotateDirection = -1f,
                cachedRotSpeed = 0f,
                minDist = 0f;

private bool rotationActive = false;

void Start () {
    cachedRotSpeed = rotSpeed;
    minDist = Mathf.Abs (theCamera.localPosition.z);
    divRatio = maxX / maxDist;
}

void Update () {

    if (Input.GetMouseButton (0)) {
        rotSpeed = cachedRotSpeed;
        dX = Input.GetAxis ("Mouse X");
        dY = Input.GetAxis ("Mouse Y");
        rotationActive = true;
    }

    if (Input.GetMouseButtonUp (0)) {
        rotationActive = false;
    }

    rotX += dY * rotSpeed * rotateDirection * Time.deltaTime;
    rotY += dX * rotSpeed * -rotateDirection * Time.deltaTime;

    rotX = Mathf.Clamp (rotX, minX, maxX);
    rotY = Mathf.Clamp (rotY, minY, maxY);

    if (thePivot != null)
        thePivot.eulerAngles = new Vector3 (rotX, rotY, 0f);

    if (!rotationActive) {
        rotSpeed = Mathf.Lerp (rotSpeed, 0, Time.deltaTime * lerpSpeed);
        if (rotSpeed < 1)
            rotSpeed = 0;
    }

    MaintainDistanceAt (Mathf.Abs (rotX));
}

void MaintainDistanceAt (float f) {
    if (f != 0) {
        float zVal = f / divRatio;
        if (zVal < minDist)
            zVal = minDist;
        theCamera.localPosition = new Vector3 (0f, 0f, zVal * -1f);
    }
}

}

For this code my camera is the child of the pivot and I rotate the pivot with the mouse click. I take in the x rotation of the pivot and calculate the distance of the camera. I modify it based on the run time x rotation of the pivot. I've added smooth speed code in it too but ignore that for now.

So the problem is, the distance code that I wrote sets the position of the camera in a linear vector, so it cuts through the egg mesh and clips it.

I want to be able to move the camera in an elliptical orbit around the egg. Any suggestions on how do I solve this? Thanks in advance.

Easy way to get elliptic pattern would be to use cos/sin function.

void Update () 
{
    float x = xAmplitude * Mathf.Cos (Time.time + phase);
    float z = zAmplitude * Mathf.Sin (Time.time + phase)
    transform.localPosition = new Vector3(x, 0, z);
}

The important part for you is the xAmplitude and zAmplitude that you'd get from your input.

I'd suggest you start with that method so you see how it behaves, then play with it until it does what you want.

EDIT: What is"phase"? Phase is the value you use to displace the starting point. In your case, I understand you don't want to spin around continuously but find the position on the ellipse based on a mouse position. You could discard Time.time which creates the continuous movement. Then you could figure out the angle between the starting point (0) and the current mouse position (or using a delta movement of the mouse, your choice) and convert that to a value between 0 and 360 or 0 and 2Pi (I would think Unity uses that one in cos/sin).

Here is a quick example, let's see if that is what you are after.

  • Place an empty game object that will be your pivot point.
  • Add a sphere as child.
  • Add a UI slider to the scene
  • Create new Movement.cs script

    public class Movement : MonoBehaviour { public Transform child = null; public float xAmplitude = 1f; public float zAmplitude = 1f; private float phase = 0; public Slider slider;

     void Start() { slider.onValueChanged.AddListener(delegate { this.phase = 2 * Mathf.PI * slider.value; float x = xAmplitude * Mathf.Cos(phase); float z = zAmplitude * Mathf.Sin(phase); child.localPosition = new Vector3(x, 0, z); }); }

    }

  • Add the script to the parent and drag the child in child and the slider in slider.

You're done. Then you can run, drag the slider and see the child moving around. Give different values to xAmplitude and zAmplitude and you have an ellipse.

this.phase = 2 * Mathf.PI * slider.value;
float x = xAmplitude * Mathf.Cos(phase);
float z = zAmplitude * Mathf.Sin(phase);

See how the time is gone, this will stop the constant movement as the value in cos/sin is fixed in time(but can still change based on our slider). The phase value is now made of the slider value (0 to 1) converted to 2Pi range. Phase is passed to sin/cos and those value are respectfully extended by the corresponding amplitude.

I was looking to do the same thing. I used some of @Everts phase as a jumping off point.

Code can be found here: https://gist.github.com/xepherys/1a22ed93e6c6efde61f1667f45a80a72 Video of the motion can be found here: https://youtu.be/4z27uTwS-gU

Complete code with comments:

using System;
using UnityEngine;

/// <summary>
/// The MainCameraEllipticalMWE class provides motion of the camera in an ellipse (or a circle)
/// given sizeX and sizeZ where one is the major axis and the other the minor axis.  Which is
/// which isn't important, and they can be equal (x == z) if the camera is to track in a circle.
///
/// The size values assume a 2D surface where x=0, z=0 is at the bottom left and built in both x+
/// and z+ directions.
/// </summary>
public class MainCameraEllipticalMWE : MonoBehaviour
{
    // sizeX and sizeZ are provided here to provide a reference point. In use, my version
    // of this script omits these values and gets values from a manager object fed into
    // the FixCamera() method where xAmplitude and zAmplitude are set.
    int sizeX = 12;
    int sizeZ = 3;
    Vector3 center;
    float height;
    float fudgeFactor = 1.0f;

    float phase = 0f;
    float xAmplitude = 0f;
    float zAmplitude = 0f;
    float x;
    float z;

    void Awake()
    {
        this.FixCamera(sizeX, sizeZ);
    }

    /// <summary>
    /// On Update(), if the appropriate key is pressed, rotate the camera around the center
    /// of the area defined. The camera will tilt as appropriate to maintain the same center
    /// point of view.  The greater the difference between the major and minor axes, the
    /// greater the difference of speed as the camera traverses the outsides of the major axis.
    /// </summary>
    void Update()
    {
        if (Input.GetKey(KeyCode.A))
        {
            // Move left
            phase -= 0.01f;
            x = xAmplitude * Mathf.Cos(phase);
            z = zAmplitude * Mathf.Sin(phase);
            transform.localPosition = new Vector3(x, height, z) + center;
            this.GetComponent<Camera>().transform.LookAt(center);
        }

        else if (Input.GetKey(KeyCode.F))
        {
            // Move right
            phase += 0.01f;
            x = xAmplitude * Mathf.Cos(phase);
            z = zAmplitude * Mathf.Sin(phase);
            transform.localPosition = new Vector3(x, height, z) + center;
            this.GetComponent<Camera>().transform.LookAt(center);
        }
    }

    /// <summary>
    /// FixCamera() provides the initial camera position, sets the camera height, and
    /// resolves the center position based on the horizontal and vertical sizes of
    /// the screen area.
    /// </summary>
    /// <param name="horizontalSize"></param>
    /// <param name="verticalSize"></param>
    public void FixCamera(int horizontalSize, int verticalSize)
    {
        xAmplitude = horizontalSize;
        zAmplitude = verticalSize;
        height = Math.Max(horizontalSize, verticalSize) + fudgeFactor;
        center = new Vector3((float)horizontalSize / 2, 0, (float)verticalSize / 2);

        float x = xAmplitude * Mathf.Cos(phase);
        float z = zAmplitude * Mathf.Sin(phase);
        transform.localPosition = new Vector3(x, height, z) + center;
        this.GetComponent<Camera>().transform.LookAt(center);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM