#if GRAPH_DESIGNER /// --------------------------------------------- /// Behavior Designer /// Copyright (c) Opsive. All Rights Reserved. /// https://www.opsive.com /// --------------------------------------------- namespace Opsive.BehaviorDesigner.Editor.Controls.NodeViews { using Opsive.GraphDesigner.Editor; using Opsive.BehaviorDesigner.Runtime.Tasks; using Opsive.Shared.Editor.UIElements.Controls; using UnityEngine.UIElements; using UnityEditor; using UnityEngine; /// /// Implements TypeControlBase for the StackedTask type. /// [ControlType(typeof(StackedTask))] public class StackedTaskNodeViewControl : TaskNodeViewControl { private const float c_ActiveIconRotationSpeed = 30; /// /// Displays information about the nested task within the Stacked Task. /// private class TaskView : VisualElement { private const string c_DarkActiveIconGUID = "1230b934cbd748345b13125468a34720"; private const string c_LightActiveIconGUID = "e57f179ee476f274dbe537179e67bf04"; private int m_Index; private Image m_ActiveImage; /// /// TaskView constructor. /// /// The index of the task. /// A reference to the task. public TaskView(int index, Task task) { m_Index = index; var horizontalLayout = new VisualElement(); horizontalLayout.AddToClassList("horizontal-layout"); horizontalLayout.style.height = 18; var label = new Label(task.ToString()); label.style.flexGrow = 1; horizontalLayout.Add(label); m_ActiveImage = new Image(); m_ActiveImage.image = Shared.Editor.Utility.EditorUtility.LoadAsset(EditorGUIUtility.isProSkin ? c_DarkActiveIconGUID : c_LightActiveIconGUID); m_ActiveImage.style.width = 16; m_ActiveImage.style.height = 16; m_ActiveImage.style.display = DisplayStyle.None; horizontalLayout.Add(m_ActiveImage); Add(horizontalLayout); } /// /// Updates the status of the task. /// /// The index that is active. public void UpdateStatus(int activeIndex) { m_ActiveImage.style.display = (m_Index == activeIndex ? DisplayStyle.Flex : DisplayStyle.None); if (m_Index == activeIndex) { if (Application.isPlaying) { var eulerAngles = m_ActiveImage.transform.rotation.eulerAngles; m_ActiveImage.transform.rotation = Quaternion.Euler(eulerAngles.x, eulerAngles.y, eulerAngles.z + c_ActiveIconRotationSpeed); } } else { m_ActiveImage.transform.rotation = Quaternion.identity; } } } private StackedTask m_StackedTask; private TaskView[] m_TaskViews; /// /// Addes the UIElements for the specified runtime node to the editor Node within the graph. /// /// A reference to the GraphWindow. /// The parent UIElement that should contain the node UIElements. /// The node that the control represents. public override void AddNodeView(GraphWindow graphWindow, VisualElement parent, object node) { base.AddNodeView(graphWindow, parent, node); m_StackedTask = node as StackedTask; if (m_StackedTask.Tasks == null) { return; } var tasks = m_StackedTask.Tasks; m_TaskViews = new TaskView[tasks.Length]; for (int i = 0; i < tasks.Length; ++i) { var task = m_StackedTask.Tasks[i]; // The task no longer exists. Replace it. if (task == null) { tasks[i] = new UnknownTask(); m_StackedTask.Tasks = tasks; } m_TaskViews[i] = new TaskView(i, m_StackedTask.Tasks[i]); parent.Add(m_TaskViews[i]); } } /// /// Internal method which updates the node with the current execution status. /// /// The status of the task. protected override TaskStatus UpdateNodeInternal() { var activeIndex = -1; TaskStatus status; if ((status = base.UpdateNodeInternal()) == TaskStatus.Running && m_StackedTask.Tasks.Length > 1) { activeIndex = m_StackedTask.ActiveIndex; for (int i = 0; i < m_TaskViews.Length; ++i) { m_TaskViews[i].UpdateStatus(activeIndex); } } else { for (int i = 0; i < m_TaskViews.Length; ++i) { m_TaskViews[i].UpdateStatus(activeIndex); } } return status; } } } #endif