/// <summary> /// Sets up a new scene for pattern creation. This method will set up a capsule with a body coordinate system, a line /// renderer for the haptic points, and the GUI and listeners needed to manage the new pattern. /// </summary> /// <param name="previousSceneName">The string of the scene that the user needs to return to when the leave this Scene UI</param> public void SetupCapsuleScene(string previousSceneName) { previousScene = previousSceneName; // Don't allow playmode to be entered while in this scene EditorApplication.playModeStateChanged += EditorApplication_playModeStateChanged; // Hook into mouse events from the scene hitListener = new BodyHitListener(); hitListener.BodyPartHit += HitListener_BodyPartHit; // Set up the In-Scene GUI hapticSceneView = new HapticSceneViewGUI { patternWindow = this }; // Connect to the OnGUI event cycle SceneView.onSceneGUIDelegate += OnGUIDelegate; // Set up the capsule capsuleGO = GameObject.CreatePrimitive(PrimitiveType.Capsule); capsuleGO.GetComponent <Renderer>().material = capsuleMaterial; capsuleGO.hideFlags = HideFlags.HideInInspector; capsuleBodyCoordinate = capsuleGO.AddComponent <BodyCoordinate>(); capsuleBodyCoordinate.drawGizmoUnselected = true; // Set up the line render for the active curve line GameObject lineHolder = new GameObject("Line Holder", typeof(LineRenderer)); lineHolder.transform.parent = capsuleGO.transform; lineHolder.hideFlags = HideFlags.HideInHierarchy; currentCurveLineRenderer = lineHolder.GetComponent <LineRenderer>(); currentCurveLineRenderer.widthMultiplier = lineRendererData.widthMultiplier; currentCurveLineRenderer.numCornerVertices = lineRendererData.numCornerVertices; currentCurveLineRenderer.numCapVertices = lineRendererData.numCapVertices; currentCurveLineRenderer.material = lineRendererData.lineMaterial; currentCurveLineRenderer.receiveShadows = lineRendererData.receiveShadows; currentCurveLineRenderer.allowOcclusionWhenDynamic = lineRendererData.allowOcclusionWhenDynamic; currentCurveLineRenderer.startColor = lineRendererData.lineStartColor; currentCurveLineRenderer.endColor = lineRendererData.lineEndColor; // Start with 0 points in the line currentCurveLineRenderer.positionCount = 0; // Focus the camera on the capsule Selection.activeGameObject = capsuleGO; SceneView.lastActiveSceneView.FrameSelected(); // Start work on the haptic pattern currentPattern = ScriptableObject.CreateInstance <ScriptableHapticPattern>(); AddNewCurve("Default"); // Don't need the event listener for a new scene anymore, wait for when this scene closes setupComplete = true; }
protected void ResetHapticPattern() { if (patternPlaying != null) { patternPlaying.hitOffset = new BodyCoordinateHit(); } patternTime = 0; patternPlaying = null; }
/// <summary> /// Starts playing a pattern on a given bodypart /// </summary> /// <param name="bodyLocation">The body part to play the pattern on</param> /// <param name="pattern">The haptic pattern to play</param> public void PlayPattern(HumanBodyBones bodyLocation, ScriptableHapticPattern pattern) { patternPlaying = pattern; bodyPartHit = bodyLocation; patternTime = 0.0f; if (patternPlaying.playbackTiming == PlaybackTiming.Custom) { StartCoroutine(Play()); } }
private void ResetPattern() { // Go through each GameObject and destroy it foreach (var bodyHitList in bodyHitsPerCurve) { for (int n = 0; n < bodyHitList.Value.Count; n++) { DestroyImmediate(bodyHitList.Value[n].gameObject); } } // Empty the dictionary of all the values bodyHitsPerCurve.Clear(); // Reset the index to 0 CurveIndex = 0; currentPattern = null; }
/// <summary> /// Load a saved haptic pattern and set it to be the active pattern to work on /// </summary> /// <param name="loadedPattern">The pattern to load</param> public void LoadPattern(ScriptableHapticPattern loadedPattern) { ResetPattern(); currentPattern = loadedPattern; // Load each curve information from the pattern for (int n = 0; n < loadedPattern.curveList.Count; n++) { bodyHitsPerCurve.Add(n, new List <BodyHitUI>()); AddCurveToDictionary(n, loadedPattern.curveList[n]); UpdateCurveKeyFrameVisualization(n, false); } // Make the first curve active UpdateCurveKeyFrameVisualization(0, true); }
/// <summary> /// Plays a specified haptic pattern on a specific body part. Use this method if one /// simply needs to give a notification to a body part through scripting. /// </summary> /// <param name="bodyLocation">The body part to notify</param> /// <param name="pattern">The haptic pattern to play</param> public void PlayPattern(HumanBodyBones bodyLocation, ScriptableHapticPattern pattern) { // Check to make sure this body part isn't ignored if (ignoreBoneSet.Contains(bodyLocation)) { return; } if (bodyAffectedByDevice.ContainsKey(bodyLocation)) { // Tell every device that has this body part to play the pattern for (int n = 0; n < bodyAffectedByDevice[bodyLocation].Count; n++) { // Check to make sure this haptic device isn't ignored if (ignoreHapticDevices.Contains(bodyAffectedByDevice[bodyLocation][n])) { return; } bodyAffectedByDevice[bodyLocation][n].PlayPattern(bodyLocation, pattern); } } }
/// <summary> /// Checks to see if the given body part that was hit has a device that should be triggered. If it does, /// it tells that device to play their haptic for that hit location. /// </summary> /// <param name="bodyLocation">The body part that was hit</param> /// <param name="hitLocation">The height/angle of the hit on the body part</param> public void BodyPartHit(HumanBodyBones bodyLocation, BodyCoordinateHit hitLocation, ScriptableHapticPattern hapticPattern = null) { // Check to make sure this body part isn't ignored if (ignoreBoneSet.Contains(bodyLocation)) { return; } if (bodyAffectedByDevice.ContainsKey(bodyLocation)) { // Tell every device that has this body part to trigger the haptics at the given location for (int n = 0; n < bodyAffectedByDevice[bodyLocation].Count; n++) { // Check to make sure this haptic device isn't ignored if (ignoreHapticDevices.Contains(bodyAffectedByDevice[bodyLocation][n])) { return; } if (hapticPattern == null) { bodyAffectedByDevice[bodyLocation][n].TriggerDevice(bodyLocation, hitLocation, 0.5f); } else { bodyAffectedByDevice[bodyLocation][n].TriggerDevice(bodyLocation, hitLocation, hapticPattern); } } } }
/// <summary> /// Updates the selected pattern from a dropdown menu /// </summary> /// <param name="target">The dropdown menu that has the list of available patterns</param> public void DropdownPatternSelection(Dropdown target) { selectedPattern = availablePatterns[target.value]; }
/// <summary> /// Plays a pattern that is triggered by an object hitting a body part that has been /// set up to recieve haptic events. /// </summary> /// <param name="bodyPart">The body part of the hit</param> /// <param name="hitLocation">The body coordinate system hit</param> /// <param name="hapticPattern">The haptic pattern</param> public void TriggerDevice(HumanBodyBones bodyPart, BodyCoordinateHit hitLocation, ScriptableHapticPattern hapticPattern) { if (bodyPart == affectedBodyPart.affectableBodyParts) { hapticPattern.hitOffset = hitLocation; PlayPattern(bodyPart, hapticPattern); } }
/// <summary> /// Handles how to render the actual elements of the UI /// </summary> public void RenderSceneGUI(SceneView sceneview) { Handles.BeginGUI(); // Make a canvas that spans the entire height of the scene and a portion of the width Rect canvasRect = new Rect(0, 0, Screen.width / UI_SCREEN_WIDTH, Screen.height); GUILayout.BeginArea(canvasRect); // Make the area behind the GUI a gray, slightly transparent canvas EditorGUI.DrawRect(canvasRect, new Color(0.5f, 0.5f, 0.5f, 0.5f)); Rect verticleLayoutBox = EditorGUILayout.BeginVertical(); GUI.Box(verticleLayoutBox, GUIContent.none); // Add header for loading/saving patterns EditorGUILayout.Space(); EditorGUILayout.HelpBox("Pattern Management", MessageType.None); // Load a saved pattern EditorGUILayout.LabelField("Load Saved Pattern:", textLabelStyle); patternToLoad = EditorGUILayout.ObjectField(patternToLoad, typeof(ScriptableHapticPattern), false) as ScriptableHapticPattern; if (patternToLoad != null) { if (GUILayout.Button("Load Pattern")) { LoadPattern(); patternToLoad = null; } } // Save changes and return to previous scene if (GUILayout.Button("Save and return")) { patternWindow.SavePattern(patternName, selectedCollisionResolution, collisionResolutionIndex, selectedPlaybackTiming, customPlaybackTiming, selectedOffsetUse, selectedHeightOvershoot, selectedAngleOvershoot); } // Save the background color to temporarily change the discard background to red to indicate the negative nature of the button Color oldColor = GUI.backgroundColor; GUI.backgroundColor = Color.red; // Discard all the changes and return to previous scene if (GUILayout.Button("Discard changes")) { bool discardResult = EditorUtility.DisplayDialog("Discard changes?", "Are you sure you want to discard all changes made to this pattern?", "Yes", "Cancel"); if (discardResult) { patternWindow.DiscardChanges(); } } GUI.backgroundColor = oldColor; // Add header for pattern settings EditorGUILayout.Space(); EditorGUILayout.HelpBox("Pattern Settings", MessageType.None); // Prompt for pattern name which will become the file name EditorGUILayout.LabelField("Pattern Name:", textLabelStyle); patternName = EditorGUILayout.TextField(patternName); // Prompt for how to handle intensity collisions EditorGUILayout.LabelField("Collision Resolution:", textLabelStyle); selectedCollisionResolution = (PatternCollisionResolution)EditorGUILayout.EnumPopup(selectedCollisionResolution); // Prompt for the index of the curve to use if the resolution is set to CurvePriority if (selectedCollisionResolution == PatternCollisionResolution.CurvePriority) { EditorGUILayout.LabelField("Curve Index:", textLabelStyle); collisionResolutionIndex = EditorGUILayout.IntField(collisionResolutionIndex); } // Prompt for the playback timing on the pattern EditorGUILayout.LabelField("Playback Timing:", textLabelStyle); selectedPlaybackTiming = (PlaybackTiming)EditorGUILayout.EnumPopup(selectedPlaybackTiming); // Prompt for custom playback timing if they have selected CustomPlayback as the option if (selectedPlaybackTiming == PlaybackTiming.Custom) { EditorGUILayout.LabelField("Custom Playback Time:", textLabelStyle); customPlaybackTiming = EditorGUILayout.FloatField(customPlaybackTiming); } // Prompt for offset use EditorGUILayout.LabelField("Offset Use:", textLabelStyle); selectedOffsetUse = (OffsetUse)EditorGUILayout.EnumPopup(selectedOffsetUse); // Prompt for height overshoot EditorGUILayout.LabelField("Height Overshoot Resolution:", textLabelStyle); selectedHeightOvershoot = (PatternOvershootResolution)EditorGUILayout.EnumPopup(selectedHeightOvershoot); // Prompt for angle overshoot EditorGUILayout.LabelField("Angle Overshoot Resolution:", textLabelStyle); selectedAngleOvershoot = (PatternOvershootResolution)EditorGUILayout.EnumPopup(selectedAngleOvershoot); // Header for management curves EditorGUILayout.Space(); EditorGUILayout.HelpBox("Curve Management", MessageType.None); // Build string list of attached curves string[] curveNames = new string[patternWindow.HapticPattern.curveList.Count]; for (int n = 0; n < patternWindow.HapticPattern.curveList.Count; n++) { curveNames[n] = patternWindow.HapticPattern.curveList[n].ToString(); } // Show list of curves on the pattern for the user to select EditorGUILayout.LabelField("Current curve:", textLabelStyle); patternWindow.CurveIndex = EditorGUILayout.Popup(patternWindow.CurveIndex, curveNames); // Allow the user to add a new curve if (GUILayout.Button("Add New Curve")) { patternWindow.AddNewCurve("Default"); } if (GUILayout.Button("Remove Current Curve")) { patternWindow.RemoveCurrentCurve(); } // Add selected curve to pattern button EditorGUILayout.LabelField("Add Saved Curve:", textLabelStyle); curveToAdd = EditorGUILayout.ObjectField(curveToAdd, typeof(ScriptableHapticCurve), false) as ScriptableHapticCurve; if (curveToAdd != null) { if (GUILayout.Button("Add Curve")) { patternWindow.AddSavedCurve(curveToAdd); curveToAdd = null; } } EditorGUILayout.EndVertical(); GUILayout.EndArea(); Handles.EndGUI(); }