//
// Copyright (c) Brian Hernandez. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
//

using UnityEngine;

namespace MFlight
{
    /// <summary>
    /// Combination of camera rig and controller for aircraft. Requires a properly set
    /// up rig. I highly recommend either using or referencing the included prefab.
    /// </summary>
    public class MouseFlightController : MonoBehaviour
    {
        [Header("Components")]
        [SerializeField] [Tooltip("Transform of the aircraft the rig follows and references")]
        private Transform aircraft = null;
        [SerializeField] [Tooltip("Transform of the object the mouse rotates to generate MouseAim position")]
        private Transform mouseAim = null;
        [SerializeField] [Tooltip("Transform of the object on the rig which the camera is attached to")]
        private Transform cameraRig = null;
        [SerializeField] [Tooltip("Transform of the camera itself")]
        private Transform cam = null;

        [Header("Options")]
        [SerializeField] [Tooltip("Follow aircraft using fixed update loop")]
        private bool useFixed = true;

        [SerializeField] [Tooltip("How quickly the camera tracks the mouse aim point.")]
        private float camSmoothSpeed = 5f;

        [SerializeField] [Tooltip("Mouse sensitivity for the mouse flight target")]
        private float mouseSensitivity = 3f;

        [SerializeField] [Tooltip("How far the boresight and mouse flight are from the aircraft")]
        private float aimDistance = 500f;

        [Space]
        [SerializeField] [Tooltip("How far the boresight and mouse flight are from the aircraft")]
        private bool showDebugInfo = false;

        private Vector3 frozenDirection = Vector3.forward;
        private bool isMouseAimFrozen = false;

        /// <summary>
        /// Get a point along the aircraft's boresight projected out to aimDistance meters.
        /// Useful for drawing a crosshair to aim fixed forward guns with, or to indicate what
        /// direction the aircraft is pointed.
        /// </summary>
        public Vector3 BoresightPos
        {
            get
            {
                return aircraft == null
                     ? transform.forward * aimDistance
                     : (aircraft.transform.forward * aimDistance) + aircraft.transform.position;
            }
        }

        /// <summary>
        /// Get the position that the mouse is indicating the aircraft should fly, projected
        /// out to aimDistance meters. Also meant to be used to draw a mouse cursor.
        /// </summary>
        public Vector3 MouseAimPos
        {
            get
            {
                if (mouseAim != null)
                {
                    return isMouseAimFrozen
                        ? GetFrozenMouseAimPos()
                        : mouseAim.position + (mouseAim.forward * aimDistance);
                }
                else
                {
                    return transform.forward * aimDistance;
                }
            }
        }

        private void Awake()
        {
            if (aircraft == null)
                Debug.LogError(name + "MouseFlightController - No aircraft transform assigned!");
            if (mouseAim == null)
                Debug.LogError(name + "MouseFlightController - No mouse aim transform assigned!");
            if (cameraRig == null)
                Debug.LogError(name + "MouseFlightController - No camera rig transform assigned!");
            if (cam == null)
                Debug.LogError(name + "MouseFlightController - No camera transform assigned!");

            // To work correctly, the entire rig must not be parented to anything.
            // When parented to something (such as an aircraft) it will inherit those
            // rotations causing unintended rotations as it gets dragged around.
            transform.parent = null;
        }

        private void Update()
        {
            if (useFixed == false)
                UpdateCameraPos();

            RotateRig();
        }

        private void FixedUpdate()
        {
            if (useFixed == true)
                UpdateCameraPos();
        }

        private void RotateRig()
        {
            if (mouseAim == null || cam == null || cameraRig == null)
                return;

            // Freeze the mouse aim direction when the free look key is pressed.
            if (Input.GetKeyDown(KeyCode.C))
            {
                isMouseAimFrozen = true;
                frozenDirection = mouseAim.forward;
            }
            else if  (Input.GetKeyUp(KeyCode.C))
            {
                isMouseAimFrozen = false;
                mouseAim.forward = frozenDirection;
            }

            // Mouse input.
            float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity;
            float mouseY = -Input.GetAxis("Mouse Y") * mouseSensitivity;

            // Rotate the aim target that the plane is meant to fly towards.
            // Use the camera's axes in world space so that mouse motion is intuitive.
            mouseAim.Rotate(cam.right, mouseY, Space.World);
            mouseAim.Rotate(cam.up, mouseX, Space.World);

            // The up vector of the camera normally is aligned to the horizon. However, when
            // looking straight up/down this can feel a bit weird. At those extremes, the camera
            // stops aligning to the horizon and instead aligns to itself.
            Vector3 upVec = (Mathf.Abs(mouseAim.forward.y) > 0.9f) ? cameraRig.up : Vector3.up;

            // Smoothly rotate the camera to face the mouse aim.
            cameraRig.rotation = Damp(cameraRig.rotation,
                                      Quaternion.LookRotation(mouseAim.forward, upVec),
                                      camSmoothSpeed,
                                      Time.deltaTime);
        }

        private Vector3 GetFrozenMouseAimPos()
        {
            if (mouseAim != null)
                return mouseAim.position + (frozenDirection * aimDistance);
            else
                return transform.forward * aimDistance;
        }

        private void UpdateCameraPos()
        {
            if (aircraft != null)
            {
                // Move the whole rig to follow the aircraft.
                transform.position = aircraft.position;
            }
        }

