// Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using UnityEngine; using System.Collections.Generic; namespace UnityEditor { class TriggerModuleUI : ModuleUI { // Keep in sync with TriggerModule.h const int k_MaxNumCollisionShapes = 6; enum OverlapOptions { Ignore = 0, Kill = 1, Callback = 2 }; SerializedProperty[] m_CollisionShapes = new SerializedProperty[k_MaxNumCollisionShapes]; SerializedProperty m_Inside; SerializedProperty m_Outside; SerializedProperty m_Enter; SerializedProperty m_Exit; SerializedProperty m_RadiusScale; SerializedProperty[] m_ShownCollisionShapes; static bool s_VisualizeBounds = false; class Texts { public GUIContent collisionShapes = EditorGUIUtility.TrTextContent("Colliders", "The list of collision shapes to use for the trigger."); public GUIContent createCollisionShape = EditorGUIUtility.TrTextContent("", "Create a GameObject containing a sphere collider and assigns it to the list."); public GUIContent inside = EditorGUIUtility.TrTextContent("Inside", "What to do for particles that are inside the collision volume."); public GUIContent outside = EditorGUIUtility.TrTextContent("Outside", "What to do for particles that are outside the collision volume."); public GUIContent enter = EditorGUIUtility.TrTextContent("Enter", "Triggered once when particles enter the collison volume."); public GUIContent exit = EditorGUIUtility.TrTextContent("Exit", "Triggered once when particles leave the collison volume."); public GUIContent radiusScale = EditorGUIUtility.TrTextContent("Radius Scale", "Scale particle bounds by this amount to get more precise collisions."); public GUIContent visualizeBounds = EditorGUIUtility.TrTextContent("Visualize Bounds", "Render the collision bounds of the particles."); public GUIContent[] overlapOptions = new GUIContent[] { EditorGUIUtility.TrTextContent("Ignore"), EditorGUIUtility.TrTextContent("Kill"), EditorGUIUtility.TrTextContent("Callback") }; } private static Texts s_Texts; public TriggerModuleUI(ParticleSystemUI owner, SerializedObject o, string displayName) : base(owner, o, "TriggerModule", displayName) { m_ToolTip = "Allows you to execute script code based on whether particles are inside or outside the collision shapes."; } protected override void Init() { // Already initialized? if (m_Inside != null) return; if (s_Texts == null) s_Texts = new Texts(); List shownCollisionShapes = new List(); for (int i = 0; i < m_CollisionShapes.Length; ++i) { m_CollisionShapes[i] = GetProperty("collisionShape" + i); // Keep name in sync with transfer func in TriggerModule.h System.Diagnostics.Debug.Assert(m_CollisionShapes[i] != null); // Always show the first collision shape if (i == 0 || m_CollisionShapes[i].objectReferenceValue != null) shownCollisionShapes.Add(m_CollisionShapes[i]); } m_ShownCollisionShapes = shownCollisionShapes.ToArray(); m_Inside = GetProperty("inside"); m_Outside = GetProperty("outside"); m_Enter = GetProperty("enter"); m_Exit = GetProperty("exit"); m_RadiusScale = GetProperty("radiusScale"); s_VisualizeBounds = EditorPrefs.GetBool("VisualizeTriggerBounds", false); } override public void OnInspectorGUI(InitialModuleUI initial) { DoListOfCollisionShapesGUI(); GUIPopup(s_Texts.inside, m_Inside, s_Texts.overlapOptions); GUIPopup(s_Texts.outside, m_Outside, s_Texts.overlapOptions); GUIPopup(s_Texts.enter, m_Enter, s_Texts.overlapOptions); GUIPopup(s_Texts.exit, m_Exit, s_Texts.overlapOptions); GUIFloat(s_Texts.radiusScale, m_RadiusScale); EditorGUI.BeginChangeCheck(); s_VisualizeBounds = GUIToggle(s_Texts.visualizeBounds, s_VisualizeBounds); if (EditorGUI.EndChangeCheck()) { EditorPrefs.SetBool("VisualizeTriggerBounds", s_VisualizeBounds); } } private static GameObject CreateDefaultCollider(string name, ParticleSystem parentOfGameObject) { GameObject go = new GameObject(name); if (go) { if (parentOfGameObject) go.transform.parent = parentOfGameObject.transform; go.AddComponent(); return go; } return null; } private void DoListOfCollisionShapesGUI() { // only show the list of colliders when we can edit them safely if (m_ParticleSystemUI.multiEdit) { for (int shapeIndex = 0; shapeIndex < k_MaxNumCollisionShapes; shapeIndex++) { int hasShape = -1; foreach (ParticleSystem ps in m_ParticleSystemUI.m_ParticleSystems) { int shapeValue = (ps.trigger.GetCollider(shapeIndex) != null) ? 1 : 0; if (hasShape == -1) { hasShape = shapeValue; } else if (shapeValue != hasShape) { EditorGUILayout.HelpBox("Collider list editing is only available when all selected systems contain the same number of colliders", MessageType.Info, true); return; } } } } int buttonPressedIndex = GUIListOfFloatObjectToggleFields(s_Texts.collisionShapes, m_ShownCollisionShapes, null, s_Texts.createCollisionShape, !m_ParticleSystemUI.multiEdit); if (buttonPressedIndex >= 0 && !m_ParticleSystemUI.multiEdit) { GameObject go = CreateDefaultCollider("Collider " + (buttonPressedIndex + 1), m_ParticleSystemUI.m_ParticleSystems[0]); go.transform.localPosition = new Vector3(0, 0, 10 + buttonPressedIndex); // ensure each collider is not at same pos m_ShownCollisionShapes[buttonPressedIndex].objectReferenceValue = go; } // Minus button Rect rect = GUILayoutUtility.GetRect(0, EditorGUI.kSingleLineHeight); //GUILayoutUtility.GetLastRect(); rect.x = rect.xMax - kPlusAddRemoveButtonWidth * 2 - kPlusAddRemoveButtonSpacing; rect.width = kPlusAddRemoveButtonWidth; if (m_ShownCollisionShapes.Length > 1) { if (MinusButton(rect)) { m_ShownCollisionShapes[m_ShownCollisionShapes.Length - 1].objectReferenceValue = null; List shownCollisionShapes = new List(m_ShownCollisionShapes); shownCollisionShapes.RemoveAt(shownCollisionShapes.Count - 1); m_ShownCollisionShapes = shownCollisionShapes.ToArray(); } } // Plus button if (m_ShownCollisionShapes.Length < k_MaxNumCollisionShapes && !m_ParticleSystemUI.multiEdit) { rect.x += kPlusAddRemoveButtonWidth + kPlusAddRemoveButtonSpacing; if (PlusButton(rect)) { List shownCollisionShapes = new List(m_ShownCollisionShapes); shownCollisionShapes.Add(m_CollisionShapes[shownCollisionShapes.Count]); m_ShownCollisionShapes = shownCollisionShapes.ToArray(); } } } override public void OnSceneViewGUI() { if (s_VisualizeBounds == false) return; Color oldColor = Handles.color; Handles.color = Color.green; Matrix4x4 oldMatrix = Handles.matrix; Vector3[] points0 = new Vector3[20]; Vector3[] points1 = new Vector3[20]; Vector3[] points2 = new Vector3[20]; Handles.SetDiscSectionPoints(points0, Vector3.zero, Vector3.forward, Vector3.right, 360, 1.0f); Handles.SetDiscSectionPoints(points1, Vector3.zero, Vector3.up, -Vector3.right, 360, 1.0f); Handles.SetDiscSectionPoints(points2, Vector3.zero, Vector3.right, Vector3.up, 360, 1.0f); Vector3[] points = new Vector3[points0.Length + points1.Length + points2.Length]; points0.CopyTo(points, 0); points1.CopyTo(points, 20); points2.CopyTo(points, 40); foreach (ParticleSystem ps in m_ParticleSystemUI.m_ParticleSystems) { if (!ps.trigger.enabled) continue; ParticleSystem.Particle[] particles = new ParticleSystem.Particle[ps.particleCount]; int count = ps.GetParticles(particles); Matrix4x4 transform = Matrix4x4.identity; if (ps.main.simulationSpace == ParticleSystemSimulationSpace.Local) { transform = ps.GetLocalToWorldMatrix(); } for (int i = 0; i < count; i++) { ParticleSystem.Particle particle = particles[i]; Vector3 size = particle.GetCurrentSize3D(ps); float radius = System.Math.Max(size.x, System.Math.Max(size.y, size.z)) * 0.5f * ps.trigger.radiusScale; Handles.matrix = transform * Matrix4x4.TRS(particle.position, Quaternion.identity, new Vector3(radius, radius, radius)); Handles.DrawPolyLine(points); } } Handles.color = oldColor; Handles.matrix = oldMatrix; } public override void UpdateCullingSupportedString(ref string text) { text += "\nTriggers module is enabled."; } } } // namespace UnityEditor