185 lines
8.6 KiB
C#
185 lines
8.6 KiB
C#
using UnityEngine;
|
|
using UnityEditor;
|
|
using UnityEditor.Playables;
|
|
using UnityEngine.Playables;
|
|
|
|
namespace Interactions
|
|
{
|
|
[CustomEditor(typeof(InteractionTimelineAction))]
|
|
public class InteractionTimelineActionEditor : UnityEditor.Editor
|
|
{
|
|
private SerializedProperty respondToEventsProp;
|
|
private SerializedProperty pauseInteractionFlowProp;
|
|
private SerializedProperty playableDirectorProp;
|
|
private SerializedProperty timelineMappingsProp;
|
|
|
|
private void OnEnable()
|
|
{
|
|
respondToEventsProp = serializedObject.FindProperty("respondToEvents");
|
|
pauseInteractionFlowProp = serializedObject.FindProperty("pauseInteractionFlow");
|
|
playableDirectorProp = serializedObject.FindProperty("playableDirector");
|
|
timelineMappingsProp = serializedObject.FindProperty("timelineMappings");
|
|
}
|
|
|
|
public override void OnInspectorGUI()
|
|
{
|
|
serializedObject.Update();
|
|
|
|
// Basic properties from the base class
|
|
EditorGUILayout.LabelField("Basic Settings", EditorStyles.boldLabel);
|
|
|
|
// Show the pause interaction flow property
|
|
EditorGUILayout.PropertyField(pauseInteractionFlowProp, new GUIContent("Pause Interaction Flow",
|
|
"If true, the interaction will wait for the timeline to complete before proceeding"));
|
|
|
|
// Show the respondToEvents list
|
|
EditorGUILayout.PropertyField(respondToEventsProp, new GUIContent("Respond To Events",
|
|
"Select which interaction events this timeline action should respond to"), true);
|
|
|
|
// Show the playable director reference
|
|
EditorGUILayout.PropertyField(playableDirectorProp, new GUIContent("Playable Director",
|
|
"The director component that will play the timeline. Auto-assigned if not specified."));
|
|
|
|
// Show the timeline mappings
|
|
EditorGUILayout.Space(10);
|
|
EditorGUILayout.LabelField("Timeline Mappings", EditorStyles.boldLabel);
|
|
|
|
if (timelineMappingsProp.arraySize == 0)
|
|
{
|
|
EditorGUILayout.HelpBox("No timeline mappings added yet. Add one to assign timeline assets to specific interaction events.", MessageType.Info);
|
|
}
|
|
|
|
EditorGUILayout.PropertyField(timelineMappingsProp, new GUIContent("Timeline Mappings"), true);
|
|
|
|
// Add buttons for quickly adding timeline mappings for common events
|
|
EditorGUILayout.Space(10);
|
|
EditorGUILayout.LabelField("Quick Add Mappings", EditorStyles.boldLabel);
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
if (GUILayout.Button("Add Player Arrival"))
|
|
{
|
|
AddTimelineMapping(InteractionEventType.PlayerArrived);
|
|
}
|
|
if (GUILayout.Button("Add Interacting Character"))
|
|
{
|
|
AddTimelineMapping(InteractionEventType.InteractingCharacterArrived);
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
if (GUILayout.Button("Add Interaction Started"))
|
|
{
|
|
AddTimelineMapping(InteractionEventType.InteractionStarted);
|
|
}
|
|
if (GUILayout.Button("Add Interaction Complete"))
|
|
{
|
|
AddTimelineMapping(InteractionEventType.InteractionComplete);
|
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
// Check for configuration issues
|
|
InteractionTimelineAction timelineAction = (InteractionTimelineAction)target;
|
|
ValidateConfiguration(timelineAction);
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
}
|
|
|
|
private void AddTimelineMapping(InteractionEventType eventType)
|
|
{
|
|
int index = timelineMappingsProp.arraySize;
|
|
timelineMappingsProp.InsertArrayElementAtIndex(index);
|
|
SerializedProperty newElement = timelineMappingsProp.GetArrayElementAtIndex(index);
|
|
|
|
// Set default values
|
|
newElement.FindPropertyRelative("eventType").enumValueIndex = (int)eventType;
|
|
|
|
// Create a default array with one empty slot for the timeline
|
|
SerializedProperty timelinesArray = newElement.FindPropertyRelative("timelines");
|
|
timelinesArray.ClearArray();
|
|
timelinesArray.InsertArrayElementAtIndex(0);
|
|
timelinesArray.GetArrayElementAtIndex(0).objectReferenceValue = null;
|
|
|
|
// Set default binding values
|
|
newElement.FindPropertyRelative("bindPlayerCharacter").boolValue = false;
|
|
newElement.FindPropertyRelative("bindPulverCharacter").boolValue = false;
|
|
newElement.FindPropertyRelative("playerTrackName").stringValue = "Player";
|
|
newElement.FindPropertyRelative("pulverTrackName").stringValue = "Pulver";
|
|
newElement.FindPropertyRelative("timeoutSeconds").floatValue = 30f;
|
|
newElement.FindPropertyRelative("loopLast").boolValue = false;
|
|
newElement.FindPropertyRelative("loopAll").boolValue = false;
|
|
|
|
// Also add the event to the respondToEvents list if it's not already there
|
|
bool found = false;
|
|
for (int i = 0; i < respondToEventsProp.arraySize; i++)
|
|
{
|
|
if (respondToEventsProp.GetArrayElementAtIndex(i).enumValueIndex == (int)eventType)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
int responseIndex = respondToEventsProp.arraySize;
|
|
respondToEventsProp.InsertArrayElementAtIndex(responseIndex);
|
|
respondToEventsProp.GetArrayElementAtIndex(responseIndex).enumValueIndex = (int)eventType;
|
|
}
|
|
}
|
|
|
|
private void ValidateConfiguration(InteractionTimelineAction timelineAction)
|
|
{
|
|
// Check if we have a PlayableDirector component
|
|
PlayableDirector director = timelineAction.GetComponent<PlayableDirector>();
|
|
if (director == null)
|
|
{
|
|
EditorGUILayout.HelpBox("This GameObject is missing a PlayableDirector component, which is required for timeline playback.", MessageType.Error);
|
|
}
|
|
|
|
// Check if we have mappings but no events to respond to
|
|
if (timelineMappingsProp.arraySize > 0 && respondToEventsProp.arraySize == 0)
|
|
{
|
|
EditorGUILayout.HelpBox("You have timeline mappings but no events to respond to. Add events to the 'Respond To Events' list.", MessageType.Warning);
|
|
}
|
|
|
|
// Check if we have events to respond to but no mappings for them
|
|
bool hasUnmappedEvents = false;
|
|
for (int i = 0; i < respondToEventsProp.arraySize; i++)
|
|
{
|
|
InteractionEventType eventType = (InteractionEventType)respondToEventsProp.GetArrayElementAtIndex(i).enumValueIndex;
|
|
bool found = false;
|
|
|
|
for (int j = 0; j < timelineMappingsProp.arraySize; j++)
|
|
{
|
|
SerializedProperty mappingProp = timelineMappingsProp.GetArrayElementAtIndex(j);
|
|
InteractionEventType mappingEventType = (InteractionEventType)mappingProp.FindPropertyRelative("eventType").enumValueIndex;
|
|
|
|
if (mappingEventType == eventType)
|
|
{
|
|
found = true;
|
|
|
|
// Check if the mapping has timelines assigned
|
|
SerializedProperty timelinesArray = mappingProp.FindPropertyRelative("timelines");
|
|
if (timelinesArray.arraySize == 0 || timelinesArray.GetArrayElementAtIndex(0).objectReferenceValue == null)
|
|
{
|
|
EditorGUILayout.HelpBox($"The mapping for {eventType} has no timeline assets assigned.", MessageType.Warning);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
hasUnmappedEvents = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (hasUnmappedEvents)
|
|
{
|
|
EditorGUILayout.HelpBox("Some events in 'Respond To Events' have no timeline mapping.", MessageType.Warning);
|
|
}
|
|
}
|
|
}
|
|
}
|