943 lines
47 KiB
C#
943 lines
47 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEditor;
|
|
using UnityEditor.SceneManagement;
|
|
|
|
// Sci-Fi Ship Controller. Copyright (c) 2018-2022 SCSM Pty Ltd. All rights reserved.
|
|
namespace SciFiShipController
|
|
{
|
|
[CustomEditor(typeof(ShipDockingStation))]
|
|
public class ShipDockingStationEditor : Editor
|
|
{
|
|
#region Enumerations
|
|
|
|
#endregion
|
|
|
|
#region Custom Editor private variables
|
|
private ShipDockingStation shipDockingStation;
|
|
private SSCManager sscManager;
|
|
|
|
// Formatting and style variables
|
|
private string txtColourName = "Black";
|
|
private Color defaultTextColour = Color.black;
|
|
private string labelText;
|
|
private GUIStyle labelFieldRichText;
|
|
private GUIStyle helpBoxRichText;
|
|
private GUIStyle buttonCompact;
|
|
private GUIStyle foldoutStyleNoLabel;
|
|
private static GUIStyle toggleCompactButtonStyleNormal = null; // Small Toggle button. e.g. G(izmo) on/off
|
|
private static GUIStyle toggleCompactButtonStyleToggled = null;
|
|
private float defaultEditorLabelWidth = 0f;
|
|
private float defaultEditorFieldWidth = 0f;
|
|
|
|
// Similar to isSceneDirtyRequired (SceneView variabled) but used for Inspector modifications.
|
|
private bool isSceneModified = false;
|
|
|
|
private int dockingPointMoveDownPos = -1;
|
|
private int dockingPointInsertPos = -1;
|
|
private int dockingPointDeletePos = -1;
|
|
|
|
#endregion
|
|
|
|
#region SceneView Variables
|
|
|
|
private bool isSceneDirtyRequired = false;
|
|
private Quaternion sceneViewTrfmRotation = Quaternion.identity;
|
|
private ShipDockingPoint dockingPointComponent = null;
|
|
|
|
private Vector3 componentHandlePosition = Vector3.zero;
|
|
//private Vector3 gizmoPosition = Vector3.zero;
|
|
private Quaternion componentHandleRotation = Quaternion.identity;
|
|
private float relativeHandleSize = 1f;
|
|
|
|
private Color fadedGizmoColour;
|
|
|
|
#endregion
|
|
|
|
#region GUIContent General
|
|
private readonly static GUIContent headerContent = new GUIContent("This module enables you to dock other ships with this gameobject or Ship");
|
|
private readonly static GUIContent[] tabTexts = { new GUIContent("General"), new GUIContent("Events") };
|
|
private readonly static GUIContent initialiseOnAwakeContent = new GUIContent("Initialise on Awake", "If enabled, Initialise() will be called as soon as Awake() runs. " +
|
|
"This should be disabled if you are instantiating the Ship or ShipDockingStation through code and using the Docking API methods.");
|
|
|
|
private readonly static GUIContent gizmoToggleBtnContent = new GUIContent("G", "Toggle gizmos and visualisations on/off for all items in the scene view");
|
|
private readonly static GUIContent gizmoBtnContent = new GUIContent("G", "Toggle gizmos on/off in the scene view");
|
|
private readonly static GUIContent gizmoFindBtnContent = new GUIContent("F", "Find (select) in the scene view.");
|
|
private readonly static GUIContent resetBtnContent = new GUIContent("R", "Reset");
|
|
|
|
private readonly static GUIContent dockingPointExportJsonContent = new GUIContent("Export Json", "Export Docking Points to a json file");
|
|
private readonly static GUIContent dockingPointImportJsonContent = new GUIContent("Import Json", "Import Docking Points from a json file");
|
|
|
|
#endregion
|
|
|
|
#region GUIContent Events
|
|
private readonly static GUIContent onPreUndockContent = new GUIContent("", "Methods that get called immediately before Undock or UndockDelayed are executed. WARNING: Do NOT call UndockShip from this event.");
|
|
private readonly static GUIContent onPostDockedContent = new GUIContent("", "Methods that get called immediately after docking is complete. WARNING: Do NOT call DockShip from this event.");
|
|
|
|
#endregion
|
|
|
|
#region GUIContent Ship Docking Point
|
|
private readonly static GUIContent dkgPtRelativePositionContent = new GUIContent("Relative Position", "Local position relative to the ShipDockingStation tranform position");
|
|
private readonly static GUIContent dkgPtRelativeRotationContent = new GUIContent("Relative Rotation", "Docking Point rotation relative, in degrees, to the ShipDockingStation rotation");
|
|
private readonly static GUIContent dkgPtEntryPathContent = new GUIContent("Docking Entry Path", "The optional Path (stored as a guidHash) which identifies the entry path a ship can take to dock at this docking point");
|
|
private readonly static GUIContent dkgPtExitPathContent = new GUIContent("Undocking Exit Path", "The optional Path (stored as a guidHash) which identifies the exit path a ship can take to depart from this docking point");
|
|
private readonly static GUIContent dkgPtHoverHeightContent = new GUIContent("Hover Height", "This is the optimum height above the docking point in the relative up direction, a ship hovers before arriving or departing");
|
|
private readonly static GUIContent dkgPtDockedShipContent = new GUIContent("Assigned Ship", "The Ship in the scene that is currently docked, docking or undocking with the Ship Docking Point. This assigned ship requires a Ship Docking component.");
|
|
#endregion
|
|
|
|
#region Serialized Properties - General
|
|
private SerializedProperty selectedTabIntProp;
|
|
private SerializedProperty dkgPtListProp;
|
|
private SerializedProperty dkgPtProp;
|
|
private SerializedProperty dkgPtShowInEditorProp;
|
|
private SerializedProperty dkgPtEntryPathGUIDHashProp;
|
|
private SerializedProperty dkgPtExitPathGUIDHashProp;
|
|
private SerializedProperty dkgPtDockedShipProp;
|
|
private SerializedProperty dkgPtSelectedInSceneViewProp;
|
|
private SerializedProperty dkgPtShowGizmosInSceneViewProp;
|
|
private SerializedProperty isDockingPointListExpandedProp;
|
|
private SerializedProperty initialiseOnAwakeProp;
|
|
#endregion
|
|
|
|
#region Serialized Properties - Events
|
|
private SerializedProperty onPreUndockProp;
|
|
private SerializedProperty onPostDockedProp;
|
|
|
|
#endregion
|
|
|
|
#region Events
|
|
|
|
public void OnEnable()
|
|
{
|
|
shipDockingStation = (ShipDockingStation)target;
|
|
|
|
// Only use if require scene view interaction
|
|
#if UNITY_2019_1_OR_NEWER
|
|
SceneView.duringSceneGui -= SceneGUI;
|
|
SceneView.duringSceneGui += SceneGUI;
|
|
#else
|
|
SceneView.onSceneGUIDelegate -= SceneGUI;
|
|
SceneView.onSceneGUIDelegate += SceneGUI;
|
|
#endif
|
|
|
|
// Used in Richtext labels
|
|
if (EditorGUIUtility.isProSkin) { txtColourName = "White"; defaultTextColour = new Color(180f / 255f, 180f / 255f, 180f / 255f, 1f); }
|
|
|
|
// Keep compiler happy - can remove this later if it isn't required
|
|
if (defaultTextColour.a > 0f) { }
|
|
|
|
defaultEditorLabelWidth = 150f; // EditorGUIUtility.labelWidth;
|
|
defaultEditorFieldWidth = EditorGUIUtility.fieldWidth;
|
|
|
|
#region Find Properties - General
|
|
selectedTabIntProp = serializedObject.FindProperty("selectedTabInt");
|
|
initialiseOnAwakeProp = serializedObject.FindProperty("initialiseOnAwake");
|
|
#endregion
|
|
|
|
#region Find Properties - Events
|
|
onPreUndockProp = serializedObject.FindProperty("onPreUndock");
|
|
onPostDockedProp = serializedObject.FindProperty("onPostDocked");
|
|
#endregion
|
|
|
|
// Reset GUIStyles
|
|
toggleCompactButtonStyleNormal = null;
|
|
toggleCompactButtonStyleToggled = null;
|
|
foldoutStyleNoLabel = null;
|
|
|
|
if (sscManager == null) { sscManager = SSCManager.GetOrCreateManager(); }
|
|
|
|
if (shipDockingStation != null && shipDockingStation.shipDockingPointList != null)
|
|
{
|
|
// If a docking point is marked as selected, turn off the transform tools
|
|
if (shipDockingStation.shipDockingPointList.Exists(dp => dp.selectedInSceneView == true))
|
|
{
|
|
Tools.hidden = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when the gameobject loses focus or Unity Editor enters/exits
|
|
/// play mode
|
|
/// </summary>
|
|
void OnDestroy()
|
|
{
|
|
#if UNITY_2019_1_OR_NEWER
|
|
SceneView.duringSceneGui -= SceneGUI;
|
|
#else
|
|
SceneView.onSceneGUIDelegate -= SceneGUI;
|
|
#endif
|
|
|
|
// Always unhide Unity tools when losing focus on this gameObject
|
|
Tools.hidden = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets called automatically 10 times per second
|
|
/// Comment out if not required
|
|
/// </summary>
|
|
void OnInspectorUpdate()
|
|
{
|
|
// OnInspectorGUI() only registers events when the mouse is positioned over the custom editor window
|
|
// This code forces OnInspectorGUI() to run every frame, so it registers events even when the mouse
|
|
// is positioned over the scene view
|
|
if (shipDockingStation.allowRepaint) { Repaint(); }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Methods
|
|
|
|
// <summary>
|
|
/// Draw the toolbar using the supplied array of tab text.
|
|
/// </summary>
|
|
/// <param name="tabGUIContent"></param>
|
|
private void DrawToolBar(GUIContent[] tabGUIContent)
|
|
{
|
|
int prevTab = selectedTabIntProp.intValue;
|
|
|
|
// Show a toolbar to allow the user to switch between viewing different areas
|
|
selectedTabIntProp.intValue = GUILayout.Toolbar(selectedTabIntProp.intValue, tabGUIContent);
|
|
|
|
// When switching tabs, disable focus on previous control
|
|
if (prevTab != selectedTabIntProp.intValue) { GUI.FocusControl(null); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draw gizmos and editable handles in the scene view
|
|
/// </summary>
|
|
/// <param name="sv"></param>
|
|
private void SceneGUI(SceneView sv)
|
|
{
|
|
if (shipDockingStation != null && shipDockingStation.gameObject.activeInHierarchy)
|
|
{
|
|
isSceneDirtyRequired = false;
|
|
|
|
// Get the rotation of the gameobject / transform in the scene
|
|
sceneViewTrfmRotation = Quaternion.LookRotation(shipDockingStation.transform.forward, shipDockingStation.transform.up);
|
|
|
|
Vector3 localScale = shipDockingStation.transform.localScale;
|
|
|
|
using (new Handles.DrawingScope(shipDockingStation.dockingPointGizmoColour))
|
|
{
|
|
int numDockingPoints = shipDockingStation.shipDockingPointList == null ? 0 : shipDockingStation.shipDockingPointList.Count;
|
|
for (int dpi = 0; dpi < numDockingPoints; dpi++)
|
|
{
|
|
dockingPointComponent = shipDockingStation.shipDockingPointList[dpi];
|
|
|
|
fadedGizmoColour = shipDockingStation.dockingPointGizmoColour;
|
|
|
|
// If this is not the selected Docking Point, show it a little more transparent
|
|
if (!dockingPointComponent.selectedInSceneView)
|
|
{
|
|
fadedGizmoColour.a *= 0.65f;
|
|
if (fadedGizmoColour.a < 0.1f) { fadedGizmoColour.a = shipDockingStation.dockingPointGizmoColour.a; }
|
|
}
|
|
|
|
if (dockingPointComponent.showGizmosInSceneView)
|
|
{
|
|
// Get component handle position
|
|
componentHandlePosition = shipDockingStation.transform.TransformPoint(new Vector3(dockingPointComponent.relativePosition.x / localScale.x, dockingPointComponent.relativePosition.y / localScale.y, dockingPointComponent.relativePosition.z / localScale.z));
|
|
|
|
relativeHandleSize = HandleUtility.GetHandleSize(componentHandlePosition);
|
|
|
|
// Get component handle rotation
|
|
componentHandleRotation = shipDockingStation.transform.rotation * Quaternion.Euler(dockingPointComponent.relativeRotation);
|
|
|
|
// Draw point in the scene that is non-interactable
|
|
if (Event.current.type == EventType.Repaint)
|
|
{
|
|
using (new Handles.DrawingScope(fadedGizmoColour))
|
|
{
|
|
// Forwards direction of the ship docking point
|
|
Handles.ArrowHandleCap(GUIUtility.GetControlID(FocusType.Passive), componentHandlePosition, componentHandleRotation, 1.25f * relativeHandleSize, EventType.Repaint);
|
|
|
|
// Draw the up direction with a sphere on top of a line
|
|
Quaternion upRotation = componentHandleRotation * Quaternion.Euler(270f, 0f, 0f);
|
|
Handles.DrawLine(componentHandlePosition, componentHandlePosition + upRotation * (Vector3.forward * relativeHandleSize * 1.25f));
|
|
Handles.SphereHandleCap(GUIUtility.GetControlID(FocusType.Passive), componentHandlePosition + (upRotation * (Vector3.forward * relativeHandleSize * 1.25f)), upRotation, 0.3f * relativeHandleSize, EventType.Repaint);
|
|
}
|
|
}
|
|
|
|
if (dockingPointComponent.selectedInSceneView)
|
|
{
|
|
// Choose which handle to draw based on which Unity tool is selected
|
|
if (Tools.current == Tool.Rotate)
|
|
{
|
|
EditorGUI.BeginChangeCheck();
|
|
|
|
// Draw a rotation handle
|
|
componentHandleRotation = Handles.RotationHandle(componentHandleRotation, componentHandlePosition);
|
|
|
|
// Use the rotation handle to edit the docking point direction
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
isSceneDirtyRequired = true;
|
|
Undo.RecordObject(shipDockingStation, "Rotate Docking Point");
|
|
|
|
dockingPointComponent.relativeRotation = (Quaternion.Inverse(shipDockingStation.transform.rotation) * componentHandleRotation).eulerAngles;
|
|
}
|
|
}
|
|
|
|
#if UNITY_2017_3_OR_NEWER
|
|
else if (Tools.current == Tool.Move || Tools.current == Tool.Transform)
|
|
#else
|
|
else if (Tools.current == Tool.Move)
|
|
#endif
|
|
{
|
|
EditorGUI.BeginChangeCheck();
|
|
|
|
// Draw a movement handle
|
|
componentHandlePosition = Handles.PositionHandle(componentHandlePosition, sceneViewTrfmRotation);
|
|
|
|
// Use the position handle to edit the position of the weapon
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
isSceneDirtyRequired = true;
|
|
Undo.RecordObject(shipDockingStation, "Move Docking Point");
|
|
//dockingPointComponent.relativePosition = shipDockingStation.transform.InverseTransformPoint(componentHandlePosition);
|
|
dockingPointComponent.relativePosition = shipDockingStation.transform.InverseTransformPoint(new Vector3(componentHandlePosition.x * localScale.x, componentHandlePosition.y * localScale.y, componentHandlePosition.z * localScale.z));
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the docking point is not selected it will be faded, else it will be the original colour.
|
|
using (new Handles.DrawingScope(fadedGizmoColour))
|
|
{
|
|
// Allow the user to select/deselect the docking point in the scene view
|
|
if (Handles.Button(componentHandlePosition, Quaternion.identity, 0.5f * relativeHandleSize, 0.25f * relativeHandleSize, Handles.SphereHandleCap))
|
|
{
|
|
if (dockingPointComponent.selectedInSceneView)
|
|
{
|
|
DeselectAllComponents();
|
|
dockingPointComponent.showInEditor = false;
|
|
}
|
|
else
|
|
{
|
|
DeselectAllComponents();
|
|
shipDockingStation.isDockingPointListExpanded = false;
|
|
ExpandList(shipDockingStation.shipDockingPointList, false);
|
|
dockingPointComponent.selectedInSceneView = true;
|
|
dockingPointComponent.showInEditor = true;
|
|
isSceneDirtyRequired = true;
|
|
// Hide Unity tools
|
|
Tools.hidden = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isSceneDirtyRequired && !Application.isPlaying)
|
|
{
|
|
isSceneDirtyRequired = false;
|
|
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Always unhide Unity tools and deselect all components when the object is disabled
|
|
Tools.hidden = false;
|
|
DeselectAllComponents();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Expand (show) or collapse (hide) all items in a list in the editor
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="componentList"></param>
|
|
/// <param name="isExpanded"></param>
|
|
private void ExpandList<T>(List<T> componentList, bool isExpanded)
|
|
{
|
|
int numComponents = componentList == null ? 0 : componentList.Count;
|
|
|
|
if (numComponents > 0)
|
|
{
|
|
System.Type compType = typeof(T);
|
|
|
|
if (compType == typeof(ShipDockingPoint))
|
|
{
|
|
for (int cpi = 0; cpi < numComponents; cpi++)
|
|
{
|
|
(componentList[cpi] as ShipDockingPoint).showInEditor = isExpanded;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deselect all components in the scene view edit mode, and unhides the Unity tools
|
|
/// </summary>
|
|
private void DeselectAllComponents()
|
|
{
|
|
// Set all components to not be selected
|
|
if (shipDockingStation != null)
|
|
{
|
|
int numDockingPoints = shipDockingStation.shipDockingPointList == null ? 0 : shipDockingStation.shipDockingPointList.Count;
|
|
for (int dpi = 0; dpi < numDockingPoints; dpi++)
|
|
{
|
|
shipDockingStation.shipDockingPointList[dpi].selectedInSceneView = false;
|
|
}
|
|
}
|
|
// Unhide Unity tools
|
|
Tools.hidden = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Toggle on/off the gizmos and visualisations of a list of components based
|
|
/// on the state of the first component in the list.
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="componentList"></param>
|
|
private void ToggleGizmos<T>(List<T> componentList)
|
|
{
|
|
int numComponents = componentList == null ? 0 : componentList.Count;
|
|
|
|
if (numComponents > 0)
|
|
{
|
|
System.Type compType = typeof(T);
|
|
|
|
if (compType == typeof(ShipDockingPoint))
|
|
{
|
|
// Examine the first component
|
|
bool showGizmos = !(componentList[0] as ShipDockingPoint).showGizmosInSceneView;
|
|
|
|
// Toggle gizmos and visualisations to opposite of first member
|
|
for (int cpi = 0; cpi < numComponents; cpi++)
|
|
{
|
|
(componentList[cpi] as ShipDockingPoint).showGizmosInSceneView = showGizmos;
|
|
}
|
|
|
|
// When no Gizmos are shown , ensure we can see the standard Unity tools.
|
|
// This allows the user to move the ShipDockingStation gameobject
|
|
if (!showGizmos) { Tools.hidden = false; }
|
|
}
|
|
|
|
SceneView.RepaintAll();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draw a (F)ind button which will select the item in the scene view.
|
|
/// Automatically show Gizmo in scene view if it is currently disabled
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="componentList"></param>
|
|
/// <param name="showInEditorProp"></param>
|
|
/// <param name="selectedInSceneViewProp"></param>
|
|
/// <param name="showGizmoInSceneViewProp"></param>
|
|
private void SelectItemInSceneViewButton<T>(List<T> componentList, SerializedProperty showInEditorProp, SerializedProperty selectedInSceneViewProp, SerializedProperty showGizmoInSceneViewProp)
|
|
{
|
|
if (GUILayout.Button(gizmoFindBtnContent, buttonCompact, GUILayout.MaxWidth(20f)))
|
|
{
|
|
serializedObject.ApplyModifiedProperties();
|
|
DeselectAllComponents();
|
|
ExpandList(componentList, false);
|
|
serializedObject.Update();
|
|
selectedInSceneViewProp.boolValue = true;
|
|
showInEditorProp.boolValue = true;
|
|
showGizmoInSceneViewProp.boolValue = true;
|
|
// Hide Unity tools
|
|
Tools.hidden = true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Display the path name and give option to select one from the scene (from SSCManager)
|
|
/// </summary>
|
|
/// <param name="propPathGUIDHash"></param>
|
|
/// <param name="shipDockingPointIndex"></param>
|
|
/// <param name="isEntryPath"></param>
|
|
/// <param name="guiContent"></param>
|
|
private void DrawPathInspector(SerializedProperty propPathGUIDHash, int shipDockingPointIndex, bool isEntryPath, GUIContent guiContent)
|
|
{
|
|
GUILayout.BeginHorizontal();
|
|
#if UNITY_2019_3_OR_NEWER
|
|
EditorGUILayout.LabelField(guiContent, GUILayout.Width(defaultEditorLabelWidth - 25f));
|
|
#else
|
|
EditorGUILayout.LabelField(guiContent, GUILayout.Width(defaultEditorLabelWidth - 29f));
|
|
#endif
|
|
PathData pathData = sscManager.GetPath(propPathGUIDHash.intValue);
|
|
|
|
int selectedIdx = sscManager.pathDataList.FindIndex(path => path.guidHash == propPathGUIDHash.intValue);
|
|
|
|
if (GUILayout.Button("..", buttonCompact, GUILayout.MaxWidth(20f)))
|
|
{
|
|
// Apply property changes
|
|
serializedObject.ApplyModifiedProperties();
|
|
|
|
// Create a drop down list of all the paths
|
|
GenericMenu dropdown = new GenericMenu();
|
|
for (int i = 0; i < sscManager.pathDataList.Count; i++)
|
|
{
|
|
// Replace space #/%/& with different chars as Unity treats them as SHIFT/CTRL/ALT in menus.
|
|
string _pathName = string.IsNullOrEmpty(sscManager.pathDataList[i].name) ? "No Name" : sscManager.pathDataList[i].name.Replace(" #", "_#").Replace(" &", " &&").Replace(" %", "_%");
|
|
dropdown.AddItem(new GUIContent(_pathName), i == selectedIdx, UpdatePath, new Vector3Int(shipDockingPointIndex, isEntryPath ? 0 : 1, sscManager.pathDataList[i].guidHash));
|
|
}
|
|
dropdown.ShowAsContext();
|
|
SceneView.RepaintAll();
|
|
|
|
serializedObject.Update();
|
|
}
|
|
|
|
EditorGUILayout.LabelField(pathData != null ? (string.IsNullOrEmpty(pathData.name) ? "No Name" : pathData.name) : "No Path");
|
|
|
|
// Clear the current path setting
|
|
if (GUILayout.Button("X", buttonCompact, GUILayout.MaxWidth(20f))) { propPathGUIDHash.intValue = 0; }
|
|
|
|
GUILayout.EndHorizontal();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Dropdown menu callback method used when a entry or exit path is selected
|
|
/// </summary>
|
|
/// <param name="obj"></param>
|
|
private void UpdatePath(object obj)
|
|
{
|
|
// The menu data is passed as 3 integers in a Vector3Int
|
|
// The index of the docking point in the list
|
|
// The selected Path guidHash
|
|
// which is being assigned to the location slot on the path.
|
|
if (obj != null && obj.GetType() == typeof(Vector3Int))
|
|
{
|
|
Vector3Int objData = (Vector3Int)obj;
|
|
|
|
bool isEntryPath = objData.y == 0;
|
|
|
|
if (shipDockingStation != null && shipDockingStation.shipDockingPointList != null)
|
|
{
|
|
Undo.RecordObject(shipDockingStation, "Set " + (isEntryPath ? "Entry" : "Exit") + " Path ");
|
|
|
|
if (isEntryPath)
|
|
{
|
|
shipDockingStation.shipDockingPointList[objData.x].guidHashEntryPath = objData.z;
|
|
}
|
|
else { shipDockingStation.shipDockingPointList[objData.x].guidHashExitPath = objData.z; }
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region OnInspectorGUI
|
|
|
|
// This function overides what is normally seen in the inspector window
|
|
// This allows stuff like buttons to be drawn there
|
|
public override void OnInspectorGUI()
|
|
{
|
|
#region Initialise
|
|
|
|
shipDockingStation.allowRepaint = false;
|
|
isSceneModified = false;
|
|
EditorGUIUtility.labelWidth = defaultEditorLabelWidth;
|
|
EditorGUIUtility.fieldWidth = defaultEditorFieldWidth;
|
|
|
|
#endregion
|
|
|
|
#region Configure Buttons and Styles
|
|
|
|
// Set up rich text GUIStyles
|
|
helpBoxRichText = new GUIStyle("HelpBox");
|
|
helpBoxRichText.richText = true;
|
|
|
|
labelFieldRichText = new GUIStyle("Label");
|
|
labelFieldRichText.richText = true;
|
|
|
|
buttonCompact = new GUIStyle("Button");
|
|
buttonCompact.fontSize = 10;
|
|
|
|
// Set up the toggle buttons styles
|
|
if (toggleCompactButtonStyleNormal == null)
|
|
{
|
|
// Create a new button or else will effect the Button style for other buttons too
|
|
toggleCompactButtonStyleNormal = new GUIStyle("Button");
|
|
toggleCompactButtonStyleNormal.fontSize = 10;
|
|
toggleCompactButtonStyleToggled = new GUIStyle(toggleCompactButtonStyleNormal);
|
|
toggleCompactButtonStyleNormal.fontStyle = FontStyle.Normal;
|
|
toggleCompactButtonStyleToggled.fontStyle = FontStyle.Bold;
|
|
toggleCompactButtonStyleToggled.normal.background = toggleCompactButtonStyleToggled.active.background;
|
|
}
|
|
|
|
if (foldoutStyleNoLabel == null)
|
|
{
|
|
// When using a no-label foldout, don't forget to set the global
|
|
// EditorGUIUtility.fieldWidth to a small value like 15, then back
|
|
// to the original afterward.
|
|
foldoutStyleNoLabel = new GUIStyle(EditorStyles.foldout);
|
|
foldoutStyleNoLabel.fixedWidth = 0.01f;
|
|
}
|
|
|
|
#endregion
|
|
|
|
// Read in all the properties
|
|
serializedObject.Update();
|
|
|
|
#region Header Info and Buttons
|
|
SSCEditorHelper.SSCVersionHeader(labelFieldRichText);
|
|
EditorGUILayout.LabelField(headerContent, helpBoxRichText);
|
|
DrawToolBar(tabTexts);
|
|
#endregion
|
|
|
|
#region General Properties
|
|
if (selectedTabIntProp.intValue == 0)
|
|
{
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
EditorGUILayout.PropertyField(initialiseOnAwakeProp, initialiseOnAwakeContent);
|
|
EditorGUILayout.EndVertical();
|
|
|
|
#region Import and Export Docking Points as json
|
|
GUILayout.BeginHorizontal();
|
|
if (GUILayout.Button(dockingPointExportJsonContent, buttonCompact, GUILayout.Width(100f)))
|
|
{
|
|
string exportPath = EditorUtility.SaveFilePanel("Save Docking Points", "Assets", shipDockingStation.name + "_docking_points", "json");
|
|
|
|
if (shipDockingStation.SaveDockingPointDataAsJson(exportPath))
|
|
{
|
|
// Check if path is in Project folder
|
|
if (exportPath.Contains(Application.dataPath))
|
|
{
|
|
// Get the folder to highlight in the Project folder
|
|
string folderPath = SSCEditorHelper.GetAssetFolderFromFilePath(exportPath);
|
|
|
|
// Get the json file in the Project folder
|
|
exportPath = "Assets" + exportPath.Replace(Application.dataPath, "");
|
|
AssetDatabase.ImportAsset(exportPath);
|
|
|
|
SSCEditorHelper.HighlightFolderInProjectWindow(folderPath, true, true);
|
|
}
|
|
Debug.Log("ShipDockingStation docking points exported to " + exportPath);
|
|
}
|
|
GUIUtility.ExitGUI();
|
|
}
|
|
if (GUILayout.Button(dockingPointImportJsonContent, buttonCompact, GUILayout.Width(100f)))
|
|
{
|
|
string importPath = string.Empty, importFileName = string.Empty;
|
|
|
|
if (SSCEditorHelper.GetFilePathFromUser("Import Docking Point Data", SSCSetup.sscFolder, new string[] { "JSON", "json" }, false, ref importPath, ref importFileName))
|
|
{
|
|
List<ShipDockingPoint> importedDockingPointList = shipDockingStation.ImportDockingPointDataFromJson(importPath, importFileName);
|
|
|
|
int numImportedDockingPoints = importedDockingPointList == null ? 0 : importedDockingPointList.Count;
|
|
|
|
if (numImportedDockingPoints > 0)
|
|
{
|
|
serializedObject.ApplyModifiedProperties();
|
|
Undo.RecordObject(shipDockingStation, "Import Docking Points");
|
|
|
|
if (shipDockingStation.shipDockingPointList != null)
|
|
{
|
|
shipDockingStation.shipDockingPointList.Clear();
|
|
}
|
|
shipDockingStation.shipDockingPointList.AddRange(importedDockingPointList);
|
|
serializedObject.Update();
|
|
}
|
|
}
|
|
|
|
GUIUtility.ExitGUI();
|
|
}
|
|
GUILayout.EndHorizontal();
|
|
|
|
#endregion
|
|
|
|
#region Docking Points
|
|
|
|
// Checking the property for being NULL doesn't check if the list is actually null.
|
|
if (shipDockingStation.shipDockingPointList == null)
|
|
{
|
|
// Apply property changes
|
|
serializedObject.ApplyModifiedProperties();
|
|
shipDockingStation.shipDockingPointList = new List<ShipDockingPoint>(10);
|
|
isSceneModified = true;
|
|
// Read in the properties
|
|
serializedObject.Update();
|
|
}
|
|
|
|
dkgPtListProp = serializedObject.FindProperty("shipDockingPointList");
|
|
int numDockingPoints = dkgPtListProp.arraySize;
|
|
|
|
#region Add-Remove Docking Points and Gizmos Buttons
|
|
|
|
// Reset button variables
|
|
dockingPointMoveDownPos = -1;
|
|
dockingPointInsertPos = -1;
|
|
dockingPointDeletePos = -1;
|
|
|
|
GUILayout.BeginHorizontal();
|
|
|
|
EditorGUI.indentLevel += 1;
|
|
EditorGUIUtility.fieldWidth = 15f;
|
|
isDockingPointListExpandedProp = serializedObject.FindProperty("isDockingPointListExpanded");
|
|
EditorGUI.BeginChangeCheck();
|
|
isDockingPointListExpandedProp.boolValue = EditorGUILayout.Foldout(isDockingPointListExpandedProp.boolValue, "", foldoutStyleNoLabel);
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
serializedObject.ApplyModifiedProperties();
|
|
ExpandList(shipDockingStation.shipDockingPointList, isDockingPointListExpandedProp.boolValue);
|
|
// Read in the properties
|
|
serializedObject.Update();
|
|
}
|
|
EditorGUI.indentLevel -= 1;
|
|
|
|
EditorGUIUtility.fieldWidth = defaultEditorFieldWidth;
|
|
EditorGUILayout.LabelField("<color=" + txtColourName + ">Ship Docking Points: " + numDockingPoints.ToString("00") + "</color>", labelFieldRichText);
|
|
|
|
if (numDockingPoints > 0)
|
|
{
|
|
if (GUILayout.Button(gizmoToggleBtnContent, GUILayout.MaxWidth(22f)))
|
|
{
|
|
serializedObject.ApplyModifiedProperties();
|
|
ToggleGizmos(shipDockingStation.shipDockingPointList);
|
|
isSceneModified = true;
|
|
serializedObject.Update();
|
|
}
|
|
}
|
|
|
|
if (GUILayout.Button("+", GUILayout.MaxWidth(30f)))
|
|
{
|
|
// Apply property changes
|
|
serializedObject.ApplyModifiedProperties();
|
|
Undo.RecordObject(shipDockingStation, "Add Docking Point");
|
|
shipDockingStation.shipDockingPointList.Add(new ShipDockingPoint());
|
|
DeselectAllComponents();
|
|
ExpandList(shipDockingStation.shipDockingPointList, false);
|
|
isSceneModified = true;
|
|
// Read in the properties
|
|
serializedObject.Update();
|
|
|
|
numDockingPoints = dkgPtListProp.arraySize;
|
|
if (numDockingPoints > 0)
|
|
{
|
|
// Force new docking point to be serialized in scene
|
|
dkgPtProp = dkgPtListProp.GetArrayElementAtIndex(numDockingPoints - 1);
|
|
dkgPtShowInEditorProp = dkgPtProp.FindPropertyRelative("showInEditor");
|
|
dkgPtShowInEditorProp.boolValue = !dkgPtShowInEditorProp.boolValue;
|
|
// Show the new docking point and select it in the scnee
|
|
dkgPtShowInEditorProp.boolValue = true;
|
|
dkgPtProp.FindPropertyRelative("showGizmosInSceneView").boolValue = true;
|
|
dkgPtProp.FindPropertyRelative("selectedInSceneView").boolValue = true;
|
|
Tools.hidden = true;
|
|
}
|
|
}
|
|
if (GUILayout.Button("-", GUILayout.MaxWidth(30f)))
|
|
{
|
|
if (numDockingPoints > 0) { dockingPointDeletePos = dkgPtListProp.arraySize - 1; }
|
|
}
|
|
|
|
GUILayout.EndHorizontal();
|
|
|
|
#endregion
|
|
|
|
#region Ship Docking Point List
|
|
|
|
numDockingPoints = dkgPtListProp.arraySize;
|
|
|
|
for (int dpIdx = 0; dpIdx < numDockingPoints; dpIdx++)
|
|
{
|
|
GUILayout.BeginVertical(EditorStyles.helpBox);
|
|
dkgPtProp = dkgPtListProp.GetArrayElementAtIndex(dpIdx);
|
|
|
|
dkgPtShowInEditorProp = dkgPtProp.FindPropertyRelative("showInEditor");
|
|
dkgPtShowGizmosInSceneViewProp = dkgPtProp.FindPropertyRelative("showGizmosInSceneView");
|
|
dkgPtSelectedInSceneViewProp = dkgPtProp.FindPropertyRelative("selectedInSceneView");
|
|
dkgPtEntryPathGUIDHashProp = dkgPtProp.FindPropertyRelative("guidHashEntryPath");
|
|
dkgPtExitPathGUIDHashProp = dkgPtProp.FindPropertyRelative("guidHashExitPath");
|
|
dkgPtDockedShipProp = dkgPtProp.FindPropertyRelative("dockedShip");
|
|
|
|
#region Docking Point Find/Move/Insert/Delete buttons
|
|
GUILayout.BeginHorizontal();
|
|
EditorGUI.indentLevel += 1;
|
|
dkgPtShowInEditorProp.boolValue = EditorGUILayout.Foldout(dkgPtShowInEditorProp.boolValue, "Ship Docking Point " + (dpIdx + 1).ToString("00"));
|
|
EditorGUI.indentLevel -= 1;
|
|
|
|
// Find (select) in the scene
|
|
SelectItemInSceneViewButton(shipDockingStation.shipDockingPointList, dkgPtShowInEditorProp, dkgPtSelectedInSceneViewProp, dkgPtShowGizmosInSceneViewProp);
|
|
|
|
// Show Gizmos button
|
|
if (dkgPtShowGizmosInSceneViewProp.boolValue)
|
|
{
|
|
// Turn gizmos off
|
|
if (GUILayout.Button(gizmoBtnContent, toggleCompactButtonStyleToggled, GUILayout.MaxWidth(22f)))
|
|
{
|
|
dkgPtShowGizmosInSceneViewProp.boolValue = false;
|
|
// If it was selected, unselect it when turning gizmos off in the scene
|
|
if (dkgPtSelectedInSceneViewProp.boolValue)
|
|
{
|
|
dkgPtSelectedInSceneViewProp.boolValue = false;
|
|
Tools.hidden = false;
|
|
}
|
|
}
|
|
}
|
|
else { if (GUILayout.Button(gizmoBtnContent, toggleCompactButtonStyleNormal, GUILayout.MaxWidth(22f))) { dkgPtShowGizmosInSceneViewProp.boolValue = true; } }
|
|
|
|
// Move down button
|
|
if (GUILayout.Button("V", buttonCompact, GUILayout.MaxWidth(20f)) && numDockingPoints > 1) { dockingPointMoveDownPos = dpIdx; }
|
|
// Create duplicate button
|
|
if (GUILayout.Button("I", buttonCompact, GUILayout.MaxWidth(20f))) { dockingPointInsertPos = dpIdx; }
|
|
// Delete button
|
|
if (GUILayout.Button("X", buttonCompact, GUILayout.MaxWidth(20f))) { dockingPointDeletePos = dpIdx; }
|
|
GUILayout.EndHorizontal();
|
|
#endregion
|
|
|
|
if (dkgPtShowInEditorProp.boolValue)
|
|
{
|
|
EditorGUILayout.PropertyField(dkgPtProp.FindPropertyRelative("relativePosition"), dkgPtRelativePositionContent);
|
|
|
|
GUILayout.BeginHorizontal();
|
|
#if UNITY_2019_3_OR_NEWER
|
|
EditorGUILayout.LabelField(dkgPtRelativeRotationContent, GUILayout.Width(defaultEditorLabelWidth - 25f));
|
|
#else
|
|
EditorGUILayout.LabelField(dkgPtRelativeRotationContent, GUILayout.Width(defaultEditorLabelWidth - 29f));
|
|
#endif
|
|
if (GUILayout.Button(resetBtnContent, buttonCompact, GUILayout.MaxWidth(20f)))
|
|
{
|
|
dkgPtProp.FindPropertyRelative("relativeRotation").vector3Value = Vector3.zero;
|
|
}
|
|
EditorGUILayout.PropertyField(dkgPtProp.FindPropertyRelative("relativeRotation"), GUIContent.none);
|
|
GUILayout.EndHorizontal();
|
|
|
|
DrawPathInspector(dkgPtEntryPathGUIDHashProp, dpIdx, true, dkgPtEntryPathContent);
|
|
DrawPathInspector(dkgPtExitPathGUIDHashProp, dpIdx, false, dkgPtExitPathContent);
|
|
|
|
EditorGUILayout.PropertyField(dkgPtProp.FindPropertyRelative("hoverHeight"), dkgPtHoverHeightContent);
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
EditorGUILayout.PropertyField(dkgPtDockedShipProp, dkgPtDockedShipContent);
|
|
if (EditorGUI.EndChangeCheck() && dkgPtDockedShipProp.objectReferenceValue != null && EditorUtility.IsPersistent(dkgPtDockedShipProp.objectReferenceValue))
|
|
{
|
|
Debug.LogWarning("ShipDockingStation - only ships in the scene can be assigned a docking point");
|
|
dkgPtDockedShipProp.objectReferenceValue = null;
|
|
}
|
|
}
|
|
|
|
// There is a bug here in 2019.4 and 2020.1 where the DisplayDialog causes formatting issues.
|
|
// EndLayoutGroup: BeginLayoutGroup must be called first.
|
|
// No 2019_4 define but earlier versions still seem to work.
|
|
//#if UNITY_2019_3_OR_NEWER
|
|
//if (dockingPointDeletePos >=0 ) { GUILayout.BeginVertical(); }
|
|
//#endif
|
|
|
|
GUILayout.EndVertical();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Move/Insert/Delete Docking Points
|
|
|
|
if (dockingPointDeletePos >= 0 || dockingPointInsertPos >= 0 || dockingPointMoveDownPos >= 0)
|
|
{
|
|
GUI.FocusControl(null);
|
|
// Don't permit multiple operations in the same pass
|
|
if (dockingPointMoveDownPos >= 0)
|
|
{
|
|
// Move down one position, or wrap round to start of list
|
|
if (dockingPointMoveDownPos < dkgPtListProp.arraySize - 1)
|
|
{
|
|
dkgPtListProp.MoveArrayElement(dockingPointMoveDownPos, dockingPointMoveDownPos + 1);
|
|
}
|
|
else { dkgPtListProp.MoveArrayElement(dockingPointMoveDownPos, 0); }
|
|
|
|
dockingPointMoveDownPos = -1;
|
|
}
|
|
else if (dockingPointInsertPos >= 0)
|
|
{
|
|
// NOTE: Undo doesn't work with Insert.
|
|
|
|
// Apply property changes before potential list changes
|
|
serializedObject.ApplyModifiedProperties();
|
|
|
|
shipDockingStation.shipDockingPointList.Insert(dockingPointInsertPos, new ShipDockingPoint(shipDockingStation.shipDockingPointList[dockingPointInsertPos]));
|
|
|
|
// Read all properties from the ShipDockingStation
|
|
serializedObject.Update();
|
|
|
|
// Hide original dockingPoint
|
|
dkgPtListProp.GetArrayElementAtIndex(dockingPointInsertPos + 1).FindPropertyRelative("showInEditor").boolValue = false;
|
|
dkgPtShowInEditorProp = dkgPtListProp.GetArrayElementAtIndex(dockingPointInsertPos).FindPropertyRelative("showInEditor");
|
|
|
|
// Force new dockingPoint to be serialized in scene
|
|
dkgPtShowInEditorProp.boolValue = !dkgPtShowInEditorProp.boolValue;
|
|
|
|
// Show inserted duplicate dockingPoint
|
|
dkgPtShowInEditorProp.boolValue = true;
|
|
|
|
// Ensure we can see the inserted docking point in the scene view
|
|
dkgPtListProp.GetArrayElementAtIndex(dockingPointInsertPos).FindPropertyRelative("showGizmosInSceneView").boolValue = true;
|
|
|
|
// Select the inserted docking point
|
|
serializedObject.ApplyModifiedProperties();
|
|
DeselectAllComponents();
|
|
shipDockingStation.shipDockingPointList[dockingPointInsertPos].selectedInSceneView = true;
|
|
Tools.hidden = true;
|
|
serializedObject.Update();
|
|
|
|
dockingPointInsertPos = -1;
|
|
|
|
isSceneModified = true;
|
|
}
|
|
else if (dockingPointDeletePos >= 0)
|
|
{
|
|
// In U2019.4+ DisplayDialog seems to trigger another OnInspectorGUI() and DeletePos is reset to -1.
|
|
int _deleteIndex = dockingPointDeletePos;
|
|
|
|
if (EditorUtility.DisplayDialog("Delete Ship Docking Point " + (dockingPointDeletePos + 1) + "?", "Docking Point " + (dockingPointDeletePos + 1).ToString("00") + " will be deleted\n\nThis action will remove the docking point from the list and cannot be undone.", "Delete Now", "Cancel"))
|
|
{
|
|
// If this docking point was selected, turn tools back on
|
|
if (dkgPtListProp.GetArrayElementAtIndex(_deleteIndex).FindPropertyRelative("selectedInSceneView").boolValue)
|
|
{
|
|
Tools.hidden = false;
|
|
}
|
|
dkgPtListProp.DeleteArrayElementAtIndex(_deleteIndex);
|
|
dockingPointDeletePos = -1;
|
|
}
|
|
}
|
|
|
|
#if UNITY_2019_3_OR_NEWER
|
|
serializedObject.ApplyModifiedProperties();
|
|
// In U2019.4+ avoid: EndLayoutGroup: BeginLayoutGroup must be called first.
|
|
if (!Application.isPlaying)
|
|
{
|
|
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
|
|
}
|
|
GUIUtility.ExitGUI();
|
|
#endif
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
}
|
|
#endregion
|
|
|
|
#region Events
|
|
else
|
|
{
|
|
// Label workaroud for content or tooltip not working with UnityEvents property drawer
|
|
EditorGUILayout.LabelField(onPreUndockContent, GUILayout.MaxHeight(5f));
|
|
EditorGUILayout.PropertyField(onPreUndockProp);
|
|
|
|
EditorGUILayout.LabelField(onPostDockedContent, GUILayout.MaxHeight(5f));
|
|
EditorGUILayout.PropertyField(onPostDockedProp);
|
|
}
|
|
#endregion
|
|
|
|
// Apply property changes
|
|
serializedObject.ApplyModifiedProperties();
|
|
|
|
#region Mark Scene Dirty if required
|
|
|
|
if (isSceneModified && !Application.isPlaying)
|
|
{
|
|
isSceneModified = false;
|
|
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
|
|
}
|
|
|
|
#endregion
|
|
|
|
shipDockingStation.allowRepaint = true;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |