328 lines
17 KiB
C#
328 lines
17 KiB
C#
using UnityEngine;
|
|
using System.Collections.Generic;
|
|
|
|
namespace RTG
|
|
{
|
|
/// <summary>
|
|
/// The main focus in this tutorial is to learn how to use the
|
|
/// 'CustomObjectLocalPivot' pivot type.
|
|
/// </summary>
|
|
public class Tut_5_CustomObjectLocalPivot : MonoBehaviour
|
|
{
|
|
/// <summary>
|
|
/// A private enum which is used by the class to differentiate between different
|
|
/// gizmo types. An example where this enum will come in handy is when we use the
|
|
/// W,E,R,T keys to switch between different types of gizmos. When the W key is
|
|
/// pressed for example, we will call the 'SetWorkGizmoId' function passing
|
|
/// GizmoId.Move as the parameter.
|
|
/// </summary>
|
|
private enum GizmoId
|
|
{
|
|
Move = 1,
|
|
Rotate,
|
|
Scale,
|
|
Universal
|
|
}
|
|
|
|
/// <summary>
|
|
/// The following 4 variables are references to the ObjectTransformGizmo behaviours
|
|
/// that will be used to move, rotate and scale our objects.
|
|
/// </summary>
|
|
private ObjectTransformGizmo _objectMoveGizmo;
|
|
private ObjectTransformGizmo _objectRotationGizmo;
|
|
private ObjectTransformGizmo _objectScaleGizmo;
|
|
private ObjectTransformGizmo _objectUniversalGizmo;
|
|
|
|
/// <summary>
|
|
/// The current work gizmo id. The work gizmo is the gizmo which is currently used
|
|
/// to transform objects. The W,E,R,T keys can be used to change the work gizmo as
|
|
/// needed.
|
|
/// </summary>
|
|
private GizmoId _workGizmoId;
|
|
/// <summary>
|
|
/// A reference to the current work gizmo. If the work gizmo id is GizmoId.Move, then
|
|
/// this will point to '_objectMoveGizmo'. For GizmoId.Rotate, it will point to
|
|
/// '_objectRotationGizmo' and so on.
|
|
/// </summary>
|
|
private ObjectTransformGizmo _workGizmo;
|
|
/// <summary>
|
|
/// A list of objects which are currently selected. This is also the list that holds
|
|
/// the gizmo target objects.
|
|
/// </summary>
|
|
private List<GameObject> _selectedObjects = new List<GameObject>();
|
|
|
|
/// <summary>
|
|
/// Performs all necessary initializations.
|
|
/// </summary>
|
|
private void Start()
|
|
{
|
|
// Create the 4 gizmos
|
|
_objectMoveGizmo = RTGizmosEngine.Get.CreateObjectMoveGizmo();
|
|
_objectRotationGizmo = RTGizmosEngine.Get.CreateObjectRotationGizmo();
|
|
_objectScaleGizmo = RTGizmosEngine.Get.CreateObjectScaleGizmo();
|
|
_objectUniversalGizmo = RTGizmosEngine.Get.CreateObjectUniversalGizmo();
|
|
|
|
// Call the 'SetEnabled' function on the parent gizmo to make sure
|
|
// the gizmos are initially hidden in the scene. We want the gizmo
|
|
// to show only when we have a target object available.
|
|
_objectMoveGizmo.Gizmo.SetEnabled(false);
|
|
_objectRotationGizmo.Gizmo.SetEnabled(false);
|
|
_objectScaleGizmo.Gizmo.SetEnabled(false);
|
|
_objectUniversalGizmo.Gizmo.SetEnabled(false);
|
|
|
|
// Link the selected objects list to the gizmos
|
|
// Note: The 'SetTargetObjects' function will instruct the gizmo to store
|
|
// a direct reference to the '_selecteObjects' list. This means that
|
|
// when you add or remove objects from this list, the gizmos will have
|
|
// access to the most recent/updated collection. You don't need to call
|
|
// 'SetTargetObjects' again when the list changes.
|
|
_objectMoveGizmo.SetTargetObjects(_selectedObjects);
|
|
_objectRotationGizmo.SetTargetObjects(_selectedObjects);
|
|
_objectScaleGizmo.SetTargetObjects(_selectedObjects);
|
|
_objectUniversalGizmo.SetTargetObjects(_selectedObjects);
|
|
|
|
// We initialize the work gizmo to the move gizmo by default. This means
|
|
// that the first time an object is clicked, the move gizmo will appear.
|
|
// You can change the default gizmo, by simply changing these 2 lines of
|
|
// code. For example, if you wanted the scale gizmo to be the default work
|
|
// gizmo, replace '_objectMoveGizmo' with '_objectScaleGizmo' and GizmoId.Move
|
|
// with GizmoId.Scale.
|
|
_workGizmo = _objectMoveGizmo;
|
|
_workGizmoId = GizmoId.Move;
|
|
|
|
// <BEGIN TUTORIAL>
|
|
// Get a reference to the object whose pivot we want to modify
|
|
GameObject doorObject = GameObject.Find("GreenCube");
|
|
|
|
// Calculate the object's world OBB and then use the 'BoxMath.CalcBoxFaceCenter'
|
|
// to calculate the center of the object's left face in world space. We will use
|
|
// this face center as our pivot.
|
|
OBB worldOBB = ObjectBounds.GetMeshWorldOBB(doorObject);
|
|
Vector3 faceCenter = BoxMath.CalcBoxFaceCenter(worldOBB.Center, worldOBB.Size, worldOBB.Rotation, BoxFace.Left);
|
|
|
|
// Use the 'SetObjectCustomLocalPivot' function to specify the object's pivot.
|
|
// Note: We need to call 'InverseTransformPoint' on the face center because the function expects
|
|
// a pivot point expressed in the object's local coordinate system.
|
|
_objectRotationGizmo.SetObjectCustomLocalPivot(doorObject, doorObject.transform.InverseTransformPoint(faceCenter));
|
|
|
|
// Change the transform pivot to 'CustomObjectLocalPivot'
|
|
_objectRotationGizmo.SetTransformPivot(GizmoObjectTransformPivot.CustomObjectLocalPivot);
|
|
// <END TUTORIAL>
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called every frame to perform all necessary updates. In this tutorial,
|
|
/// we listen to user input and take action.
|
|
/// </summary>
|
|
private void Update()
|
|
{
|
|
// Check if the left mouse button was pressed in the current frame.
|
|
// Note: Something that was left out of the video tutorial by mistake. We
|
|
// only take the mouse click into account if no gizmo is currently
|
|
// hovered. When a gizmo is hovered, we ignore clicks because in that
|
|
// case a click usually represents the intent of clicking and dragging
|
|
// the gizmo handles. If we didn't perform this check, clicking on a
|
|
// gizmo might actually disable it instead if the click does not hover
|
|
// a game object (i.e. thin air click).
|
|
if (RTInput.WasLeftMouseButtonPressedThisFrame() &&
|
|
RTGizmosEngine.Get.HoveredGizmo == null)
|
|
{
|
|
// Pick a game object
|
|
GameObject pickedObject = PickGameObject();
|
|
if (pickedObject != null)
|
|
{
|
|
// Is the CTRL key pressed?
|
|
if (RTInput.IsKeyPressed(KeyCode.LeftControl))
|
|
{
|
|
// The CTRL key is pressed; it means we find ourselves in 2 possible situations:
|
|
// a) the picked object is already selected, in which case we deselect it;
|
|
// b) the picked object is not selected, in which case we append it to the selection.
|
|
if (_selectedObjects.Contains(pickedObject)) _selectedObjects.Remove(pickedObject);
|
|
else _selectedObjects.Add(pickedObject);
|
|
|
|
// The selection has changed
|
|
OnSelectionChanged();
|
|
}
|
|
else
|
|
{
|
|
// The CTRL key is not pressed; in this case we just clear the selection and
|
|
// select only the object that we clicked on.
|
|
_selectedObjects.Clear();
|
|
_selectedObjects.Add(pickedObject);
|
|
|
|
// The selection has changed
|
|
OnSelectionChanged();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If we reach this point, it means no object was picked. This means that we clicked
|
|
// in thin air, so we just clear the selected objects list.
|
|
_selectedObjects.Clear();
|
|
OnSelectionChanged();
|
|
|
|
// The selection has changed
|
|
OnSelectionChanged();
|
|
}
|
|
}
|
|
|
|
// If the G key was pressed, we change the transform space to Global. Otherwise,
|
|
// if the L key was pressed, we change it to Local.
|
|
if (RTInput.WasKeyPressedThisFrame(KeyCode.G)) SetTransformSpace(GizmoSpace.Global);
|
|
else if (RTInput.WasKeyPressedThisFrame(KeyCode.L)) SetTransformSpace(GizmoSpace.Local);
|
|
|
|
// We will change the pivot type when the P key is pressed
|
|
if (RTInput.WasKeyPressedThisFrame(KeyCode.P))
|
|
{
|
|
// Retrieve the current transform pivot and activate the other one instead.
|
|
// Note: In order to retrieve the current transform pivot, it is enough to
|
|
// use the 'TransformPivot' property of one of our gizmos. This works
|
|
// because all gizmos use the same transform pivot in this example. We
|
|
// make sure of that inside the 'SetTransformPivot' function.
|
|
GizmoObjectTransformPivot currentPivot = _objectMoveGizmo.TransformPivot;
|
|
if (currentPivot == GizmoObjectTransformPivot.ObjectGroupCenter) SetTransformPivot(GizmoObjectTransformPivot.ObjectMeshPivot);
|
|
else SetTransformPivot(GizmoObjectTransformPivot.ObjectGroupCenter);
|
|
}
|
|
|
|
// Switch between different gizmo types using the W,E,R,T keys.
|
|
// Note: We use the 'SetWorkGizmoId' function to perform the switch.
|
|
if (RTInput.WasKeyPressedThisFrame(KeyCode.W)) SetWorkGizmoId(GizmoId.Move);
|
|
else if (RTInput.WasKeyPressedThisFrame(KeyCode.E)) SetWorkGizmoId(GizmoId.Rotate);
|
|
else if (RTInput.WasKeyPressedThisFrame(KeyCode.R)) SetWorkGizmoId(GizmoId.Scale);
|
|
else if (RTInput.WasKeyPressedThisFrame(KeyCode.T)) SetWorkGizmoId(GizmoId.Universal);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Uses the mouse position to pick a game object in the scene. Returns
|
|
/// the picked game object or null if no object is picked.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Objects must have colliders attached.
|
|
/// </remarks>
|
|
private GameObject PickGameObject()
|
|
{
|
|
// Build a ray using the current mouse cursor position
|
|
Ray ray = Camera.main.ScreenPointToRay(RTInput.MousePosition);
|
|
|
|
// Check if the ray intersects a game object. If it does, return it
|
|
RaycastHit rayHit;
|
|
if (Physics.Raycast(ray, out rayHit, float.MaxValue))
|
|
return rayHit.collider.gameObject;
|
|
|
|
// No object is intersected by the ray. Return null.
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// This function is called to change the type of work gizmo. This is
|
|
/// used in the 'Update' function in response to the user pressing the
|
|
/// W,E,R,T keys to switch between different gizmo types.
|
|
/// </summary>
|
|
private void SetWorkGizmoId(GizmoId gizmoId)
|
|
{
|
|
// If the specified gizmo id is the same as the current id, there is nothing left to do
|
|
if (gizmoId == _workGizmoId) return;
|
|
|
|
// Start with a clean slate and disable all gizmos
|
|
_objectMoveGizmo.Gizmo.SetEnabled(false);
|
|
_objectRotationGizmo.Gizmo.SetEnabled(false);
|
|
_objectScaleGizmo.Gizmo.SetEnabled(false);
|
|
_objectUniversalGizmo.Gizmo.SetEnabled(false);
|
|
|
|
// At this point all gizmos are disabled. Now we need to check the gizmo id
|
|
// and adjust the '_workGizmo' variable.
|
|
_workGizmoId = gizmoId;
|
|
if (gizmoId == GizmoId.Move) _workGizmo = _objectMoveGizmo;
|
|
else if (gizmoId == GizmoId.Rotate) _workGizmo = _objectRotationGizmo;
|
|
else if (gizmoId == GizmoId.Scale) _workGizmo = _objectScaleGizmo;
|
|
else if (gizmoId == GizmoId.Universal) _workGizmo = _objectUniversalGizmo;
|
|
|
|
// If we have any selected objects, we need to make sure the work gizmo is enabled
|
|
if (_selectedObjects.Count != 0)
|
|
{
|
|
// Make sure the work gizmo is enabled. There is no need to check if the gizmo is already
|
|
// enabled. The 'SetEnabled' call will simply be ignored if that is the case.
|
|
_workGizmo.Gizmo.SetEnabled(true);
|
|
|
|
// When working with transform spaces and pivots, the gizmos need to know about the pivot object.
|
|
// This piece of information is necessary when the transform space is set to local because in that
|
|
// case the gizmo will have its rotation synchronized with the target objects rotation. But because
|
|
// there is more than one target object, we need to tell the gizmo which object to use. This is the
|
|
// role if the pivot object in this case. This pivot object is also useful when the transform pivot
|
|
// is set to 'ObjectMeshPivot' because it will be used to adjust the position of the gizmo.
|
|
_workGizmo.SetTargetPivotObject(_selectedObjects[_selectedObjects.Count - 1]);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called from the 'Update' function whenever the '_selectedObjects' list
|
|
/// changes. It is responsible for updating the gizmos accordingly.
|
|
/// </summary>
|
|
private void OnSelectionChanged()
|
|
{
|
|
// If we have any selected objects, we need to make sure the work gizmo is enabled
|
|
if (_selectedObjects.Count != 0)
|
|
{
|
|
// Make sure the work gizmo is enabled. There is no need to check if the gizmo is already
|
|
// enabled. The 'SetEnabled' call will simply be ignored if that is the case.
|
|
_workGizmo.Gizmo.SetEnabled(true);
|
|
|
|
// When working with transform spaces and pivots, the gizmos need to know about the pivot object.
|
|
// This piece of information is necessary when the transform space is set to local because in that
|
|
// case the gizmo will have its rotation synchronized with the target objects rotation. But because
|
|
// there is more than one target object, we need to tell the gizmo which object to use. This is the
|
|
// role if the pivot object in this case. This pivot object is also useful when the transform pivot
|
|
// is set to 'ObjectMeshPivot' because it will be used to adjust the position of the gizmo.
|
|
_workGizmo.SetTargetPivotObject(_selectedObjects[_selectedObjects.Count - 1]);
|
|
|
|
// The last step we need to perform is to make sure that the work gizmo is positioned
|
|
// and rotated correctly. This is because the gizmos (as will become more clear in
|
|
// later tutorials) have 2 properties such as transform space and transform pivot and
|
|
// when the selected objects change, these 2 properties will dictate how the gizmo should
|
|
// be positioned and rotated. In order to ensure that the correct position and rotation
|
|
// are used, we need to call 'RefreshPositionAndRotation'.
|
|
_workGizmo.RefreshPositionAndRotation();
|
|
}
|
|
else
|
|
{
|
|
// The target object is null. In this case, we don't want any gizmos to be visible
|
|
// in the scene, so we disable all of them.
|
|
_objectMoveGizmo.Gizmo.SetEnabled(false);
|
|
_objectRotationGizmo.Gizmo.SetEnabled(false);
|
|
_objectScaleGizmo.Gizmo.SetEnabled(false);
|
|
_objectUniversalGizmo.Gizmo.SetEnabled(false);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called from the 'Update' function in response to user input in order
|
|
/// to change the transform space for all gizmos. The parameter represents
|
|
/// the desired transform space and is of type 'GizmoSpace'. This is an enum
|
|
/// with 2 mamebers: Global and Local.
|
|
/// </summary>
|
|
private void SetTransformSpace(GizmoSpace transformSpace)
|
|
{
|
|
// In order to change the transform space for a gizmo, we need to call the
|
|
// gizmo's 'SetTransformSpace' function. We do this for all gizmos and pass
|
|
// the specified transform space as parameter.
|
|
_objectMoveGizmo.SetTransformSpace(transformSpace);
|
|
_objectRotationGizmo.SetTransformSpace(transformSpace);
|
|
_objectScaleGizmo.SetTransformSpace(transformSpace);
|
|
_objectUniversalGizmo.SetTransformSpace(transformSpace);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called from the 'Update' function in response to user input in order
|
|
/// to change the transform pivot for all gizmos. The parameter represents
|
|
/// the desired transform pivot and is of type 'GizmoObjectTransformPivot'.
|
|
/// </summary>
|
|
private void SetTransformPivot(GizmoObjectTransformPivot transformPivot)
|
|
{
|
|
_objectMoveGizmo.SetTransformPivot(transformPivot);
|
|
_objectRotationGizmo.SetTransformPivot(transformPivot);
|
|
_objectScaleGizmo.SetTransformPivot(transformPivot);
|
|
_objectUniversalGizmo.SetTransformPivot(transformPivot);
|
|
}
|
|
}
|
|
}
|