Website may be up and down over next few months. I'm currently doing a complete overhaul of everything. Going back to simple individual .htm pages, new overall site theme, sanitizing and cleaning up html of all pages and blog posts, attempting to implement a new tooling and publishing system etc etc.

The code snippet below allows you to attach a translation gizmo to a object to allow the user to move that object while the game is running. Works just like the unity translation gizmo.

TranslationGizmo

    using System;        
    using UnityEngine;

    /// <summary>
    /// The translation gizmo.
    /// </summary>
    public class TranslationGizmo : MonoBehaviour
    {
        #region Fields

        /// <summary>
        /// The camera reference used to determin distance to translation gizmo.
        /// </summary>
        public UnityEngine.Camera cameraReference;

        /// <summary>
        /// Used to determine how thick the envelope is around a axis line when attempting to click and drag it with the mouse.
        /// </summary>
        public float axisEnvelope = 0.04f;

        /// <summary>
        /// Determines the length of the cone.
        /// </summary>
        public float coneLength = 0.85f;

        /// <summary>
        /// Specifies how many segments the cone is made up of.
        /// </summary>
        public int coneSegments = 30;

        /// <summary>
        /// Determines the diameter of the cone.
        /// </summary>
        public float coneSize = 0.075f;

        /// <summary>
        /// Determines the length of the axis lines.
        /// </summary>
        public float lineLength = 0.13f;

        /// <summary>
        /// The line material used to render the lines.
        /// </summary>
        public Material lineMaterial;

        /// <summary>
        /// Determines the size of the square panning gizmos.
        /// </summary>
        public float panScale = 0.3f;

        /// <summary>
        /// Determines the transparency level of the axis plane quads.
        /// </summary>
        public float planeTransparency = 0.3f;

        /// <summary>
        /// The color that will be used for the X axis.
        /// </summary>
        public Color xAxisColor = Color.red;

        /// <summary>
        /// The color that will be used for the Y axis.
        /// </summary>
        public Color yAxisColor = Color.green;

        /// <summary>
        /// The color that will be used for the Z axis.
        /// </summary>
        public Color zAxisColor = Color.blue;

        /// <summary>
        /// Holds the button state used to determine if dragging has started.
        /// </summary>
        protected bool inputButtonState;

        /// <summary>
        /// Holds the relative mouse X position.
        /// </summary>
        protected float mouseAxisX;

        /// <summary>
        /// Holds the relative mouse Y position.
        /// </summary>
        protected float mouseAxisY;

        /// <summary>
        /// Holds the pre-calculated distance value from the camera to the game object.
        /// </summary>
        private float cameraDistance;

        /// <summary>
        /// The hit mouse down.
        /// </summary>
        private Vector3 hitMouseDown;

        /// <summary>
        /// The hit mouse move.
        /// </summary>
        private Vector3 hitMouseMove;

        /// <summary>
        /// Holds wheather or not the input axis has changed.
        /// </summary>
        private bool inputAxisChanged;

        /// <summary>
        /// Holds wheather or not the mouse if being dragged.
        /// </summary>
        private bool isDragging;

        /// <summary>
        /// Holds XY plane information.
        /// </summary>
        private Plane planeXY;

        /// <summary>
        /// Holds XZ plane information.
        /// </summary>
        private Plane planeXZ;

        /// <summary>
        /// Holds YZ plane information.
        /// </summary>
        private Plane planeYZ;

        /// <summary>
        /// Holds the previous input button state.
        /// </summary>
        private bool previousInputButtonState;

        /// <summary>
        /// Holds the rotation matrix used to determine at what rotation the gizmo will be drawn at.
        /// </summary>
        private Matrix4x4 rotationMatrix;

        /// <summary>
        /// Holds the value for the <see cref="WorldTranslation"/> property.
        /// </summary>
        [SerializeField]
        private bool worldTranslation;

        /// <summary>
        /// Gets or sets a value indicating whether world translation is enabled.
        /// </summary>
        /// <value>
        ///   <c>true</c> if world translation is active; otherwise, <c>false</c> for local translation.
        /// </value>
        public bool WorldTranslation
        {
            get
            {
                return this.worldTranslation;
            }

            set
            {
                this.worldTranslation = value;
            }
        }

        /// <summary>
        /// Holds the type of axis movement is currently being made.
        /// </summary>
        private TypeOfMove typeMovementBeingMade;

        #endregion

        #region Public Events

        /// <summary>
        /// Occurs when position changed.
        /// </summary>
        public event EventHandler<Vector3EventArgs> PositionChanged;

        #endregion

        #region Enums

        /// <summary>
        /// Enum defining the types of axis movement.
        /// </summary>
        private enum TypeOfMove
        {
            /// <summary>
            /// No axis movement.
            /// </summary>
            None,

            /// <summary>
            /// Movement along the X axis.
            /// </summary>
            XAxis,

            /// <summary>
            /// Movement along the 
            /// </summary>
            X2Axis,

            /// <summary>
            /// Movement along the Y axis.
            /// </summary>
            YAxis,

            /// <summary>
            /// The y 2 axis.
            /// </summary>
            Y2Axis,

            /// <summary>
            /// Movement along the Z axis.
            /// </summary>
            ZAxis,

            /// <summary>
            /// The z 2 axis.
            /// </summary>
            Z2Axis,

            /// <summary>
            /// Movement along the XZ axis.
            /// </summary>
            XZAxis,

            /// <summary>
            /// Movement along the YZ axis.
            /// </summary>
            YZAxis,

            /// <summary>
            /// Movement along the XY axis.
            /// </summary>
            XYAxis,
        };

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// Awake is called when the script instance is being loaded.
        /// </summary>
        public void Awake()
        {
            // if no material assigned setup a default material
            if (this.lineMaterial == null)
            {
#if !UNITY_5
                this.lineMaterial =
                     new Material(
                         "Shader \"Lines/Colored Blended\" {" + "SubShader { Pass { " + "    Blend SrcAlpha OneMinusSrcAlpha "
                         + "    ZWrite Off Cull Off Fog { Mode Off } ZTest Always" + "    BindChannels {"
                         + "      Bind \"vertex\", vertex Bind \"color\", color }" + "} } }");
#else
                // Unity has a built-in shader that is useful for drawing
                // simple colored things.
                var shader = Shader.Find("Hidden/Internal-Colored");
                this.lineMaterial = new Material(shader);
                this.lineMaterial.hideFlags = HideFlags.HideAndDontSave;
                // Turn on alpha blending
                this.lineMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
                this.lineMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
                // Turn backface culling off
                this.lineMaterial.SetInt("_Cull", (int)UnityEngine.Rendering.CullMode.Off);
                // Turn off depth writes
                this.lineMaterial.SetInt("_ZWrite", 0);
#endif
            }
        }

        /// <summary>
        /// Update is called every frame, if the MonoBehaviour is enabled.
        /// </summary>
        public void Update()
        {
            if (cameraReference == null)
            {
                return;
            }

            // calculate distance once between the camera and the game objects position
            var transformReference = this.transform;
            var position = transformReference.position;
            this.cameraDistance = Vector3.Distance(this.cameraReference.transform.position, position) * this.lineLength;

            this.previousInputButtonState = this.inputButtonState;
            this.GetInputButtonState();

            // if the input button is released stop moving
            if (!this.inputButtonState)
            {
                this.isDragging = false;
                this.typeMovementBeingMade = TypeOfMove.None;
                return;
            }

            // only handle dragging is already dragging or if input button state was pressed
            if (this.isDragging || (!this.previousInputButtonState && this.inputButtonState))
            {
                // get mouse/input values
                this.GetInputValues();
                this.inputAxisChanged = Math.Abs(this.mouseAxisX) > float.Epsilon || Math.Abs(this.mouseAxisY) > float.Epsilon;

                // update planes based on world or local translation
                if (this.worldTranslation)
                {
                    // update planes
                    this.planeXZ.SetNormalAndPosition(Vector3.up, position);
                    this.planeXY.SetNormalAndPosition(Vector3.forward, position);
                    this.planeYZ.SetNormalAndPosition(Vector3.right, position);
                }
                else
                {
                    // update planes
                    this.planeXZ.SetNormalAndPosition(transformReference.up, position);
                    this.planeXY.SetNormalAndPosition(transformReference.forward, position);
                    this.planeYZ.SetNormalAndPosition(transformReference.right, position);
                }

                // cast a ray once
                var ray = this.cameraReference.ScreenPointToRay(Input.mousePosition);

                // handle axis translations
                this.HandleXZ(ray);
                this.HandleXY(ray);
                this.HandleYZ(ray);
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Get the state of the input button used to determine if dragging has occurred.
        /// </summary>
        protected virtual void GetInputButtonState()
        {
            this.inputButtonState = Input.GetMouseButton(0);
        }

        /// <summary>
        /// Gets the X/Y input values
        /// </summary>
        protected virtual void GetInputValues()
        {
            this.mouseAxisX = Input.GetAxis("Mouse X");
            this.mouseAxisY = Input.GetAxis("Mouse Y");
        }

        /// <summary>
        /// Raises the <see cref="PositionChanged"/> event.
        /// </summary>
        /// <param name="previousPosition">
        /// The previous Position.
        /// </param>
        /// <param name="position">
        /// The position.
        /// </param>
        protected virtual void OnPositionChanged(Vector3 previousPosition, Vector3 position)
        {
            var handler = this.PositionChanged;
            if (handler != null)
            {
                handler(this, new Vector3EventArgs(previousPosition, position));
            }
        }

        /// <summary>
        /// Uses OpenGL to draw a line for the axis.
        /// </summary>
        /// <param name="color">
        /// The color to use when drawing the cone.
        /// </param>
        /// <param name="axis">
        /// Defines axis that the cone will be drawn against.
        /// </param>
        /// <param name="axisU">
        /// Represents the "X" axis to draw the base of the cone against.
        /// </param>
        /// <param name="axisV">
        /// Represents the "Y" axis to draw the base of the cone against.
        /// </param>
        private void DrawAxis(Color color, Vector3 axis, Vector3 axisU, Vector3 axisV)
        {
            // scale the axis by the camera distance
            axis = axis * this.cameraDistance;

            // start drawing lines
            GL.Begin(GL.LINES);
            GL.Color(color);
            GL.Vertex3(0, 0, 0);
            GL.Vertex3(axis.x, axis.y, axis.z);
            GL.End();

            // draw the cone for the axis
            this.DrawCone(axis, axisU, axisV, this.cameraDistance * this.coneSize, this.coneLength, color);
        }

        /// <summary>
        /// Uses OpenGL to draw a cone for the axis.
        /// </summary>
        /// <param name="axis">
        /// Defines axis that the cone will be drawn against.
        /// </param>
        /// <param name="axisU">
        /// Represents the "X" axis to draw the base of the cone against.
        /// </param>
        /// <param name="axisV">
        /// Represents the "Y" axis to draw the base of the cone against.
        /// </param>
        /// <param name="radius">
        /// Specifies the radius for the base of the cone.
        /// </param>
        /// <param name="length">
        /// Specifies how tall the cone is.
        /// </param>
        /// <param name="color">
        /// The color to use when drawing the cone.
        /// </param>
        private void DrawCone(Vector3 axis, Vector3 axisU, Vector3 axisV, float radius, float length, Color color)
        {
            GL.Begin(GL.TRIANGLES);
            GL.Color(color);

            // pre-calculate the angle step for each cone segment
            var angle = (2 * Mathf.PI) / this.coneSegments;

            for (var i = 0; i <= this.coneSegments; i++)
            {
                var pt = axisU * Mathf.Cos(angle * i) * radius;
                pt += axisV * Mathf.Sin(angle * i) * radius;
                pt += axis * length;
                GL.Vertex(pt);

                pt = axisU * Mathf.Cos(angle * (i + 1)) * radius;
                pt += axisV * Mathf.Sin(angle * (i + 1)) * radius;
                pt += axis * length;
                GL.Vertex(pt);

                GL.Vertex(axis);
            }

            GL.End();
        }

        /// <summary>
        /// Uses OpenGL to draw a square plane for the axis.
        /// </summary>
        /// <param name="size">
        /// Defines size of the plane that will be drawn for the axis.
        /// </param>
        /// <param name="axisU">
        /// Represents the "X" axis to draw the plane against.
        /// </param>
        /// <param name="axisV">
        /// Represents the "Y" axis to draw the plane against.
        /// </param>
        /// <param name="color">
        /// The color to use when drawing the plane.
        /// </param>
        private void DrawQuad(float size, Vector3 axisU, Vector3 axisV, Color color)
        {
            color.a = this.planeTransparency;
            var pts = new Vector3[4];
            pts[0] = Vector3.zero;
            pts[1] = axisU * size;
            pts[2] = (axisU + axisV) * size;
            pts[3] = axisV * size;

            GL.Begin(GL.QUADS);
            GL.Color(color);
            GL.Vertex(pts[0]);
            GL.Vertex(pts[1]);
            GL.Vertex(pts[2]);
            GL.Vertex(pts[3]);
            GL.End();
            color.a = 1f;

            GL.Begin(GL.LINES);
            GL.Color(color);
            GL.Vertex(pts[0]);
            GL.Vertex(pts[1]);
            GL.Vertex(pts[1]);
            GL.Vertex(pts[2]);
            GL.Vertex(pts[2]);
            GL.Vertex(pts[3]);
            GL.Vertex(pts[3]);
            GL.Vertex(pts[0]);
            GL.End();
        }

        /// <summary>
        /// Start is called just before any of the Update methods is called the first time.
        /// </summary>
        public void Start()
        {
            if (this.cameraReference == null)
            {
                this.cameraReference = UnityEngine.Camera.main;
            }
        }

        /// <summary>
        /// Handles dragging along the XU plane.
        /// </summary>
        /// <param name="ray">
        /// The reay used to determine if the XY plane was being interacted with.
        /// </param>
        private void HandleXY(Ray ray)
        {
            float enter;
            this.planeXY.Raycast(ray, out enter);
            var hit = ray.GetPoint(enter);
            hit = this.rotationMatrix.inverse.MultiplyPoint(hit);

            if (this.typeMovementBeingMade == TypeOfMove.None && hit.x > 0f && hit.x <= this.panScale * this.cameraDistance && hit.y > 0
                && hit.y <= this.panScale * this.cameraDistance)
            {
                this.typeMovementBeingMade = TypeOfMove.XYAxis;
                this.hitMouseDown = hit;
                this.isDragging = true;
            }

            if (this.typeMovementBeingMade == TypeOfMove.XYAxis)
            {
                this.hitMouseMove = hit - this.hitMouseDown;
                this.hitMouseMove.z = 0;
                this.UpdatePosition(this.worldTranslation ? this.hitMouseMove : this.transform.localRotation * this.hitMouseMove);
            }

            if (this.typeMovementBeingMade == TypeOfMove.None && hit.x > 0f && hit.x <= this.cameraDistance
                && Mathf.Abs(hit.y) < this.axisEnvelope * this.cameraDistance)
            {
                // case x
                this.typeMovementBeingMade = TypeOfMove.X2Axis;
                this.hitMouseDown = hit;
                this.isDragging = true;
            }

            if (this.typeMovementBeingMade == TypeOfMove.X2Axis && this.inputAxisChanged)
            {
                this.hitMouseMove = hit - this.hitMouseDown;
                this.hitMouseMove.y = 0;
                this.hitMouseMove.z = 0;
                this.UpdatePosition(this.worldTranslation ? this.hitMouseMove : this.transform.localRotation * this.hitMouseMove);
            }

            if (this.typeMovementBeingMade == TypeOfMove.None && hit.y > 0f && hit.y <= this.cameraDistance
                && Mathf.Abs(hit.x) < this.axisEnvelope * this.cameraDistance)
            {
                // case y
                this.typeMovementBeingMade = TypeOfMove.YAxis;
                this.hitMouseDown = hit;
                this.isDragging = true;
            }

            if (this.typeMovementBeingMade == TypeOfMove.YAxis && this.inputAxisChanged)
            {
                this.hitMouseMove = hit - this.hitMouseDown;
                this.hitMouseMove.z = 0;
                this.hitMouseMove.x = 0;
                this.UpdatePosition(this.worldTranslation ? this.hitMouseMove : this.transform.localRotation * this.hitMouseMove);
            }
        }

        /// <summary>
        /// Handles dragging along the XZ plane.
        /// </summary>
        /// <param name="ray">
        /// The reay used to determine if the XZ plane was being interacted with.
        /// </param>
        private void HandleXZ(Ray ray)
        {
            float enter;
            this.planeXZ.Raycast(ray, out enter);
            var hit = ray.GetPoint(enter);
            hit = this.rotationMatrix.inverse.MultiplyPoint(hit);

            if (this.typeMovementBeingMade == TypeOfMove.None && hit.x > 0f && hit.x <= this.panScale * this.cameraDistance && hit.z > 0
                && hit.z <= this.panScale * this.cameraDistance)
            {
                this.typeMovementBeingMade = TypeOfMove.XZAxis;
                this.hitMouseDown = hit;
                this.isDragging = true;
            }

            if (this.typeMovementBeingMade == TypeOfMove.XZAxis && this.inputAxisChanged)
            {
                this.hitMouseMove = hit - this.hitMouseDown;
                this.hitMouseMove.y = 0;
                this.UpdatePosition(this.worldTranslation ? this.hitMouseMove : this.transform.localRotation * this.hitMouseMove);
            }

            if (this.typeMovementBeingMade == TypeOfMove.None && hit.x > 0f && hit.x <= this.cameraDistance
                && Mathf.Abs(hit.z) < this.axisEnvelope * this.cameraDistance)
            {
                // case x
                this.typeMovementBeingMade = TypeOfMove.XAxis;
                this.hitMouseDown = hit;
                this.isDragging = true;
            }

            if (this.typeMovementBeingMade == TypeOfMove.XAxis && this.inputAxisChanged)
            {
                this.hitMouseMove = hit - this.hitMouseDown;
                this.hitMouseMove.y = 0;
                this.hitMouseMove.z = 0;
                this.UpdatePosition(this.worldTranslation ? this.hitMouseMove : this.transform.localRotation * this.hitMouseMove);
            }

            if (this.typeMovementBeingMade == TypeOfMove.None && hit.z > 0f && hit.z <= this.cameraDistance
                && Mathf.Abs(hit.x) < this.axisEnvelope * this.cameraDistance)
            {
                // case  z
                this.typeMovementBeingMade = TypeOfMove.ZAxis;
                this.hitMouseDown = hit;
                this.isDragging = true;
            }

            if (this.typeMovementBeingMade == TypeOfMove.ZAxis && this.inputAxisChanged)
            {
                this.hitMouseMove = hit - this.hitMouseDown;
                this.hitMouseMove.y = 0;
                this.hitMouseMove.x = 0;
                this.UpdatePosition(this.worldTranslation ? this.hitMouseMove : this.transform.localRotation * this.hitMouseMove);
            }
        }

        /// <summary>
        /// Handles dragging along the YZ plane.
        /// </summary>
        /// <param name="ray">
        /// The reay used to determine if the YZ plane was being interacted with.
        /// </param>
        private void HandleYZ(Ray ray)
        {
            float enter;
            this.planeYZ.Raycast(ray, out enter);
            var hit = ray.GetPoint(enter);
            hit = this.rotationMatrix.inverse.MultiplyPoint(hit);

            if (this.typeMovementBeingMade == TypeOfMove.None && hit.z > 0 && hit.z <= this.panScale * this.cameraDistance && hit.y > 0
                && hit.y <= this.panScale * this.cameraDistance)
            {
                this.typeMovementBeingMade = TypeOfMove.YZAxis;
                this.hitMouseDown = hit;
                this.isDragging = true;
            }

            if (this.typeMovementBeingMade == TypeOfMove.YZAxis)
            {
                this.hitMouseMove = hit - this.hitMouseDown;
                this.hitMouseMove.x = 0;
                this.UpdatePosition(this.worldTranslation ? this.hitMouseMove : this.transform.localRotation * this.hitMouseMove);
            }

            if (this.typeMovementBeingMade == TypeOfMove.None && hit.z > 0f && hit.z <= this.cameraDistance
                && Mathf.Abs(hit.y) < this.axisEnvelope * this.cameraDistance)
            {
                // case  z
                this.typeMovementBeingMade = TypeOfMove.Z2Axis;
                this.hitMouseDown = hit;
                this.isDragging = true;
            }

            if (this.typeMovementBeingMade == TypeOfMove.Z2Axis && this.inputAxisChanged)
            {
                this.hitMouseMove = hit - this.hitMouseDown;
                this.hitMouseMove.y = 0;
                this.hitMouseMove.x = 0;
                this.UpdatePosition(this.worldTranslation ? this.hitMouseMove : this.transform.localRotation * this.hitMouseMove);
            }

            if (this.typeMovementBeingMade == TypeOfMove.None && hit.y > 0f && hit.y <= this.cameraDistance
                && Mathf.Abs(hit.z) < this.axisEnvelope * this.cameraDistance)
            {
                // case y
                this.typeMovementBeingMade = TypeOfMove.Y2Axis;
                this.hitMouseDown = hit;
                this.isDragging = true;
            }

            if (this.typeMovementBeingMade == TypeOfMove.Y2Axis && this.inputAxisChanged)
            {
                this.hitMouseMove = hit - this.hitMouseDown;
                this.hitMouseMove.z = 0;
                this.hitMouseMove.x = 0;
                this.UpdatePosition(this.worldTranslation ? this.hitMouseMove : this.transform.localRotation * this.hitMouseMove);
            }
        }

        /// <summary>
        /// Called by unity when after camera has rendered the scene.
        /// </summary>
        private void OnRenderObject()
        {         
            // set the material pass is a material has been specified
            var material = this.lineMaterial;
            if (material != null)
            {
                material.SetPass(0);
            }

            // push a matrix on to the stack
            GL.PushMatrix();
            this.rotationMatrix = this.transform.localToWorldMatrix;

            // setup rotation matrix
            if (this.worldTranslation)
            {
                this.rotationMatrix = Matrix4x4.TRS(this.transform.position, Quaternion.identity, Vector3.one);
            }
            else
            {
                this.rotationMatrix = Matrix4x4.TRS(this.transform.position, this.transform.localRotation, Vector3.one);
            }

            GL.MultMatrix(this.rotationMatrix);

            // draw each axis
            this.DrawAxis(this.xAxisColor, Vector3.right, Vector3.up, Vector3.forward);
            this.DrawAxis(this.yAxisColor, Vector3.up, Vector3.right, Vector3.forward);
            this.DrawAxis(this.zAxisColor, Vector3.forward, Vector3.right, Vector3.up);

            // draw each plane quad
            this.DrawQuad(this.panScale * this.cameraDistance, Vector3.forward, Vector3.up, this.xAxisColor);
            this.DrawQuad(this.panScale * this.cameraDistance, Vector3.right, Vector3.forward, this.yAxisColor);
            this.DrawQuad(this.panScale * this.cameraDistance, Vector3.right, Vector3.up, this.zAxisColor);

            GL.PopMatrix();
        }

        /// <summary>
        /// Updates the transform position and raises the <see cref="PositionChanged"/> event.
        /// </summary>
        /// <param name="value">
        /// The value to add on to the existing transform position.
        /// </param>
        private void UpdatePosition(Vector3 value)
        {
            var position = this.transform.position;
            var previousPosition = position;
            position += value;
            this.transform.position = position;
            this.OnPositionChanged(previousPosition, position);
        }

        #endregion
    }

    /// 
    /// Provides event arguments for the  type.
    /// 
    public class Vector3EventArgs : EventArgs
    {
        #region Constructors and Destructors

        /// 
        /// Initializes a new instance of the  class.
        /// 
        /// 
        /// The previous value.
        /// 
        /// 
        /// The current value.
        /// 
        public Vector3EventArgs(Vector3 previousValue, Vector3 value)
        {
            this.Value = value;
            this.PreviousValue = previousValue;
        }

        /// 
        /// Initializes a new instance of the  class.
        /// 
        public Vector3EventArgs()
        {
        }

        #endregion

        #region Public Properties

        /// 
        /// Gets or sets a previous  value.
        /// 
        public Vector3 PreviousValue { get; set; }

        /// 
        /// Gets or sets a  value.
        /// 
        public Vector3 Value { get; set; }

        #endregion
    }

Add comment



biuquote
  • Comment
  • Preview
Loading






Created by: X

Just another personal website in this crazy online world

Name of author Dean Lunz (aka Created by: X)
Computer programming nerd, and tech geek.
About Me -- Resume