        // Thanks to <PRESIDIO_ANONYMIZED_PERSON>
        // http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/
        /// <summary>
        /// Creates dampened motion between a and b that is framerate independent.
        /// </summary>
        /// <param name="a">Initial parameter</param>
        /// <param name="b">Target parameter</param>
        /// <param name="lambda">Smoothing factor</param>
        /// <param name="dt">Time since last damp call</param>
        /// <returns></returns>
        private Quaternion Damp(Quaternion a, Quaternion b, float lambda, float dt)
        {
            return Quaternion.Slerp(a, b, 1 - Mathf.Exp(-lambda * dt));
        }

        private void OnDrawGizmos()
        {
            if (showDebugInfo == true)
            {
                Color oldColor = Gizmos.color;

                // Draw the boresight position.
                if (aircraft != null)
                {
                    Gizmos.color = Color.white;
                    Gizmos.DrawWireSphere(BoresightPos, 10f);
                }

                if (mouseAim != null)
                {
                    // Draw the position of the mouse aim position.
                    Gizmos.color = Color.red;
                    Gizmos.DrawWireSphere(MouseAimPos, 10f);

                    // Draw axes for the mouse aim transform.
                    Gizmos.color = Color.blue;
                    Gizmos.DrawRay(mouseAim.position, mouseAim.forward * 50f);
                    Gizmos.color = Color.green;
                    Gizmos.DrawRay(mouseAim.position, mouseAim.up * 50f);
                    Gizmos.color = Color.red;
                    Gizmos.DrawRay(mouseAim.position, mouseAim.right * 50f);
                }

                Gizmos.color = oldColor;
            }
        }
    }
}


перепиши этот на Lua используя maf: 


In general, any function that returns a vector or a rotation will write its result into the last parameter, out. The function also returns out for convenience, to allow for method chaining. If out is unspecified, then the result will be written into the first parameter instead.

Vectors
maf.vector(x, y, z) or maf.vec3(x, y, z)
Create a new vector. Omitted elements will be set to zero.

The x, y, and z keys contain the individual components of the vector.

The following metamethods are defined for vectors:

v + v - Add two vectors.
v - v - Subtract two vectors.
v * v - Multiply two vectors.
v * n - Scale a vector by a number.
v / v - Divide two vectors.
-v - Negate a vector.
#v - Get the length of a vector.
vector:unpack()
Get the individual components of the vector.

vector:set(x, y, z)
Set the individual components of a vector. x can also be a vector.

vector:add(v2, [out])
Add two vectors.

vector:sub(v2, [out])
Subtract two vectors.

vector:mul(v2, [out])
Multiply two vectors.

vector:div(v2, [out])
Divide two vectors.

vector:scale(s, [out])
Multiply each component of the vector by a number s.

vector:length()
Return the length of the vector.

vector:normalize([out])
Set the length of a vector to 1.

vector:distance(v2)
Return the distance between two vectors.

vector:angle(v2)
Return the angle between two vectors, in radians.

vector:dot(v2)
Return the dot product of two vectors, defined as v1.x * v2.x + v1.y * v2.y + v1.z * v2.z.

vector:cross(v2, [out])
Return the cross product of two vectors. The cross product is a vector that is perpendicular to the two input vectors.

vector:lerp(v2, t, [out])
Mixes two vectors together using linear interpolation. The parameter t controls how they are mixed (0 will return vector, 1 will return v2, .5 will return a 50/50 blend of the two vectors, etc.).

vector:project(u, [out])
Projects vector onto u, storing the result in out.

vector:rotate(r, [out])
Applies a rotation r to the vector.

Rotations
maf.rotation(x, y, z, w) or maf.quat(x, y, z, w)
Creates a new rotation (quaternion) from x, y, z, w components. Unless you are some sort of wizard that understands quaternions, it's probably easier to use .fromAngleAxis, .fromDirection, or .between.

The following metamethods are defined for rotations:

q + q - Add two rotations.
q - q - Subtract two rotations.
q * q - Multiply two rotations, resulting in a rotation that is equivalent to applying the first rotation followed by the second.
q * v - Multiply a rotation by a vector, returning the rotated vector.
-q - Negate a rotation.
#q - Get the length of a rotation.
rotation:unpack()
Get the individual components of the rotation.

rotation:set(x, y, z, w)
Set the individual components of a rotation. x can also be a rotation.

rotation.fromAngleAxis(angle, x, y, z)
Creates a new rotation from an angle/axis pair. This is the same as creating a new rotation and calling :setAngleAxis on it. x can also be a vec3.

rotation:setAngleAxis(angle, x, y, z)
Set the rotation's values using angle/axis representation. x can also be a vec3.

angle, x, y, z = rotation:getAngleAxis()
Get the angle/axis representation of the rotation.

rotation.between(v1, v2)
Create a new rotation that represents the rotation between v1 and v2. Both vectors should be normalized.

rotation:setBetween(v1, v2)
Update an existing rotation to represent the rotation between v1 and v2. Both vectors should be normalized.

rotation.fromDirection(x, y, z)
Create a new rotation from a direction vector. The rotation will represent the rotation from the forward vector (0, 0, -1) to the direction vector. x can also be a vec3.

rotation:setDirection(x, y, z)
Set a rotation's value to those of the direction vector. x can also be a vec3.

rotation:add(r2, [out])
Add two rotations together. To combine two rotations together, multiply them instead.

rotation:sub(r2, [out])
Subtract two rotations.

rotation:mul(r2, [out])
Multiply two rotations together, returning a rotation that combines the two.

rotation:scale(s, [out])
Multiply each component of the rotation by a number s.

rotation:length()
Return the length of the rotation.

rotation:normalize([out])
Normalize a rotation, setting its length to 1.

rotation:lerp(r2, t, [out])
Mix two rotations together using linear interpolation. This is faster than :slerp but less accurate.

rotation:slerp(r2, t, [out])
Mix two rotations together using spherical linear interpolation.