Files

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);
}
}
}