From 239bf8fefeba557fb882fa462aba68132a4dab6b Mon Sep 17 00:00:00 2001 From: Christopher Date: Wed, 17 Aug 2016 14:11:57 +0100 Subject: [PATCH] Converted tabs to spaces in all .cs files --- .../Scripts/Commands/PlayAnimState.cs | 57 +- .../Scripts/Commands/SetAnimFloat.cs | 102 +- .../Scripts/Commands/SetAnimInteger.cs | 102 +- .../Scripts/Commands/SetAnimTrigger.cs | 98 +- .../Fungus/Audio/Editor/ControlAudioEditor.cs | 92 +- .../Audio/Scripts/Commands/ControlAudio.cs | 492 ++-- .../Audio/Scripts/Commands/PlayMusic.cs | 90 +- .../Audio/Scripts/Commands/PlaySound.cs | 102 +- .../Audio/Scripts/Commands/PlayUsfxrSound.cs | 70 +- .../Audio/Scripts/Commands/SetAudioPitch.cs | 96 +- .../Audio/Scripts/Commands/SetAudioVolume.cs | 64 +- .../Audio/Scripts/Commands/StopMusic.cs | 40 +- .../Fungus/Audio/Scripts/MusicController.cs | 298 +-- .../Fungus/Camera/Editor/CameraMenuItems.cs | 16 +- Assets/Fungus/Camera/Editor/ViewEditor.cs | 494 ++-- .../Fungus/Camera/Scripts/CameraController.cs | 1128 ++++----- .../Camera/Scripts/Commands/FadeScreen.cs | 122 +- .../Camera/Scripts/Commands/FadeToView.cs | 216 +- .../Camera/Scripts/Commands/Fullscreen.cs | 86 +- .../Camera/Scripts/Commands/MoveToView.cs | 188 +- .../Camera/Scripts/Commands/ShakeCamera.cs | 112 +- .../Camera/Scripts/Commands/StartSwipe.cs | 114 +- .../Camera/Scripts/Commands/StopSwipe.cs | 34 +- Assets/Fungus/Camera/Scripts/View.cs | 62 +- .../Flowchart/Editor/AssetModProcessor.cs | 68 +- Assets/Fungus/Flowchart/Editor/BlockEditor.cs | 2116 ++++++++--------- .../Fungus/Flowchart/Editor/BlockInspector.cs | 448 ++-- Assets/Fungus/Flowchart/Editor/CallEditor.cs | 84 +- .../Fungus/Flowchart/Editor/CommandEditor.cs | 414 ++-- .../Flowchart/Editor/CommandListAdaptor.cs | 870 +++---- Assets/Fungus/Flowchart/Editor/CustomGUI.cs | 66 +- .../Flowchart/Editor/EditorExtensions.cs | 104 +- .../Fungus/Flowchart/Editor/EditorZoomArea.cs | 134 +- .../Flowchart/Editor/EventHandlerEditor.cs | 98 +- .../SerializedPropertyExtensions.cs | 2 +- .../Flowchart/Editor/FlowchartEditor.cs | 538 ++--- .../Flowchart/Editor/FlowchartMenuItems.cs | 120 +- .../Flowchart/Editor/FlowchartWindow.cs | 1658 ++++++------- .../Flowchart/Editor/FungusEditorResources.cs | 356 +-- Assets/Fungus/Flowchart/Editor/GLDraw.cs | 568 ++--- Assets/Fungus/Flowchart/Editor/IfEditor.cs | 164 +- .../Flowchart/Editor/InvokeEventEditor.cs | 120 +- .../Flowchart/Editor/InvokeMethodEditor.cs | 856 +++---- Assets/Fungus/Flowchart/Editor/LabelEditor.cs | 122 +- .../Flowchart/Editor/SetVariableEditor.cs | 296 +-- .../Fungus/Flowchart/Editor/VariableEditor.cs | 656 ++--- .../Flowchart/Editor/VariableListAdaptor.cs | 366 +-- Assets/Fungus/Flowchart/Scripts/Block.cs | 578 ++--- Assets/Fungus/Flowchart/Scripts/Command.cs | 428 ++-- .../Flowchart/Scripts/CommandCopyBuffer.cs | 106 +- .../Flowchart/Scripts/Commands/Break.cs | 114 +- .../Fungus/Flowchart/Scripts/Commands/Call.cs | 236 +- .../Flowchart/Scripts/Commands/CallMethod.cs | 108 +- .../Flowchart/Scripts/Commands/Comment.cs | 56 +- .../Flowchart/Scripts/Commands/Condition.cs | 84 +- .../Flowchart/Scripts/Commands/DebugLog.cs | 100 +- .../Scripts/Commands/DeleteSaveKey.cs | 80 +- .../Flowchart/Scripts/Commands/Destroy.cs | 82 +- .../Fungus/Flowchart/Scripts/Commands/Else.cs | 104 +- .../Flowchart/Scripts/Commands/ElseIf.cs | 116 +- .../Fungus/Flowchart/Scripts/Commands/End.cs | 66 +- .../Fungus/Flowchart/Scripts/Commands/If.cs | 380 +-- .../Flowchart/Scripts/Commands/InvokeEvent.cs | 250 +- .../Scripts/Commands/InvokeMethod.cs | 714 +++--- .../Fungus/Flowchart/Scripts/Commands/Jump.cs | 74 +- .../Flowchart/Scripts/Commands/Label.cs | 42 +- .../Flowchart/Scripts/Commands/LoadScene.cs | 84 +- .../Scripts/Commands/LoadVariable.cs | 162 +- .../Flowchart/Scripts/Commands/OpenURL.cs | 50 +- .../Fungus/Flowchart/Scripts/Commands/Quit.cs | 34 +- .../Flowchart/Scripts/Commands/RandomFloat.cs | 92 +- .../Scripts/Commands/RandomInteger.cs | 92 +- .../Scripts/Commands/ReadTextFile.cs | 88 +- .../Flowchart/Scripts/Commands/Reset.cs | 40 +- .../Scripts/Commands/SaveVariable.cs | 180 +- .../Flowchart/Scripts/Commands/SendMessage.cs | 136 +- .../Flowchart/Scripts/Commands/SetActive.cs | 88 +- .../Scripts/Commands/SetSaveProfile.cs | 56 +- .../Flowchart/Scripts/Commands/SetVariable.cs | 390 +-- .../Flowchart/Scripts/Commands/SpawnObject.cs | 180 +- .../Fungus/Flowchart/Scripts/Commands/Stop.cs | 30 +- .../Scripts/Commands/StopFlowchart.cs | 96 +- .../Fungus/Flowchart/Scripts/Commands/Wait.cs | 90 +- .../Flowchart/Scripts/Commands/While.cs | 96 +- .../Fungus/Flowchart/Scripts/EventHandler.cs | 118 +- .../Scripts/EventHandlers/FlowchartEnabled.cs | 36 +- .../Scripts/EventHandlers/GameStarted.cs | 22 +- .../Scripts/EventHandlers/KeyPressed.cs | 90 +- .../Scripts/EventHandlers/MessageReceived.cs | 40 +- Assets/Fungus/Flowchart/Scripts/Flowchart.cs | 1616 ++++++------- .../Fungus/Flowchart/Scripts/FungusState.cs | 14 +- Assets/Fungus/Flowchart/Scripts/Node.cs | 10 +- .../Fungus/Flowchart/Scripts/SceneLoader.cs | 184 +- .../Flowchart/Scripts/StringFormatter.cs | 92 +- Assets/Fungus/Flowchart/Scripts/Variable.cs | 120 +- .../Scripts/VariableTypes/AnimatorVariable.cs | 80 +- .../VariableTypes/AudioSourceVariable.cs | 80 +- .../Scripts/VariableTypes/BooleanVariable.cs | 120 +- .../Scripts/VariableTypes/ColorVariable.cs | 80 +- .../Scripts/VariableTypes/FloatVariable.cs | 140 +- .../VariableTypes/GameObjectVariable.cs | 80 +- .../Scripts/VariableTypes/IntegerVariable.cs | 134 +- .../Scripts/VariableTypes/MaterialVariable.cs | 80 +- .../Scripts/VariableTypes/ObjectVariable.cs | 80 +- .../Scripts/VariableTypes/SpriteVariable.cs | 80 +- .../Scripts/VariableTypes/StringVariable.cs | 128 +- .../Scripts/VariableTypes/TextureVariable.cs | 80 +- .../VariableTypes/TransformVariable.cs | 80 +- .../Scripts/VariableTypes/Vector2Variable.cs | 80 +- .../Scripts/VariableTypes/Vector3Variable.cs | 80 +- .../Fungus/Lua/Scripts/Commands/ExecuteLua.cs | 8 +- .../Narrative/Editor/CharacterEditor.cs | 152 +- .../Narrative/Editor/LocalizationEditor.cs | 220 +- Assets/Fungus/Narrative/Editor/MenuEditor.cs | 92 +- .../Narrative/Editor/MenuTimerEditor.cs | 66 +- .../Narrative/Editor/NarrativeMenuItems.cs | 104 +- .../Fungus/Narrative/Editor/PortraitEditor.cs | 548 ++--- Assets/Fungus/Narrative/Editor/SayEditor.cs | 402 ++-- Assets/Fungus/Narrative/Editor/StageEditor.cs | 148 +- Assets/Fungus/Narrative/Scripts/Character.cs | 138 +- .../Narrative/Scripts/Commands/ClearMenu.cs | 54 +- .../Scripts/Commands/ControlStage.cs | 358 +-- .../Scripts/Commands/Conversation.cs | 36 +- .../Fungus/Narrative/Scripts/Commands/Menu.cs | 210 +- .../Narrative/Scripts/Commands/MenuTimer.cs | 106 +- .../Narrative/Scripts/Commands/Portrait.cs | 392 +-- .../Fungus/Narrative/Scripts/Commands/Say.cs | 280 +-- .../Narrative/Scripts/Commands/SetLanguage.cs | 84 +- .../Scripts/Commands/SetMenuDialog.cs | 68 +- .../Scripts/Commands/SetSayDialog.cs | 68 +- .../Narrative/Scripts/ConversationManager.cs | 84 +- Assets/Fungus/Narrative/Scripts/CustomTag.cs | 48 +- .../Fungus/Narrative/Scripts/DialogInput.cs | 220 +- .../Fungus/Narrative/Scripts/Localization.cs | 1070 ++++----- Assets/Fungus/Narrative/Scripts/MenuDialog.cs | 390 +-- .../Narrative/Scripts/PortraitController.cs | 1456 ++++++------ Assets/Fungus/Narrative/Scripts/SayDialog.cs | 746 +++--- Assets/Fungus/Narrative/Scripts/Stage.cs | 92 +- .../Fungus/Sprite/Editor/SetColliderEditor.cs | 60 +- .../Fungus/Sprite/Editor/SpriteMenuItems.cs | 46 +- Assets/Fungus/Sprite/Scripts/Clickable2D.cs | 176 +- .../Sprite/Scripts/Commands/FadeSprite.cs | 168 +- .../Sprite/Scripts/Commands/SetClickable2D.cs | 72 +- .../Sprite/Scripts/Commands/SetCollider.cs | 146 +- .../Sprite/Scripts/Commands/SetDraggable2D.cs | 74 +- .../Sprite/Scripts/Commands/SetLayerOrder.cs | 102 +- .../Sprite/Scripts/Commands/SetMouseCursor.cs | 94 +- .../Sprite/Scripts/Commands/SetSpriteOrder.cs | 118 +- .../Sprite/Scripts/Commands/ShowSprite.cs | 124 +- Assets/Fungus/Sprite/Scripts/Draggable2D.cs | 484 ++-- .../Scripts/EventHandlers/DragCancelled.cs | 52 +- .../Scripts/EventHandlers/DragCompleted.cs | 142 +- .../Scripts/EventHandlers/DragEntered.cs | 78 +- .../Scripts/EventHandlers/DragExited.cs | 78 +- .../Scripts/EventHandlers/DragStarted.cs | 48 +- .../Scripts/EventHandlers/ObjectClicked.cs | 50 +- Assets/Fungus/Sprite/Scripts/Parallax.cs | 130 +- Assets/Fungus/Sprite/Scripts/SpriteFader.cs | 192 +- .../Scripts/Editor/LuaBindingsEditor.cs | 36 +- .../Scripts/Editor/LuaScriptEditor.cs | 30 +- .../Scripts/Editor/LuaStoreEditor.cs | 32 +- .../Scripts/Editor/LuaUtilsEditor.cs | 8 +- .../FungusLua/Scripts/Editor/MenuItems.cs | 24 +- .../FungusLua/Scripts/ExecuteHandler.cs | 8 +- .../Thirdparty/FungusLua/Scripts/InfoText.cs | 6 +- .../FungusLua/Scripts/LuaBindings.cs | 50 +- .../FungusLua/Scripts/LuaEnvironment.cs | 206 +- .../Thirdparty/FungusLua/Scripts/LuaScript.cs | 4 +- .../Thirdparty/FungusLua/Scripts/LuaStore.cs | 172 +- .../Thirdparty/FungusLua/Scripts/LuaUtils.cs | 514 ++-- .../FungusLua/Scripts/PODTypeFactory.cs | 144 +- .../FungusLua/Scripts/StringSubstituter.cs | 114 +- Assets/Fungus/UI/Editor/WriteEditor.cs | 144 +- Assets/Fungus/UI/Editor/WriterAudioEditor.cs | 84 +- Assets/Fungus/UI/Scripts/Commands/FadeUI.cs | 240 +- Assets/Fungus/UI/Scripts/Commands/GetText.cs | 166 +- .../UI/Scripts/Commands/SetInteractable.cs | 158 +- .../UI/Scripts/Commands/SetSliderValue.cs | 66 +- Assets/Fungus/UI/Scripts/Commands/SetText.cs | 204 +- Assets/Fungus/UI/Scripts/Commands/TweenUI.cs | 228 +- Assets/Fungus/UI/Scripts/Commands/Write.cs | 272 +-- .../UI/Scripts/EventHandlers/ButtonClicked.cs | 60 +- .../UI/Scripts/EventHandlers/EndEdit.cs | 54 +- Assets/Fungus/UI/Scripts/SelectOnEnable.cs | 26 +- Assets/Fungus/UI/Scripts/TextTagParser.cs | 600 ++--- Assets/Fungus/UI/Scripts/Writer.cs | 1442 +++++------ Assets/Fungus/UI/Scripts/WriterAudio.cs | 484 ++-- .../iTween/Scripts/Commands/LookFrom.cs | 128 +- .../Fungus/iTween/Scripts/Commands/LookTo.cs | 128 +- .../Fungus/iTween/Scripts/Commands/MoveAdd.cs | 90 +- .../iTween/Scripts/Commands/MoveFrom.cs | 106 +- .../Fungus/iTween/Scripts/Commands/MoveTo.cs | 106 +- .../iTween/Scripts/Commands/PunchPosition.cs | 90 +- .../iTween/Scripts/Commands/PunchRotation.cs | 90 +- .../iTween/Scripts/Commands/PunchScale.cs | 82 +- .../iTween/Scripts/Commands/RotateAdd.cs | 90 +- .../iTween/Scripts/Commands/RotateFrom.cs | 106 +- .../iTween/Scripts/Commands/RotateTo.cs | 106 +- .../iTween/Scripts/Commands/ScaleAdd.cs | 82 +- .../iTween/Scripts/Commands/ScaleFrom.cs | 100 +- .../Fungus/iTween/Scripts/Commands/ScaleTo.cs | 100 +- .../iTween/Scripts/Commands/ShakePosition.cs | 108 +- .../iTween/Scripts/Commands/ShakeRotation.cs | 92 +- .../iTween/Scripts/Commands/ShakeScale.cs | 74 +- .../iTween/Scripts/Commands/StopTween.cs | 62 +- .../iTween/Scripts/Commands/StopTweens.cs | 24 +- .../iTween/Scripts/Commands/iTweenCommand.cs | 220 +- 207 files changed, 20789 insertions(+), 20790 deletions(-) mode change 100755 => 100644 Assets/Fungus/Animation/Scripts/Commands/PlayAnimState.cs mode change 100755 => 100644 Assets/Fungus/Flowchart/Editor/EditorExtensions.cs mode change 100755 => 100644 Assets/Fungus/Flowchart/Editor/FlowchartWindow.cs mode change 100755 => 100644 Assets/Fungus/Flowchart/Editor/GLDraw.cs mode change 100755 => 100644 Assets/Fungus/Flowchart/Editor/InvokeMethodEditor.cs mode change 100755 => 100644 Assets/Fungus/Flowchart/Scripts/Commands/InvokeMethod.cs mode change 100755 => 100644 Assets/Fungus/Narrative/Scripts/Commands/Portrait.cs diff --git a/Assets/Fungus/Animation/Scripts/Commands/PlayAnimState.cs b/Assets/Fungus/Animation/Scripts/Commands/PlayAnimState.cs old mode 100755 new mode 100644 index b5feda4d..f958ec8c --- a/Assets/Fungus/Animation/Scripts/Commands/PlayAnimState.cs +++ b/Assets/Fungus/Animation/Scripts/Commands/PlayAnimState.cs @@ -9,50 +9,49 @@ using System.Collections; namespace Fungus { - [CommandInfo("Animation", - "Play Anim State", - "Plays a state of an animator according to the state name")] - [AddComponentMenu("")] - public class PlayAnimState : Command - { + [CommandInfo("Animation", + "Play Anim State", + "Plays a state of an animator according to the state name")] + [AddComponentMenu("")] + public class PlayAnimState : Command + { [Tooltip("Reference to an Animator component in a game object")] - public AnimatorData animator = new AnimatorData(); + public AnimatorData animator = new AnimatorData(); - [Tooltip("Name of the state you want to play")] - public StringData stateName = new StringData(); + [Tooltip("Name of the state you want to play")] + public StringData stateName = new StringData(); [Tooltip("Layer to play animation on")] - public IntegerData layer = new IntegerData(-1); + public IntegerData layer = new IntegerData(-1); [Tooltip("Start time of animation")] - public FloatData time = new FloatData(0f); + public FloatData time = new FloatData(0f); public override void OnEnter() - { - if (animator.Value != null) - { + { + if (animator.Value != null) + { animator.Value.Play(stateName.Value, layer.Value, time.Value); - } + } - Continue(); - } + Continue(); + } - public override string GetSummary() - { - if (animator.Value == null) - { - return "Error: No animator selected"; - } + public override string GetSummary() + { + if (animator.Value == null) + { + return "Error: No animator selected"; + } return animator.Value.name + " (" + stateName.Value + ")"; - } + } - public override Color GetButtonColor() - { - return new Color32(170, 204, 169, 255); - } + public override Color GetButtonColor() + { + return new Color32(170, 204, 169, 255); + } } } - diff --git a/Assets/Fungus/Animation/Scripts/Commands/SetAnimFloat.cs b/Assets/Fungus/Animation/Scripts/Commands/SetAnimFloat.cs index fe5e2c69..2040c701 100644 --- a/Assets/Fungus/Animation/Scripts/Commands/SetAnimFloat.cs +++ b/Assets/Fungus/Animation/Scripts/Commands/SetAnimFloat.cs @@ -10,68 +10,68 @@ using System.Collections; namespace Fungus { - [CommandInfo("Animation", - "Set Anim Float", - "Sets a float parameter on an Animator component to control a Unity animation")] - [AddComponentMenu("")] - [ExecuteInEditMode] - public class SetAnimFloat : Command - { - [Tooltip("Reference to an Animator component in a game object")] - public AnimatorData _animator; + [CommandInfo("Animation", + "Set Anim Float", + "Sets a float parameter on an Animator component to control a Unity animation")] + [AddComponentMenu("")] + [ExecuteInEditMode] + public class SetAnimFloat : Command + { + [Tooltip("Reference to an Animator component in a game object")] + public AnimatorData _animator; - [Tooltip("Name of the float Animator parameter that will have its value changed")] - public StringData _parameterName; + [Tooltip("Name of the float Animator parameter that will have its value changed")] + public StringData _parameterName; - [Tooltip("The float value to set the parameter to")] - public FloatData value; + [Tooltip("The float value to set the parameter to")] + public FloatData value; - public override void OnEnter() - { - if (_animator.Value != null) - { - _animator.Value.SetFloat(_parameterName.Value, value.Value); - } + public override void OnEnter() + { + if (_animator.Value != null) + { + _animator.Value.SetFloat(_parameterName.Value, value.Value); + } - Continue(); - } + Continue(); + } - public override string GetSummary() - { - if (_animator.Value == null) - { - return "Error: No animator selected"; - } + public override string GetSummary() + { + if (_animator.Value == null) + { + return "Error: No animator selected"; + } - return _animator.Value.name + " (" + _parameterName.Value + ")"; - } + return _animator.Value.name + " (" + _parameterName.Value + ")"; + } - public override Color GetButtonColor() - { - return new Color32(170, 204, 169, 255); - } + public override Color GetButtonColor() + { + return new Color32(170, 204, 169, 255); + } - #region Backwards compatibility + #region Backwards compatibility - [HideInInspector] [FormerlySerializedAs("animator")] public Animator animatorOLD; - [HideInInspector] [FormerlySerializedAs("parameterName")] public string parameterNameOLD = ""; + [HideInInspector] [FormerlySerializedAs("animator")] public Animator animatorOLD; + [HideInInspector] [FormerlySerializedAs("parameterName")] public string parameterNameOLD = ""; - protected virtual void OnEnable() - { - if (animatorOLD != null) - { - _animator.Value = animatorOLD; - animatorOLD = null; - } + protected virtual void OnEnable() + { + if (animatorOLD != null) + { + _animator.Value = animatorOLD; + animatorOLD = null; + } - if (parameterNameOLD != "") - { - _parameterName.Value = parameterNameOLD; - parameterNameOLD = ""; - } - } + if (parameterNameOLD != "") + { + _parameterName.Value = parameterNameOLD; + parameterNameOLD = ""; + } + } - #endregion - } + #endregion + } } \ No newline at end of file diff --git a/Assets/Fungus/Animation/Scripts/Commands/SetAnimInteger.cs b/Assets/Fungus/Animation/Scripts/Commands/SetAnimInteger.cs index ecc1b3ec..96d723f5 100644 --- a/Assets/Fungus/Animation/Scripts/Commands/SetAnimInteger.cs +++ b/Assets/Fungus/Animation/Scripts/Commands/SetAnimInteger.cs @@ -10,68 +10,68 @@ using System.Collections; namespace Fungus { - [CommandInfo("Animation", - "Set Anim Integer", - "Sets an integer parameter on an Animator component to control a Unity animation")] - [AddComponentMenu("")] - [ExecuteInEditMode] - public class SetAnimInteger : Command - { - [Tooltip("Reference to an Animator component in a game object")] - public AnimatorData _animator; + [CommandInfo("Animation", + "Set Anim Integer", + "Sets an integer parameter on an Animator component to control a Unity animation")] + [AddComponentMenu("")] + [ExecuteInEditMode] + public class SetAnimInteger : Command + { + [Tooltip("Reference to an Animator component in a game object")] + public AnimatorData _animator; - [Tooltip("Name of the integer Animator parameter that will have its value changed")] - public StringData _parameterName; + [Tooltip("Name of the integer Animator parameter that will have its value changed")] + public StringData _parameterName; - [Tooltip("The integer value to set the parameter to")] - public IntegerData value; + [Tooltip("The integer value to set the parameter to")] + public IntegerData value; - public override void OnEnter() - { - if (_animator.Value != null) - { - _animator.Value.SetInteger(_parameterName.Value, value.Value); - } + public override void OnEnter() + { + if (_animator.Value != null) + { + _animator.Value.SetInteger(_parameterName.Value, value.Value); + } - Continue(); - } + Continue(); + } - public override string GetSummary() - { - if (_animator.Value == null) - { - return "Error: No animator selected"; - } + public override string GetSummary() + { + if (_animator.Value == null) + { + return "Error: No animator selected"; + } - return _animator.Value.name + " (" + _parameterName.Value + ")"; - } + return _animator.Value.name + " (" + _parameterName.Value + ")"; + } - public override Color GetButtonColor() - { - return new Color32(170, 204, 169, 255); - } + public override Color GetButtonColor() + { + return new Color32(170, 204, 169, 255); + } - #region Backwards compatibility + #region Backwards compatibility - [HideInInspector] [FormerlySerializedAs("animator")] public Animator animatorOLD; - [HideInInspector] [FormerlySerializedAs("parameterName")] public string parameterNameOLD = ""; + [HideInInspector] [FormerlySerializedAs("animator")] public Animator animatorOLD; + [HideInInspector] [FormerlySerializedAs("parameterName")] public string parameterNameOLD = ""; - protected virtual void OnEnable() - { - if (animatorOLD != null) - { - _animator.Value = animatorOLD; - animatorOLD = null; - } + protected virtual void OnEnable() + { + if (animatorOLD != null) + { + _animator.Value = animatorOLD; + animatorOLD = null; + } - if (parameterNameOLD != "") - { - _parameterName.Value = parameterNameOLD; - parameterNameOLD = ""; - } - } + if (parameterNameOLD != "") + { + _parameterName.Value = parameterNameOLD; + parameterNameOLD = ""; + } + } - #endregion - } + #endregion + } } \ No newline at end of file diff --git a/Assets/Fungus/Animation/Scripts/Commands/SetAnimTrigger.cs b/Assets/Fungus/Animation/Scripts/Commands/SetAnimTrigger.cs index af1d549a..da300a21 100644 --- a/Assets/Fungus/Animation/Scripts/Commands/SetAnimTrigger.cs +++ b/Assets/Fungus/Animation/Scripts/Commands/SetAnimTrigger.cs @@ -10,65 +10,65 @@ using System.Collections; namespace Fungus { - [CommandInfo("Animation", - "Set Anim Trigger", - "Sets a trigger parameter on an Animator component to control a Unity animation")] - [AddComponentMenu("")] - [ExecuteInEditMode] - public class SetAnimTrigger : Command - { - [Tooltip("Reference to an Animator component in a game object")] - public AnimatorData _animator; + [CommandInfo("Animation", + "Set Anim Trigger", + "Sets a trigger parameter on an Animator component to control a Unity animation")] + [AddComponentMenu("")] + [ExecuteInEditMode] + public class SetAnimTrigger : Command + { + [Tooltip("Reference to an Animator component in a game object")] + public AnimatorData _animator; - [Tooltip("Name of the trigger Animator parameter that will have its value changed")] - public StringData _parameterName; + [Tooltip("Name of the trigger Animator parameter that will have its value changed")] + public StringData _parameterName; - public override void OnEnter() - { - if (_animator.Value != null) - { - _animator.Value.SetTrigger(_parameterName.Value); - } + public override void OnEnter() + { + if (_animator.Value != null) + { + _animator.Value.SetTrigger(_parameterName.Value); + } - Continue(); - } + Continue(); + } - public override string GetSummary() - { - if (_animator.Value == null) - { - return "Error: No animator selected"; - } + public override string GetSummary() + { + if (_animator.Value == null) + { + return "Error: No animator selected"; + } - return _animator.Value.name + " (" + _parameterName.Value + ")"; - } + return _animator.Value.name + " (" + _parameterName.Value + ")"; + } - public override Color GetButtonColor() - { - return new Color32(170, 204, 169, 255); - } + public override Color GetButtonColor() + { + return new Color32(170, 204, 169, 255); + } - #region Backwards compatibility + #region Backwards compatibility - [HideInInspector] [FormerlySerializedAs("animator")] public Animator animatorOLD; - [HideInInspector] [FormerlySerializedAs("parameterName")] public string parameterNameOLD = ""; + [HideInInspector] [FormerlySerializedAs("animator")] public Animator animatorOLD; + [HideInInspector] [FormerlySerializedAs("parameterName")] public string parameterNameOLD = ""; - protected virtual void OnEnable() - { - if (animatorOLD != null) - { - _animator.Value = animatorOLD; - animatorOLD = null; - } + protected virtual void OnEnable() + { + if (animatorOLD != null) + { + _animator.Value = animatorOLD; + animatorOLD = null; + } - if (parameterNameOLD != "") - { - _parameterName.Value = parameterNameOLD; - parameterNameOLD = ""; - } - } + if (parameterNameOLD != "") + { + _parameterName.Value = parameterNameOLD; + parameterNameOLD = ""; + } + } - #endregion - } + #endregion + } } \ No newline at end of file diff --git a/Assets/Fungus/Audio/Editor/ControlAudioEditor.cs b/Assets/Fungus/Audio/Editor/ControlAudioEditor.cs index e71fb2ba..e6f7af58 100644 --- a/Assets/Fungus/Audio/Editor/ControlAudioEditor.cs +++ b/Assets/Fungus/Audio/Editor/ControlAudioEditor.cs @@ -16,53 +16,53 @@ using Rotorz.ReorderableList; namespace Fungus { - - [CustomEditor (typeof(ControlAudio))] - public class ControlAudioEditor : CommandEditor - { - protected SerializedProperty controlProp; - protected SerializedProperty audioSourceProp; - protected SerializedProperty startVolumeProp; - protected SerializedProperty endVolumeProp; - protected SerializedProperty fadeDurationProp; - protected SerializedProperty waitUntilFinishedProp; - - protected virtual void OnEnable() - { - if (NullTargetCheck()) // Check for an orphaned editor instance - return; + + [CustomEditor (typeof(ControlAudio))] + public class ControlAudioEditor : CommandEditor + { + protected SerializedProperty controlProp; + protected SerializedProperty audioSourceProp; + protected SerializedProperty startVolumeProp; + protected SerializedProperty endVolumeProp; + protected SerializedProperty fadeDurationProp; + protected SerializedProperty waitUntilFinishedProp; + + protected virtual void OnEnable() + { + if (NullTargetCheck()) // Check for an orphaned editor instance + return; - controlProp = serializedObject.FindProperty("control"); - audioSourceProp = serializedObject.FindProperty("_audioSource"); - startVolumeProp = serializedObject.FindProperty("startVolume"); - endVolumeProp = serializedObject.FindProperty("endVolume"); - fadeDurationProp = serializedObject.FindProperty("fadeDuration"); - waitUntilFinishedProp = serializedObject.FindProperty("waitUntilFinished"); - } - - public override void DrawCommandGUI() - { - serializedObject.Update(); + controlProp = serializedObject.FindProperty("control"); + audioSourceProp = serializedObject.FindProperty("_audioSource"); + startVolumeProp = serializedObject.FindProperty("startVolume"); + endVolumeProp = serializedObject.FindProperty("endVolume"); + fadeDurationProp = serializedObject.FindProperty("fadeDuration"); + waitUntilFinishedProp = serializedObject.FindProperty("waitUntilFinished"); + } + + public override void DrawCommandGUI() + { + serializedObject.Update(); - ControlAudio t = target as ControlAudio; + ControlAudio t = target as ControlAudio; - EditorGUILayout.PropertyField(controlProp); - EditorGUILayout.PropertyField(audioSourceProp); - string fadeLabel = "Fade Out Duration"; - if (t.control != ControlAudio.controlType.StopLoop && t.control != ControlAudio.controlType.PauseLoop) - { - fadeLabel = "Fade In Duration"; - string volumeLabel = "End Volume"; - if (t.control == ControlAudio.controlType.ChangeVolume) - { - fadeLabel = "Fade Duration"; - volumeLabel = "New Volume"; - } - EditorGUILayout.PropertyField(endVolumeProp,new GUIContent(volumeLabel)); - } - EditorGUILayout.PropertyField(fadeDurationProp,new GUIContent(fadeLabel)); - EditorGUILayout.PropertyField(waitUntilFinishedProp); - serializedObject.ApplyModifiedProperties(); - } - } + EditorGUILayout.PropertyField(controlProp); + EditorGUILayout.PropertyField(audioSourceProp); + string fadeLabel = "Fade Out Duration"; + if (t.control != ControlAudio.controlType.StopLoop && t.control != ControlAudio.controlType.PauseLoop) + { + fadeLabel = "Fade In Duration"; + string volumeLabel = "End Volume"; + if (t.control == ControlAudio.controlType.ChangeVolume) + { + fadeLabel = "Fade Duration"; + volumeLabel = "New Volume"; + } + EditorGUILayout.PropertyField(endVolumeProp,new GUIContent(volumeLabel)); + } + EditorGUILayout.PropertyField(fadeDurationProp,new GUIContent(fadeLabel)); + EditorGUILayout.PropertyField(waitUntilFinishedProp); + serializedObject.ApplyModifiedProperties(); + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Audio/Scripts/Commands/ControlAudio.cs b/Assets/Fungus/Audio/Scripts/Commands/ControlAudio.cs index c4b4d42c..a8abfd46 100644 --- a/Assets/Fungus/Audio/Scripts/Commands/ControlAudio.cs +++ b/Assets/Fungus/Audio/Scripts/Commands/ControlAudio.cs @@ -9,225 +9,225 @@ using System.Collections; namespace Fungus { - [CommandInfo("Audio", - "Control Audio", - "Plays, loops, or stops an audiosource. Any AudioSources with the same tag as the target Audio Source will automatically be stoped.")] - [ExecuteInEditMode] - public class ControlAudio : Command - { - public enum controlType - { - PlayOnce, - PlayLoop, - PauseLoop, - StopLoop, - ChangeVolume - } + [CommandInfo("Audio", + "Control Audio", + "Plays, loops, or stops an audiosource. Any AudioSources with the same tag as the target Audio Source will automatically be stoped.")] + [ExecuteInEditMode] + public class ControlAudio : Command + { + public enum controlType + { + PlayOnce, + PlayLoop, + PauseLoop, + StopLoop, + ChangeVolume + } - [Tooltip("What to do to audio")] - public controlType control; + [Tooltip("What to do to audio")] + public controlType control; - [Tooltip("Audio clip to play")] - public AudioSourceData _audioSource; + [Tooltip("Audio clip to play")] + public AudioSourceData _audioSource; - [Range(0,1)] - [Tooltip("Start audio at this volume")] - public float startVolume = 1; + [Range(0,1)] + [Tooltip("Start audio at this volume")] + public float startVolume = 1; - [Range(0,1)] - [Tooltip("End audio at this volume")] - public float endVolume = 1; - - [Tooltip("Time to fade between current volume level and target volume level.")] - public float fadeDuration; + [Range(0,1)] + [Tooltip("End audio at this volume")] + public float endVolume = 1; + + [Tooltip("Time to fade between current volume level and target volume level.")] + public float fadeDuration; - [Tooltip("Wait until this command has finished before executing the next command.")] - public bool waitUntilFinished = false; - - public override void OnEnter() - { - if (_audioSource.Value == null) - { - Continue(); - return; - } + [Tooltip("Wait until this command has finished before executing the next command.")] + public bool waitUntilFinished = false; + + public override void OnEnter() + { + if (_audioSource.Value == null) + { + Continue(); + return; + } if (control != controlType.ChangeVolume) { - _audioSource.Value.volume = endVolume; + _audioSource.Value.volume = endVolume; } - switch(control) - { - case controlType.PlayOnce: - StopAudioWithSameTag(); - PlayOnce(); - break; - case controlType.PlayLoop: - StopAudioWithSameTag(); - PlayLoop(); - break; - case controlType.PauseLoop: - PauseLoop(); - break; - case controlType.StopLoop: - StopLoop(_audioSource.Value); - break; - case controlType.ChangeVolume: - ChangeVolume(); - break; - } - if (!waitUntilFinished) - { - Continue(); - } - } + switch(control) + { + case controlType.PlayOnce: + StopAudioWithSameTag(); + PlayOnce(); + break; + case controlType.PlayLoop: + StopAudioWithSameTag(); + PlayLoop(); + break; + case controlType.PauseLoop: + PauseLoop(); + break; + case controlType.StopLoop: + StopLoop(_audioSource.Value); + break; + case controlType.ChangeVolume: + ChangeVolume(); + break; + } + if (!waitUntilFinished) + { + Continue(); + } + } - /** - * If there's other music playing in the scene, assign it the same tag as the new music you want to play and - * the old music will be automatically stopped. - */ - protected void StopAudioWithSameTag() - { - // Don't stop audio if there's no tag assigned - if (_audioSource.Value == null || - _audioSource.Value.tag == "Untagged") - { - return; - } + /** + * If there's other music playing in the scene, assign it the same tag as the new music you want to play and + * the old music will be automatically stopped. + */ + protected void StopAudioWithSameTag() + { + // Don't stop audio if there's no tag assigned + if (_audioSource.Value == null || + _audioSource.Value.tag == "Untagged") + { + return; + } - AudioSource[] audioSources = GameObject.FindObjectsOfType(); - foreach (AudioSource a in audioSources) - { - if ((a.GetComponent() != _audioSource.Value) && (a.tag == _audioSource.Value.tag)) - { - StopLoop(a.GetComponent()); - } - } - } + AudioSource[] audioSources = GameObject.FindObjectsOfType(); + foreach (AudioSource a in audioSources) + { + if ((a.GetComponent() != _audioSource.Value) && (a.tag == _audioSource.Value.tag)) + { + StopLoop(a.GetComponent()); + } + } + } - protected void PlayOnce() - { - if (fadeDuration > 0) - { - // Fade volume in - LeanTween.value(_audioSource.Value.gameObject, - _audioSource.Value.volume, - endVolume, - fadeDuration - ).setOnUpdate( - (float updateVolume)=>{ - _audioSource.Value.volume = updateVolume; - }); - } + protected void PlayOnce() + { + if (fadeDuration > 0) + { + // Fade volume in + LeanTween.value(_audioSource.Value.gameObject, + _audioSource.Value.volume, + endVolume, + fadeDuration + ).setOnUpdate( + (float updateVolume)=>{ + _audioSource.Value.volume = updateVolume; + }); + } - _audioSource.Value.PlayOneShot(_audioSource.Value.clip); + _audioSource.Value.PlayOneShot(_audioSource.Value.clip); - if (waitUntilFinished) - { - StartCoroutine(WaitAndContinue()); - } - } + if (waitUntilFinished) + { + StartCoroutine(WaitAndContinue()); + } + } - protected virtual IEnumerator WaitAndContinue() - { - // Poll the audiosource until playing has finished - // This allows for things like effects added to the audiosource. - while (_audioSource.Value.isPlaying) - { - yield return null; - } + protected virtual IEnumerator WaitAndContinue() + { + // Poll the audiosource until playing has finished + // This allows for things like effects added to the audiosource. + while (_audioSource.Value.isPlaying) + { + yield return null; + } - Continue(); - } + Continue(); + } - protected void PlayLoop() - { - if (fadeDuration > 0) - { - _audioSource.Value.volume = 0; - _audioSource.Value.loop = true; - _audioSource.Value.GetComponent().Play(); - LeanTween.value(_audioSource.Value.gameObject,0,endVolume,fadeDuration - ).setOnUpdate( - (float updateVolume)=>{ - _audioSource.Value.volume = updateVolume; - } - ).setOnComplete( - ()=>{ - if (waitUntilFinished) - { - Continue(); - } - } - ); - } - else - { - _audioSource.Value.volume = 1; - _audioSource.Value.loop = true; - _audioSource.Value.GetComponent().Play(); - } - } + protected void PlayLoop() + { + if (fadeDuration > 0) + { + _audioSource.Value.volume = 0; + _audioSource.Value.loop = true; + _audioSource.Value.GetComponent().Play(); + LeanTween.value(_audioSource.Value.gameObject,0,endVolume,fadeDuration + ).setOnUpdate( + (float updateVolume)=>{ + _audioSource.Value.volume = updateVolume; + } + ).setOnComplete( + ()=>{ + if (waitUntilFinished) + { + Continue(); + } + } + ); + } + else + { + _audioSource.Value.volume = 1; + _audioSource.Value.loop = true; + _audioSource.Value.GetComponent().Play(); + } + } - protected void PauseLoop() - { - if (fadeDuration > 0) - { - LeanTween.value(_audioSource.Value.gameObject,_audioSource.Value.volume,0,fadeDuration - ).setOnUpdate( - (float updateVolume)=>{ - _audioSource.Value.volume = updateVolume; - } - ).setOnComplete( - ()=>{ - - _audioSource.Value.GetComponent().Pause(); - if (waitUntilFinished) - { - Continue(); - } - } - ); - } - else - { - _audioSource.Value.GetComponent().Pause(); - } - } + protected void PauseLoop() + { + if (fadeDuration > 0) + { + LeanTween.value(_audioSource.Value.gameObject,_audioSource.Value.volume,0,fadeDuration + ).setOnUpdate( + (float updateVolume)=>{ + _audioSource.Value.volume = updateVolume; + } + ).setOnComplete( + ()=>{ + + _audioSource.Value.GetComponent().Pause(); + if (waitUntilFinished) + { + Continue(); + } + } + ); + } + else + { + _audioSource.Value.GetComponent().Pause(); + } + } - protected void StopLoop(AudioSource source) - { - if (fadeDuration > 0) - { - LeanTween.value(source.gameObject,_audioSource.Value.volume,0,fadeDuration - ).setOnUpdate( - (float updateVolume)=>{ - source.volume = updateVolume; - } - ).setOnComplete( - ()=>{ - - source.GetComponent().Stop(); - if (waitUntilFinished) - { - Continue(); - } - } - ); - } - else - { - source.GetComponent().Stop(); - } - } + protected void StopLoop(AudioSource source) + { + if (fadeDuration > 0) + { + LeanTween.value(source.gameObject,_audioSource.Value.volume,0,fadeDuration + ).setOnUpdate( + (float updateVolume)=>{ + source.volume = updateVolume; + } + ).setOnComplete( + ()=>{ + + source.GetComponent().Stop(); + if (waitUntilFinished) + { + Continue(); + } + } + ); + } + else + { + source.GetComponent().Stop(); + } + } - protected void ChangeVolume() - { - LeanTween.value(_audioSource.Value.gameObject,_audioSource.Value.volume,endVolume,fadeDuration - ).setOnUpdate( - (float updateVolume)=>{ - _audioSource.Value.volume = updateVolume; + protected void ChangeVolume() + { + LeanTween.value(_audioSource.Value.gameObject,_audioSource.Value.volume,endVolume,fadeDuration + ).setOnUpdate( + (float updateVolume)=>{ + _audioSource.Value.volume = updateVolume; }).setOnComplete( ()=>{ if (waitUntilFinished) @@ -235,58 +235,58 @@ namespace Fungus Continue(); } }); - } + } - void AudioFinished() - { - if (waitUntilFinished) - { - Continue(); - } - } + void AudioFinished() + { + if (waitUntilFinished) + { + Continue(); + } + } - public override string GetSummary() - { - if (_audioSource.Value == null) - { - return "Error: No sound clip selected"; - } - string fadeType = ""; - if (fadeDuration > 0) - { - fadeType = " Fade out"; - if (control != controlType.StopLoop) - { - fadeType = " Fade in volume to " + endVolume; - } - if (control == controlType.ChangeVolume) - { - fadeType = " to " + endVolume; - } - fadeType += " over " + fadeDuration + " seconds."; - } - return control.ToString() + " \"" + _audioSource.Value.name + "\"" + fadeType; - } - - public override Color GetButtonColor() - { - return new Color32(242, 209, 176, 255); - } + public override string GetSummary() + { + if (_audioSource.Value == null) + { + return "Error: No sound clip selected"; + } + string fadeType = ""; + if (fadeDuration > 0) + { + fadeType = " Fade out"; + if (control != controlType.StopLoop) + { + fadeType = " Fade in volume to " + endVolume; + } + if (control == controlType.ChangeVolume) + { + fadeType = " to " + endVolume; + } + fadeType += " over " + fadeDuration + " seconds."; + } + return control.ToString() + " \"" + _audioSource.Value.name + "\"" + fadeType; + } + + public override Color GetButtonColor() + { + return new Color32(242, 209, 176, 255); + } - #region Backwards compatibility + #region Backwards compatibility - [HideInInspector] [FormerlySerializedAs("audioSource")] public AudioSource audioSourceOLD; + [HideInInspector] [FormerlySerializedAs("audioSource")] public AudioSource audioSourceOLD; - protected virtual void OnEnable() - { - if (audioSourceOLD != null) - { - _audioSource.Value = audioSourceOLD; - audioSourceOLD = null; - } - } + protected virtual void OnEnable() + { + if (audioSourceOLD != null) + { + _audioSource.Value = audioSourceOLD; + audioSourceOLD = null; + } + } - #endregion - } - + #endregion + } + } \ No newline at end of file diff --git a/Assets/Fungus/Audio/Scripts/Commands/PlayMusic.cs b/Assets/Fungus/Audio/Scripts/Commands/PlayMusic.cs index 2ad860b1..a4e97d89 100644 --- a/Assets/Fungus/Audio/Scripts/Commands/PlayMusic.cs +++ b/Assets/Fungus/Audio/Scripts/Commands/PlayMusic.cs @@ -8,50 +8,50 @@ using System.Collections; namespace Fungus { - [CommandInfo("Audio", - "Play Music", - "Plays looping game music. If any game music is already playing, it is stopped. Game music will continue playing across scene loads.")] - [AddComponentMenu("")] - public class PlayMusic : Command - { - [Tooltip("Music sound clip to play")] - public AudioClip musicClip; - - [Tooltip("Time to begin playing in seconds. If the audio file is compressed, the time index may be inaccurate.")] - public float atTime; - - [Tooltip("The music will start playing again at end.")] - public bool loop = true; - - [Tooltip("Length of time to fade out previous playing music.")] - public float fadeDuration = 1f; - - public override void OnEnter() - { - MusicController musicController = MusicController.GetInstance(); - if (musicController != null) - { - float startTime = Mathf.Max(0, atTime); - musicController.PlayMusic(musicClip, loop, fadeDuration, startTime); - } - - Continue(); - } - - public override string GetSummary() - { - if (musicClip == null) - { - return "Error: No music clip selected"; - } - - return musicClip.name; - } - - public override Color GetButtonColor() - { - return new Color32(242, 209, 176, 255); - } - } + [CommandInfo("Audio", + "Play Music", + "Plays looping game music. If any game music is already playing, it is stopped. Game music will continue playing across scene loads.")] + [AddComponentMenu("")] + public class PlayMusic : Command + { + [Tooltip("Music sound clip to play")] + public AudioClip musicClip; + + [Tooltip("Time to begin playing in seconds. If the audio file is compressed, the time index may be inaccurate.")] + public float atTime; + + [Tooltip("The music will start playing again at end.")] + public bool loop = true; + + [Tooltip("Length of time to fade out previous playing music.")] + public float fadeDuration = 1f; + + public override void OnEnter() + { + MusicController musicController = MusicController.GetInstance(); + if (musicController != null) + { + float startTime = Mathf.Max(0, atTime); + musicController.PlayMusic(musicClip, loop, fadeDuration, startTime); + } + + Continue(); + } + + public override string GetSummary() + { + if (musicClip == null) + { + return "Error: No music clip selected"; + } + + return musicClip.name; + } + + public override Color GetButtonColor() + { + return new Color32(242, 209, 176, 255); + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Audio/Scripts/Commands/PlaySound.cs b/Assets/Fungus/Audio/Scripts/Commands/PlaySound.cs index 802f6933..2d4b1839 100644 --- a/Assets/Fungus/Audio/Scripts/Commands/PlaySound.cs +++ b/Assets/Fungus/Audio/Scripts/Commands/PlaySound.cs @@ -8,65 +8,65 @@ using System.Collections; namespace Fungus { - [CommandInfo("Audio", - "Play Sound", - "Plays a once-off sound effect. Multiple sound effects can be played at the same time.")] - [AddComponentMenu("")] - public class PlaySound : Command - { - [Tooltip("Sound effect clip to play")] - public AudioClip soundClip; + [CommandInfo("Audio", + "Play Sound", + "Plays a once-off sound effect. Multiple sound effects can be played at the same time.")] + [AddComponentMenu("")] + public class PlaySound : Command + { + [Tooltip("Sound effect clip to play")] + public AudioClip soundClip; - [Range(0,1)] - [Tooltip("Volume level of the sound effect")] - public float volume = 1; + [Range(0,1)] + [Tooltip("Volume level of the sound effect")] + public float volume = 1; - [Tooltip("Wait until the sound has finished playing before continuing execution.")] - public bool waitUntilFinished; + [Tooltip("Wait until the sound has finished playing before continuing execution.")] + public bool waitUntilFinished; - public override void OnEnter() - { - if (soundClip == null) - { - Continue(); - return; - } + public override void OnEnter() + { + if (soundClip == null) + { + Continue(); + return; + } - MusicController musicController = MusicController.GetInstance(); - if (musicController != null) - { - musicController.PlaySound(soundClip, volume); - } + MusicController musicController = MusicController.GetInstance(); + if (musicController != null) + { + musicController.PlaySound(soundClip, volume); + } - if (waitUntilFinished) - { - Invoke("DoWait", soundClip.length); - } - else - { - Continue(); - } - } + if (waitUntilFinished) + { + Invoke("DoWait", soundClip.length); + } + else + { + Continue(); + } + } - protected virtual void DoWait() - { - Continue(); - } + protected virtual void DoWait() + { + Continue(); + } - public override string GetSummary() - { - if (soundClip == null) - { - return "Error: No sound clip selected"; - } + public override string GetSummary() + { + if (soundClip == null) + { + return "Error: No sound clip selected"; + } - return soundClip.name; - } + return soundClip.name; + } - public override Color GetButtonColor() - { - return new Color32(242, 209, 176, 255); - } - } + public override Color GetButtonColor() + { + return new Color32(242, 209, 176, 255); + } + } } diff --git a/Assets/Fungus/Audio/Scripts/Commands/PlayUsfxrSound.cs b/Assets/Fungus/Audio/Scripts/Commands/PlayUsfxrSound.cs index 4a5b7cfd..be6259ec 100644 --- a/Assets/Fungus/Audio/Scripts/Commands/PlayUsfxrSound.cs +++ b/Assets/Fungus/Audio/Scripts/Commands/PlayUsfxrSound.cs @@ -6,25 +6,25 @@ namespace Fungus { using System; using UnityEngine; - using UnityEngine.Serialization; + using UnityEngine.Serialization; [CommandInfo("Audio", - "Play Usfxr Sound", - "Plays a usfxr synth sound. Use the usfxr editor [Tools > Fungus > Utilities > Generate usfxr Sound Effects] to create the SettingsString. Set a ParentTransform if using positional sound. See https://github.com/zeh/usfxr for more information about usfxr.")] - [AddComponentMenu("")] - [ExecuteInEditMode] - public class PlayUsfxrSound : Command - { + "Play Usfxr Sound", + "Plays a usfxr synth sound. Use the usfxr editor [Tools > Fungus > Utilities > Generate usfxr Sound Effects] to create the SettingsString. Set a ParentTransform if using positional sound. See https://github.com/zeh/usfxr for more information about usfxr.")] + [AddComponentMenu("")] + [ExecuteInEditMode] + public class PlayUsfxrSound : Command + { protected SfxrSynth _synth = new SfxrSynth(); - [Tooltip("Transform to use for positional audio")] + [Tooltip("Transform to use for positional audio")] public Transform ParentTransform = null; - [Tooltip("Settings string which describes the audio")] - public StringDataMulti _SettingsString = new StringDataMulti(""); + [Tooltip("Settings string which describes the audio")] + public StringDataMulti _SettingsString = new StringDataMulti(""); - [Tooltip("Time to wait before executing the next command")] - public float waitDuration = 0; + [Tooltip("Time to wait before executing the next command")] + public float waitDuration = 0; //Call this if the settings have changed protected void UpdateCache() { @@ -42,20 +42,20 @@ public override void OnEnter() { _synth.SetParentTransform(ParentTransform); _synth.Play(); - if (waitDuration == 0f) - { - Continue(); - } - else - { - Invoke ("DoWait", waitDuration); - } + if (waitDuration == 0f) + { + Continue(); + } + else + { + Invoke ("DoWait", waitDuration); + } } - protected void DoWait() - { - Continue(); - } + protected void DoWait() + { + Continue(); + } public override string GetSummary() { if (String.IsNullOrEmpty(_SettingsString.Value)) { @@ -71,19 +71,19 @@ return new Color32(128, 200, 200, 255); } - #region Backwards compatibility + #region Backwards compatibility - [HideInInspector] [FormerlySerializedAs("SettingsString")] public String SettingsStringOLD = ""; + [HideInInspector] [FormerlySerializedAs("SettingsString")] public String SettingsStringOLD = ""; - protected virtual void OnEnable() - { - if (SettingsStringOLD != "") - { - _SettingsString.Value = SettingsStringOLD; - SettingsStringOLD = ""; - } - } + protected virtual void OnEnable() + { + if (SettingsStringOLD != "") + { + _SettingsString.Value = SettingsStringOLD; + SettingsStringOLD = ""; + } + } - #endregion + #endregion } } diff --git a/Assets/Fungus/Audio/Scripts/Commands/SetAudioPitch.cs b/Assets/Fungus/Audio/Scripts/Commands/SetAudioPitch.cs index 720b5ae7..c565ea7c 100644 --- a/Assets/Fungus/Audio/Scripts/Commands/SetAudioPitch.cs +++ b/Assets/Fungus/Audio/Scripts/Commands/SetAudioPitch.cs @@ -8,53 +8,53 @@ using System.Collections; namespace Fungus { - [CommandInfo("Audio", - "Set Audio Pitch", - "Sets the global pitch level for audio played with Play Music and Play Sound commands.")] - [AddComponentMenu("")] - public class SetAudioPitch : Command - { - [Range(0,1)] - [Tooltip("Global pitch level for audio played using the Play Music and Play Sound commands")] - public float pitch = 1; - - [Range(0,30)] - [Tooltip("Time to fade between current pitch level and target pitch level.")] - public float fadeDuration; - - [Tooltip("Wait until the pitch change has finished before executing next command")] - public bool waitUntilFinished = true; - - public override void OnEnter() - { - System.Action onComplete = () => { - if (waitUntilFinished) - { - Continue(); - } - }; - - MusicController musicController = MusicController.GetInstance(); - if (musicController != null) - { - musicController.SetAudioPitch(pitch, fadeDuration, onComplete); - } - - if (!waitUntilFinished) - { - Continue(); - } - } - - public override string GetSummary() - { - return "Set to " + pitch + " over " + fadeDuration + " seconds."; - } - - public override Color GetButtonColor() - { - return new Color32(242, 209, 176, 255); - } - } + [CommandInfo("Audio", + "Set Audio Pitch", + "Sets the global pitch level for audio played with Play Music and Play Sound commands.")] + [AddComponentMenu("")] + public class SetAudioPitch : Command + { + [Range(0,1)] + [Tooltip("Global pitch level for audio played using the Play Music and Play Sound commands")] + public float pitch = 1; + + [Range(0,30)] + [Tooltip("Time to fade between current pitch level and target pitch level.")] + public float fadeDuration; + + [Tooltip("Wait until the pitch change has finished before executing next command")] + public bool waitUntilFinished = true; + + public override void OnEnter() + { + System.Action onComplete = () => { + if (waitUntilFinished) + { + Continue(); + } + }; + + MusicController musicController = MusicController.GetInstance(); + if (musicController != null) + { + musicController.SetAudioPitch(pitch, fadeDuration, onComplete); + } + + if (!waitUntilFinished) + { + Continue(); + } + } + + public override string GetSummary() + { + return "Set to " + pitch + " over " + fadeDuration + " seconds."; + } + + public override Color GetButtonColor() + { + return new Color32(242, 209, 176, 255); + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Audio/Scripts/Commands/SetAudioVolume.cs b/Assets/Fungus/Audio/Scripts/Commands/SetAudioVolume.cs index e233fafa..ab90d8c5 100644 --- a/Assets/Fungus/Audio/Scripts/Commands/SetAudioVolume.cs +++ b/Assets/Fungus/Audio/Scripts/Commands/SetAudioVolume.cs @@ -8,51 +8,51 @@ using System.Collections; namespace Fungus { - [CommandInfo("Audio", - "Set Audio Volume", - "Sets the global volume level for audio played with Play Music and Play Sound commands.")] - [AddComponentMenu("")] - public class SetAudioVolume : Command - { - [Range(0,1)] - [Tooltip("Global volume level for audio played using Play Music and Play Sound")] - public float volume = 1f; - - [Range(0,30)] - [Tooltip("Time to fade between current volume level and target volume level.")] - public float fadeDuration = 1f; + [CommandInfo("Audio", + "Set Audio Volume", + "Sets the global volume level for audio played with Play Music and Play Sound commands.")] + [AddComponentMenu("")] + public class SetAudioVolume : Command + { + [Range(0,1)] + [Tooltip("Global volume level for audio played using Play Music and Play Sound")] + public float volume = 1f; + + [Range(0,30)] + [Tooltip("Time to fade between current volume level and target volume level.")] + public float fadeDuration = 1f; [Tooltip("Wait until the volume fade has completed before continuing.")] public bool waitUntilFinished = true; - public override void OnEnter() - { - MusicController musicController = MusicController.GetInstance(); - if (musicController != null) - { + public override void OnEnter() + { + MusicController musicController = MusicController.GetInstance(); + if (musicController != null) + { musicController.SetAudioVolume(volume, fadeDuration, () => { if (waitUntilFinished) { Continue(); } }); - } + } if (!waitUntilFinished) { - Continue(); + Continue(); } - } - - public override string GetSummary() - { - return "Set to " + volume + " over " + fadeDuration + " seconds."; - } - - public override Color GetButtonColor() - { - return new Color32(242, 209, 176, 255); - } - } + } + + public override string GetSummary() + { + return "Set to " + volume + " over " + fadeDuration + " seconds."; + } + + public override Color GetButtonColor() + { + return new Color32(242, 209, 176, 255); + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Audio/Scripts/Commands/StopMusic.cs b/Assets/Fungus/Audio/Scripts/Commands/StopMusic.cs index ffc8c5da..c1beb15f 100644 --- a/Assets/Fungus/Audio/Scripts/Commands/StopMusic.cs +++ b/Assets/Fungus/Audio/Scripts/Commands/StopMusic.cs @@ -8,27 +8,27 @@ using System.Collections; namespace Fungus { - [CommandInfo("Audio", - "Stop Music", - "Stops the currently playing game music.")] - [AddComponentMenu("")] - public class StopMusic : Command - { - public override void OnEnter() - { - MusicController musicController = MusicController.GetInstance(); - if (musicController != null) - { - musicController.StopMusic(); - } + [CommandInfo("Audio", + "Stop Music", + "Stops the currently playing game music.")] + [AddComponentMenu("")] + public class StopMusic : Command + { + public override void OnEnter() + { + MusicController musicController = MusicController.GetInstance(); + if (musicController != null) + { + musicController.StopMusic(); + } - Continue(); - } + Continue(); + } - public override Color GetButtonColor() - { - return new Color32(242, 209, 176, 255); - } - } + public override Color GetButtonColor() + { + return new Color32(242, 209, 176, 255); + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Audio/Scripts/MusicController.cs b/Assets/Fungus/Audio/Scripts/MusicController.cs index 91897b27..628842d3 100644 --- a/Assets/Fungus/Audio/Scripts/MusicController.cs +++ b/Assets/Fungus/Audio/Scripts/MusicController.cs @@ -8,163 +8,163 @@ using System.Collections; namespace Fungus { - /** - * Singleton music manager component. - * Provides basic music and sound effect functionality. - * Music playback persists across scene loads. - */ - [RequireComponent(typeof(AudioSource))] - public class MusicController : MonoBehaviour - { - static MusicController instance; - - /** - * Returns the MusicController singleton instance. - * Will create a MusicController game object if none currently exists. - */ - static public MusicController GetInstance() - { - if (instance == null) - { - GameObject go = new GameObject("MusicController"); - DontDestroyOnLoad(go); - instance = go.AddComponent(); - } - - return instance; - } - - protected virtual void Start() - { - GetComponent().playOnAwake = false; - GetComponent().loop = true; - } - - /** - * Plays game music using an audio clip. - * One music clip may be played at a time. - * @param musicClip The music clip to play - * @param atTime Time in the music clip to start at - */ - public void PlayMusic(AudioClip musicClip, bool loop, float fadeDuration, float atTime) - { - AudioSource audioSource = GetComponent(); - if (audioSource == null || audioSource.clip == musicClip) - { - return; - } - - if (fadeDuration == 0f) - { - audioSource.clip = musicClip; - audioSource.loop = loop; - audioSource.time = atTime; // May be inaccurate if the audio source is compressed http://docs.unity3d.com/ScriptReference/AudioSource-time.html BK - audioSource.Play(); - } - else - { - float startVolume = audioSource.volume; - - LeanTween.value(gameObject, startVolume, 0f, fadeDuration) - .setOnUpdate( (v) => { - // Fade out current music - audioSource.volume = v; - }).setOnComplete( () => { - // Play new music - audioSource.volume = startVolume; - audioSource.clip = musicClip; - audioSource.loop = loop; - audioSource.time = atTime; // May be inaccurate if the audio source is compressed http://docs.unity3d.com/ScriptReference/AudioSource-time.html BK - audioSource.Play(); - }); - } - } - - /** - * Stops playing game music. - */ - public virtual void StopMusic() - { - GetComponent().Stop(); - } - - /** - * Fades the game music volume to required level over a period of time. - * @param volume The new music volume value [0..1] + /** + * Singleton music manager component. + * Provides basic music and sound effect functionality. + * Music playback persists across scene loads. + */ + [RequireComponent(typeof(AudioSource))] + public class MusicController : MonoBehaviour + { + static MusicController instance; + + /** + * Returns the MusicController singleton instance. + * Will create a MusicController game object if none currently exists. + */ + static public MusicController GetInstance() + { + if (instance == null) + { + GameObject go = new GameObject("MusicController"); + DontDestroyOnLoad(go); + instance = go.AddComponent(); + } + + return instance; + } + + protected virtual void Start() + { + GetComponent().playOnAwake = false; + GetComponent().loop = true; + } + + /** + * Plays game music using an audio clip. + * One music clip may be played at a time. + * @param musicClip The music clip to play + * @param atTime Time in the music clip to start at + */ + public void PlayMusic(AudioClip musicClip, bool loop, float fadeDuration, float atTime) + { + AudioSource audioSource = GetComponent(); + if (audioSource == null || audioSource.clip == musicClip) + { + return; + } + + if (fadeDuration == 0f) + { + audioSource.clip = musicClip; + audioSource.loop = loop; + audioSource.time = atTime; // May be inaccurate if the audio source is compressed http://docs.unity3d.com/ScriptReference/AudioSource-time.html BK + audioSource.Play(); + } + else + { + float startVolume = audioSource.volume; + + LeanTween.value(gameObject, startVolume, 0f, fadeDuration) + .setOnUpdate( (v) => { + // Fade out current music + audioSource.volume = v; + }).setOnComplete( () => { + // Play new music + audioSource.volume = startVolume; + audioSource.clip = musicClip; + audioSource.loop = loop; + audioSource.time = atTime; // May be inaccurate if the audio source is compressed http://docs.unity3d.com/ScriptReference/AudioSource-time.html BK + audioSource.Play(); + }); + } + } + + /** + * Stops playing game music. + */ + public virtual void StopMusic() + { + GetComponent().Stop(); + } + + /** + * Fades the game music volume to required level over a period of time. + * @param volume The new music volume value [0..1] * @param duration The length of time in seconds needed to complete the volume change. * @param onComplete Delegate function to call when fade completes. - */ - public virtual void SetAudioVolume(float volume, float duration, System.Action onComplete) - { - AudioSource audio = GetComponent(); + */ + public virtual void SetAudioVolume(float volume, float duration, System.Action onComplete) + { + AudioSource audio = GetComponent(); if (Mathf.Approximately(duration, 0f)) - { - audio.volume = volume; - return; - } - - LeanTween.value(gameObject, - audio.volume, - volume, - duration).setOnUpdate( (v) => { - audio.volume = v; + { + audio.volume = volume; + return; + } + + LeanTween.value(gameObject, + audio.volume, + volume, + duration).setOnUpdate( (v) => { + audio.volume = v; }).setOnComplete( () => { if (onComplete != null) { onComplete(); } }); - } - - /** - * Shifts the game music pitch to required value over a period of time. - * @param volume The new music pitch value - * @param duration The length of time in seconds needed to complete the pitch change. - * @param onComplete A delegate method to call when the pitch shift has completed. - */ - public virtual void SetAudioPitch(float pitch, float duration, System.Action onComplete) - { - AudioSource audio = GetComponent(); - - if (duration == 0f) - { - audio.pitch = pitch; - if (onComplete != null) - { - onComplete(); - } - return; - } - - LeanTween.value(gameObject, - audio.pitch, - pitch, - duration).setOnUpdate( (p) => { - audio.pitch = p; - }).setOnComplete( () => { - if (onComplete != null) - { - onComplete(); - } - }); - } - - /** - * Plays a sound effect once, at the specified volume. - * Multiple sound effects can be played at the same time. - * @param soundClip The sound effect clip to play - * @param volume The volume level of the sound effect - */ - public virtual void PlaySound(AudioClip soundClip, float volume) - { - GetComponent().PlayOneShot(soundClip, volume); - } - - public virtual void PlaySoundAtTime(AudioClip soundClip, float volume, float atTime) - { - GetComponent().time = atTime; // This may not work BK - GetComponent().PlayOneShot(soundClip, volume); - } - } + } + + /** + * Shifts the game music pitch to required value over a period of time. + * @param volume The new music pitch value + * @param duration The length of time in seconds needed to complete the pitch change. + * @param onComplete A delegate method to call when the pitch shift has completed. + */ + public virtual void SetAudioPitch(float pitch, float duration, System.Action onComplete) + { + AudioSource audio = GetComponent(); + + if (duration == 0f) + { + audio.pitch = pitch; + if (onComplete != null) + { + onComplete(); + } + return; + } + + LeanTween.value(gameObject, + audio.pitch, + pitch, + duration).setOnUpdate( (p) => { + audio.pitch = p; + }).setOnComplete( () => { + if (onComplete != null) + { + onComplete(); + } + }); + } + + /** + * Plays a sound effect once, at the specified volume. + * Multiple sound effects can be played at the same time. + * @param soundClip The sound effect clip to play + * @param volume The volume level of the sound effect + */ + public virtual void PlaySound(AudioClip soundClip, float volume) + { + GetComponent().PlayOneShot(soundClip, volume); + } + + public virtual void PlaySoundAtTime(AudioClip soundClip, float volume, float atTime) + { + GetComponent().time = atTime; // This may not work BK + GetComponent().PlayOneShot(soundClip, volume); + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Camera/Editor/CameraMenuItems.cs b/Assets/Fungus/Camera/Editor/CameraMenuItems.cs index 6aa06c47..d2632259 100644 --- a/Assets/Fungus/Camera/Editor/CameraMenuItems.cs +++ b/Assets/Fungus/Camera/Editor/CameraMenuItems.cs @@ -10,13 +10,13 @@ using System.Collections; namespace Fungus { - public class CameraMenuItems - { - [MenuItem("Tools/Fungus/Create/View", false, 100)] - static void CreateView() - { - FlowchartMenuItems.SpawnPrefab("View"); - } - } + public class CameraMenuItems + { + [MenuItem("Tools/Fungus/Create/View", false, 100)] + static void CreateView() + { + FlowchartMenuItems.SpawnPrefab("View"); + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Camera/Editor/ViewEditor.cs b/Assets/Fungus/Camera/Editor/ViewEditor.cs index 47ba66b7..6a5c8e4e 100644 --- a/Assets/Fungus/Camera/Editor/ViewEditor.cs +++ b/Assets/Fungus/Camera/Editor/ViewEditor.cs @@ -11,257 +11,257 @@ using System.Collections.Generic; namespace Fungus { - [CanEditMultipleObjects] - [CustomEditor (typeof(View))] - public class ViewEditor : Editor - { - static Color viewColor = Color.yellow; + [CanEditMultipleObjects] + [CustomEditor (typeof(View))] + public class ViewEditor : Editor + { + static Color viewColor = Color.yellow; - protected SerializedProperty primaryAspectRatioProp; - protected SerializedProperty secondaryAspectRatioProp; - protected SerializedProperty viewSizeProp; + protected SerializedProperty primaryAspectRatioProp; + protected SerializedProperty secondaryAspectRatioProp; + protected SerializedProperty viewSizeProp; - // Draw Views when they're not selected + // Draw Views when they're not selected #if UNITY_5_0 - [DrawGizmo(GizmoType.NotSelected | GizmoType.SelectedOrChild)] + [DrawGizmo(GizmoType.NotSelected | GizmoType.SelectedOrChild)] #else - [DrawGizmo(GizmoType.NotInSelectionHierarchy | GizmoType.InSelectionHierarchy)] + [DrawGizmo(GizmoType.NotInSelectionHierarchy | GizmoType.InSelectionHierarchy)] #endif - static void RenderCustomGizmo(Transform objectTransform, GizmoType gizmoType) - { - View view = objectTransform.gameObject.GetComponent(); - if (view != null) - { - DrawView(view, false); - } - } - - protected virtual Vector2 LookupAspectRatio(int index) - { - switch (index) - { - default: - case 1: - return new Vector2(4, 3); - case 2: - return new Vector2(3, 2); - case 3: - return new Vector2(16, 10); - case 4: - return new Vector2(17, 10); - case 5: - return new Vector2(16, 9); - case 6: - return new Vector2(2, 1); - case 7: - return new Vector2(3, 4); - case 8: - return new Vector2(2, 3); - case 9: - return new Vector2(10, 16); - case 10: - return new Vector2(10, 17); - case 11: - return new Vector2(9, 16); - case 12: - return new Vector2(1, 2); - } - } - - protected virtual void OnEnable() - { - primaryAspectRatioProp = serializedObject.FindProperty ("primaryAspectRatio"); - secondaryAspectRatioProp = serializedObject.FindProperty ("secondaryAspectRatio"); - viewSizeProp = serializedObject.FindProperty("viewSize"); - } - - public override void OnInspectorGUI() - { - serializedObject.Update(); - - EditorGUI.BeginChangeCheck(); - - EditorGUILayout.PropertyField(viewSizeProp); - - string[] ratios = { "", "Landscape / 4:3", "Landscape / 3:2", "Landscape / 16:10", "Landscape / 17:10", "Landscape / 16:9", "Landscape / 2:1", "Portrait / 3:4", "Portrait / 2:3", "Portrait / 10:16", "Portrait / 10:17", "Portrait / 9:16", "Portrait / 1:2" }; - - EditorGUILayout.PropertyField(primaryAspectRatioProp, new GUIContent("Primary Aspect Ratio", "Width and height values that define the primary aspect ratio (e.g. 4:3)")); - int primaryIndex = EditorGUILayout.Popup("Select Aspect Ratio", 0, ratios); - if (primaryIndex > 0) - { - primaryAspectRatioProp.vector2Value = LookupAspectRatio(primaryIndex); - } - EditorGUILayout.Separator(); - - EditorGUILayout.PropertyField(secondaryAspectRatioProp, new GUIContent("Secondary Aspect Ratio", "Width and height values that define the primary aspect ratio (e.g. 4:3)")); - int secondaryIndex = EditorGUILayout.Popup("Select Aspect Ratio", 0, ratios); - if (secondaryIndex > 0) - { - secondaryAspectRatioProp.vector2Value = LookupAspectRatio(secondaryIndex); - } - EditorGUILayout.Separator(); - - if (EditorGUI.EndChangeCheck()) - { - // Avoid divide by zero errors - if (primaryAspectRatioProp.vector2Value.y == 0) - { - primaryAspectRatioProp.vector2Value = new Vector2(primaryAspectRatioProp.vector2Value.x, 1f); - } - if (secondaryAspectRatioProp.vector2Value.y == 0) - { - secondaryAspectRatioProp.vector2Value = new Vector2(secondaryAspectRatioProp.vector2Value.x, 1f); - } - - SceneView.RepaintAll(); - } - - serializedObject.ApplyModifiedProperties(); - } - - protected virtual void OnSceneGUI () - { - View t = target as View; - if (t.enabled) - { - EditViewBounds(); - } - } - - protected virtual void EditViewBounds() - { - View view = target as View; - - DrawView(view, true); - - Vector3 pos = view.transform.position; - - float viewSize = CalculateLocalViewSize(view); - - Vector3[] handles = new Vector3[2]; - handles[0] = view.transform.TransformPoint(new Vector3(0, -viewSize, 0)); - handles[1] = view.transform.TransformPoint(new Vector3(0, viewSize, 0)); - - Handles.color = Color.white; - - for (int i = 0; i < 2; ++i) - { - Vector3 newPos = Handles.FreeMoveHandle(handles[i], - Quaternion.identity, - HandleUtility.GetHandleSize(pos) * 0.1f, - Vector3.zero, - Handles.CubeCap); - if (newPos != handles[i]) - { - Undo.RecordObject(view, "Set View Size"); - view.viewSize = (newPos - pos).magnitude; - EditorUtility.SetDirty(view); - break; - } - } - } - - public static void DrawView(View view, bool drawInterior) - { - float height = CalculateLocalViewSize(view); - float widthA = height * (view.primaryAspectRatio.x / view.primaryAspectRatio.y); - float widthB = height * (view.secondaryAspectRatio.x / view.secondaryAspectRatio.y); - - Color transparent = new Color(1,1,1,0f); - Color fill = viewColor; - Color outline = viewColor; - - bool highlight = Selection.activeGameObject == view.gameObject; - - Flowchart flowchart = FlowchartWindow.GetFlowchart(); - if (flowchart != null) - { - foreach (Command command in flowchart.selectedCommands) - { - MoveToView moveToViewCommand = command as MoveToView; - if (moveToViewCommand != null && - moveToViewCommand.targetView == view) - { - highlight = true; - } - else - { - FadeToView fadeToViewCommand = command as FadeToView; - if (fadeToViewCommand != null && - fadeToViewCommand.targetView == view) - { - highlight = true; - } - } - } - } - - if (highlight) - { - fill = outline = Color.green; - fill.a = 0.1f; - outline.a = 1f; - } - else - { - fill.a = 0.1f; - outline.a = 0.5f; - } - - if (drawInterior) - { - // Draw left box - { - Vector3[] verts = new Vector3[4]; - verts[0] = view.transform.TransformPoint(new Vector3(-widthB, -height, 0)); - verts[1] = view.transform.TransformPoint(new Vector3(-widthB, height, 0)); - verts[2] = view.transform.TransformPoint(new Vector3(-widthA, height, 0)); - verts[3] = view.transform.TransformPoint(new Vector3(-widthA, -height, 0)); - - Handles.DrawSolidRectangleWithOutline(verts, fill, transparent); - } - - // Draw right box - { - Vector3[] verts = new Vector3[4]; - verts[0] = view.transform.TransformPoint(new Vector3(widthA, -height, 0)); - verts[1] = view.transform.TransformPoint(new Vector3(widthA, height, 0)); - verts[2] = view.transform.TransformPoint(new Vector3(widthB, height, 0)); - verts[3] = view.transform.TransformPoint(new Vector3(widthB, -height, 0)); - - Handles.DrawSolidRectangleWithOutline(verts, fill, transparent); - } - - // Draw inner box - { - Vector3[] verts = new Vector3[4]; - verts[0] = view.transform.TransformPoint(new Vector3(-widthA, -height, 0)); - verts[1] = view.transform.TransformPoint(new Vector3(-widthA, height, 0)); - verts[2] = view.transform.TransformPoint(new Vector3(widthA, height, 0)); - verts[3] = view.transform.TransformPoint(new Vector3(widthA, -height, 0)); - - Handles.DrawSolidRectangleWithOutline(verts, transparent, outline ); - } - } - - // Draw outer box - { - Vector3[] verts = new Vector3[4]; - verts[0] = view.transform.TransformPoint(new Vector3(-widthB, -height, 0)); - verts[1] = view.transform.TransformPoint(new Vector3(-widthB, height, 0)); - verts[2] = view.transform.TransformPoint(new Vector3(widthB, height, 0)); - verts[3] = view.transform.TransformPoint(new Vector3(widthB, -height, 0)); - - Handles.DrawSolidRectangleWithOutline(verts, transparent, outline ); - } - } - - // Calculate view size in local coordinates - // Kinda expensive, but accurate and only called in editor. - static float CalculateLocalViewSize(View view) - { - return view.transform.InverseTransformPoint(view.transform.position + new Vector3(0, view.viewSize, 0)).magnitude; - } - } + static void RenderCustomGizmo(Transform objectTransform, GizmoType gizmoType) + { + View view = objectTransform.gameObject.GetComponent(); + if (view != null) + { + DrawView(view, false); + } + } + + protected virtual Vector2 LookupAspectRatio(int index) + { + switch (index) + { + default: + case 1: + return new Vector2(4, 3); + case 2: + return new Vector2(3, 2); + case 3: + return new Vector2(16, 10); + case 4: + return new Vector2(17, 10); + case 5: + return new Vector2(16, 9); + case 6: + return new Vector2(2, 1); + case 7: + return new Vector2(3, 4); + case 8: + return new Vector2(2, 3); + case 9: + return new Vector2(10, 16); + case 10: + return new Vector2(10, 17); + case 11: + return new Vector2(9, 16); + case 12: + return new Vector2(1, 2); + } + } + + protected virtual void OnEnable() + { + primaryAspectRatioProp = serializedObject.FindProperty ("primaryAspectRatio"); + secondaryAspectRatioProp = serializedObject.FindProperty ("secondaryAspectRatio"); + viewSizeProp = serializedObject.FindProperty("viewSize"); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + EditorGUI.BeginChangeCheck(); + + EditorGUILayout.PropertyField(viewSizeProp); + + string[] ratios = { "", "Landscape / 4:3", "Landscape / 3:2", "Landscape / 16:10", "Landscape / 17:10", "Landscape / 16:9", "Landscape / 2:1", "Portrait / 3:4", "Portrait / 2:3", "Portrait / 10:16", "Portrait / 10:17", "Portrait / 9:16", "Portrait / 1:2" }; + + EditorGUILayout.PropertyField(primaryAspectRatioProp, new GUIContent("Primary Aspect Ratio", "Width and height values that define the primary aspect ratio (e.g. 4:3)")); + int primaryIndex = EditorGUILayout.Popup("Select Aspect Ratio", 0, ratios); + if (primaryIndex > 0) + { + primaryAspectRatioProp.vector2Value = LookupAspectRatio(primaryIndex); + } + EditorGUILayout.Separator(); + + EditorGUILayout.PropertyField(secondaryAspectRatioProp, new GUIContent("Secondary Aspect Ratio", "Width and height values that define the primary aspect ratio (e.g. 4:3)")); + int secondaryIndex = EditorGUILayout.Popup("Select Aspect Ratio", 0, ratios); + if (secondaryIndex > 0) + { + secondaryAspectRatioProp.vector2Value = LookupAspectRatio(secondaryIndex); + } + EditorGUILayout.Separator(); + + if (EditorGUI.EndChangeCheck()) + { + // Avoid divide by zero errors + if (primaryAspectRatioProp.vector2Value.y == 0) + { + primaryAspectRatioProp.vector2Value = new Vector2(primaryAspectRatioProp.vector2Value.x, 1f); + } + if (secondaryAspectRatioProp.vector2Value.y == 0) + { + secondaryAspectRatioProp.vector2Value = new Vector2(secondaryAspectRatioProp.vector2Value.x, 1f); + } + + SceneView.RepaintAll(); + } + + serializedObject.ApplyModifiedProperties(); + } + + protected virtual void OnSceneGUI () + { + View t = target as View; + if (t.enabled) + { + EditViewBounds(); + } + } + + protected virtual void EditViewBounds() + { + View view = target as View; + + DrawView(view, true); + + Vector3 pos = view.transform.position; + + float viewSize = CalculateLocalViewSize(view); + + Vector3[] handles = new Vector3[2]; + handles[0] = view.transform.TransformPoint(new Vector3(0, -viewSize, 0)); + handles[1] = view.transform.TransformPoint(new Vector3(0, viewSize, 0)); + + Handles.color = Color.white; + + for (int i = 0; i < 2; ++i) + { + Vector3 newPos = Handles.FreeMoveHandle(handles[i], + Quaternion.identity, + HandleUtility.GetHandleSize(pos) * 0.1f, + Vector3.zero, + Handles.CubeCap); + if (newPos != handles[i]) + { + Undo.RecordObject(view, "Set View Size"); + view.viewSize = (newPos - pos).magnitude; + EditorUtility.SetDirty(view); + break; + } + } + } + + public static void DrawView(View view, bool drawInterior) + { + float height = CalculateLocalViewSize(view); + float widthA = height * (view.primaryAspectRatio.x / view.primaryAspectRatio.y); + float widthB = height * (view.secondaryAspectRatio.x / view.secondaryAspectRatio.y); + + Color transparent = new Color(1,1,1,0f); + Color fill = viewColor; + Color outline = viewColor; + + bool highlight = Selection.activeGameObject == view.gameObject; + + Flowchart flowchart = FlowchartWindow.GetFlowchart(); + if (flowchart != null) + { + foreach (Command command in flowchart.selectedCommands) + { + MoveToView moveToViewCommand = command as MoveToView; + if (moveToViewCommand != null && + moveToViewCommand.targetView == view) + { + highlight = true; + } + else + { + FadeToView fadeToViewCommand = command as FadeToView; + if (fadeToViewCommand != null && + fadeToViewCommand.targetView == view) + { + highlight = true; + } + } + } + } + + if (highlight) + { + fill = outline = Color.green; + fill.a = 0.1f; + outline.a = 1f; + } + else + { + fill.a = 0.1f; + outline.a = 0.5f; + } + + if (drawInterior) + { + // Draw left box + { + Vector3[] verts = new Vector3[4]; + verts[0] = view.transform.TransformPoint(new Vector3(-widthB, -height, 0)); + verts[1] = view.transform.TransformPoint(new Vector3(-widthB, height, 0)); + verts[2] = view.transform.TransformPoint(new Vector3(-widthA, height, 0)); + verts[3] = view.transform.TransformPoint(new Vector3(-widthA, -height, 0)); + + Handles.DrawSolidRectangleWithOutline(verts, fill, transparent); + } + + // Draw right box + { + Vector3[] verts = new Vector3[4]; + verts[0] = view.transform.TransformPoint(new Vector3(widthA, -height, 0)); + verts[1] = view.transform.TransformPoint(new Vector3(widthA, height, 0)); + verts[2] = view.transform.TransformPoint(new Vector3(widthB, height, 0)); + verts[3] = view.transform.TransformPoint(new Vector3(widthB, -height, 0)); + + Handles.DrawSolidRectangleWithOutline(verts, fill, transparent); + } + + // Draw inner box + { + Vector3[] verts = new Vector3[4]; + verts[0] = view.transform.TransformPoint(new Vector3(-widthA, -height, 0)); + verts[1] = view.transform.TransformPoint(new Vector3(-widthA, height, 0)); + verts[2] = view.transform.TransformPoint(new Vector3(widthA, height, 0)); + verts[3] = view.transform.TransformPoint(new Vector3(widthA, -height, 0)); + + Handles.DrawSolidRectangleWithOutline(verts, transparent, outline ); + } + } + + // Draw outer box + { + Vector3[] verts = new Vector3[4]; + verts[0] = view.transform.TransformPoint(new Vector3(-widthB, -height, 0)); + verts[1] = view.transform.TransformPoint(new Vector3(-widthB, height, 0)); + verts[2] = view.transform.TransformPoint(new Vector3(widthB, height, 0)); + verts[3] = view.transform.TransformPoint(new Vector3(widthB, -height, 0)); + + Handles.DrawSolidRectangleWithOutline(verts, transparent, outline ); + } + } + + // Calculate view size in local coordinates + // Kinda expensive, but accurate and only called in editor. + static float CalculateLocalViewSize(View view) + { + return view.transform.InverseTransformPoint(view.transform.position + new Vector3(0, view.viewSize, 0)).magnitude; + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Camera/Scripts/CameraController.cs b/Assets/Fungus/Camera/Scripts/CameraController.cs index aa04e021..661658df 100644 --- a/Assets/Fungus/Camera/Scripts/CameraController.cs +++ b/Assets/Fungus/Camera/Scripts/CameraController.cs @@ -11,597 +11,597 @@ using Fungus; namespace Fungus { - /** - * Controller for main camera. - * Supports several types of camera transition including snap, pan & fade. - */ - public class CameraController : MonoBehaviour - { - /** - * Full screen texture used for screen fade effect. - */ - public Texture2D screenFadeTexture; - - /** - * Icon to display when swipe pan mode is active. - */ - public Texture2D swipePanIcon; - - /** - * Position of continue and swipe icons in normalized screen space coords. - * (0,0) = top left, (1,1) = bottom right - */ - public Vector2 swipeIconPosition = new Vector2(1,0); + /** + * Controller for main camera. + * Supports several types of camera transition including snap, pan & fade. + */ + public class CameraController : MonoBehaviour + { + /** + * Full screen texture used for screen fade effect. + */ + public Texture2D screenFadeTexture; + + /** + * Icon to display when swipe pan mode is active. + */ + public Texture2D swipePanIcon; + + /** + * Position of continue and swipe icons in normalized screen space coords. + * (0,0) = top left, (1,1) = bottom right + */ + public Vector2 swipeIconPosition = new Vector2(1,0); - /** - * Set the camera z coordinate to a fixed value every frame. - */ - public bool setCameraZ = true; + /** + * Set the camera z coordinate to a fixed value every frame. + */ + public bool setCameraZ = true; - /** - * Fixed Z coordinate of main camera. - */ - public float cameraZ = -10f; + /** + * Fixed Z coordinate of main camera. + */ + public float cameraZ = -10f; - [HideInInspector] - public bool waiting; - - protected float fadeAlpha = 0f; - - // Swipe panning control - [HideInInspector] - public bool swipePanActive; - public Camera swipeCamera; + [HideInInspector] + public bool waiting; + + protected float fadeAlpha = 0f; + + // Swipe panning control + [HideInInspector] + public bool swipePanActive; + public Camera swipeCamera; - [HideInInspector] - public float swipeSpeedMultiplier = 1f; - protected View swipePanViewA; - protected View swipePanViewB; - protected Vector3 previousMousePos; - - protected class CameraView - { - public Vector3 cameraPos; - public Quaternion cameraRot; - public float cameraSize; - }; - - protected Dictionary storedViews = new Dictionary(); - - protected static CameraController instance; - - /** - * Returns the CameraController singleton instance. - * Will create a CameraController game object if none currently exists. - */ - static public CameraController GetInstance() - { - if (instance == null) - { - GameObject go = new GameObject("CameraController"); - instance = go.AddComponent(); - } - - return instance; - } - - public static Texture2D CreateColorTexture(Color color, int width, int height) - { - Color[] pixels = new Color[width * height]; - for (int i = 0; i < pixels.Length; i++) - { - pixels[i] = color; - } - Texture2D texture = new Texture2D(width, height, TextureFormat.ARGB32, false); - texture.SetPixels(pixels); - texture.Apply(); - - return texture; - } - - protected virtual void OnGUI() - { - if (swipePanActive) - { - // Draw the swipe panning icon - if (swipePanIcon) - { - float x = Screen.width * swipeIconPosition.x; - float y = Screen.height * swipeIconPosition.y; - float width = swipePanIcon.width; - float height = swipePanIcon.height; - - x = Mathf.Max(x, 0); - y = Mathf.Max(y, 0); - x = Mathf.Min(x, Screen.width - width); - y = Mathf.Min(y, Screen.height - height); - - Rect rect = new Rect(x, y, width, height); - GUI.DrawTexture(rect, swipePanIcon); - } - } - - // Draw full screen fade texture - if (fadeAlpha > 0f && - screenFadeTexture != null) - { - // 1 = scene fully visible - // 0 = scene fully obscured - GUI.color = new Color(1,1,1, fadeAlpha); - GUI.depth = -1000; - GUI.DrawTexture(new Rect(0, 0, Screen.width, Screen.height), screenFadeTexture); - } - } - - /** - * Perform a fullscreen fade over a duration. - */ - public virtual void Fade(float targetAlpha, float fadeDuration, Action fadeAction) - { - StartCoroutine(FadeInternal(targetAlpha, fadeDuration, fadeAction)); - } - - /** - * Fade out, move camera to view and then fade back in. - */ - public virtual void FadeToView(Camera camera, View view, float fadeDuration, bool fadeOut, Action fadeAction) - { - swipePanActive = false; - fadeAlpha = 0f; + [HideInInspector] + public float swipeSpeedMultiplier = 1f; + protected View swipePanViewA; + protected View swipePanViewB; + protected Vector3 previousMousePos; + + protected class CameraView + { + public Vector3 cameraPos; + public Quaternion cameraRot; + public float cameraSize; + }; + + protected Dictionary storedViews = new Dictionary(); + + protected static CameraController instance; + + /** + * Returns the CameraController singleton instance. + * Will create a CameraController game object if none currently exists. + */ + static public CameraController GetInstance() + { + if (instance == null) + { + GameObject go = new GameObject("CameraController"); + instance = go.AddComponent(); + } + + return instance; + } + + public static Texture2D CreateColorTexture(Color color, int width, int height) + { + Color[] pixels = new Color[width * height]; + for (int i = 0; i < pixels.Length; i++) + { + pixels[i] = color; + } + Texture2D texture = new Texture2D(width, height, TextureFormat.ARGB32, false); + texture.SetPixels(pixels); + texture.Apply(); + + return texture; + } + + protected virtual void OnGUI() + { + if (swipePanActive) + { + // Draw the swipe panning icon + if (swipePanIcon) + { + float x = Screen.width * swipeIconPosition.x; + float y = Screen.height * swipeIconPosition.y; + float width = swipePanIcon.width; + float height = swipePanIcon.height; + + x = Mathf.Max(x, 0); + y = Mathf.Max(y, 0); + x = Mathf.Min(x, Screen.width - width); + y = Mathf.Min(y, Screen.height - height); + + Rect rect = new Rect(x, y, width, height); + GUI.DrawTexture(rect, swipePanIcon); + } + } + + // Draw full screen fade texture + if (fadeAlpha > 0f && + screenFadeTexture != null) + { + // 1 = scene fully visible + // 0 = scene fully obscured + GUI.color = new Color(1,1,1, fadeAlpha); + GUI.depth = -1000; + GUI.DrawTexture(new Rect(0, 0, Screen.width, Screen.height), screenFadeTexture); + } + } + + /** + * Perform a fullscreen fade over a duration. + */ + public virtual void Fade(float targetAlpha, float fadeDuration, Action fadeAction) + { + StartCoroutine(FadeInternal(targetAlpha, fadeDuration, fadeAction)); + } + + /** + * Fade out, move camera to view and then fade back in. + */ + public virtual void FadeToView(Camera camera, View view, float fadeDuration, bool fadeOut, Action fadeAction) + { + swipePanActive = false; + fadeAlpha = 0f; - float outDuration; - float inDuration; + float outDuration; + float inDuration; - if (fadeOut) - { - outDuration = fadeDuration / 2f; - inDuration = fadeDuration / 2f; - } - else - { - outDuration = 0; - inDuration = fadeDuration; - } + if (fadeOut) + { + outDuration = fadeDuration / 2f; + inDuration = fadeDuration / 2f; + } + else + { + outDuration = 0; + inDuration = fadeDuration; + } - // Fade out - Fade(1f, outDuration, delegate { - - // Snap to new view - PanToPosition(camera, view.transform.position, view.transform.rotation, view.viewSize, 0f, null); - - // Fade in - Fade(0f, inDuration, delegate { - if (fadeAction != null) - { - fadeAction(); - } - }); - }); - } - - protected virtual IEnumerator FadeInternal(float targetAlpha, float fadeDuration, Action fadeAction) - { - float startAlpha = fadeAlpha; - float timer = 0; - - // If already at the target alpha then complete immediately - if (startAlpha == targetAlpha) - { - yield return null; - } - else - { - while (timer < fadeDuration) - { - float t = timer / fadeDuration; - timer += Time.deltaTime; - - t = Mathf.Clamp01(t); - - fadeAlpha = Mathf.Lerp(startAlpha, targetAlpha, t); - yield return null; - } - } - - fadeAlpha = targetAlpha; - - if (fadeAction != null) - { - fadeAction(); - } - } - - /** - * Positions camera so sprite is centered and fills the screen. - * @param spriteRenderer The sprite to center the camera on - */ - public virtual void CenterOnSprite(Camera camera, SpriteRenderer spriteRenderer) - { - if (camera == null) - { - Debug.LogWarning("Camera is null"); - return; - } + // Fade out + Fade(1f, outDuration, delegate { + + // Snap to new view + PanToPosition(camera, view.transform.position, view.transform.rotation, view.viewSize, 0f, null); + + // Fade in + Fade(0f, inDuration, delegate { + if (fadeAction != null) + { + fadeAction(); + } + }); + }); + } + + protected virtual IEnumerator FadeInternal(float targetAlpha, float fadeDuration, Action fadeAction) + { + float startAlpha = fadeAlpha; + float timer = 0; + + // If already at the target alpha then complete immediately + if (startAlpha == targetAlpha) + { + yield return null; + } + else + { + while (timer < fadeDuration) + { + float t = timer / fadeDuration; + timer += Time.deltaTime; + + t = Mathf.Clamp01(t); + + fadeAlpha = Mathf.Lerp(startAlpha, targetAlpha, t); + yield return null; + } + } + + fadeAlpha = targetAlpha; + + if (fadeAction != null) + { + fadeAction(); + } + } + + /** + * Positions camera so sprite is centered and fills the screen. + * @param spriteRenderer The sprite to center the camera on + */ + public virtual void CenterOnSprite(Camera camera, SpriteRenderer spriteRenderer) + { + if (camera == null) + { + Debug.LogWarning("Camera is null"); + return; + } - if (spriteRenderer == null) - { - Debug.LogWarning("Sprite renderer is null"); - return; - } + if (spriteRenderer == null) + { + Debug.LogWarning("Sprite renderer is null"); + return; + } - swipePanActive = false; - - Sprite sprite = spriteRenderer.sprite; - Vector3 extents = sprite.bounds.extents; - float localScaleY = spriteRenderer.transform.localScale.y; + swipePanActive = false; + + Sprite sprite = spriteRenderer.sprite; + Vector3 extents = sprite.bounds.extents; + float localScaleY = spriteRenderer.transform.localScale.y; - camera.orthographicSize = extents.y * localScaleY; - Vector3 pos = spriteRenderer.transform.position; - camera.transform.position = new Vector3(pos.x, pos.y, 0); - - SetCameraZ(camera); - } - - public virtual void PanToView(Camera camera, View view, float duration, Action arriveAction) - { - PanToPosition(camera, view.transform.position, view.transform.rotation, view.viewSize, duration, arriveAction); - } - - /** - * Moves camera from current position to a target position over a period of time. - */ - public virtual void PanToPosition(Camera camera, Vector3 targetPosition, Quaternion targetRotation, float targetSize, float duration, Action arriveAction) - { - if (camera == null) - { - Debug.LogWarning("Camera is null"); - return; - } + camera.orthographicSize = extents.y * localScaleY; + Vector3 pos = spriteRenderer.transform.position; + camera.transform.position = new Vector3(pos.x, pos.y, 0); + + SetCameraZ(camera); + } + + public virtual void PanToView(Camera camera, View view, float duration, Action arriveAction) + { + PanToPosition(camera, view.transform.position, view.transform.rotation, view.viewSize, duration, arriveAction); + } + + /** + * Moves camera from current position to a target position over a period of time. + */ + public virtual void PanToPosition(Camera camera, Vector3 targetPosition, Quaternion targetRotation, float targetSize, float duration, Action arriveAction) + { + if (camera == null) + { + Debug.LogWarning("Camera is null"); + return; + } - // Stop any pan that is currently active - StopAllCoroutines(); - - swipePanActive = false; - - if (duration == 0f) - { - // Move immediately - camera.orthographicSize = targetSize; - camera.transform.position = targetPosition; - camera.transform.rotation = targetRotation; + // Stop any pan that is currently active + StopAllCoroutines(); + + swipePanActive = false; + + if (duration == 0f) + { + // Move immediately + camera.orthographicSize = targetSize; + camera.transform.position = targetPosition; + camera.transform.rotation = targetRotation; - SetCameraZ(camera); + SetCameraZ(camera); - if (arriveAction != null) - { - arriveAction(); - } - } - else - { - StartCoroutine(PanInternal(camera, targetPosition, targetRotation, targetSize, duration, arriveAction)); - } - } - - /** - * Stores the current camera view using a name. - */ - public virtual void StoreView(Camera camera, string viewName) - { - if (camera != null) - { - Debug.LogWarning("Camera is null"); - return; - } + if (arriveAction != null) + { + arriveAction(); + } + } + else + { + StartCoroutine(PanInternal(camera, targetPosition, targetRotation, targetSize, duration, arriveAction)); + } + } + + /** + * Stores the current camera view using a name. + */ + public virtual void StoreView(Camera camera, string viewName) + { + if (camera != null) + { + Debug.LogWarning("Camera is null"); + return; + } - CameraView currentView = new CameraView(); - currentView.cameraPos = camera.transform.position; - currentView.cameraRot = camera.transform.rotation; - currentView.cameraSize = camera.orthographicSize; - storedViews[viewName] = currentView; - } - - /** - * Moves the camera to a previously stored camera view over a period of time. - */ - public virtual void PanToStoredView(Camera camera, string viewName, float duration, Action arriveAction) - { - if (camera == null) - { - Debug.LogWarning("Camera is null"); - return; - } + CameraView currentView = new CameraView(); + currentView.cameraPos = camera.transform.position; + currentView.cameraRot = camera.transform.rotation; + currentView.cameraSize = camera.orthographicSize; + storedViews[viewName] = currentView; + } + + /** + * Moves the camera to a previously stored camera view over a period of time. + */ + public virtual void PanToStoredView(Camera camera, string viewName, float duration, Action arriveAction) + { + if (camera == null) + { + Debug.LogWarning("Camera is null"); + return; + } - if (!storedViews.ContainsKey(viewName)) - { - // View has not previously been stored - if (arriveAction != null) - { - arriveAction(); - } - return; - } - - CameraView cameraView = storedViews[viewName]; - - if (duration == 0f) - { - // Move immediately - camera.transform.position = cameraView.cameraPos; - camera.transform.rotation = cameraView.cameraRot; - camera.orthographicSize = cameraView.cameraSize; + if (!storedViews.ContainsKey(viewName)) + { + // View has not previously been stored + if (arriveAction != null) + { + arriveAction(); + } + return; + } + + CameraView cameraView = storedViews[viewName]; + + if (duration == 0f) + { + // Move immediately + camera.transform.position = cameraView.cameraPos; + camera.transform.rotation = cameraView.cameraRot; + camera.orthographicSize = cameraView.cameraSize; - SetCameraZ(camera); + SetCameraZ(camera); - if (arriveAction != null) - { - arriveAction(); - } - } - else - { - StartCoroutine(PanInternal(camera, cameraView.cameraPos, cameraView.cameraRot, cameraView.cameraSize, duration, arriveAction)); - } - } - - protected virtual IEnumerator PanInternal(Camera camera, Vector3 targetPos, Quaternion targetRot, float targetSize, float duration, Action arriveAction) - { - if (camera == null) - { - Debug.LogWarning("Camera is null"); - yield break; - } + if (arriveAction != null) + { + arriveAction(); + } + } + else + { + StartCoroutine(PanInternal(camera, cameraView.cameraPos, cameraView.cameraRot, cameraView.cameraSize, duration, arriveAction)); + } + } + + protected virtual IEnumerator PanInternal(Camera camera, Vector3 targetPos, Quaternion targetRot, float targetSize, float duration, Action arriveAction) + { + if (camera == null) + { + Debug.LogWarning("Camera is null"); + yield break; + } - float timer = 0; - float startSize = camera.orthographicSize; - float endSize = targetSize; - Vector3 startPos = camera.transform.position; - Vector3 endPos = targetPos; - Quaternion startRot = camera.transform.rotation; - Quaternion endRot = targetRot; - - bool arrived = false; - while (!arrived) - { - timer += Time.deltaTime; - if (timer > duration) - { - arrived = true; - timer = duration; - } - - // Apply smoothed lerp to camera position and orthographic size - float t = 1f; - if (duration > 0f) - { - t = timer / duration; - } + float timer = 0; + float startSize = camera.orthographicSize; + float endSize = targetSize; + Vector3 startPos = camera.transform.position; + Vector3 endPos = targetPos; + Quaternion startRot = camera.transform.rotation; + Quaternion endRot = targetRot; + + bool arrived = false; + while (!arrived) + { + timer += Time.deltaTime; + if (timer > duration) + { + arrived = true; + timer = duration; + } + + // Apply smoothed lerp to camera position and orthographic size + float t = 1f; + if (duration > 0f) + { + t = timer / duration; + } - if (camera != null) - { - camera.orthographicSize = Mathf.Lerp(startSize, endSize, Mathf.SmoothStep(0f, 1f, t)); - camera.transform.position = Vector3.Lerp(startPos, endPos, Mathf.SmoothStep(0f, 1f, t)); - camera.transform.rotation = Quaternion.Lerp(startRot, endRot, Mathf.SmoothStep(0f, 1f, t)); - } + if (camera != null) + { + camera.orthographicSize = Mathf.Lerp(startSize, endSize, Mathf.SmoothStep(0f, 1f, t)); + camera.transform.position = Vector3.Lerp(startPos, endPos, Mathf.SmoothStep(0f, 1f, t)); + camera.transform.rotation = Quaternion.Lerp(startRot, endRot, Mathf.SmoothStep(0f, 1f, t)); + } - SetCameraZ(camera); - - if (arrived && - arriveAction != null) - { - arriveAction(); - } - - yield return null; - } - } - - /** - * Moves camera smoothly through a sequence of Views over a period of time - */ - public virtual void PanToPath(Camera camera, View[] viewList, float duration, Action arriveAction) - { - if (camera == null) - { - Debug.LogWarning("Camera is null"); - return; - } + SetCameraZ(camera); + + if (arrived && + arriveAction != null) + { + arriveAction(); + } + + yield return null; + } + } + + /** + * Moves camera smoothly through a sequence of Views over a period of time + */ + public virtual void PanToPath(Camera camera, View[] viewList, float duration, Action arriveAction) + { + if (camera == null) + { + Debug.LogWarning("Camera is null"); + return; + } - swipePanActive = false; - - List pathList = new List(); - - // Add current camera position as first point in path - // Note: We use the z coord to tween the camera orthographic size - Vector3 startPos = new Vector3(camera.transform.position.x, - camera.transform.position.y, - camera.orthographicSize); - pathList.Add(startPos); - - for (int i = 0; i < viewList.Length; ++i) - { - View view = viewList[i]; - - Vector3 viewPos = new Vector3(view.transform.position.x, - view.transform.position.y, - view.viewSize); - pathList.Add(viewPos); - } - - StartCoroutine(PanToPathInternal(camera, duration, arriveAction, pathList.ToArray())); - } - - protected virtual IEnumerator PanToPathInternal(Camera camera, float duration, Action arriveAction, Vector3[] path) - { - if (camera == null) - { - Debug.LogWarning("Camera is null"); - yield break; - } + swipePanActive = false; + + List pathList = new List(); + + // Add current camera position as first point in path + // Note: We use the z coord to tween the camera orthographic size + Vector3 startPos = new Vector3(camera.transform.position.x, + camera.transform.position.y, + camera.orthographicSize); + pathList.Add(startPos); + + for (int i = 0; i < viewList.Length; ++i) + { + View view = viewList[i]; + + Vector3 viewPos = new Vector3(view.transform.position.x, + view.transform.position.y, + view.viewSize); + pathList.Add(viewPos); + } + + StartCoroutine(PanToPathInternal(camera, duration, arriveAction, pathList.ToArray())); + } + + protected virtual IEnumerator PanToPathInternal(Camera camera, float duration, Action arriveAction, Vector3[] path) + { + if (camera == null) + { + Debug.LogWarning("Camera is null"); + yield break; + } - float timer = 0; - - while (timer < duration) - { - timer += Time.deltaTime; - timer = Mathf.Min(timer, duration); - float percent = timer / duration; - - Vector3 point = iTween.PointOnPath(path, percent); - - camera.transform.position = new Vector3(point.x, point.y, 0); - camera.orthographicSize = point.z; + float timer = 0; + + while (timer < duration) + { + timer += Time.deltaTime; + timer = Mathf.Min(timer, duration); + float percent = timer / duration; + + Vector3 point = iTween.PointOnPath(path, percent); + + camera.transform.position = new Vector3(point.x, point.y, 0); + camera.orthographicSize = point.z; - SetCameraZ(camera); - - yield return null; - } - - if (arriveAction != null) - { - arriveAction(); - } - } - - /** - * Activates swipe panning mode. - * The player can pan the camera within the area between viewA & viewB. - */ - public virtual void StartSwipePan(Camera camera, View viewA, View viewB, float duration, float speedMultiplier, Action arriveAction) - { - if (camera == null) - { - Debug.LogWarning("Camera is null"); - return; - } + SetCameraZ(camera); + + yield return null; + } + + if (arriveAction != null) + { + arriveAction(); + } + } + + /** + * Activates swipe panning mode. + * The player can pan the camera within the area between viewA & viewB. + */ + public virtual void StartSwipePan(Camera camera, View viewA, View viewB, float duration, float speedMultiplier, Action arriveAction) + { + if (camera == null) + { + Debug.LogWarning("Camera is null"); + return; + } - swipePanViewA = viewA; - swipePanViewB = viewB; - swipeSpeedMultiplier = speedMultiplier; - - Vector3 cameraPos = camera.transform.position; - - Vector3 targetPosition = CalcCameraPosition(cameraPos, swipePanViewA, swipePanViewB); - float targetSize = CalcCameraSize(cameraPos, swipePanViewA, swipePanViewB); - - PanToPosition(camera, targetPosition, Quaternion.identity, targetSize, duration, delegate { - - swipePanActive = true; - swipeCamera = camera; - - if (arriveAction != null) - { - arriveAction(); - } - }); - } - - /** - * Deactivates swipe panning mode. - */ - public virtual void StopSwipePan() - { - swipePanActive = false; - swipePanViewA = null; - swipePanViewB = null; - swipeCamera = null; - } - - protected virtual void SetCameraZ(Camera camera) - { - if (!setCameraZ) - { - return; - } + swipePanViewA = viewA; + swipePanViewB = viewB; + swipeSpeedMultiplier = speedMultiplier; + + Vector3 cameraPos = camera.transform.position; + + Vector3 targetPosition = CalcCameraPosition(cameraPos, swipePanViewA, swipePanViewB); + float targetSize = CalcCameraSize(cameraPos, swipePanViewA, swipePanViewB); + + PanToPosition(camera, targetPosition, Quaternion.identity, targetSize, duration, delegate { + + swipePanActive = true; + swipeCamera = camera; + + if (arriveAction != null) + { + arriveAction(); + } + }); + } + + /** + * Deactivates swipe panning mode. + */ + public virtual void StopSwipePan() + { + swipePanActive = false; + swipePanViewA = null; + swipePanViewB = null; + swipeCamera = null; + } + + protected virtual void SetCameraZ(Camera camera) + { + if (!setCameraZ) + { + return; + } - if (camera == null) - { - Debug.LogWarning("Camera is null"); - return; - } + if (camera == null) + { + Debug.LogWarning("Camera is null"); + return; + } - camera.transform.position = new Vector3(camera.transform.position.x, camera.transform.position.y, cameraZ); - } - - protected virtual void Update() - { - if (!swipePanActive) - { - return; - } + camera.transform.position = new Vector3(camera.transform.position.x, camera.transform.position.y, cameraZ); + } + + protected virtual void Update() + { + if (!swipePanActive) + { + return; + } - if (swipeCamera == null) - { - Debug.LogWarning("Camera is null"); - return; - } + if (swipeCamera == null) + { + Debug.LogWarning("Camera is null"); + return; + } - Vector3 delta = Vector3.zero; - - if (Input.touchCount > 0) - { - if (Input.GetTouch(0).phase == TouchPhase.Moved) - { - delta = Input.GetTouch(0).deltaPosition; - } - } - - if (Input.GetMouseButtonDown(0)) - { - previousMousePos = Input.mousePosition; - } - else if (Input.GetMouseButton(0)) - { - delta = Input.mousePosition - previousMousePos; - previousMousePos = Input.mousePosition; - } + Vector3 delta = Vector3.zero; + + if (Input.touchCount > 0) + { + if (Input.GetTouch(0).phase == TouchPhase.Moved) + { + delta = Input.GetTouch(0).deltaPosition; + } + } + + if (Input.GetMouseButtonDown(0)) + { + previousMousePos = Input.mousePosition; + } + else if (Input.GetMouseButton(0)) + { + delta = Input.mousePosition - previousMousePos; + previousMousePos = Input.mousePosition; + } - Vector3 cameraDelta = swipeCamera.ScreenToViewportPoint(delta); - cameraDelta.x *= -2f * swipeSpeedMultiplier; - cameraDelta.y *= -2f * swipeSpeedMultiplier; - cameraDelta.z = 0f; - - Vector3 cameraPos = swipeCamera.transform.position; - - cameraPos += cameraDelta; - - swipeCamera.transform.position = CalcCameraPosition(cameraPos, swipePanViewA, swipePanViewB); - swipeCamera.orthographicSize = CalcCameraSize(cameraPos, swipePanViewA, swipePanViewB); - } - - // Clamp camera position to region defined by the two views - protected virtual Vector3 CalcCameraPosition(Vector3 pos, View viewA, View viewB) - { - Vector3 safePos = pos; - - // Clamp camera position to region defined by the two views - safePos.x = Mathf.Max(safePos.x, Mathf.Min(viewA.transform.position.x, viewB.transform.position.x)); - safePos.x = Mathf.Min(safePos.x, Mathf.Max(viewA.transform.position.x, viewB.transform.position.x)); - safePos.y = Mathf.Max(safePos.y, Mathf.Min(viewA.transform.position.y, viewB.transform.position.y)); - safePos.y = Mathf.Min(safePos.y, Mathf.Max(viewA.transform.position.y, viewB.transform.position.y)); - - return safePos; - } - - // Smoothly interpolate camera orthographic size based on relative position to two views - protected virtual float CalcCameraSize(Vector3 pos, View viewA, View viewB) - { - // Get ray and point in same space - Vector3 toViewB = viewB.transform.position - viewA.transform.position; - Vector3 localPos = pos - viewA.transform.position; - - // Normalize - float distance = toViewB.magnitude; - toViewB /= distance; - localPos /= distance; - - // Project point onto ray - float t = Vector3.Dot(toViewB, localPos); - t = Mathf.Clamp01(t); // Not really necessary but no harm - - float cameraSize = Mathf.Lerp(viewA.viewSize, viewB.viewSize, t); - - return cameraSize; - } - } + Vector3 cameraDelta = swipeCamera.ScreenToViewportPoint(delta); + cameraDelta.x *= -2f * swipeSpeedMultiplier; + cameraDelta.y *= -2f * swipeSpeedMultiplier; + cameraDelta.z = 0f; + + Vector3 cameraPos = swipeCamera.transform.position; + + cameraPos += cameraDelta; + + swipeCamera.transform.position = CalcCameraPosition(cameraPos, swipePanViewA, swipePanViewB); + swipeCamera.orthographicSize = CalcCameraSize(cameraPos, swipePanViewA, swipePanViewB); + } + + // Clamp camera position to region defined by the two views + protected virtual Vector3 CalcCameraPosition(Vector3 pos, View viewA, View viewB) + { + Vector3 safePos = pos; + + // Clamp camera position to region defined by the two views + safePos.x = Mathf.Max(safePos.x, Mathf.Min(viewA.transform.position.x, viewB.transform.position.x)); + safePos.x = Mathf.Min(safePos.x, Mathf.Max(viewA.transform.position.x, viewB.transform.position.x)); + safePos.y = Mathf.Max(safePos.y, Mathf.Min(viewA.transform.position.y, viewB.transform.position.y)); + safePos.y = Mathf.Min(safePos.y, Mathf.Max(viewA.transform.position.y, viewB.transform.position.y)); + + return safePos; + } + + // Smoothly interpolate camera orthographic size based on relative position to two views + protected virtual float CalcCameraSize(Vector3 pos, View viewA, View viewB) + { + // Get ray and point in same space + Vector3 toViewB = viewB.transform.position - viewA.transform.position; + Vector3 localPos = pos - viewA.transform.position; + + // Normalize + float distance = toViewB.magnitude; + toViewB /= distance; + localPos /= distance; + + // Project point onto ray + float t = Vector3.Dot(toViewB, localPos); + t = Mathf.Clamp01(t); // Not really necessary but no harm + + float cameraSize = Mathf.Lerp(viewA.viewSize, viewB.viewSize, t); + + return cameraSize; + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Camera/Scripts/Commands/FadeScreen.cs b/Assets/Fungus/Camera/Scripts/Commands/FadeScreen.cs index b2edef66..5acc9a5b 100644 --- a/Assets/Fungus/Camera/Scripts/Commands/FadeScreen.cs +++ b/Assets/Fungus/Camera/Scripts/Commands/FadeScreen.cs @@ -9,69 +9,69 @@ using System.Collections; namespace Fungus { - [CommandInfo("Camera", - "Fade Screen", - "Draws a fullscreen texture over the scene to give a fade effect. Setting Target Alpha to 1 will obscure the screen, alpha 0 will reveal the screen. " + - "If no Fade Texture is provided then a default flat color texture is used.")] - [AddComponentMenu("")] - public class FadeScreen : Command - { - [Tooltip("Time for fade effect to complete")] - public float duration = 1f; + [CommandInfo("Camera", + "Fade Screen", + "Draws a fullscreen texture over the scene to give a fade effect. Setting Target Alpha to 1 will obscure the screen, alpha 0 will reveal the screen. " + + "If no Fade Texture is provided then a default flat color texture is used.")] + [AddComponentMenu("")] + public class FadeScreen : Command + { + [Tooltip("Time for fade effect to complete")] + public float duration = 1f; - [Tooltip("Current target alpha transparency value. The fade gradually adjusts the alpha to approach this target value.")] - public float targetAlpha = 1f; + [Tooltip("Current target alpha transparency value. The fade gradually adjusts the alpha to approach this target value.")] + public float targetAlpha = 1f; - [Tooltip("Wait until the fade has finished before executing next command")] - public bool waitUntilFinished = true; + [Tooltip("Wait until the fade has finished before executing next command")] + public bool waitUntilFinished = true; - [Tooltip("Color to render fullscreen fade texture with when screen is obscured.")] - public Color fadeColor = Color.black; + [Tooltip("Color to render fullscreen fade texture with when screen is obscured.")] + public Color fadeColor = Color.black; - [Tooltip("Optional texture to use when rendering the fullscreen fade effect.")] - public Texture2D fadeTexture; - - public override void OnEnter() - { - CameraController cameraController = CameraController.GetInstance(); - - if (waitUntilFinished) - { - cameraController.waiting = true; - } - - if (fadeTexture) - { - cameraController.screenFadeTexture = fadeTexture; - } - else - { - cameraController.screenFadeTexture = CameraController.CreateColorTexture(fadeColor, 32, 32); - } - - cameraController.Fade(targetAlpha, duration, delegate { - if (waitUntilFinished) - { - cameraController.waiting = false; - Continue(); - } - }); - - if (!waitUntilFinished) - { - Continue(); - } - } - - public override string GetSummary() - { - return "Fade to " + targetAlpha + " over " + duration + " seconds"; - } - - public override Color GetButtonColor() - { - return new Color32(216, 228, 170, 255); - } - } - + [Tooltip("Optional texture to use when rendering the fullscreen fade effect.")] + public Texture2D fadeTexture; + + public override void OnEnter() + { + CameraController cameraController = CameraController.GetInstance(); + + if (waitUntilFinished) + { + cameraController.waiting = true; + } + + if (fadeTexture) + { + cameraController.screenFadeTexture = fadeTexture; + } + else + { + cameraController.screenFadeTexture = CameraController.CreateColorTexture(fadeColor, 32, 32); + } + + cameraController.Fade(targetAlpha, duration, delegate { + if (waitUntilFinished) + { + cameraController.waiting = false; + Continue(); + } + }); + + if (!waitUntilFinished) + { + Continue(); + } + } + + public override string GetSummary() + { + return "Fade to " + targetAlpha + " over " + duration + " seconds"; + } + + public override Color GetButtonColor() + { + return new Color32(216, 228, 170, 255); + } + } + } \ No newline at end of file diff --git a/Assets/Fungus/Camera/Scripts/Commands/FadeToView.cs b/Assets/Fungus/Camera/Scripts/Commands/FadeToView.cs index c38dc8dd..da7a2677 100644 --- a/Assets/Fungus/Camera/Scripts/Commands/FadeToView.cs +++ b/Assets/Fungus/Camera/Scripts/Commands/FadeToView.cs @@ -9,113 +9,113 @@ using System.Collections; namespace Fungus { - [CommandInfo("Camera", - "Fade To View", - "Fades the camera out and in again at a position specified by a View object.")] - [AddComponentMenu("")] - public class FadeToView : Command - { - [Tooltip("Time for fade effect to complete")] - public float duration = 1f; - - [Tooltip("Fade from fully visible to opaque at start of fade")] - public bool fadeOut = true; - - [Tooltip("View to transition to when Fade is complete")] - public View targetView; - - [Tooltip("Wait until the fade has finished before executing next command")] - public bool waitUntilFinished = true; - - [Tooltip("Color to render fullscreen fade texture with when screen is obscured.")] - public Color fadeColor = Color.black; - - [Tooltip("Optional texture to use when rendering the fullscreen fade effect.")] - public Texture2D fadeTexture; - - [Tooltip("Camera to use for the fade. Will use main camera if set to none.")] - public Camera targetCamera; - - protected virtual void AcquireCamera() - { - if (targetCamera != null) - { - return; - } - - targetCamera = Camera.main; - if (targetCamera == null) - { - targetCamera = GameObject.FindObjectOfType(); - } - } - - public virtual void Start() - { - AcquireCamera(); - } - - public override void OnEnter() - { - AcquireCamera(); - if (targetCamera == null || - targetView == null) - { - Continue(); - return; - } - - CameraController cameraController = CameraController.GetInstance(); - - if (waitUntilFinished) - { - cameraController.waiting = true; - } - - if (fadeTexture) - { - cameraController.screenFadeTexture = fadeTexture; - } - else - { - cameraController.screenFadeTexture = CameraController.CreateColorTexture(fadeColor, 32, 32); - } - - cameraController.FadeToView(targetCamera, targetView, duration, fadeOut, delegate { - if (waitUntilFinished) - { - cameraController.waiting = false; - Continue(); - } - }); - - if (!waitUntilFinished) - { - Continue(); - } - } - - public override void OnStopExecuting() - { - CameraController.GetInstance().StopAllCoroutines(); - } - - public override string GetSummary() - { - if (targetView == null) - { - return "Error: No view selected"; - } - else - { - return targetView.name; - } - } - - public override Color GetButtonColor() - { - return new Color32(216, 228, 170, 255); - } - } + [CommandInfo("Camera", + "Fade To View", + "Fades the camera out and in again at a position specified by a View object.")] + [AddComponentMenu("")] + public class FadeToView : Command + { + [Tooltip("Time for fade effect to complete")] + public float duration = 1f; + + [Tooltip("Fade from fully visible to opaque at start of fade")] + public bool fadeOut = true; + + [Tooltip("View to transition to when Fade is complete")] + public View targetView; + + [Tooltip("Wait until the fade has finished before executing next command")] + public bool waitUntilFinished = true; + + [Tooltip("Color to render fullscreen fade texture with when screen is obscured.")] + public Color fadeColor = Color.black; + + [Tooltip("Optional texture to use when rendering the fullscreen fade effect.")] + public Texture2D fadeTexture; + + [Tooltip("Camera to use for the fade. Will use main camera if set to none.")] + public Camera targetCamera; + + protected virtual void AcquireCamera() + { + if (targetCamera != null) + { + return; + } + + targetCamera = Camera.main; + if (targetCamera == null) + { + targetCamera = GameObject.FindObjectOfType(); + } + } + + public virtual void Start() + { + AcquireCamera(); + } + + public override void OnEnter() + { + AcquireCamera(); + if (targetCamera == null || + targetView == null) + { + Continue(); + return; + } + + CameraController cameraController = CameraController.GetInstance(); + + if (waitUntilFinished) + { + cameraController.waiting = true; + } + + if (fadeTexture) + { + cameraController.screenFadeTexture = fadeTexture; + } + else + { + cameraController.screenFadeTexture = CameraController.CreateColorTexture(fadeColor, 32, 32); + } + + cameraController.FadeToView(targetCamera, targetView, duration, fadeOut, delegate { + if (waitUntilFinished) + { + cameraController.waiting = false; + Continue(); + } + }); + + if (!waitUntilFinished) + { + Continue(); + } + } + + public override void OnStopExecuting() + { + CameraController.GetInstance().StopAllCoroutines(); + } + + public override string GetSummary() + { + if (targetView == null) + { + return "Error: No view selected"; + } + else + { + return targetView.name; + } + } + + public override Color GetButtonColor() + { + return new Color32(216, 228, 170, 255); + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Camera/Scripts/Commands/Fullscreen.cs b/Assets/Fungus/Camera/Scripts/Commands/Fullscreen.cs index cfa20c6a..bb0eb781 100644 --- a/Assets/Fungus/Camera/Scripts/Commands/Fullscreen.cs +++ b/Assets/Fungus/Camera/Scripts/Commands/Fullscreen.cs @@ -9,48 +9,48 @@ using System.Collections; namespace Fungus { - [CommandInfo("Camera", - "Fullscreen", - "Sets the application to fullscreen, windowed or toggles the current state.")] - [AddComponentMenu("")] - public class Fullscreen : Command - { - public enum FullscreenMode - { - Toggle, - Fullscreen, - Windowed - } - - public FullscreenMode fullscreenMode; - - public override void OnEnter() - { - switch (fullscreenMode) - { - case FullscreenMode.Toggle: - Screen.fullScreen = !Screen.fullScreen; - break; - case FullscreenMode.Fullscreen: - Screen.fullScreen = true; - break; - case FullscreenMode.Windowed: - Screen.fullScreen = false; - break; - } - - Continue(); - } - - public override string GetSummary() - { - return fullscreenMode.ToString(); - } - - public override Color GetButtonColor() - { - return new Color32(216, 228, 170, 255); - } - } + [CommandInfo("Camera", + "Fullscreen", + "Sets the application to fullscreen, windowed or toggles the current state.")] + [AddComponentMenu("")] + public class Fullscreen : Command + { + public enum FullscreenMode + { + Toggle, + Fullscreen, + Windowed + } + + public FullscreenMode fullscreenMode; + + public override void OnEnter() + { + switch (fullscreenMode) + { + case FullscreenMode.Toggle: + Screen.fullScreen = !Screen.fullScreen; + break; + case FullscreenMode.Fullscreen: + Screen.fullScreen = true; + break; + case FullscreenMode.Windowed: + Screen.fullScreen = false; + break; + } + + Continue(); + } + + public override string GetSummary() + { + return fullscreenMode.ToString(); + } + + public override Color GetButtonColor() + { + return new Color32(216, 228, 170, 255); + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Camera/Scripts/Commands/MoveToView.cs b/Assets/Fungus/Camera/Scripts/Commands/MoveToView.cs index 8363797a..e8085121 100644 --- a/Assets/Fungus/Camera/Scripts/Commands/MoveToView.cs +++ b/Assets/Fungus/Camera/Scripts/Commands/MoveToView.cs @@ -9,99 +9,99 @@ using System.Collections; namespace Fungus { - [CommandInfo("Camera", - "Move To View", - "Moves the camera to a location specified by a View object.")] - [AddComponentMenu("")] - public class MoveToView : Command - { - [Tooltip("Time for move effect to complete")] - public float duration = 1; - - [Tooltip("View to transition to when move is complete")] - public Fungus.View targetView; - - [Tooltip("Wait until the fade has finished before executing next command")] - public bool waitUntilFinished = true; - - [Tooltip("Camera to use for the pan. Will use main camera if set to none.")] - public Camera targetCamera; - - protected virtual void AcquireCamera() - { - if (targetCamera != null) - { - return; - } - - targetCamera = Camera.main; - if (targetCamera == null) - { - targetCamera = GameObject.FindObjectOfType(); - } - } - - public virtual void Start() - { - AcquireCamera(); - } - - public override void OnEnter() - { - AcquireCamera(); - if (targetCamera == null || - targetView == null) - { - Continue(); - return; - } - - CameraController cameraController = CameraController.GetInstance(); - - if (waitUntilFinished) - { - cameraController.waiting = true; - } - - Vector3 targetPosition = targetView.transform.position; - Quaternion targetRotation = targetView.transform.rotation; - float targetSize = targetView.viewSize; - - cameraController.PanToPosition(targetCamera, targetPosition, targetRotation, targetSize, duration, delegate { - if (waitUntilFinished) - { - cameraController.waiting = false; - Continue(); - } - }); - - if (!waitUntilFinished) - { - Continue(); - } - } - - public override void OnStopExecuting() - { - CameraController.GetInstance().StopAllCoroutines(); - } - - public override string GetSummary() - { - if (targetView == null) - { - return "Error: No view selected"; - } - else - { - return targetView.name; - } - } - - public override Color GetButtonColor() - { - return new Color32(216, 228, 170, 255); - } - } + [CommandInfo("Camera", + "Move To View", + "Moves the camera to a location specified by a View object.")] + [AddComponentMenu("")] + public class MoveToView : Command + { + [Tooltip("Time for move effect to complete")] + public float duration = 1; + + [Tooltip("View to transition to when move is complete")] + public Fungus.View targetView; + + [Tooltip("Wait until the fade has finished before executing next command")] + public bool waitUntilFinished = true; + + [Tooltip("Camera to use for the pan. Will use main camera if set to none.")] + public Camera targetCamera; + + protected virtual void AcquireCamera() + { + if (targetCamera != null) + { + return; + } + + targetCamera = Camera.main; + if (targetCamera == null) + { + targetCamera = GameObject.FindObjectOfType(); + } + } + + public virtual void Start() + { + AcquireCamera(); + } + + public override void OnEnter() + { + AcquireCamera(); + if (targetCamera == null || + targetView == null) + { + Continue(); + return; + } + + CameraController cameraController = CameraController.GetInstance(); + + if (waitUntilFinished) + { + cameraController.waiting = true; + } + + Vector3 targetPosition = targetView.transform.position; + Quaternion targetRotation = targetView.transform.rotation; + float targetSize = targetView.viewSize; + + cameraController.PanToPosition(targetCamera, targetPosition, targetRotation, targetSize, duration, delegate { + if (waitUntilFinished) + { + cameraController.waiting = false; + Continue(); + } + }); + + if (!waitUntilFinished) + { + Continue(); + } + } + + public override void OnStopExecuting() + { + CameraController.GetInstance().StopAllCoroutines(); + } + + public override string GetSummary() + { + if (targetView == null) + { + return "Error: No view selected"; + } + else + { + return targetView.name; + } + } + + public override Color GetButtonColor() + { + return new Color32(216, 228, 170, 255); + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Camera/Scripts/Commands/ShakeCamera.cs b/Assets/Fungus/Camera/Scripts/Commands/ShakeCamera.cs index 382278c4..3e33ca57 100644 --- a/Assets/Fungus/Camera/Scripts/Commands/ShakeCamera.cs +++ b/Assets/Fungus/Camera/Scripts/Commands/ShakeCamera.cs @@ -9,61 +9,61 @@ using System.Collections; namespace Fungus { - [CommandInfo("Camera", - "Shake Camera", - "Applies a camera shake effect to the main camera.")] - [AddComponentMenu("")] - public class ShakeCamera : Command - { - [Tooltip("Time for camera shake effect to complete")] - public float duration = 0.5f; - - [Tooltip("Magnitude of shake effect in x & y axes")] - public Vector2 amount = new Vector2(1, 1); - - [Tooltip("Wait until the shake effect has finished before executing next command")] - public bool waitUntilFinished; - - public override void OnEnter() - { - Vector3 v = new Vector3(); - v = amount; + [CommandInfo("Camera", + "Shake Camera", + "Applies a camera shake effect to the main camera.")] + [AddComponentMenu("")] + public class ShakeCamera : Command + { + [Tooltip("Time for camera shake effect to complete")] + public float duration = 0.5f; + + [Tooltip("Magnitude of shake effect in x & y axes")] + public Vector2 amount = new Vector2(1, 1); + + [Tooltip("Wait until the shake effect has finished before executing next command")] + public bool waitUntilFinished; + + public override void OnEnter() + { + Vector3 v = new Vector3(); + v = amount; - Hashtable tweenParams = new Hashtable(); - tweenParams.Add("amount", v); - tweenParams.Add("time", duration); - tweenParams.Add("oncomplete", "OniTweenComplete"); - tweenParams.Add("oncompletetarget", gameObject); - tweenParams.Add("oncompleteparams", this); - iTween.ShakePosition(Camera.main.gameObject, tweenParams); - - if (!waitUntilFinished) - { - Continue(); - } - } - - protected virtual void OniTweenComplete(object param) - { - Command command = param as Command; - if (command != null && command.Equals(this)) - { - if (waitUntilFinished) - { - Continue(); - } - } - } - - public override string GetSummary() - { - return "For " + duration + " seconds."; - } - - public override Color GetButtonColor() - { - return new Color32(216, 228, 170, 255); - } - } - + Hashtable tweenParams = new Hashtable(); + tweenParams.Add("amount", v); + tweenParams.Add("time", duration); + tweenParams.Add("oncomplete", "OniTweenComplete"); + tweenParams.Add("oncompletetarget", gameObject); + tweenParams.Add("oncompleteparams", this); + iTween.ShakePosition(Camera.main.gameObject, tweenParams); + + if (!waitUntilFinished) + { + Continue(); + } + } + + protected virtual void OniTweenComplete(object param) + { + Command command = param as Command; + if (command != null && command.Equals(this)) + { + if (waitUntilFinished) + { + Continue(); + } + } + } + + public override string GetSummary() + { + return "For " + duration + " seconds."; + } + + public override Color GetButtonColor() + { + return new Color32(216, 228, 170, 255); + } + } + } \ No newline at end of file diff --git a/Assets/Fungus/Camera/Scripts/Commands/StartSwipe.cs b/Assets/Fungus/Camera/Scripts/Commands/StartSwipe.cs index 47169bdb..d4629578 100644 --- a/Assets/Fungus/Camera/Scripts/Commands/StartSwipe.cs +++ b/Assets/Fungus/Camera/Scripts/Commands/StartSwipe.cs @@ -9,73 +9,73 @@ using System.Collections; namespace Fungus { - [CommandInfo("Camera", - "Start Swipe", - "Activates swipe panning mode where the player can pan the camera within the area between viewA & viewB.")] - [AddComponentMenu("")] - public class StartSwipe : Command - { - [Tooltip("Defines one extreme of the scrollable area that the player can pan around")] - public View viewA; + [CommandInfo("Camera", + "Start Swipe", + "Activates swipe panning mode where the player can pan the camera within the area between viewA & viewB.")] + [AddComponentMenu("")] + public class StartSwipe : Command + { + [Tooltip("Defines one extreme of the scrollable area that the player can pan around")] + public View viewA; - [Tooltip("Defines one extreme of the scrollable area that the player can pan around")] - public View viewB; + [Tooltip("Defines one extreme of the scrollable area that the player can pan around")] + public View viewB; - [Tooltip("Time to move the camera to a valid starting position between the two views")] - public float duration = 0.5f; + [Tooltip("Time to move the camera to a valid starting position between the two views")] + public float duration = 0.5f; - [Tooltip("Multiplier factor for speed of swipe pan")] - public float speedMultiplier = 1f; + [Tooltip("Multiplier factor for speed of swipe pan")] + public float speedMultiplier = 1f; - [Tooltip("Camera to use for the pan. Will use main camera if set to none.")] - public Camera targetCamera; - - public virtual void Start() - { - if (targetCamera == null) - { - targetCamera = Camera.main; - } - if (targetCamera == null) - { - targetCamera = GameObject.FindObjectOfType(); - } - } + [Tooltip("Camera to use for the pan. Will use main camera if set to none.")] + public Camera targetCamera; + + public virtual void Start() + { + if (targetCamera == null) + { + targetCamera = Camera.main; + } + if (targetCamera == null) + { + targetCamera = GameObject.FindObjectOfType(); + } + } - public override void OnEnter() - { - if (targetCamera == null || - viewA == null || - viewB == null) - { - Continue(); - return; - } + public override void OnEnter() + { + if (targetCamera == null || + viewA == null || + viewB == null) + { + Continue(); + return; + } - CameraController cameraController = CameraController.GetInstance(); + CameraController cameraController = CameraController.GetInstance(); - cameraController.StartSwipePan(targetCamera, viewA, viewB, duration, speedMultiplier, () => Continue() ); - } + cameraController.StartSwipePan(targetCamera, viewA, viewB, duration, speedMultiplier, () => Continue() ); + } - public override string GetSummary() - { - if (viewA == null) - { - return "Error: No view selected for View A"; - } + public override string GetSummary() + { + if (viewA == null) + { + return "Error: No view selected for View A"; + } - if (viewB == null) - { - return "Error: No view selected for View B"; - } + if (viewB == null) + { + return "Error: No view selected for View B"; + } - return viewA.name + " to " + viewB.name; - } + return viewA.name + " to " + viewB.name; + } - public override Color GetButtonColor() - { - return new Color32(216, 228, 170, 255); - } - } + public override Color GetButtonColor() + { + return new Color32(216, 228, 170, 255); + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Camera/Scripts/Commands/StopSwipe.cs b/Assets/Fungus/Camera/Scripts/Commands/StopSwipe.cs index 56c68c3b..75748d26 100644 --- a/Assets/Fungus/Camera/Scripts/Commands/StopSwipe.cs +++ b/Assets/Fungus/Camera/Scripts/Commands/StopSwipe.cs @@ -9,25 +9,25 @@ using System.Collections; namespace Fungus { - [CommandInfo("Camera", - "Stop Swipe", - "Deactivates swipe panning mode.")] - [AddComponentMenu("")] - public class StopSwipe : Command - { - public override void OnEnter() - { - CameraController cameraController = CameraController.GetInstance(); + [CommandInfo("Camera", + "Stop Swipe", + "Deactivates swipe panning mode.")] + [AddComponentMenu("")] + public class StopSwipe : Command + { + public override void OnEnter() + { + CameraController cameraController = CameraController.GetInstance(); - cameraController.StopSwipePan(); + cameraController.StopSwipePan(); - Continue(); - } + Continue(); + } - public override Color GetButtonColor() - { - return new Color32(216, 228, 170, 255); - } - } + public override Color GetButtonColor() + { + return new Color32(216, 228, 170, 255); + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Camera/Scripts/View.cs b/Assets/Fungus/Camera/Scripts/View.cs index a5976911..b466dd13 100644 --- a/Assets/Fungus/Camera/Scripts/View.cs +++ b/Assets/Fungus/Camera/Scripts/View.cs @@ -8,38 +8,38 @@ using System.Collections; namespace Fungus { - /** - * Defines a camera view point. - * The position and rotation are specified using the game object's transform, so this class - * only needs to specify the ortographic view size. - */ - [ExecuteInEditMode] - public class View : MonoBehaviour - { - /** - * Orthographic size of the camera view in world units. - */ - [Tooltip("Orthographic size of the camera view in world units.")] - public float viewSize = 0.5f; + /** + * Defines a camera view point. + * The position and rotation are specified using the game object's transform, so this class + * only needs to specify the ortographic view size. + */ + [ExecuteInEditMode] + public class View : MonoBehaviour + { + /** + * Orthographic size of the camera view in world units. + */ + [Tooltip("Orthographic size of the camera view in world units.")] + public float viewSize = 0.5f; - /** - * Aspect ratio of the primary view rectangle. - * e.g. a 4:3 aspect ratio = 1.333 - */ - [Tooltip("Aspect ratio of the primary view rectangle. (e.g. 4:3 aspect ratio = 1.333)")] - public Vector2 primaryAspectRatio = new Vector2(4, 3); + /** + * Aspect ratio of the primary view rectangle. + * e.g. a 4:3 aspect ratio = 1.333 + */ + [Tooltip("Aspect ratio of the primary view rectangle. (e.g. 4:3 aspect ratio = 1.333)")] + public Vector2 primaryAspectRatio = new Vector2(4, 3); - /** - * Aspect ratio of the secondary view rectangle. - * e.g. a 2:1 aspect ratio = 2/1 = 2.0 - */ - [Tooltip("Aspect ratio of the secondary view rectangle. (e.g. 2:1 aspect ratio = 2.0)")] - public Vector2 secondaryAspectRatio = new Vector2(2, 1); + /** + * Aspect ratio of the secondary view rectangle. + * e.g. a 2:1 aspect ratio = 2/1 = 2.0 + */ + [Tooltip("Aspect ratio of the secondary view rectangle. (e.g. 2:1 aspect ratio = 2.0)")] + public Vector2 secondaryAspectRatio = new Vector2(2, 1); - protected virtual void Update() - { - // Disable scaling to avoid complicating the orthographic size calculations - transform.localScale = new Vector3(1,1,1); - } - } + protected virtual void Update() + { + // Disable scaling to avoid complicating the orthographic size calculations + transform.localScale = new Vector3(1,1,1); + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Flowchart/Editor/AssetModProcessor.cs b/Assets/Fungus/Flowchart/Editor/AssetModProcessor.cs index 45539adc..83854bef 100644 --- a/Assets/Fungus/Flowchart/Editor/AssetModProcessor.cs +++ b/Assets/Fungus/Flowchart/Editor/AssetModProcessor.cs @@ -12,41 +12,41 @@ using System.Collections.Generic; namespace Fungus { - /** - * Prevents saving of selected blocks and commands to avoid version control conflicts - */ - public class AssetModProcessor : UnityEditor.AssetModificationProcessor - { - public static string[] OnWillSaveAssets(string[] paths) - { - string sceneName = ""; - - foreach (string path in paths) - { - if (path.Contains(".unity")) - { - sceneName = Path.GetFileNameWithoutExtension(path); - } - } - - if (sceneName.Length == 0) - { - return paths; - } + /** + * Prevents saving of selected blocks and commands to avoid version control conflicts + */ + public class AssetModProcessor : UnityEditor.AssetModificationProcessor + { + public static string[] OnWillSaveAssets(string[] paths) + { + string sceneName = ""; + + foreach (string path in paths) + { + if (path.Contains(".unity")) + { + sceneName = Path.GetFileNameWithoutExtension(path); + } + } + + if (sceneName.Length == 0) + { + return paths; + } - // Reset these variables before save so that they won't cause a git conflict - Flowchart[] flowcharts = UnityEngine.Object.FindObjectsOfType(); - foreach (Flowchart f in flowcharts) - { - if (!f.saveSelection) - { - f.selectedBlock = null; - f.ClearSelectedCommands(); - } - } + // Reset these variables before save so that they won't cause a git conflict + Flowchart[] flowcharts = UnityEngine.Object.FindObjectsOfType(); + foreach (Flowchart f in flowcharts) + { + if (!f.saveSelection) + { + f.selectedBlock = null; + f.ClearSelectedCommands(); + } + } - return paths; - } + return paths; + } - } + } } \ No newline at end of file diff --git a/Assets/Fungus/Flowchart/Editor/BlockEditor.cs b/Assets/Fungus/Flowchart/Editor/BlockEditor.cs index 12a1eee6..0e5c1937 100644 --- a/Assets/Fungus/Flowchart/Editor/BlockEditor.cs +++ b/Assets/Fungus/Flowchart/Editor/BlockEditor.cs @@ -18,767 +18,767 @@ using System.Reflection; namespace Fungus { - [CustomEditor (typeof(Block))] - public class BlockEditor : Editor - { - protected class SetEventHandlerOperation - { - public Block block; - public Type eventHandlerType; - } - - protected class AddCommandOperation - { - public Block block; - public Type commandType; - public int index; - } - - public static List actionList = new List(); - - protected Texture2D upIcon; - protected Texture2D downIcon; - protected Texture2D addIcon; - protected Texture2D duplicateIcon; - protected Texture2D deleteIcon; - - protected virtual void OnEnable() - { - upIcon = Resources.Load("Icons/up") as Texture2D; - downIcon = Resources.Load("Icons/down") as Texture2D; - addIcon = Resources.Load("Icons/add") as Texture2D; - duplicateIcon = Resources.Load("Icons/duplicate") as Texture2D; - deleteIcon = Resources.Load("Icons/delete") as Texture2D; - } - - public virtual void DrawBlockName(Flowchart flowchart) - { - serializedObject.Update(); - - SerializedProperty blockNameProperty = serializedObject.FindProperty("blockName"); - Rect blockLabelRect = new Rect(45, 5, 120, 16); - EditorGUI.LabelField(blockLabelRect, new GUIContent("Block Name")); - Rect blockNameRect = new Rect(45, 21, 180, 16); - EditorGUI.PropertyField(blockNameRect, blockNameProperty, new GUIContent("")); - - // Ensure block name is unique for this Flowchart - Block block = target as Block; - string uniqueName = flowchart.GetUniqueBlockKey(blockNameProperty.stringValue, block); - if (uniqueName != block.blockName) - { - blockNameProperty.stringValue = uniqueName; - } - - serializedObject.ApplyModifiedProperties(); - } - - public virtual void DrawBlockGUI(Flowchart flowchart) - { - serializedObject.Update(); - - // Execute any queued cut, copy, paste, etc. operations from the prevous GUI update - // We need to defer applying these operations until the following update because - // the ReorderableList control emits GUI errors if you clear the list in the same frame - // as drawing the control (e.g. select all and then delete) - if (Event.current.type == EventType.Layout) - { - foreach (Action action in actionList) - { - if (action != null) - { - action(); - } - } - actionList.Clear(); - } - - Block block = target as Block; - - SerializedProperty commandListProperty = serializedObject.FindProperty("commandList"); - - if (block == flowchart.selectedBlock) - { - SerializedProperty descriptionProp = serializedObject.FindProperty("description"); - EditorGUILayout.PropertyField(descriptionProp); - - DrawEventHandlerGUI(flowchart); - - block.UpdateIndentLevels(); - - // Make sure each command has a reference to its parent block - foreach (Command command in block.commandList) - { - if (command == null) // Will be deleted from the list later on - { - continue; - } - command.parentBlock = block; - } - - ReorderableListGUI.Title("Commands"); - CommandListAdaptor adaptor = new CommandListAdaptor(commandListProperty, 0); - adaptor.nodeRect = block.nodeRect; - - ReorderableListFlags flags = ReorderableListFlags.HideAddButton | ReorderableListFlags.HideRemoveButtons | ReorderableListFlags.DisableContextMenu; - - if (block.commandList.Count == 0) - { - EditorGUILayout.HelpBox("Press the + button below to add a command to the list.", MessageType.Info); - } - else - { - ReorderableListControl.DrawControlFromState(adaptor, null, flags); - } - - // EventType.contextClick doesn't register since we moved the Block Editor to be inside - // a GUI Area, no idea why. As a workaround we just check for right click instead. - if (Event.current.type == EventType.mouseUp && - Event.current.button == 1) - { - ShowContextMenu(); - Event.current.Use(); - } - - if (GUIUtility.keyboardControl == 0) //Only call keyboard shortcuts when not typing in a text field - { - Event e = Event.current; - - // Copy keyboard shortcut - if (e.type == EventType.ValidateCommand && e.commandName == "Copy") - { - if (flowchart.selectedCommands.Count > 0) - { - e.Use(); - } - } - - if (e.type == EventType.ExecuteCommand && e.commandName == "Copy") - { - actionList.Add(Copy); - e.Use(); - } - - // Cut keyboard shortcut - if (e.type == EventType.ValidateCommand && e.commandName == "Cut") - { - if (flowchart.selectedCommands.Count > 0) - { - e.Use(); - } - } - - if (e.type == EventType.ExecuteCommand && e.commandName == "Cut") - { - actionList.Add(Cut); - e.Use(); - } - - // Paste keyboard shortcut - if (e.type == EventType.ValidateCommand && e.commandName == "Paste") - { - CommandCopyBuffer commandCopyBuffer = CommandCopyBuffer.GetInstance(); - if (commandCopyBuffer.HasCommands()) - { - e.Use(); - } - } - - if (e.type == EventType.ExecuteCommand && e.commandName == "Paste") - { - actionList.Add(Paste); - e.Use(); - } - - // Duplicate keyboard shortcut - if (e.type == EventType.ValidateCommand && e.commandName == "Duplicate") - { - if (flowchart.selectedCommands.Count > 0) - { - e.Use(); - } - } - - if (e.type == EventType.ExecuteCommand && e.commandName == "Duplicate") - { - actionList.Add(Copy); - actionList.Add(Paste); - e.Use(); - } - - // Delete keyboard shortcut - if (e.type == EventType.ValidateCommand && e.commandName == "Delete") - { - if (flowchart.selectedCommands.Count > 0) - { - e.Use(); - } - } - - if (e.type == EventType.ExecuteCommand && e.commandName == "Delete") - { - actionList.Add(Delete); - e.Use(); - } - - // SelectAll keyboard shortcut - if (e.type == EventType.ValidateCommand && e.commandName == "SelectAll") - { - e.Use(); - } - - if (e.type == EventType.ExecuteCommand && e.commandName == "SelectAll") - { - actionList.Add(SelectAll); - e.Use(); - } - } - - } - - // Remove any null entries in the command list. - // This can happen when a command class is deleted or renamed. - for (int i = commandListProperty.arraySize - 1; i >= 0; --i) - { - SerializedProperty commandProperty = commandListProperty.GetArrayElementAtIndex(i); - if (commandProperty.objectReferenceValue == null) - { - commandListProperty.DeleteArrayElementAtIndex(i); - } - } - - serializedObject.ApplyModifiedProperties(); - } - - public virtual void DrawButtonToolbar() - { - GUILayout.BeginHorizontal(); - - // Previous Command - if ((Event.current.type == EventType.keyDown) && (Event.current.keyCode == KeyCode.PageUp)) - { - SelectPrevious(); - GUI.FocusControl("dummycontrol"); - Event.current.Use(); - } - // Next Command - if ((Event.current.type == EventType.keyDown) && (Event.current.keyCode == KeyCode.PageDown)) - { - SelectNext(); - GUI.FocusControl("dummycontrol"); - Event.current.Use(); - } - - if (GUILayout.Button(upIcon)) - { - SelectPrevious(); - } - - // Down Button - if (GUILayout.Button(downIcon)) - { - SelectNext(); - } - - GUILayout.FlexibleSpace(); - - // Add Button - if (GUILayout.Button(addIcon)) - { - ShowCommandMenu(); - } - - // Duplicate Button - if (GUILayout.Button(duplicateIcon)) - { - Copy(); - Paste(); - } - - // Delete Button - if (GUILayout.Button(deleteIcon)) - { - Delete(); - } - - GUILayout.EndHorizontal(); - } - - protected virtual void DrawEventHandlerGUI(Flowchart flowchart) - { - // Show available Event Handlers in a drop down list with type of current - // event handler selected. - List eventHandlerTypes = EditorExtensions.FindDerivedTypes(typeof(EventHandler)).ToList(); - - Block block = target as Block; - System.Type currentType = null; - if (block.eventHandler != null) - { - currentType = block.eventHandler.GetType(); - } - - string currentHandlerName = ""; - if (currentType != null) - { - EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(currentType); - if (info != null) - { - currentHandlerName = info.EventHandlerName; - } - } - - EditorGUILayout.BeginHorizontal(); - EditorGUILayout.PrefixLabel(new GUIContent("Execute On Event")); - if (GUILayout.Button(new GUIContent(currentHandlerName), EditorStyles.popup)) - { - SetEventHandlerOperation noneOperation = new SetEventHandlerOperation(); - noneOperation.block = block; - noneOperation.eventHandlerType = null; - - GenericMenu eventHandlerMenu = new GenericMenu(); - eventHandlerMenu.AddItem(new GUIContent("None"), false, OnSelectEventHandler, noneOperation); - - // Add event handlers with no category first - foreach (System.Type type in eventHandlerTypes) - { - EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(type); - if (info != null && - info.Category.Length == 0) - { - SetEventHandlerOperation operation = new SetEventHandlerOperation(); - operation.block = block; - operation.eventHandlerType = type; - - eventHandlerMenu.AddItem(new GUIContent(info.EventHandlerName), false, OnSelectEventHandler, operation); - } - } - - // Add event handlers with a category afterwards - foreach (System.Type type in eventHandlerTypes) - { - EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(type); - if (info != null && - info.Category.Length > 0) - { - SetEventHandlerOperation operation = new SetEventHandlerOperation(); - operation.block = block; - operation.eventHandlerType = type; - string typeName = info.Category + "/" + info.EventHandlerName; - eventHandlerMenu.AddItem(new GUIContent(typeName), false, OnSelectEventHandler, operation); - } - } - - - eventHandlerMenu.ShowAsContext(); - } - EditorGUILayout.EndHorizontal(); - - if (block.eventHandler != null) - { - EventHandlerEditor eventHandlerEditor = Editor.CreateEditor(block.eventHandler) as EventHandlerEditor; - if (eventHandlerEditor != null) - { - eventHandlerEditor.DrawInspectorGUI(); - DestroyImmediate(eventHandlerEditor); - } - } - } - - protected void OnSelectEventHandler(object obj) - { - SetEventHandlerOperation operation = obj as SetEventHandlerOperation; - Block block = operation.block; - System.Type selectedType = operation.eventHandlerType; - if (block == null) - { - return; - } - - Undo.RecordObject(block, "Set Event Handler"); - - if (block.eventHandler != null) - { - Undo.DestroyObjectImmediate(block.eventHandler); - } - - if (selectedType != null) - { - EventHandler newHandler = Undo.AddComponent(block.gameObject, selectedType) as EventHandler; - newHandler.parentBlock = block; - block.eventHandler = newHandler; - } - - // Because this is an async call, we need to force prefab instances to record changes - PrefabUtility.RecordPrefabInstancePropertyModifications(block); - } - - static public void BlockField(SerializedProperty property, GUIContent label, GUIContent nullLabel, Flowchart flowchart) - { - if (flowchart == null) - { - return; - } - - Block block = property.objectReferenceValue as Block; - - // Build dictionary of child blocks - List blockNames = new List(); - - int selectedIndex = 0; - blockNames.Add(nullLabel); - Block[] blocks = flowchart.GetComponents(); - for (int i = 0; i < blocks.Length; ++i) - { - blockNames.Add(new GUIContent(blocks[i].blockName)); - - if (block == blocks[i]) - { - selectedIndex = i + 1; - } - } - - selectedIndex = EditorGUILayout.Popup(label, selectedIndex, blockNames.ToArray()); - if (selectedIndex == 0) - { - block = null; // Option 'None' - } - else - { - block = blocks[selectedIndex - 1]; - } - - property.objectReferenceValue = block; - } - - static public Block BlockField(Rect position, GUIContent nullLabel, Flowchart flowchart, Block block) - { - if (flowchart == null) - { - return null; - } - - Block result = block; - - // Build dictionary of child blocks - List blockNames = new List(); - - int selectedIndex = 0; - blockNames.Add(nullLabel); - Block[] blocks = flowchart.GetComponents(); - for (int i = 0; i < blocks.Length; ++i) - { - blockNames.Add(new GUIContent(blocks[i].name)); - - if (block == blocks[i]) - { - selectedIndex = i + 1; - } - } - - selectedIndex = EditorGUI.Popup(position, selectedIndex, blockNames.ToArray()); - if (selectedIndex == 0) - { - result = null; // Option 'None' - } - else - { - result = blocks[selectedIndex - 1]; - } - - return result; - } - - // Compare delegate for sorting the list of command attributes - protected static int CompareCommandAttributes(KeyValuePair x, KeyValuePair y) - { - int compare = (x.Value.Category.CompareTo(y.Value.Category)); - if (compare == 0) - { - compare = (x.Value.CommandName.CompareTo(y.Value.CommandName)); - } - return compare; - } - - [MenuItem("Tools/Fungus/Utilities/Export Reference Docs")] - protected static void ExportReferenceDocs() - { - string path = EditorUtility.SaveFolderPanel("Export Reference Docs", "", ""); - if(path.Length == 0) - { - return; - } - - ExportCommandInfo(path); - ExportEventHandlerInfo(path); - - FlowchartWindow.ShowNotification("Exported Reference Documentation"); - } - - protected static void ExportCommandInfo(string path) - { - // Dump command info - List menuTypes = EditorExtensions.FindDerivedTypes(typeof(Command)).ToList(); - List> filteredAttributes = GetFilteredCommandInfoAttribute(menuTypes); - filteredAttributes.Sort( CompareCommandAttributes ); - - // Build list of command categories - List commandCategories = new List(); - foreach(var keyPair in filteredAttributes) - { - CommandInfoAttribute info = keyPair.Value; - if (info.Category != "" && - !commandCategories.Contains(info.Category)) - { - commandCategories.Add (info.Category); - } - } - commandCategories.Sort(); - - // Output the commands in each category - foreach (string category in commandCategories) - { - string markdown = ""; - foreach(var keyPair in filteredAttributes) - { - CommandInfoAttribute info = keyPair.Value; - - if (info.Category == category || - info.Category == "" && category == "Scripting") - { - markdown += "## " + info.CommandName + "\n"; - markdown += info.HelpText + "\n"; - markdown += GetPropertyInfo(keyPair.Key); - } - } - - string filePath = path + "/commands/" + category.ToLower() + "_commands.md"; - - Directory.CreateDirectory(Path.GetDirectoryName(filePath)); - File.WriteAllText(filePath, markdown); - } - } - - protected static void ExportEventHandlerInfo(string path) - { - List eventHandlerTypes = EditorExtensions.FindDerivedTypes(typeof(EventHandler)).ToList(); - List eventHandlerCategories = new List(); - eventHandlerCategories.Add("Core"); - foreach (System.Type type in eventHandlerTypes) - { - EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(type); - if (info != null && - info.Category != "" && - !eventHandlerCategories.Contains(info.Category)) - { - eventHandlerCategories.Add(info.Category); - } - } - eventHandlerCategories.Sort(); - - // Output the commands in each category - foreach (string category in eventHandlerCategories) - { - string markdown = ""; - - foreach (System.Type type in eventHandlerTypes) - { - EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(type); - - if (info != null && - info.Category == category || - info.Category == "" && category == "Core") - { - markdown += "## " + info.EventHandlerName + "\n"; - markdown += info.HelpText + "\n"; - markdown += GetPropertyInfo(type); - } - } - - string filePath = path + "/event_handlers/" + category.ToLower() + "_events.md"; - - Directory.CreateDirectory(Path.GetDirectoryName(filePath)); - File.WriteAllText(filePath, markdown); - } - } - - protected static string GetPropertyInfo(System.Type type) - { - string markdown = ""; - foreach(FieldInfo field in type.GetFields() ) - { - TooltipAttribute attribute = (TooltipAttribute)Attribute.GetCustomAttribute(field, typeof(TooltipAttribute)); - if (attribute == null ) - { - continue; - } - - // Change field name to how it's displayed in the inspector - string propertyName = Regex.Replace(field.Name, "(\\B[A-Z])", " $1"); - if (propertyName.Length > 1) - { - propertyName = propertyName.Substring(0,1).ToUpper() + propertyName.Substring(1); - } - else - { - propertyName = propertyName.ToUpper(); - } - - markdown += propertyName + " | " + field.FieldType + " | " + attribute.tooltip + "\n"; - } - - if (markdown.Length > 0) - { - markdown = "\nProperty | Type | Description\n --- | --- | ---\n" + markdown + "\n"; - } - - return markdown; - } - - protected virtual void ShowCommandMenu() - { - Block block = target as Block; - - Flowchart flowchart = block.GetFlowchart(); - - // Use index of last selected command in list, or end of list if nothing selected. - int index = -1; - foreach (Command command in flowchart.selectedCommands) - { - if (command.commandIndex + 1 > index) - { - index = command.commandIndex + 1; - } - } - if (index == -1) - { - index = block.commandList.Count; - } - - GenericMenu commandMenu = new GenericMenu(); - - // Build menu list - List menuTypes = EditorExtensions.FindDerivedTypes(typeof(Command)).ToList(); - List> filteredAttributes = GetFilteredCommandInfoAttribute(menuTypes); - - filteredAttributes.Sort( CompareCommandAttributes ); - - foreach(var keyPair in filteredAttributes) - { - // Skip command type if the Flowchart doesn't support it - if (!flowchart.IsCommandSupported(keyPair.Value)) - { - continue; - } - - AddCommandOperation commandOperation = new AddCommandOperation(); - - commandOperation.block = block; - commandOperation.commandType = keyPair.Key; - commandOperation.index = index; - - GUIContent menuItem; - if (keyPair.Value.Category == "") - { - menuItem = new GUIContent(keyPair.Value.CommandName); - } - else - { - menuItem = new GUIContent (keyPair.Value.Category + "/" + keyPair.Value.CommandName); - } - - commandMenu.AddItem(menuItem, false, AddCommandCallback, commandOperation); - } - - commandMenu.ShowAsContext(); - } - - protected static List> GetFilteredCommandInfoAttribute(List menuTypes) - { - Dictionary> filteredAttributes = new Dictionary>(); - - foreach (System.Type type in menuTypes) - { - object[] attributes = type.GetCustomAttributes(false); - foreach (object obj in attributes) - { - CommandInfoAttribute infoAttr = obj as CommandInfoAttribute; - if (infoAttr != null) - { - string dictionaryName = string.Format("{0}/{1}", infoAttr.Category, infoAttr.CommandName); - - int existingItemPriority = -1; - if (filteredAttributes.ContainsKey(dictionaryName)) - { - existingItemPriority = filteredAttributes[dictionaryName].Value.Priority; - } - - if (infoAttr.Priority > existingItemPriority) - { - KeyValuePair keyValuePair = new KeyValuePair(type, infoAttr); - filteredAttributes[dictionaryName] = keyValuePair; - } - } - } - } - return filteredAttributes.Values.ToList>(); - } - - protected static void AddCommandCallback(object obj) - { - AddCommandOperation commandOperation = obj as AddCommandOperation; - - Block block = commandOperation.block; - if (block == null) - { - return; - } - - Flowchart flowchart = block.GetFlowchart(); - - flowchart.ClearSelectedCommands(); - - Command newCommand = Undo.AddComponent(block.gameObject, commandOperation.commandType) as Command; - block.GetFlowchart().AddSelectedCommand(newCommand); - newCommand.parentBlock = block; - newCommand.itemId = flowchart.NextItemId(); - - // Let command know it has just been added to the block - newCommand.OnCommandAdded(block); - - Undo.RecordObject(block, "Set command type"); - if (commandOperation.index < block.commandList.Count - 1) - { - block.commandList.Insert(commandOperation.index, newCommand); - } - else - { - block.commandList.Add(newCommand); - } - - // Because this is an async call, we need to force prefab instances to record changes - PrefabUtility.RecordPrefabInstancePropertyModifications(block); - } - - public virtual void ShowContextMenu() - { - Block block = target as Block; - Flowchart flowchart = block.GetFlowchart(); - - if (flowchart == null) - { - return; - } - - bool showCut = false; - bool showCopy = false; - bool showDelete = false; - bool showPaste = false; + [CustomEditor (typeof(Block))] + public class BlockEditor : Editor + { + protected class SetEventHandlerOperation + { + public Block block; + public Type eventHandlerType; + } + + protected class AddCommandOperation + { + public Block block; + public Type commandType; + public int index; + } + + public static List actionList = new List(); + + protected Texture2D upIcon; + protected Texture2D downIcon; + protected Texture2D addIcon; + protected Texture2D duplicateIcon; + protected Texture2D deleteIcon; + + protected virtual void OnEnable() + { + upIcon = Resources.Load("Icons/up") as Texture2D; + downIcon = Resources.Load("Icons/down") as Texture2D; + addIcon = Resources.Load("Icons/add") as Texture2D; + duplicateIcon = Resources.Load("Icons/duplicate") as Texture2D; + deleteIcon = Resources.Load("Icons/delete") as Texture2D; + } + + public virtual void DrawBlockName(Flowchart flowchart) + { + serializedObject.Update(); + + SerializedProperty blockNameProperty = serializedObject.FindProperty("blockName"); + Rect blockLabelRect = new Rect(45, 5, 120, 16); + EditorGUI.LabelField(blockLabelRect, new GUIContent("Block Name")); + Rect blockNameRect = new Rect(45, 21, 180, 16); + EditorGUI.PropertyField(blockNameRect, blockNameProperty, new GUIContent("")); + + // Ensure block name is unique for this Flowchart + Block block = target as Block; + string uniqueName = flowchart.GetUniqueBlockKey(blockNameProperty.stringValue, block); + if (uniqueName != block.blockName) + { + blockNameProperty.stringValue = uniqueName; + } + + serializedObject.ApplyModifiedProperties(); + } + + public virtual void DrawBlockGUI(Flowchart flowchart) + { + serializedObject.Update(); + + // Execute any queued cut, copy, paste, etc. operations from the prevous GUI update + // We need to defer applying these operations until the following update because + // the ReorderableList control emits GUI errors if you clear the list in the same frame + // as drawing the control (e.g. select all and then delete) + if (Event.current.type == EventType.Layout) + { + foreach (Action action in actionList) + { + if (action != null) + { + action(); + } + } + actionList.Clear(); + } + + Block block = target as Block; + + SerializedProperty commandListProperty = serializedObject.FindProperty("commandList"); + + if (block == flowchart.selectedBlock) + { + SerializedProperty descriptionProp = serializedObject.FindProperty("description"); + EditorGUILayout.PropertyField(descriptionProp); + + DrawEventHandlerGUI(flowchart); + + block.UpdateIndentLevels(); + + // Make sure each command has a reference to its parent block + foreach (Command command in block.commandList) + { + if (command == null) // Will be deleted from the list later on + { + continue; + } + command.parentBlock = block; + } + + ReorderableListGUI.Title("Commands"); + CommandListAdaptor adaptor = new CommandListAdaptor(commandListProperty, 0); + adaptor.nodeRect = block.nodeRect; + + ReorderableListFlags flags = ReorderableListFlags.HideAddButton | ReorderableListFlags.HideRemoveButtons | ReorderableListFlags.DisableContextMenu; + + if (block.commandList.Count == 0) + { + EditorGUILayout.HelpBox("Press the + button below to add a command to the list.", MessageType.Info); + } + else + { + ReorderableListControl.DrawControlFromState(adaptor, null, flags); + } + + // EventType.contextClick doesn't register since we moved the Block Editor to be inside + // a GUI Area, no idea why. As a workaround we just check for right click instead. + if (Event.current.type == EventType.mouseUp && + Event.current.button == 1) + { + ShowContextMenu(); + Event.current.Use(); + } + + if (GUIUtility.keyboardControl == 0) //Only call keyboard shortcuts when not typing in a text field + { + Event e = Event.current; + + // Copy keyboard shortcut + if (e.type == EventType.ValidateCommand && e.commandName == "Copy") + { + if (flowchart.selectedCommands.Count > 0) + { + e.Use(); + } + } + + if (e.type == EventType.ExecuteCommand && e.commandName == "Copy") + { + actionList.Add(Copy); + e.Use(); + } + + // Cut keyboard shortcut + if (e.type == EventType.ValidateCommand && e.commandName == "Cut") + { + if (flowchart.selectedCommands.Count > 0) + { + e.Use(); + } + } + + if (e.type == EventType.ExecuteCommand && e.commandName == "Cut") + { + actionList.Add(Cut); + e.Use(); + } + + // Paste keyboard shortcut + if (e.type == EventType.ValidateCommand && e.commandName == "Paste") + { + CommandCopyBuffer commandCopyBuffer = CommandCopyBuffer.GetInstance(); + if (commandCopyBuffer.HasCommands()) + { + e.Use(); + } + } + + if (e.type == EventType.ExecuteCommand && e.commandName == "Paste") + { + actionList.Add(Paste); + e.Use(); + } + + // Duplicate keyboard shortcut + if (e.type == EventType.ValidateCommand && e.commandName == "Duplicate") + { + if (flowchart.selectedCommands.Count > 0) + { + e.Use(); + } + } + + if (e.type == EventType.ExecuteCommand && e.commandName == "Duplicate") + { + actionList.Add(Copy); + actionList.Add(Paste); + e.Use(); + } + + // Delete keyboard shortcut + if (e.type == EventType.ValidateCommand && e.commandName == "Delete") + { + if (flowchart.selectedCommands.Count > 0) + { + e.Use(); + } + } + + if (e.type == EventType.ExecuteCommand && e.commandName == "Delete") + { + actionList.Add(Delete); + e.Use(); + } + + // SelectAll keyboard shortcut + if (e.type == EventType.ValidateCommand && e.commandName == "SelectAll") + { + e.Use(); + } + + if (e.type == EventType.ExecuteCommand && e.commandName == "SelectAll") + { + actionList.Add(SelectAll); + e.Use(); + } + } + + } + + // Remove any null entries in the command list. + // This can happen when a command class is deleted or renamed. + for (int i = commandListProperty.arraySize - 1; i >= 0; --i) + { + SerializedProperty commandProperty = commandListProperty.GetArrayElementAtIndex(i); + if (commandProperty.objectReferenceValue == null) + { + commandListProperty.DeleteArrayElementAtIndex(i); + } + } + + serializedObject.ApplyModifiedProperties(); + } + + public virtual void DrawButtonToolbar() + { + GUILayout.BeginHorizontal(); + + // Previous Command + if ((Event.current.type == EventType.keyDown) && (Event.current.keyCode == KeyCode.PageUp)) + { + SelectPrevious(); + GUI.FocusControl("dummycontrol"); + Event.current.Use(); + } + // Next Command + if ((Event.current.type == EventType.keyDown) && (Event.current.keyCode == KeyCode.PageDown)) + { + SelectNext(); + GUI.FocusControl("dummycontrol"); + Event.current.Use(); + } + + if (GUILayout.Button(upIcon)) + { + SelectPrevious(); + } + + // Down Button + if (GUILayout.Button(downIcon)) + { + SelectNext(); + } + + GUILayout.FlexibleSpace(); + + // Add Button + if (GUILayout.Button(addIcon)) + { + ShowCommandMenu(); + } + + // Duplicate Button + if (GUILayout.Button(duplicateIcon)) + { + Copy(); + Paste(); + } + + // Delete Button + if (GUILayout.Button(deleteIcon)) + { + Delete(); + } + + GUILayout.EndHorizontal(); + } + + protected virtual void DrawEventHandlerGUI(Flowchart flowchart) + { + // Show available Event Handlers in a drop down list with type of current + // event handler selected. + List eventHandlerTypes = EditorExtensions.FindDerivedTypes(typeof(EventHandler)).ToList(); + + Block block = target as Block; + System.Type currentType = null; + if (block.eventHandler != null) + { + currentType = block.eventHandler.GetType(); + } + + string currentHandlerName = ""; + if (currentType != null) + { + EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(currentType); + if (info != null) + { + currentHandlerName = info.EventHandlerName; + } + } + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.PrefixLabel(new GUIContent("Execute On Event")); + if (GUILayout.Button(new GUIContent(currentHandlerName), EditorStyles.popup)) + { + SetEventHandlerOperation noneOperation = new SetEventHandlerOperation(); + noneOperation.block = block; + noneOperation.eventHandlerType = null; + + GenericMenu eventHandlerMenu = new GenericMenu(); + eventHandlerMenu.AddItem(new GUIContent("None"), false, OnSelectEventHandler, noneOperation); + + // Add event handlers with no category first + foreach (System.Type type in eventHandlerTypes) + { + EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(type); + if (info != null && + info.Category.Length == 0) + { + SetEventHandlerOperation operation = new SetEventHandlerOperation(); + operation.block = block; + operation.eventHandlerType = type; + + eventHandlerMenu.AddItem(new GUIContent(info.EventHandlerName), false, OnSelectEventHandler, operation); + } + } + + // Add event handlers with a category afterwards + foreach (System.Type type in eventHandlerTypes) + { + EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(type); + if (info != null && + info.Category.Length > 0) + { + SetEventHandlerOperation operation = new SetEventHandlerOperation(); + operation.block = block; + operation.eventHandlerType = type; + string typeName = info.Category + "/" + info.EventHandlerName; + eventHandlerMenu.AddItem(new GUIContent(typeName), false, OnSelectEventHandler, operation); + } + } + + + eventHandlerMenu.ShowAsContext(); + } + EditorGUILayout.EndHorizontal(); + + if (block.eventHandler != null) + { + EventHandlerEditor eventHandlerEditor = Editor.CreateEditor(block.eventHandler) as EventHandlerEditor; + if (eventHandlerEditor != null) + { + eventHandlerEditor.DrawInspectorGUI(); + DestroyImmediate(eventHandlerEditor); + } + } + } + + protected void OnSelectEventHandler(object obj) + { + SetEventHandlerOperation operation = obj as SetEventHandlerOperation; + Block block = operation.block; + System.Type selectedType = operation.eventHandlerType; + if (block == null) + { + return; + } + + Undo.RecordObject(block, "Set Event Handler"); + + if (block.eventHandler != null) + { + Undo.DestroyObjectImmediate(block.eventHandler); + } + + if (selectedType != null) + { + EventHandler newHandler = Undo.AddComponent(block.gameObject, selectedType) as EventHandler; + newHandler.parentBlock = block; + block.eventHandler = newHandler; + } + + // Because this is an async call, we need to force prefab instances to record changes + PrefabUtility.RecordPrefabInstancePropertyModifications(block); + } + + static public void BlockField(SerializedProperty property, GUIContent label, GUIContent nullLabel, Flowchart flowchart) + { + if (flowchart == null) + { + return; + } + + Block block = property.objectReferenceValue as Block; + + // Build dictionary of child blocks + List blockNames = new List(); + + int selectedIndex = 0; + blockNames.Add(nullLabel); + Block[] blocks = flowchart.GetComponents(); + for (int i = 0; i < blocks.Length; ++i) + { + blockNames.Add(new GUIContent(blocks[i].blockName)); + + if (block == blocks[i]) + { + selectedIndex = i + 1; + } + } + + selectedIndex = EditorGUILayout.Popup(label, selectedIndex, blockNames.ToArray()); + if (selectedIndex == 0) + { + block = null; // Option 'None' + } + else + { + block = blocks[selectedIndex - 1]; + } + + property.objectReferenceValue = block; + } + + static public Block BlockField(Rect position, GUIContent nullLabel, Flowchart flowchart, Block block) + { + if (flowchart == null) + { + return null; + } + + Block result = block; + + // Build dictionary of child blocks + List blockNames = new List(); + + int selectedIndex = 0; + blockNames.Add(nullLabel); + Block[] blocks = flowchart.GetComponents(); + for (int i = 0; i < blocks.Length; ++i) + { + blockNames.Add(new GUIContent(blocks[i].name)); + + if (block == blocks[i]) + { + selectedIndex = i + 1; + } + } + + selectedIndex = EditorGUI.Popup(position, selectedIndex, blockNames.ToArray()); + if (selectedIndex == 0) + { + result = null; // Option 'None' + } + else + { + result = blocks[selectedIndex - 1]; + } + + return result; + } + + // Compare delegate for sorting the list of command attributes + protected static int CompareCommandAttributes(KeyValuePair x, KeyValuePair y) + { + int compare = (x.Value.Category.CompareTo(y.Value.Category)); + if (compare == 0) + { + compare = (x.Value.CommandName.CompareTo(y.Value.CommandName)); + } + return compare; + } + + [MenuItem("Tools/Fungus/Utilities/Export Reference Docs")] + protected static void ExportReferenceDocs() + { + string path = EditorUtility.SaveFolderPanel("Export Reference Docs", "", ""); + if(path.Length == 0) + { + return; + } + + ExportCommandInfo(path); + ExportEventHandlerInfo(path); + + FlowchartWindow.ShowNotification("Exported Reference Documentation"); + } + + protected static void ExportCommandInfo(string path) + { + // Dump command info + List menuTypes = EditorExtensions.FindDerivedTypes(typeof(Command)).ToList(); + List> filteredAttributes = GetFilteredCommandInfoAttribute(menuTypes); + filteredAttributes.Sort( CompareCommandAttributes ); + + // Build list of command categories + List commandCategories = new List(); + foreach(var keyPair in filteredAttributes) + { + CommandInfoAttribute info = keyPair.Value; + if (info.Category != "" && + !commandCategories.Contains(info.Category)) + { + commandCategories.Add (info.Category); + } + } + commandCategories.Sort(); + + // Output the commands in each category + foreach (string category in commandCategories) + { + string markdown = ""; + foreach(var keyPair in filteredAttributes) + { + CommandInfoAttribute info = keyPair.Value; + + if (info.Category == category || + info.Category == "" && category == "Scripting") + { + markdown += "## " + info.CommandName + "\n"; + markdown += info.HelpText + "\n"; + markdown += GetPropertyInfo(keyPair.Key); + } + } + + string filePath = path + "/commands/" + category.ToLower() + "_commands.md"; + + Directory.CreateDirectory(Path.GetDirectoryName(filePath)); + File.WriteAllText(filePath, markdown); + } + } + + protected static void ExportEventHandlerInfo(string path) + { + List eventHandlerTypes = EditorExtensions.FindDerivedTypes(typeof(EventHandler)).ToList(); + List eventHandlerCategories = new List(); + eventHandlerCategories.Add("Core"); + foreach (System.Type type in eventHandlerTypes) + { + EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(type); + if (info != null && + info.Category != "" && + !eventHandlerCategories.Contains(info.Category)) + { + eventHandlerCategories.Add(info.Category); + } + } + eventHandlerCategories.Sort(); + + // Output the commands in each category + foreach (string category in eventHandlerCategories) + { + string markdown = ""; + + foreach (System.Type type in eventHandlerTypes) + { + EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(type); + + if (info != null && + info.Category == category || + info.Category == "" && category == "Core") + { + markdown += "## " + info.EventHandlerName + "\n"; + markdown += info.HelpText + "\n"; + markdown += GetPropertyInfo(type); + } + } + + string filePath = path + "/event_handlers/" + category.ToLower() + "_events.md"; + + Directory.CreateDirectory(Path.GetDirectoryName(filePath)); + File.WriteAllText(filePath, markdown); + } + } + + protected static string GetPropertyInfo(System.Type type) + { + string markdown = ""; + foreach(FieldInfo field in type.GetFields() ) + { + TooltipAttribute attribute = (TooltipAttribute)Attribute.GetCustomAttribute(field, typeof(TooltipAttribute)); + if (attribute == null ) + { + continue; + } + + // Change field name to how it's displayed in the inspector + string propertyName = Regex.Replace(field.Name, "(\\B[A-Z])", " $1"); + if (propertyName.Length > 1) + { + propertyName = propertyName.Substring(0,1).ToUpper() + propertyName.Substring(1); + } + else + { + propertyName = propertyName.ToUpper(); + } + + markdown += propertyName + " | " + field.FieldType + " | " + attribute.tooltip + "\n"; + } + + if (markdown.Length > 0) + { + markdown = "\nProperty | Type | Description\n --- | --- | ---\n" + markdown + "\n"; + } + + return markdown; + } + + protected virtual void ShowCommandMenu() + { + Block block = target as Block; + + Flowchart flowchart = block.GetFlowchart(); + + // Use index of last selected command in list, or end of list if nothing selected. + int index = -1; + foreach (Command command in flowchart.selectedCommands) + { + if (command.commandIndex + 1 > index) + { + index = command.commandIndex + 1; + } + } + if (index == -1) + { + index = block.commandList.Count; + } + + GenericMenu commandMenu = new GenericMenu(); + + // Build menu list + List menuTypes = EditorExtensions.FindDerivedTypes(typeof(Command)).ToList(); + List> filteredAttributes = GetFilteredCommandInfoAttribute(menuTypes); + + filteredAttributes.Sort( CompareCommandAttributes ); + + foreach(var keyPair in filteredAttributes) + { + // Skip command type if the Flowchart doesn't support it + if (!flowchart.IsCommandSupported(keyPair.Value)) + { + continue; + } + + AddCommandOperation commandOperation = new AddCommandOperation(); + + commandOperation.block = block; + commandOperation.commandType = keyPair.Key; + commandOperation.index = index; + + GUIContent menuItem; + if (keyPair.Value.Category == "") + { + menuItem = new GUIContent(keyPair.Value.CommandName); + } + else + { + menuItem = new GUIContent (keyPair.Value.Category + "/" + keyPair.Value.CommandName); + } + + commandMenu.AddItem(menuItem, false, AddCommandCallback, commandOperation); + } + + commandMenu.ShowAsContext(); + } + + protected static List> GetFilteredCommandInfoAttribute(List menuTypes) + { + Dictionary> filteredAttributes = new Dictionary>(); + + foreach (System.Type type in menuTypes) + { + object[] attributes = type.GetCustomAttributes(false); + foreach (object obj in attributes) + { + CommandInfoAttribute infoAttr = obj as CommandInfoAttribute; + if (infoAttr != null) + { + string dictionaryName = string.Format("{0}/{1}", infoAttr.Category, infoAttr.CommandName); + + int existingItemPriority = -1; + if (filteredAttributes.ContainsKey(dictionaryName)) + { + existingItemPriority = filteredAttributes[dictionaryName].Value.Priority; + } + + if (infoAttr.Priority > existingItemPriority) + { + KeyValuePair keyValuePair = new KeyValuePair(type, infoAttr); + filteredAttributes[dictionaryName] = keyValuePair; + } + } + } + } + return filteredAttributes.Values.ToList>(); + } + + protected static void AddCommandCallback(object obj) + { + AddCommandOperation commandOperation = obj as AddCommandOperation; + + Block block = commandOperation.block; + if (block == null) + { + return; + } + + Flowchart flowchart = block.GetFlowchart(); + + flowchart.ClearSelectedCommands(); + + Command newCommand = Undo.AddComponent(block.gameObject, commandOperation.commandType) as Command; + block.GetFlowchart().AddSelectedCommand(newCommand); + newCommand.parentBlock = block; + newCommand.itemId = flowchart.NextItemId(); + + // Let command know it has just been added to the block + newCommand.OnCommandAdded(block); + + Undo.RecordObject(block, "Set command type"); + if (commandOperation.index < block.commandList.Count - 1) + { + block.commandList.Insert(commandOperation.index, newCommand); + } + else + { + block.commandList.Add(newCommand); + } + + // Because this is an async call, we need to force prefab instances to record changes + PrefabUtility.RecordPrefabInstancePropertyModifications(block); + } + + public virtual void ShowContextMenu() + { + Block block = target as Block; + Flowchart flowchart = block.GetFlowchart(); + + if (flowchart == null) + { + return; + } + + bool showCut = false; + bool showCopy = false; + bool showDelete = false; + bool showPaste = false; bool showPlay = false; - if (flowchart.selectedCommands.Count > 0) - { - showCut = true; - showCopy = true; - showDelete = true; + if (flowchart.selectedCommands.Count > 0) + { + showCut = true; + showCopy = true; + showDelete = true; if (flowchart.selectedCommands.Count == 1 && Application.isPlaying) { showPlay = true; @@ -786,51 +786,51 @@ namespace Fungus } - - CommandCopyBuffer commandCopyBuffer = CommandCopyBuffer.GetInstance(); - - if (commandCopyBuffer.HasCommands()) - { - showPaste = true; - } - - GenericMenu commandMenu = new GenericMenu(); - - if (showCut) - { - commandMenu.AddItem (new GUIContent ("Cut"), false, Cut); - } - else - { - commandMenu.AddDisabledItem(new GUIContent ("Cut")); - } - - if (showCopy) - { - commandMenu.AddItem (new GUIContent ("Copy"), false, Copy); - } - else - { - commandMenu.AddDisabledItem(new GUIContent ("Copy")); - } - - if (showPaste) - { - commandMenu.AddItem (new GUIContent ("Paste"), false, Paste); - } - else - { - commandMenu.AddDisabledItem(new GUIContent ("Paste")); - } - - if (showDelete) - { - commandMenu.AddItem (new GUIContent ("Delete"), false, Delete); - } - else - { - commandMenu.AddDisabledItem(new GUIContent ("Delete")); - } + + CommandCopyBuffer commandCopyBuffer = CommandCopyBuffer.GetInstance(); + + if (commandCopyBuffer.HasCommands()) + { + showPaste = true; + } + + GenericMenu commandMenu = new GenericMenu(); + + if (showCut) + { + commandMenu.AddItem (new GUIContent ("Cut"), false, Cut); + } + else + { + commandMenu.AddDisabledItem(new GUIContent ("Cut")); + } + + if (showCopy) + { + commandMenu.AddItem (new GUIContent ("Copy"), false, Copy); + } + else + { + commandMenu.AddDisabledItem(new GUIContent ("Copy")); + } + + if (showPaste) + { + commandMenu.AddItem (new GUIContent ("Paste"), false, Paste); + } + else + { + commandMenu.AddDisabledItem(new GUIContent ("Paste")); + } + + if (showDelete) + { + commandMenu.AddItem (new GUIContent ("Delete"), false, Delete); + } + else + { + commandMenu.AddDisabledItem(new GUIContent ("Delete")); + } if (showPlay) { @@ -839,192 +839,192 @@ namespace Fungus } commandMenu.AddSeparator(""); - - commandMenu.AddItem (new GUIContent ("Select All"), false, SelectAll); - commandMenu.AddItem (new GUIContent ("Select None"), false, SelectNone); + + commandMenu.AddItem (new GUIContent ("Select All"), false, SelectAll); + commandMenu.AddItem (new GUIContent ("Select None"), false, SelectNone); commandMenu.ShowAsContext(); - } - - protected void SelectAll() - { - Block block = target as Block; - Flowchart flowchart = block.GetFlowchart(); - - if (flowchart == null || - flowchart.selectedBlock == null) - { - return; - } - - flowchart.ClearSelectedCommands(); - Undo.RecordObject(flowchart, "Select All"); - foreach (Command command in flowchart.selectedBlock.commandList) - { - flowchart.AddSelectedCommand(command); - } - - Repaint(); - } - - protected void SelectNone() - { - Block block = target as Block; - Flowchart flowchart = block.GetFlowchart(); - - if (flowchart == null || - flowchart.selectedBlock == null) - { - return; - } - - Undo.RecordObject(flowchart, "Select None"); - flowchart.ClearSelectedCommands(); - - Repaint(); - } - - protected void Cut() - { - Copy(); - Delete(); - } - - protected void Copy() - { - Block block = target as Block; - Flowchart flowchart = block.GetFlowchart(); - - if (flowchart == null || - flowchart.selectedBlock == null) - { - return; - } - - CommandCopyBuffer commandCopyBuffer = CommandCopyBuffer.GetInstance(); - commandCopyBuffer.Clear(); - - // Scan through all commands in execution order to see if each needs to be copied - foreach (Command command in flowchart.selectedBlock.commandList) - { - if (flowchart.selectedCommands.Contains(command)) - { - System.Type type = command.GetType(); - Command newCommand = Undo.AddComponent(commandCopyBuffer.gameObject, type) as Command; - System.Reflection.FieldInfo[] fields = type.GetFields(); - foreach (System.Reflection.FieldInfo field in fields) - { - field.SetValue(newCommand, field.GetValue(command)); - } - } - } - } - - protected void Paste() - { - Block block = target as Block; - Flowchart flowchart = block.GetFlowchart(); - - if (flowchart == null || - flowchart.selectedBlock == null) - { - return; - } - - CommandCopyBuffer commandCopyBuffer = CommandCopyBuffer.GetInstance(); - - // Find where to paste commands in block (either at end or after last selected command) - int pasteIndex = flowchart.selectedBlock.commandList.Count; - if (flowchart.selectedCommands.Count > 0) - { - for (int i = 0; i < flowchart.selectedBlock.commandList.Count; ++i) - { - Command command = flowchart.selectedBlock.commandList[i]; - - foreach (Command selectedCommand in flowchart.selectedCommands) - { - if (command == selectedCommand) - { - pasteIndex = i + 1; - } - } - } - } - - foreach (Command command in commandCopyBuffer.GetCommands()) - { - // Using the Editor copy / paste functionality instead instead of reflection - // because this does a deep copy of the command properties. - if (ComponentUtility.CopyComponent(command)) - { - if (ComponentUtility.PasteComponentAsNew(flowchart.gameObject)) - { - Command[] commands = flowchart.GetComponents(); - Command pastedCommand = commands.Last(); - if (pastedCommand != null) - { - pastedCommand.itemId = flowchart.NextItemId(); - flowchart.selectedBlock.commandList.Insert(pasteIndex++, pastedCommand); - } - } - - // This stops the user pasting the command manually into another game object. - ComponentUtility.CopyComponent(flowchart.transform); - } - } - - // Because this is an async call, we need to force prefab instances to record changes - PrefabUtility.RecordPrefabInstancePropertyModifications(block); - - Repaint(); - } - - protected void Delete() - { - Block block = target as Block; - Flowchart flowchart = block.GetFlowchart(); - - if (flowchart == null || - flowchart.selectedBlock == null) - { - return; - } - int lastSelectedIndex = 0; - for (int i = flowchart.selectedBlock.commandList.Count - 1; i >= 0; --i) - { - Command command = flowchart.selectedBlock.commandList[i]; - foreach (Command selectedCommand in flowchart.selectedCommands) - { - if (command == selectedCommand) - { - command.OnCommandRemoved(block); - - // Order of destruction is important here for undo to work - Undo.DestroyObjectImmediate(command); - - Undo.RecordObject(flowchart.selectedBlock, "Delete"); - flowchart.selectedBlock.commandList.RemoveAt(i); - - lastSelectedIndex = i; - - break; - } - } - } - - Undo.RecordObject(flowchart, "Delete"); - flowchart.ClearSelectedCommands(); - - if (lastSelectedIndex < flowchart.selectedBlock.commandList.Count) - { - Command nextCommand = flowchart.selectedBlock.commandList[lastSelectedIndex]; - block.GetFlowchart().AddSelectedCommand(nextCommand); - } - - Repaint(); - } - - protected void PlayCommand() + } + + protected void SelectAll() + { + Block block = target as Block; + Flowchart flowchart = block.GetFlowchart(); + + if (flowchart == null || + flowchart.selectedBlock == null) + { + return; + } + + flowchart.ClearSelectedCommands(); + Undo.RecordObject(flowchart, "Select All"); + foreach (Command command in flowchart.selectedBlock.commandList) + { + flowchart.AddSelectedCommand(command); + } + + Repaint(); + } + + protected void SelectNone() + { + Block block = target as Block; + Flowchart flowchart = block.GetFlowchart(); + + if (flowchart == null || + flowchart.selectedBlock == null) + { + return; + } + + Undo.RecordObject(flowchart, "Select None"); + flowchart.ClearSelectedCommands(); + + Repaint(); + } + + protected void Cut() + { + Copy(); + Delete(); + } + + protected void Copy() + { + Block block = target as Block; + Flowchart flowchart = block.GetFlowchart(); + + if (flowchart == null || + flowchart.selectedBlock == null) + { + return; + } + + CommandCopyBuffer commandCopyBuffer = CommandCopyBuffer.GetInstance(); + commandCopyBuffer.Clear(); + + // Scan through all commands in execution order to see if each needs to be copied + foreach (Command command in flowchart.selectedBlock.commandList) + { + if (flowchart.selectedCommands.Contains(command)) + { + System.Type type = command.GetType(); + Command newCommand = Undo.AddComponent(commandCopyBuffer.gameObject, type) as Command; + System.Reflection.FieldInfo[] fields = type.GetFields(); + foreach (System.Reflection.FieldInfo field in fields) + { + field.SetValue(newCommand, field.GetValue(command)); + } + } + } + } + + protected void Paste() + { + Block block = target as Block; + Flowchart flowchart = block.GetFlowchart(); + + if (flowchart == null || + flowchart.selectedBlock == null) + { + return; + } + + CommandCopyBuffer commandCopyBuffer = CommandCopyBuffer.GetInstance(); + + // Find where to paste commands in block (either at end or after last selected command) + int pasteIndex = flowchart.selectedBlock.commandList.Count; + if (flowchart.selectedCommands.Count > 0) + { + for (int i = 0; i < flowchart.selectedBlock.commandList.Count; ++i) + { + Command command = flowchart.selectedBlock.commandList[i]; + + foreach (Command selectedCommand in flowchart.selectedCommands) + { + if (command == selectedCommand) + { + pasteIndex = i + 1; + } + } + } + } + + foreach (Command command in commandCopyBuffer.GetCommands()) + { + // Using the Editor copy / paste functionality instead instead of reflection + // because this does a deep copy of the command properties. + if (ComponentUtility.CopyComponent(command)) + { + if (ComponentUtility.PasteComponentAsNew(flowchart.gameObject)) + { + Command[] commands = flowchart.GetComponents(); + Command pastedCommand = commands.Last(); + if (pastedCommand != null) + { + pastedCommand.itemId = flowchart.NextItemId(); + flowchart.selectedBlock.commandList.Insert(pasteIndex++, pastedCommand); + } + } + + // This stops the user pasting the command manually into another game object. + ComponentUtility.CopyComponent(flowchart.transform); + } + } + + // Because this is an async call, we need to force prefab instances to record changes + PrefabUtility.RecordPrefabInstancePropertyModifications(block); + + Repaint(); + } + + protected void Delete() + { + Block block = target as Block; + Flowchart flowchart = block.GetFlowchart(); + + if (flowchart == null || + flowchart.selectedBlock == null) + { + return; + } + int lastSelectedIndex = 0; + for (int i = flowchart.selectedBlock.commandList.Count - 1; i >= 0; --i) + { + Command command = flowchart.selectedBlock.commandList[i]; + foreach (Command selectedCommand in flowchart.selectedCommands) + { + if (command == selectedCommand) + { + command.OnCommandRemoved(block); + + // Order of destruction is important here for undo to work + Undo.DestroyObjectImmediate(command); + + Undo.RecordObject(flowchart.selectedBlock, "Delete"); + flowchart.selectedBlock.commandList.RemoveAt(i); + + lastSelectedIndex = i; + + break; + } + } + } + + Undo.RecordObject(flowchart, "Delete"); + flowchart.ClearSelectedCommands(); + + if (lastSelectedIndex < flowchart.selectedBlock.commandList.Count) + { + Command nextCommand = flowchart.selectedBlock.commandList[lastSelectedIndex]; + block.GetFlowchart().AddSelectedCommand(nextCommand); + } + + Repaint(); + } + + protected void PlayCommand() { Block targetBlock = target as Block; Flowchart flowchart = targetBlock.GetFlowchart(); @@ -1062,74 +1062,74 @@ namespace Fungus } protected void SelectPrevious() - { - Block block = target as Block; - Flowchart flowchart = block.GetFlowchart(); - - int firstSelectedIndex = flowchart.selectedBlock.commandList.Count; - bool firstSelectedCommandFound = false; - if (flowchart.selectedCommands.Count > 0) - { - for (int i = 0; i < flowchart.selectedBlock.commandList.Count; i++) - { - Command commandInBlock = flowchart.selectedBlock.commandList[i]; - - foreach (Command selectedCommand in flowchart.selectedCommands) - { - if (commandInBlock == selectedCommand) - { - if (!firstSelectedCommandFound) - { - firstSelectedIndex = i; - firstSelectedCommandFound = true; - break; - } - } - } - if (firstSelectedCommandFound) - { - break; - } - } - } - if (firstSelectedIndex > 0) - { - flowchart.ClearSelectedCommands(); - flowchart.AddSelectedCommand(flowchart.selectedBlock.commandList[firstSelectedIndex-1]); - } - - Repaint(); - } - - protected void SelectNext() - { - Block block = target as Block; - Flowchart flowchart = block.GetFlowchart(); - - int lastSelectedIndex = -1; - if (flowchart.selectedCommands.Count > 0) - { - for (int i = 0; i < flowchart.selectedBlock.commandList.Count; i++) - { - Command commandInBlock = flowchart.selectedBlock.commandList[i]; - - foreach (Command selectedCommand in flowchart.selectedCommands) - { - if (commandInBlock == selectedCommand) - { - lastSelectedIndex = i; - } - } - } - } - if (lastSelectedIndex < flowchart.selectedBlock.commandList.Count-1) - { - flowchart.ClearSelectedCommands(); - flowchart.AddSelectedCommand(flowchart.selectedBlock.commandList[lastSelectedIndex+1]); - } - - Repaint(); - } - } - + { + Block block = target as Block; + Flowchart flowchart = block.GetFlowchart(); + + int firstSelectedIndex = flowchart.selectedBlock.commandList.Count; + bool firstSelectedCommandFound = false; + if (flowchart.selectedCommands.Count > 0) + { + for (int i = 0; i < flowchart.selectedBlock.commandList.Count; i++) + { + Command commandInBlock = flowchart.selectedBlock.commandList[i]; + + foreach (Command selectedCommand in flowchart.selectedCommands) + { + if (commandInBlock == selectedCommand) + { + if (!firstSelectedCommandFound) + { + firstSelectedIndex = i; + firstSelectedCommandFound = true; + break; + } + } + } + if (firstSelectedCommandFound) + { + break; + } + } + } + if (firstSelectedIndex > 0) + { + flowchart.ClearSelectedCommands(); + flowchart.AddSelectedCommand(flowchart.selectedBlock.commandList[firstSelectedIndex-1]); + } + + Repaint(); + } + + protected void SelectNext() + { + Block block = target as Block; + Flowchart flowchart = block.GetFlowchart(); + + int lastSelectedIndex = -1; + if (flowchart.selectedCommands.Count > 0) + { + for (int i = 0; i < flowchart.selectedBlock.commandList.Count; i++) + { + Command commandInBlock = flowchart.selectedBlock.commandList[i]; + + foreach (Command selectedCommand in flowchart.selectedCommands) + { + if (commandInBlock == selectedCommand) + { + lastSelectedIndex = i; + } + } + } + } + if (lastSelectedIndex < flowchart.selectedBlock.commandList.Count-1) + { + flowchart.ClearSelectedCommands(); + flowchart.AddSelectedCommand(flowchart.selectedBlock.commandList[lastSelectedIndex+1]); + } + + Repaint(); + } + } + } \ No newline at end of file diff --git a/Assets/Fungus/Flowchart/Editor/BlockInspector.cs b/Assets/Fungus/Flowchart/Editor/BlockInspector.cs index 05fec847..35961ef2 100644 --- a/Assets/Fungus/Flowchart/Editor/BlockInspector.cs +++ b/Assets/Fungus/Flowchart/Editor/BlockInspector.cs @@ -12,122 +12,122 @@ using System.Linq; namespace Fungus { - /** - * Temp hidden object which lets us use the entire inspector window to inspect - * the block command list. - */ - public class BlockInspector : ScriptableObject - { - [FormerlySerializedAs("sequence")] - public Block block; - } - - /** - * Custom editor for the temp hidden object. - */ - [CustomEditor (typeof(BlockInspector), true)] - public class BlockInspectorEditor : Editor - { - protected Vector2 blockScrollPos; - protected Vector2 commandScrollPos; - protected bool resize = false; - protected bool clamp = false; - protected float topPanelHeight = 50; + /** + * Temp hidden object which lets us use the entire inspector window to inspect + * the block command list. + */ + public class BlockInspector : ScriptableObject + { + [FormerlySerializedAs("sequence")] + public Block block; + } + + /** + * Custom editor for the temp hidden object. + */ + [CustomEditor (typeof(BlockInspector), true)] + public class BlockInspectorEditor : Editor + { + protected Vector2 blockScrollPos; + protected Vector2 commandScrollPos; + protected bool resize = false; + protected bool clamp = false; + protected float topPanelHeight = 50; protected float windowHeight = 0f; - // Cache the block and command editors so we only create and destroy them - // when a different block / command is selected. - protected BlockEditor activeBlockEditor; - protected CommandEditor activeCommandEditor; - protected Command activeCommand; // Command currently being inspected - - // Cached command editors to avoid creating / destroying editors more than necessary - // This list is static so persists between - protected static List cachedCommandEditors = new List(); - - protected void OnDestroy() - { - ClearEditors(); - } - - protected void OnEnable() - { - ClearEditors(); - } - - protected void OnDisable() - { - ClearEditors(); - } - - protected void ClearEditors() - { - foreach (CommandEditor commandEditor in cachedCommandEditors) - { - DestroyImmediate(commandEditor); - } - - cachedCommandEditors.Clear(); - activeCommandEditor = null; - } - - public override void OnInspectorGUI () - { - BlockInspector blockInspector = target as BlockInspector; - Block block = blockInspector.block; - - if (block == null) - { - return; - } - - Flowchart flowchart = block.GetFlowchart(); - - if (activeBlockEditor == null || - block != activeBlockEditor.target) - { - DestroyImmediate(activeBlockEditor); - activeBlockEditor = Editor.CreateEditor(block) as BlockEditor; - } - - activeBlockEditor.DrawBlockName(flowchart); + // Cache the block and command editors so we only create and destroy them + // when a different block / command is selected. + protected BlockEditor activeBlockEditor; + protected CommandEditor activeCommandEditor; + protected Command activeCommand; // Command currently being inspected + + // Cached command editors to avoid creating / destroying editors more than necessary + // This list is static so persists between + protected static List cachedCommandEditors = new List(); + + protected void OnDestroy() + { + ClearEditors(); + } + + protected void OnEnable() + { + ClearEditors(); + } + + protected void OnDisable() + { + ClearEditors(); + } + + protected void ClearEditors() + { + foreach (CommandEditor commandEditor in cachedCommandEditors) + { + DestroyImmediate(commandEditor); + } + + cachedCommandEditors.Clear(); + activeCommandEditor = null; + } + + public override void OnInspectorGUI () + { + BlockInspector blockInspector = target as BlockInspector; + Block block = blockInspector.block; + + if (block == null) + { + return; + } + + Flowchart flowchart = block.GetFlowchart(); + + if (activeBlockEditor == null || + block != activeBlockEditor.target) + { + DestroyImmediate(activeBlockEditor); + activeBlockEditor = Editor.CreateEditor(block) as BlockEditor; + } + + activeBlockEditor.DrawBlockName(flowchart); UpdateWindowHeight(); float width = EditorGUIUtility.currentViewWidth; float height = windowHeight; - // Using a custom rect area to get the correct 5px indent for the scroll views - Rect blockRect = new Rect(5, topPanelHeight, width - 5, height + 10); - GUILayout.BeginArea(blockRect); - - blockScrollPos = GUILayout.BeginScrollView(blockScrollPos, GUILayout.Height(flowchart.blockViewHeight)); - activeBlockEditor.DrawBlockGUI(flowchart); - GUILayout.EndScrollView(); - - Command inspectCommand = null; - if (flowchart.selectedCommands.Count == 1) - { - inspectCommand = flowchart.selectedCommands[0]; - } - - if (Application.isPlaying && - inspectCommand != null && - inspectCommand.parentBlock != block) - { - GUILayout.EndArea(); - Repaint(); - return; - } - - // Only change the activeCommand at the start of the GUI call sequence - if (Event.current.type == EventType.Layout) - { - activeCommand = inspectCommand; - } - - DrawCommandUI(flowchart, inspectCommand); - } + // Using a custom rect area to get the correct 5px indent for the scroll views + Rect blockRect = new Rect(5, topPanelHeight, width - 5, height + 10); + GUILayout.BeginArea(blockRect); + + blockScrollPos = GUILayout.BeginScrollView(blockScrollPos, GUILayout.Height(flowchart.blockViewHeight)); + activeBlockEditor.DrawBlockGUI(flowchart); + GUILayout.EndScrollView(); + + Command inspectCommand = null; + if (flowchart.selectedCommands.Count == 1) + { + inspectCommand = flowchart.selectedCommands[0]; + } + + if (Application.isPlaying && + inspectCommand != null && + inspectCommand.parentBlock != block) + { + GUILayout.EndArea(); + Repaint(); + return; + } + + // Only change the activeCommand at the start of the GUI call sequence + if (Event.current.type == EventType.Layout) + { + activeCommand = inspectCommand; + } + + DrawCommandUI(flowchart, inspectCommand); + } /// /// In Unity 5.4, Screen.height returns the pixel height instead of the point height @@ -147,121 +147,121 @@ namespace Fungus } } - public void DrawCommandUI(Flowchart flowchart, Command inspectCommand) - { - ResizeScrollView(flowchart); - - GUILayout.Space(7); - - activeBlockEditor.DrawButtonToolbar(); - - commandScrollPos = GUILayout.BeginScrollView(commandScrollPos); - - if (inspectCommand != null) - { - if (activeCommandEditor == null || - inspectCommand != activeCommandEditor.target) - { - // See if we have a cached version of the command editor already, - var editors = (from e in cachedCommandEditors where (e != null && e.target == inspectCommand) select e); - - if (editors.Count() > 0) - { - // Use cached editor - activeCommandEditor = editors.First(); - } - else - { - // No cached editor, so create a new one. - activeCommandEditor = Editor.CreateEditor(inspectCommand) as CommandEditor; - cachedCommandEditors.Add(activeCommandEditor); - } - } - if (activeCommandEditor != null) - { - activeCommandEditor.DrawCommandInspectorGUI(); - } - } - - GUILayout.EndScrollView(); - - GUILayout.EndArea(); - - // Draw the resize bar after everything else has finished drawing - // This is mainly to avoid incorrect indenting. - Rect resizeRect = new Rect(0, topPanelHeight + flowchart.blockViewHeight + 1, Screen.width, 4f); - GUI.color = new Color(0.64f, 0.64f, 0.64f); - GUI.DrawTexture(resizeRect, EditorGUIUtility.whiteTexture); - resizeRect.height = 1; - GUI.color = new Color32(132, 132, 132, 255); - GUI.DrawTexture(resizeRect, EditorGUIUtility.whiteTexture); - resizeRect.y += 3; - GUI.DrawTexture(resizeRect, EditorGUIUtility.whiteTexture); - GUI.color = Color.white; - - Repaint(); - } - - private void ResizeScrollView(Flowchart flowchart) - { - Rect cursorChangeRect = new Rect(0, flowchart.blockViewHeight + 1, Screen.width, 4f); - - EditorGUIUtility.AddCursorRect(cursorChangeRect, MouseCursor.ResizeVertical); - - if (Event.current.type == EventType.mouseDown && cursorChangeRect.Contains(Event.current.mousePosition)) - { - resize = true; - } - - if (resize) - { - Undo.RecordObject(flowchart, "Resize view"); - flowchart.blockViewHeight = Event.current.mousePosition.y; - } - - ClampBlockViewHeight(flowchart); - - // Stop resizing if mouse is outside inspector window. - // This isn't standard Unity UI behavior but it is robust and safe. - if (resize && Event.current.type == EventType.mouseDrag) - { - Rect windowRect = new Rect(0, 0, Screen.width, Screen.height); - if (!windowRect.Contains(Event.current.mousePosition)) - { - resize = false; - } - } - - if (Event.current.type == EventType.MouseUp) - { - resize = false; - } - } - - protected virtual void ClampBlockViewHeight(Flowchart flowchart) - { - // Screen.height seems to temporarily reset to 480 for a single frame whenever a command like - // Copy, Paste, etc. happens. Only clamp the block view height when one of these operations is not occuring. - - if (Event.current.commandName != "") - { - clamp = false; - } - - if (clamp) - { - // Make sure block view is always clamped to visible area - float height = flowchart.blockViewHeight; - height = Mathf.Max(200, height); - height = Mathf.Min(Screen.height - 200,height); - flowchart.blockViewHeight = height; - } - - if (Event.current.type == EventType.Repaint) - { - clamp = true; - } - } - } + public void DrawCommandUI(Flowchart flowchart, Command inspectCommand) + { + ResizeScrollView(flowchart); + + GUILayout.Space(7); + + activeBlockEditor.DrawButtonToolbar(); + + commandScrollPos = GUILayout.BeginScrollView(commandScrollPos); + + if (inspectCommand != null) + { + if (activeCommandEditor == null || + inspectCommand != activeCommandEditor.target) + { + // See if we have a cached version of the command editor already, + var editors = (from e in cachedCommandEditors where (e != null && e.target == inspectCommand) select e); + + if (editors.Count() > 0) + { + // Use cached editor + activeCommandEditor = editors.First(); + } + else + { + // No cached editor, so create a new one. + activeCommandEditor = Editor.CreateEditor(inspectCommand) as CommandEditor; + cachedCommandEditors.Add(activeCommandEditor); + } + } + if (activeCommandEditor != null) + { + activeCommandEditor.DrawCommandInspectorGUI(); + } + } + + GUILayout.EndScrollView(); + + GUILayout.EndArea(); + + // Draw the resize bar after everything else has finished drawing + // This is mainly to avoid incorrect indenting. + Rect resizeRect = new Rect(0, topPanelHeight + flowchart.blockViewHeight + 1, Screen.width, 4f); + GUI.color = new Color(0.64f, 0.64f, 0.64f); + GUI.DrawTexture(resizeRect, EditorGUIUtility.whiteTexture); + resizeRect.height = 1; + GUI.color = new Color32(132, 132, 132, 255); + GUI.DrawTexture(resizeRect, EditorGUIUtility.whiteTexture); + resizeRect.y += 3; + GUI.DrawTexture(resizeRect, EditorGUIUtility.whiteTexture); + GUI.color = Color.white; + + Repaint(); + } + + private void ResizeScrollView(Flowchart flowchart) + { + Rect cursorChangeRect = new Rect(0, flowchart.blockViewHeight + 1, Screen.width, 4f); + + EditorGUIUtility.AddCursorRect(cursorChangeRect, MouseCursor.ResizeVertical); + + if (Event.current.type == EventType.mouseDown && cursorChangeRect.Contains(Event.current.mousePosition)) + { + resize = true; + } + + if (resize) + { + Undo.RecordObject(flowchart, "Resize view"); + flowchart.blockViewHeight = Event.current.mousePosition.y; + } + + ClampBlockViewHeight(flowchart); + + // Stop resizing if mouse is outside inspector window. + // This isn't standard Unity UI behavior but it is robust and safe. + if (resize && Event.current.type == EventType.mouseDrag) + { + Rect windowRect = new Rect(0, 0, Screen.width, Screen.height); + if (!windowRect.Contains(Event.current.mousePosition)) + { + resize = false; + } + } + + if (Event.current.type == EventType.MouseUp) + { + resize = false; + } + } + + protected virtual void ClampBlockViewHeight(Flowchart flowchart) + { + // Screen.height seems to temporarily reset to 480 for a single frame whenever a command like + // Copy, Paste, etc. happens. Only clamp the block view height when one of these operations is not occuring. + + if (Event.current.commandName != "") + { + clamp = false; + } + + if (clamp) + { + // Make sure block view is always clamped to visible area + float height = flowchart.blockViewHeight; + height = Mathf.Max(200, height); + height = Mathf.Min(Screen.height - 200,height); + flowchart.blockViewHeight = height; + } + + if (Event.current.type == EventType.Repaint) + { + clamp = true; + } + } + } } diff --git a/Assets/Fungus/Flowchart/Editor/CallEditor.cs b/Assets/Fungus/Flowchart/Editor/CallEditor.cs index ad0aa554..da3ffda1 100644 --- a/Assets/Fungus/Flowchart/Editor/CallEditor.cs +++ b/Assets/Fungus/Flowchart/Editor/CallEditor.cs @@ -10,57 +10,57 @@ using System.Collections.Generic; namespace Fungus { - [CustomEditor (typeof(Call))] - public class CallEditor : CommandEditor - { - protected SerializedProperty targetFlowchartProp; - protected SerializedProperty targetBlockProp; - protected SerializedProperty startIndexProp; - protected SerializedProperty callModeProp; + [CustomEditor (typeof(Call))] + public class CallEditor : CommandEditor + { + protected SerializedProperty targetFlowchartProp; + protected SerializedProperty targetBlockProp; + protected SerializedProperty startIndexProp; + protected SerializedProperty callModeProp; - protected virtual void OnEnable() - { - if (NullTargetCheck()) // Check for an orphaned editor instance - return; + protected virtual void OnEnable() + { + if (NullTargetCheck()) // Check for an orphaned editor instance + return; - targetFlowchartProp = serializedObject.FindProperty("targetFlowchart"); - targetBlockProp = serializedObject.FindProperty("targetBlock"); - startIndexProp = serializedObject.FindProperty("startIndex"); - callModeProp = serializedObject.FindProperty("callMode"); - } + targetFlowchartProp = serializedObject.FindProperty("targetFlowchart"); + targetBlockProp = serializedObject.FindProperty("targetBlock"); + startIndexProp = serializedObject.FindProperty("startIndex"); + callModeProp = serializedObject.FindProperty("callMode"); + } - public override void DrawCommandGUI() - { - serializedObject.Update(); + public override void DrawCommandGUI() + { + serializedObject.Update(); - Call t = target as Call; + Call t = target as Call; - Flowchart flowchart = null; - if (targetFlowchartProp.objectReferenceValue == null) - { - flowchart = t.GetFlowchart(); - } - else - { - flowchart = targetFlowchartProp.objectReferenceValue as Flowchart; - } + Flowchart flowchart = null; + if (targetFlowchartProp.objectReferenceValue == null) + { + flowchart = t.GetFlowchart(); + } + else + { + flowchart = targetFlowchartProp.objectReferenceValue as Flowchart; + } - EditorGUILayout.PropertyField(targetFlowchartProp); + EditorGUILayout.PropertyField(targetFlowchartProp); - if (flowchart != null) - { - BlockEditor.BlockField(targetBlockProp, - new GUIContent("Target Block", "Block to call"), - new GUIContent(""), - flowchart); + if (flowchart != null) + { + BlockEditor.BlockField(targetBlockProp, + new GUIContent("Target Block", "Block to call"), + new GUIContent(""), + flowchart); - EditorGUILayout.PropertyField(startIndexProp); - } + EditorGUILayout.PropertyField(startIndexProp); + } - EditorGUILayout.PropertyField(callModeProp); + EditorGUILayout.PropertyField(callModeProp); - serializedObject.ApplyModifiedProperties(); - } - } + serializedObject.ApplyModifiedProperties(); + } + } } diff --git a/Assets/Fungus/Flowchart/Editor/CommandEditor.cs b/Assets/Fungus/Flowchart/Editor/CommandEditor.cs index 6f2f2831..285d43ab 100644 --- a/Assets/Fungus/Flowchart/Editor/CommandEditor.cs +++ b/Assets/Fungus/Flowchart/Editor/CommandEditor.cs @@ -13,219 +13,219 @@ using Rotorz.ReorderableList; namespace Fungus { - [CustomEditor (typeof(Command), true)] - public class CommandEditor : Editor - { - public static Command selectedCommand; - - public static CommandInfoAttribute GetCommandInfo(System.Type commandType) - { - object[] attributes = commandType.GetCustomAttributes(typeof(CommandInfoAttribute), false); - foreach (object obj in attributes) - { - CommandInfoAttribute commandInfoAttr = obj as CommandInfoAttribute; - if (commandInfoAttr != null) - { - return commandInfoAttr; - } - } - - return null; - } - - public virtual void DrawCommandInspectorGUI() - { - Command t = target as Command; - if (t == null) - { - return; - } - - Flowchart flowchart = t.GetFlowchart(); - if (flowchart == null) - { - return; - } - - CommandInfoAttribute commandInfoAttr = CommandEditor.GetCommandInfo(t.GetType()); - if (commandInfoAttr == null) - { - return; - } - - GUILayout.BeginVertical(GUI.skin.box); - - if (t.enabled) - { - if (flowchart.colorCommands) - { - GUI.backgroundColor = t.GetButtonColor(); - } - else - { - GUI.backgroundColor = Color.white; - } - } - else - { - GUI.backgroundColor = Color.grey; - } - GUILayout.BeginHorizontal(GUI.skin.button); - - string commandName = commandInfoAttr.CommandName; - GUILayout.Label(commandName, GUILayout.MinWidth(80), GUILayout.ExpandWidth(true)); - - GUILayout.FlexibleSpace(); - - GUILayout.Label(new GUIContent("(" + t.itemId + ")")); - - GUILayout.Space(10); - - GUI.backgroundColor = Color.white; - bool enabled = t.enabled; - enabled = GUILayout.Toggle(enabled, new GUIContent()); - - if (t.enabled != enabled) - { - Undo.RecordObject(t, "Set Enabled"); - t.enabled = enabled; - } - - GUILayout.EndHorizontal(); - GUI.backgroundColor = Color.white; - - EditorGUILayout.Separator(); - - DrawCommandGUI(); - - EditorGUILayout.Separator(); - - if (t.errorMessage.Length > 0) - { - GUIStyle style = new GUIStyle(GUI.skin.label); - style.normal.textColor = new Color(1,0,0); - EditorGUILayout.LabelField(new GUIContent("Error: " + t.errorMessage), style); - } - - GUILayout.EndVertical(); - - // Display help text - CommandInfoAttribute infoAttr = CommandEditor.GetCommandInfo(t.GetType()); - if (infoAttr != null) - { - EditorGUILayout.HelpBox(infoAttr.HelpText, MessageType.Info, true); - } - } - - public virtual void DrawCommandGUI() - { - Command t = target as Command; - - // Code below was copied from here - // http://answers.unity3d.com/questions/550829/how-to-add-a-script-field-in-custom-inspector.html - - // Users should not be able to change the MonoScript for the command using the usual Script field. - // Doing so could cause block.commandList to contain null entries. - // To avoid this we manually display all properties, except for m_Script. - serializedObject.Update(); - SerializedProperty iterator = serializedObject.GetIterator(); - bool enterChildren = true; - while (iterator.NextVisible(enterChildren)) - { - enterChildren = false; - - if (iterator.name == "m_Script") - { - continue; - } - - if (!t.IsPropertyVisible(iterator.name)) - { - continue; - } - - if (iterator.isArray && - t.IsReorderableArray(iterator.name)) - { - ReorderableListGUI.Title(new GUIContent(iterator.displayName, iterator.tooltip)); - ReorderableListGUI.ListField(iterator); - } - else - { - EditorGUILayout.PropertyField(iterator, true, new GUILayoutOption[0]); - } - } - - serializedObject.ApplyModifiedProperties(); - } + [CustomEditor (typeof(Command), true)] + public class CommandEditor : Editor + { + public static Command selectedCommand; + + public static CommandInfoAttribute GetCommandInfo(System.Type commandType) + { + object[] attributes = commandType.GetCustomAttributes(typeof(CommandInfoAttribute), false); + foreach (object obj in attributes) + { + CommandInfoAttribute commandInfoAttr = obj as CommandInfoAttribute; + if (commandInfoAttr != null) + { + return commandInfoAttr; + } + } + + return null; + } + + public virtual void DrawCommandInspectorGUI() + { + Command t = target as Command; + if (t == null) + { + return; + } + + Flowchart flowchart = t.GetFlowchart(); + if (flowchart == null) + { + return; + } + + CommandInfoAttribute commandInfoAttr = CommandEditor.GetCommandInfo(t.GetType()); + if (commandInfoAttr == null) + { + return; + } + + GUILayout.BeginVertical(GUI.skin.box); + + if (t.enabled) + { + if (flowchart.colorCommands) + { + GUI.backgroundColor = t.GetButtonColor(); + } + else + { + GUI.backgroundColor = Color.white; + } + } + else + { + GUI.backgroundColor = Color.grey; + } + GUILayout.BeginHorizontal(GUI.skin.button); + + string commandName = commandInfoAttr.CommandName; + GUILayout.Label(commandName, GUILayout.MinWidth(80), GUILayout.ExpandWidth(true)); + + GUILayout.FlexibleSpace(); + + GUILayout.Label(new GUIContent("(" + t.itemId + ")")); + + GUILayout.Space(10); + + GUI.backgroundColor = Color.white; + bool enabled = t.enabled; + enabled = GUILayout.Toggle(enabled, new GUIContent()); + + if (t.enabled != enabled) + { + Undo.RecordObject(t, "Set Enabled"); + t.enabled = enabled; + } + + GUILayout.EndHorizontal(); + GUI.backgroundColor = Color.white; + + EditorGUILayout.Separator(); + + DrawCommandGUI(); + + EditorGUILayout.Separator(); + + if (t.errorMessage.Length > 0) + { + GUIStyle style = new GUIStyle(GUI.skin.label); + style.normal.textColor = new Color(1,0,0); + EditorGUILayout.LabelField(new GUIContent("Error: " + t.errorMessage), style); + } + + GUILayout.EndVertical(); + + // Display help text + CommandInfoAttribute infoAttr = CommandEditor.GetCommandInfo(t.GetType()); + if (infoAttr != null) + { + EditorGUILayout.HelpBox(infoAttr.HelpText, MessageType.Info, true); + } + } + + public virtual void DrawCommandGUI() + { + Command t = target as Command; + + // Code below was copied from here + // http://answers.unity3d.com/questions/550829/how-to-add-a-script-field-in-custom-inspector.html + + // Users should not be able to change the MonoScript for the command using the usual Script field. + // Doing so could cause block.commandList to contain null entries. + // To avoid this we manually display all properties, except for m_Script. + serializedObject.Update(); + SerializedProperty iterator = serializedObject.GetIterator(); + bool enterChildren = true; + while (iterator.NextVisible(enterChildren)) + { + enterChildren = false; + + if (iterator.name == "m_Script") + { + continue; + } + + if (!t.IsPropertyVisible(iterator.name)) + { + continue; + } + + if (iterator.isArray && + t.IsReorderableArray(iterator.name)) + { + ReorderableListGUI.Title(new GUIContent(iterator.displayName, iterator.tooltip)); + ReorderableListGUI.ListField(iterator); + } + else + { + EditorGUILayout.PropertyField(iterator, true, new GUILayoutOption[0]); + } + } + + serializedObject.ApplyModifiedProperties(); + } - static public void ObjectField(SerializedProperty property, GUIContent label, GUIContent nullLabel, List objectList) where T : Object - { - if (property == null) - { - return; - } - - List objectNames = new List(); - - T selectedObject = property.objectReferenceValue as T; - - int selectedIndex = 0; - objectNames.Add(nullLabel); - for (int i = 0; i < objectList.Count; ++i) - { - if (objectList[i] == null) continue; - objectNames.Add(new GUIContent(objectList[i].name)); - + static public void ObjectField(SerializedProperty property, GUIContent label, GUIContent nullLabel, List objectList) where T : Object + { + if (property == null) + { + return; + } + + List objectNames = new List(); + + T selectedObject = property.objectReferenceValue as T; + + int selectedIndex = 0; + objectNames.Add(nullLabel); + for (int i = 0; i < objectList.Count; ++i) + { + if (objectList[i] == null) continue; + objectNames.Add(new GUIContent(objectList[i].name)); + - if (selectedObject == objectList[i]) - { - selectedIndex = i + 1; - } + if (selectedObject == objectList[i]) + { + selectedIndex = i + 1; + } - } + } - T result; + T result; - selectedIndex = EditorGUILayout.Popup(label, selectedIndex, objectNames.ToArray()); - if (selectedIndex == 0) - { - result = null; // Null option - } - else - { - result = objectList[selectedIndex - 1]; - } - - property.objectReferenceValue = result; - } + selectedIndex = EditorGUILayout.Popup(label, selectedIndex, objectNames.ToArray()); + if (selectedIndex == 0) + { + result = null; // Null option + } + else + { + result = objectList[selectedIndex - 1]; + } + + property.objectReferenceValue = result; + } - /** - * When modifying custom editor code you can occasionally end up with orphaned editor instances. - * When this happens, you'll get a null exception error every time the scene serializes / deserialized. - * Once this situation occurs, the only way to fix it is to restart the Unity editor. - * - * As a workaround, this function detects if this command editor is an orphan and deletes it. - * To use it, just call this function at the top of the OnEnable() method in your custom editor. - */ - protected virtual bool NullTargetCheck() - { - try - { - // The serializedObject accessor create a new SerializedObject if needed. - // However, this will fail with a null exception if the target object no longer exists. - #pragma warning disable 0219 - SerializedObject so = serializedObject; - } - catch (System.NullReferenceException) - { - DestroyImmediate(this); - return true; - } - - return false; - } - } + /** + * When modifying custom editor code you can occasionally end up with orphaned editor instances. + * When this happens, you'll get a null exception error every time the scene serializes / deserialized. + * Once this situation occurs, the only way to fix it is to restart the Unity editor. + * + * As a workaround, this function detects if this command editor is an orphan and deletes it. + * To use it, just call this function at the top of the OnEnable() method in your custom editor. + */ + protected virtual bool NullTargetCheck() + { + try + { + // The serializedObject accessor create a new SerializedObject if needed. + // However, this will fail with a null exception if the target object no longer exists. + #pragma warning disable 0219 + SerializedObject so = serializedObject; + } + catch (System.NullReferenceException) + { + DestroyImmediate(this); + return true; + } + + return false; + } + } } diff --git a/Assets/Fungus/Flowchart/Editor/CommandListAdaptor.cs b/Assets/Fungus/Flowchart/Editor/CommandListAdaptor.cs index d9717834..01e3a54a 100644 --- a/Assets/Fungus/Flowchart/Editor/CommandListAdaptor.cs +++ b/Assets/Fungus/Flowchart/Editor/CommandListAdaptor.cs @@ -16,460 +16,460 @@ using Rotorz.ReorderableList; namespace Fungus { - public class CommandListAdaptor : IReorderableListAdaptor { - - protected SerializedProperty _arrayProperty; - - public float fixedItemHeight; - - public Rect nodeRect = new Rect(); + public class CommandListAdaptor : IReorderableListAdaptor { + + protected SerializedProperty _arrayProperty; + + public float fixedItemHeight; + + public Rect nodeRect = new Rect(); - public SerializedProperty this[int index] { - get { return _arrayProperty.GetArrayElementAtIndex(index); } - } - - public SerializedProperty arrayProperty { - get { return _arrayProperty; } - } - - public CommandListAdaptor(SerializedProperty arrayProperty, float fixedItemHeight) { - if (arrayProperty == null) - throw new ArgumentNullException("Array property was null."); - if (!arrayProperty.isArray) - throw new InvalidOperationException("Specified serialized propery is not an array."); - - this._arrayProperty = arrayProperty; - this.fixedItemHeight = fixedItemHeight; - } - - public CommandListAdaptor(SerializedProperty arrayProperty) : this(arrayProperty, 0f) { - } - - public int Count { - get { return _arrayProperty.arraySize; } - } - - public virtual bool CanDrag(int index) { - return true; - } - - public virtual bool CanRemove(int index) { - return true; - } - - public void Add() { - Command newCommand = AddNewCommand(); - if (newCommand == null) - { - return; - } - - int newIndex = _arrayProperty.arraySize; - ++_arrayProperty.arraySize; - _arrayProperty.GetArrayElementAtIndex(newIndex).objectReferenceValue = newCommand; - } - - public void Insert(int index) { - Command newCommand = AddNewCommand(); - if (newCommand == null) - { - return; - } - - _arrayProperty.InsertArrayElementAtIndex(index); - _arrayProperty.GetArrayElementAtIndex(index).objectReferenceValue = newCommand; - } - - Command AddNewCommand() - { - Flowchart flowchart = FlowchartWindow.GetFlowchart(); - if (flowchart == null) - { - return null; - } - - Block block = flowchart.selectedBlock; - if (block == null) - { - return null; - } - - Command newCommand = Undo.AddComponent(block.gameObject) as Command; - newCommand.itemId = flowchart.NextItemId(); - flowchart.ClearSelectedCommands(); - flowchart.AddSelectedCommand(newCommand); - - return newCommand; - } - - public void Duplicate(int index) { - - Command command = _arrayProperty.GetArrayElementAtIndex(index).objectReferenceValue as Command; - - // Add the command as a new component - Block parentBlock = command.GetComponent(); - - System.Type type = command.GetType(); - Command newCommand = Undo.AddComponent(parentBlock.gameObject, type) as Command; - newCommand.itemId = newCommand.GetFlowchart().NextItemId(); - System.Reflection.FieldInfo[] fields = type.GetFields(); - foreach (System.Reflection.FieldInfo field in fields) - { - field.SetValue(newCommand, field.GetValue(command)); - } - - _arrayProperty.InsertArrayElementAtIndex(index); - _arrayProperty.GetArrayElementAtIndex(index).objectReferenceValue = newCommand; - } - - public void Remove(int index) { - // Remove the Fungus Command component - Command command = _arrayProperty.GetArrayElementAtIndex(index).objectReferenceValue as Command; - if (command != null) - { - Undo.DestroyObjectImmediate(command); - } - - _arrayProperty.GetArrayElementAtIndex(index).objectReferenceValue = null; - _arrayProperty.DeleteArrayElementAtIndex(index); - } - - public void Move(int sourceIndex, int destIndex) { - if (destIndex > sourceIndex) - --destIndex; - _arrayProperty.MoveArrayElement(sourceIndex, destIndex); - } - - public void Clear() { - while (Count > 0) - { - Remove(0); - } - } + public SerializedProperty this[int index] { + get { return _arrayProperty.GetArrayElementAtIndex(index); } + } + + public SerializedProperty arrayProperty { + get { return _arrayProperty; } + } + + public CommandListAdaptor(SerializedProperty arrayProperty, float fixedItemHeight) { + if (arrayProperty == null) + throw new ArgumentNullException("Array property was null."); + if (!arrayProperty.isArray) + throw new InvalidOperationException("Specified serialized propery is not an array."); + + this._arrayProperty = arrayProperty; + this.fixedItemHeight = fixedItemHeight; + } + + public CommandListAdaptor(SerializedProperty arrayProperty) : this(arrayProperty, 0f) { + } + + public int Count { + get { return _arrayProperty.arraySize; } + } + + public virtual bool CanDrag(int index) { + return true; + } + + public virtual bool CanRemove(int index) { + return true; + } + + public void Add() { + Command newCommand = AddNewCommand(); + if (newCommand == null) + { + return; + } + + int newIndex = _arrayProperty.arraySize; + ++_arrayProperty.arraySize; + _arrayProperty.GetArrayElementAtIndex(newIndex).objectReferenceValue = newCommand; + } + + public void Insert(int index) { + Command newCommand = AddNewCommand(); + if (newCommand == null) + { + return; + } + + _arrayProperty.InsertArrayElementAtIndex(index); + _arrayProperty.GetArrayElementAtIndex(index).objectReferenceValue = newCommand; + } + + Command AddNewCommand() + { + Flowchart flowchart = FlowchartWindow.GetFlowchart(); + if (flowchart == null) + { + return null; + } + + Block block = flowchart.selectedBlock; + if (block == null) + { + return null; + } + + Command newCommand = Undo.AddComponent(block.gameObject) as Command; + newCommand.itemId = flowchart.NextItemId(); + flowchart.ClearSelectedCommands(); + flowchart.AddSelectedCommand(newCommand); + + return newCommand; + } + + public void Duplicate(int index) { + + Command command = _arrayProperty.GetArrayElementAtIndex(index).objectReferenceValue as Command; + + // Add the command as a new component + Block parentBlock = command.GetComponent(); + + System.Type type = command.GetType(); + Command newCommand = Undo.AddComponent(parentBlock.gameObject, type) as Command; + newCommand.itemId = newCommand.GetFlowchart().NextItemId(); + System.Reflection.FieldInfo[] fields = type.GetFields(); + foreach (System.Reflection.FieldInfo field in fields) + { + field.SetValue(newCommand, field.GetValue(command)); + } + + _arrayProperty.InsertArrayElementAtIndex(index); + _arrayProperty.GetArrayElementAtIndex(index).objectReferenceValue = newCommand; + } + + public void Remove(int index) { + // Remove the Fungus Command component + Command command = _arrayProperty.GetArrayElementAtIndex(index).objectReferenceValue as Command; + if (command != null) + { + Undo.DestroyObjectImmediate(command); + } + + _arrayProperty.GetArrayElementAtIndex(index).objectReferenceValue = null; + _arrayProperty.DeleteArrayElementAtIndex(index); + } + + public void Move(int sourceIndex, int destIndex) { + if (destIndex > sourceIndex) + --destIndex; + _arrayProperty.MoveArrayElement(sourceIndex, destIndex); + } + + public void Clear() { + while (Count > 0) + { + Remove(0); + } + } - public void BeginGUI() - {} + public void BeginGUI() + {} - public void EndGUI() - {} + public void EndGUI() + {} - public void DrawItemBackground(Rect position, int index) { - } + public void DrawItemBackground(Rect position, int index) { + } - public void DrawItem(Rect position, int index) - { - Command command = this[index].objectReferenceValue as Command; - - if (command == null) - { - return; - } - - CommandInfoAttribute commandInfoAttr = CommandEditor.GetCommandInfo(command.GetType()); - if (commandInfoAttr == null) - { - return; - } - - Flowchart flowchart = command.GetFlowchart(); - if (flowchart == null) - { - return; - } - - bool isComment = command.GetType() == typeof(Comment); - bool isLabel = (command.GetType() == typeof(Label)); - - bool error = false; - string summary = command.GetSummary(); - if (summary == null) - { - summary = ""; - } - else - { - summary = summary.Replace("\n", "").Replace("\r", ""); - } - if (summary.StartsWith("Error:")) - { - error = true; - } + public void DrawItem(Rect position, int index) + { + Command command = this[index].objectReferenceValue as Command; + + if (command == null) + { + return; + } + + CommandInfoAttribute commandInfoAttr = CommandEditor.GetCommandInfo(command.GetType()); + if (commandInfoAttr == null) + { + return; + } + + Flowchart flowchart = command.GetFlowchart(); + if (flowchart == null) + { + return; + } + + bool isComment = command.GetType() == typeof(Comment); + bool isLabel = (command.GetType() == typeof(Label)); + + bool error = false; + string summary = command.GetSummary(); + if (summary == null) + { + summary = ""; + } + else + { + summary = summary.Replace("\n", "").Replace("\r", ""); + } + if (summary.StartsWith("Error:")) + { + error = true; + } - if (isComment || isLabel) - { - summary = " " + summary + ""; - } - else - { - summary = "" + summary + ""; - } + if (isComment || isLabel) + { + summary = " " + summary + ""; + } + else + { + summary = "" + summary + ""; + } - bool commandIsSelected = false; - foreach (Command selectedCommand in flowchart.selectedCommands) - { - if (selectedCommand == command) - { - commandIsSelected = true; - break; - } - } - - string commandName = commandInfoAttr.CommandName; - - GUIStyle commandLabelStyle = new GUIStyle(GUI.skin.box); - commandLabelStyle.normal.background = FungusEditorResources.texCommandBackground; - int borderSize = 5; - commandLabelStyle.border.top = borderSize; - commandLabelStyle.border.bottom = borderSize; - commandLabelStyle.border.left = borderSize; - commandLabelStyle.border.right = borderSize; - commandLabelStyle.alignment = TextAnchor.MiddleLeft; - commandLabelStyle.richText = true; - commandLabelStyle.fontSize = 11; - commandLabelStyle.padding.top -= 1; - - float indentSize = 20; - for (int i = 0; i < command.indentLevel; ++i) - { - Rect indentRect = position; - indentRect.x += i * indentSize - 21; - indentRect.width = indentSize + 1; - indentRect.y -= 2; - indentRect.height += 5; - GUI.backgroundColor = new Color(0.5f, 0.5f, 0.5f, 1f); - GUI.Box(indentRect, "", commandLabelStyle); - } - - float commandNameWidth = Mathf.Max(commandLabelStyle.CalcSize(new GUIContent(commandName)).x, 90f); - float indentWidth = command.indentLevel * indentSize; - - Rect commandLabelRect = position; - commandLabelRect.x += indentWidth - 21; - commandLabelRect.y -= 2; - commandLabelRect.width -= (indentSize * command.indentLevel - 22); - commandLabelRect.height += 5; + bool commandIsSelected = false; + foreach (Command selectedCommand in flowchart.selectedCommands) + { + if (selectedCommand == command) + { + commandIsSelected = true; + break; + } + } + + string commandName = commandInfoAttr.CommandName; + + GUIStyle commandLabelStyle = new GUIStyle(GUI.skin.box); + commandLabelStyle.normal.background = FungusEditorResources.texCommandBackground; + int borderSize = 5; + commandLabelStyle.border.top = borderSize; + commandLabelStyle.border.bottom = borderSize; + commandLabelStyle.border.left = borderSize; + commandLabelStyle.border.right = borderSize; + commandLabelStyle.alignment = TextAnchor.MiddleLeft; + commandLabelStyle.richText = true; + commandLabelStyle.fontSize = 11; + commandLabelStyle.padding.top -= 1; + + float indentSize = 20; + for (int i = 0; i < command.indentLevel; ++i) + { + Rect indentRect = position; + indentRect.x += i * indentSize - 21; + indentRect.width = indentSize + 1; + indentRect.y -= 2; + indentRect.height += 5; + GUI.backgroundColor = new Color(0.5f, 0.5f, 0.5f, 1f); + GUI.Box(indentRect, "", commandLabelStyle); + } + + float commandNameWidth = Mathf.Max(commandLabelStyle.CalcSize(new GUIContent(commandName)).x, 90f); + float indentWidth = command.indentLevel * indentSize; + + Rect commandLabelRect = position; + commandLabelRect.x += indentWidth - 21; + commandLabelRect.y -= 2; + commandLabelRect.width -= (indentSize * command.indentLevel - 22); + commandLabelRect.height += 5; - // There's a weird incompatibility between the Reorderable list control used for the command list and - // the UnityEvent list control used in some commands. In play mode, if you click on the reordering grabber - // for a command in the list it causes the UnityEvent list to spew null exception errors. - // The workaround for now is to hide the reordering grabber from mouse clicks by extending the command - // selection rectangle to cover it. We are planning to totally replace the command list display system. - Rect clickRect = position; - clickRect.x -= 20; - clickRect.width += 20; + // There's a weird incompatibility between the Reorderable list control used for the command list and + // the UnityEvent list control used in some commands. In play mode, if you click on the reordering grabber + // for a command in the list it causes the UnityEvent list to spew null exception errors. + // The workaround for now is to hide the reordering grabber from mouse clicks by extending the command + // selection rectangle to cover it. We are planning to totally replace the command list display system. + Rect clickRect = position; + clickRect.x -= 20; + clickRect.width += 20; - // Select command via left click - if (Event.current.type == EventType.MouseDown && - Event.current.button == 0 && - clickRect.Contains(Event.current.mousePosition)) - { - if (flowchart.selectedCommands.Contains(command) && Event.current.button == 0) - { - // Left click on already selected command - // Command key and shift key is not pressed - if (!EditorGUI.actionKey && !Event.current.shift) - { - BlockEditor.actionList.Add ( delegate { - flowchart.selectedCommands.Remove(command); - flowchart.ClearSelectedCommands(); - }); - } + // Select command via left click + if (Event.current.type == EventType.MouseDown && + Event.current.button == 0 && + clickRect.Contains(Event.current.mousePosition)) + { + if (flowchart.selectedCommands.Contains(command) && Event.current.button == 0) + { + // Left click on already selected command + // Command key and shift key is not pressed + if (!EditorGUI.actionKey && !Event.current.shift) + { + BlockEditor.actionList.Add ( delegate { + flowchart.selectedCommands.Remove(command); + flowchart.ClearSelectedCommands(); + }); + } - // Command key pressed - if (EditorGUI.actionKey) - { - BlockEditor.actionList.Add ( delegate { - flowchart.selectedCommands.Remove(command); - }); - Event.current.Use(); - } - } - else - { - bool shift = Event.current.shift; + // Command key pressed + if (EditorGUI.actionKey) + { + BlockEditor.actionList.Add ( delegate { + flowchart.selectedCommands.Remove(command); + }); + Event.current.Use(); + } + } + else + { + bool shift = Event.current.shift; - // Left click and no command key - if (!shift && !EditorGUI.actionKey && Event.current.button == 0) - { - BlockEditor.actionList.Add ( delegate { - flowchart.ClearSelectedCommands(); - }); - Event.current.Use(); - } + // Left click and no command key + if (!shift && !EditorGUI.actionKey && Event.current.button == 0) + { + BlockEditor.actionList.Add ( delegate { + flowchart.ClearSelectedCommands(); + }); + Event.current.Use(); + } - BlockEditor.actionList.Add ( delegate { - flowchart.AddSelectedCommand(command); - }); + BlockEditor.actionList.Add ( delegate { + flowchart.AddSelectedCommand(command); + }); - // Find first and last selected commands - int firstSelectedIndex = -1; - int lastSelectedIndex = -1; - if (flowchart.selectedCommands.Count > 0) - { - if ( flowchart.selectedBlock != null) - { - for (int i = 0; i < flowchart.selectedBlock.commandList.Count; i++) - { - Command commandInBlock = flowchart.selectedBlock.commandList[i]; - foreach (Command selectedCommand in flowchart.selectedCommands) - { - if (commandInBlock == selectedCommand) - { - firstSelectedIndex = i; - break; - } - } - } - for (int i = flowchart.selectedBlock.commandList.Count - 1; i >=0; i--) - { - Command commandInBlock = flowchart.selectedBlock.commandList[i]; - foreach (Command selectedCommand in flowchart.selectedCommands) - { - if (commandInBlock == selectedCommand) - { - lastSelectedIndex = i; - break; - } - } - } - } - } + // Find first and last selected commands + int firstSelectedIndex = -1; + int lastSelectedIndex = -1; + if (flowchart.selectedCommands.Count > 0) + { + if ( flowchart.selectedBlock != null) + { + for (int i = 0; i < flowchart.selectedBlock.commandList.Count; i++) + { + Command commandInBlock = flowchart.selectedBlock.commandList[i]; + foreach (Command selectedCommand in flowchart.selectedCommands) + { + if (commandInBlock == selectedCommand) + { + firstSelectedIndex = i; + break; + } + } + } + for (int i = flowchart.selectedBlock.commandList.Count - 1; i >=0; i--) + { + Command commandInBlock = flowchart.selectedBlock.commandList[i]; + foreach (Command selectedCommand in flowchart.selectedCommands) + { + if (commandInBlock == selectedCommand) + { + lastSelectedIndex = i; + break; + } + } + } + } + } - if (shift) - { - int currentIndex = command.commandIndex; - if (firstSelectedIndex == -1 || - lastSelectedIndex == -1) - { - // No selected command found - select entire list - firstSelectedIndex = 0; - lastSelectedIndex = currentIndex; - } - else - { - if (currentIndex < firstSelectedIndex) - { - firstSelectedIndex = currentIndex; - } - if (currentIndex > lastSelectedIndex) - { - lastSelectedIndex = currentIndex; - } - } + if (shift) + { + int currentIndex = command.commandIndex; + if (firstSelectedIndex == -1 || + lastSelectedIndex == -1) + { + // No selected command found - select entire list + firstSelectedIndex = 0; + lastSelectedIndex = currentIndex; + } + else + { + if (currentIndex < firstSelectedIndex) + { + firstSelectedIndex = currentIndex; + } + if (currentIndex > lastSelectedIndex) + { + lastSelectedIndex = currentIndex; + } + } - for (int i = Math.Min(firstSelectedIndex, lastSelectedIndex); i < Math.Max(firstSelectedIndex, lastSelectedIndex); ++i) - { - Command selectedCommand = flowchart.selectedBlock.commandList[i]; - BlockEditor.actionList.Add ( delegate { - flowchart.AddSelectedCommand(selectedCommand); - }); - } - } + for (int i = Math.Min(firstSelectedIndex, lastSelectedIndex); i < Math.Max(firstSelectedIndex, lastSelectedIndex); ++i) + { + Command selectedCommand = flowchart.selectedBlock.commandList[i]; + BlockEditor.actionList.Add ( delegate { + flowchart.AddSelectedCommand(selectedCommand); + }); + } + } - Event.current.Use(); - } - GUIUtility.keyboardControl = 0; // Fix for textarea not refeshing (change focus) - } - - Color commandLabelColor = Color.white; - if (flowchart.colorCommands) - { - commandLabelColor = command.GetButtonColor(); - } - - if (commandIsSelected) - { - commandLabelColor = Color.green; - } - else if (!command.enabled) - { - commandLabelColor = Color.grey; - } - else if (error) - { - // TODO: Show warning icon - } - - GUI.backgroundColor = commandLabelColor; - - if (isComment) - { - GUI.Label(commandLabelRect, "", commandLabelStyle); - } - else - { - string commandNameLabel; - if (flowchart.showLineNumbers) - { - commandNameLabel = command.commandIndex.ToString() + ": " + commandName; - } - else - { - commandNameLabel = commandName; - } + Event.current.Use(); + } + GUIUtility.keyboardControl = 0; // Fix for textarea not refeshing (change focus) + } + + Color commandLabelColor = Color.white; + if (flowchart.colorCommands) + { + commandLabelColor = command.GetButtonColor(); + } + + if (commandIsSelected) + { + commandLabelColor = Color.green; + } + else if (!command.enabled) + { + commandLabelColor = Color.grey; + } + else if (error) + { + // TODO: Show warning icon + } + + GUI.backgroundColor = commandLabelColor; + + if (isComment) + { + GUI.Label(commandLabelRect, "", commandLabelStyle); + } + else + { + string commandNameLabel; + if (flowchart.showLineNumbers) + { + commandNameLabel = command.commandIndex.ToString() + ": " + commandName; + } + else + { + commandNameLabel = commandName; + } - GUI.Label(commandLabelRect, commandNameLabel, commandLabelStyle); - } - - if (command.executingIconTimer > Time.realtimeSinceStartup) - { - Rect iconRect = new Rect(commandLabelRect); - iconRect.x += iconRect.width - commandLabelRect.width - 20; - iconRect.width = 20; - iconRect.height = 20; + GUI.Label(commandLabelRect, commandNameLabel, commandLabelStyle); + } + + if (command.executingIconTimer > Time.realtimeSinceStartup) + { + Rect iconRect = new Rect(commandLabelRect); + iconRect.x += iconRect.width - commandLabelRect.width - 20; + iconRect.width = 20; + iconRect.height = 20; - Color storeColor = GUI.color; + Color storeColor = GUI.color; - float alpha = (command.executingIconTimer - Time.realtimeSinceStartup) / Block.executingIconFadeTime; - alpha = Mathf.Clamp01(alpha); + float alpha = (command.executingIconTimer - Time.realtimeSinceStartup) / Block.executingIconFadeTime; + alpha = Mathf.Clamp01(alpha); - GUI.color = new Color(1f, 1f, 1f, alpha); - GUI.Label(iconRect, FungusEditorResources.texPlaySmall, new GUIStyle()); + GUI.color = new Color(1f, 1f, 1f, alpha); + GUI.Label(iconRect, FungusEditorResources.texPlaySmall, new GUIStyle()); - GUI.color = storeColor; - } - - Rect summaryRect = new Rect(commandLabelRect); - if (isComment) - { - summaryRect.x += 5; - } - else - { - summaryRect.x += commandNameWidth + 5; - summaryRect.width -= commandNameWidth + 5; - } - - GUIStyle summaryStyle = new GUIStyle(); - summaryStyle.fontSize = 10; - summaryStyle.padding.top += 5; - summaryStyle.richText = true; - summaryStyle.wordWrap = false; - summaryStyle.clipping = TextClipping.Clip; - commandLabelStyle.alignment = TextAnchor.MiddleLeft; - GUI.Label(summaryRect, summary, summaryStyle); - - if (error) - { - GUISkin editorSkin = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector); - Rect errorRect = new Rect(summaryRect); - errorRect.x += errorRect.width - 20; - errorRect.y += 2; - errorRect.width = 20; - GUI.Label(errorRect, editorSkin.GetStyle("CN EntryError").normal.background); - summaryRect.width -= 20; - } - - GUI.backgroundColor = Color.white; - } - - public virtual float GetItemHeight(int index) { - return fixedItemHeight != 0f - ? fixedItemHeight - : EditorGUI.GetPropertyHeight(this[index], GUIContent.none, false) - ; - } - - - } + GUI.color = storeColor; + } + + Rect summaryRect = new Rect(commandLabelRect); + if (isComment) + { + summaryRect.x += 5; + } + else + { + summaryRect.x += commandNameWidth + 5; + summaryRect.width -= commandNameWidth + 5; + } + + GUIStyle summaryStyle = new GUIStyle(); + summaryStyle.fontSize = 10; + summaryStyle.padding.top += 5; + summaryStyle.richText = true; + summaryStyle.wordWrap = false; + summaryStyle.clipping = TextClipping.Clip; + commandLabelStyle.alignment = TextAnchor.MiddleLeft; + GUI.Label(summaryRect, summary, summaryStyle); + + if (error) + { + GUISkin editorSkin = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector); + Rect errorRect = new Rect(summaryRect); + errorRect.x += errorRect.width - 20; + errorRect.y += 2; + errorRect.width = 20; + GUI.Label(errorRect, editorSkin.GetStyle("CN EntryError").normal.background); + summaryRect.width -= 20; + } + + GUI.backgroundColor = Color.white; + } + + public virtual float GetItemHeight(int index) { + return fixedItemHeight != 0f + ? fixedItemHeight + : EditorGUI.GetPropertyHeight(this[index], GUIContent.none, false) + ; + } + + + } } diff --git a/Assets/Fungus/Flowchart/Editor/CustomGUI.cs b/Assets/Fungus/Flowchart/Editor/CustomGUI.cs index e773e7f5..6ea94695 100644 --- a/Assets/Fungus/Flowchart/Editor/CustomGUI.cs +++ b/Assets/Fungus/Flowchart/Editor/CustomGUI.cs @@ -12,39 +12,39 @@ using System.Reflection; namespace Fungus { - /** - * Utility functions for drawing custom UI in the editor - */ - public static class CustomGUI - { - public static Texture2D CreateBlackTexture() - { - Texture2D blackTex = new Texture2D(1,2); - blackTex.SetPixel(0, 0, Color.gray); - blackTex.SetPixel(1, 0, Color.black); - blackTex.Apply(); - blackTex.hideFlags = HideFlags.DontSave; - return blackTex; - } + /** + * Utility functions for drawing custom UI in the editor + */ + public static class CustomGUI + { + public static Texture2D CreateBlackTexture() + { + Texture2D blackTex = new Texture2D(1,2); + blackTex.SetPixel(0, 0, Color.gray); + blackTex.SetPixel(1, 0, Color.black); + blackTex.Apply(); + blackTex.hideFlags = HideFlags.DontSave; + return blackTex; + } - public static Texture2D CreateColorTexture(Color color) - { - Texture2D colorTex = new Texture2D(1,1); - colorTex.SetPixel(0, 0, color); - colorTex.Apply(); - colorTex.hideFlags = HideFlags.DontSave; - return colorTex; - } - - public static void DrawLineSeparator(Texture2D blackTex, float width) - { - GUIStyle separatorStyle = new GUIStyle(GUI.skin.box); - separatorStyle.fixedWidth = width; - separatorStyle.fixedHeight = 2; - separatorStyle.margin.top = 10; - separatorStyle.margin.bottom = 10; - GUILayout.Box(blackTex,separatorStyle); - } - } + public static Texture2D CreateColorTexture(Color color) + { + Texture2D colorTex = new Texture2D(1,1); + colorTex.SetPixel(0, 0, color); + colorTex.Apply(); + colorTex.hideFlags = HideFlags.DontSave; + return colorTex; + } + + public static void DrawLineSeparator(Texture2D blackTex, float width) + { + GUIStyle separatorStyle = new GUIStyle(GUI.skin.box); + separatorStyle.fixedWidth = width; + separatorStyle.fixedHeight = 2; + separatorStyle.margin.top = 10; + separatorStyle.margin.bottom = 10; + GUILayout.Box(blackTex,separatorStyle); + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Flowchart/Editor/EditorExtensions.cs b/Assets/Fungus/Flowchart/Editor/EditorExtensions.cs old mode 100755 new mode 100644 index b002c67b..196f1b3e --- a/Assets/Fungus/Flowchart/Editor/EditorExtensions.cs +++ b/Assets/Fungus/Flowchart/Editor/EditorExtensions.cs @@ -11,62 +11,62 @@ using System.Linq; namespace Fungus { - public static class EditorExtensions - { - /// - /// FindDerivedTypesFromAssembly allows a user to query all of types derived from a - /// particular Type at runtime. - /// Example usage: - /// foreach (System.Type st in EditorUtility.FindDerivedTypesFromAssembly(System.Reflection.Assembly.GetAssembly(typeof(BaseTimelineEvent)), typeof(BaseTimelineEvent), true)) - /// - /// The assembly to search in - /// The base Type from which all returned Types derive - /// If true, only class Types will be returned - /// - public static System.Type[] FindDerivedTypesFromAssembly(this System.Reflection.Assembly assembly, System.Type baseType, bool classOnly = true) - { - if (assembly == null) - Debug.LogError("Assembly must be defined"); - if (baseType == null) - Debug.LogError("Base type must be defined"); + public static class EditorExtensions + { + /// + /// FindDerivedTypesFromAssembly allows a user to query all of types derived from a + /// particular Type at runtime. + /// Example usage: + /// foreach (System.Type st in EditorUtility.FindDerivedTypesFromAssembly(System.Reflection.Assembly.GetAssembly(typeof(BaseTimelineEvent)), typeof(BaseTimelineEvent), true)) + /// + /// The assembly to search in + /// The base Type from which all returned Types derive + /// If true, only class Types will be returned + /// + public static System.Type[] FindDerivedTypesFromAssembly(this System.Reflection.Assembly assembly, System.Type baseType, bool classOnly = true) + { + if (assembly == null) + Debug.LogError("Assembly must be defined"); + if (baseType == null) + Debug.LogError("Base type must be defined"); - // Iterate through all available types in the assembly - var types = assembly.GetTypes().Where(type => - { - if (classOnly && !type.IsClass) - return false; - - if (baseType.IsInterface) - { - var it = type.GetInterface(baseType.FullName); + // Iterate through all available types in the assembly + var types = assembly.GetTypes().Where(type => + { + if (classOnly && !type.IsClass) + return false; + + if (baseType.IsInterface) + { + var it = type.GetInterface(baseType.FullName); - if (it != null) - return true; - } - else if (type.IsSubclassOf(baseType)) - { - return true; - } + if (it != null) + return true; + } + else if (type.IsSubclassOf(baseType)) + { + return true; + } - return false; - } - ); + return false; + } + ); - return types.ToArray(); - } + return types.ToArray(); + } - /// - /// A convenient method for calling the above. - /// Example usage: - /// List subTypes = EditorUtility.FindDerivedTypes(typeof(BaseTimelineEvent)).ToList(); - /// - /// - /// - /// - public static System.Type[] FindDerivedTypes(System.Type baseType, bool classOnly = true) - { - return FindDerivedTypesFromAssembly(System.Reflection.Assembly.GetAssembly(baseType), baseType, classOnly); - } - } + /// + /// A convenient method for calling the above. + /// Example usage: + /// List subTypes = EditorUtility.FindDerivedTypes(typeof(BaseTimelineEvent)).ToList(); + /// + /// + /// + /// + public static System.Type[] FindDerivedTypes(System.Type baseType, bool classOnly = true) + { + return FindDerivedTypesFromAssembly(System.Reflection.Assembly.GetAssembly(baseType), baseType, classOnly); + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Flowchart/Editor/EditorZoomArea.cs b/Assets/Fungus/Flowchart/Editor/EditorZoomArea.cs index ba5dccac..b2f58013 100644 --- a/Assets/Fungus/Flowchart/Editor/EditorZoomArea.cs +++ b/Assets/Fungus/Flowchart/Editor/EditorZoomArea.cs @@ -10,82 +10,82 @@ using UnityEngine; namespace Fungus { - // Helper Rect extension methods - public static class RectExtensions - { - public static Vector2 TopLeft(this Rect rect) - { - return new Vector2(rect.xMin, rect.yMin); - } + // Helper Rect extension methods + public static class RectExtensions + { + public static Vector2 TopLeft(this Rect rect) + { + return new Vector2(rect.xMin, rect.yMin); + } - public static Rect ScaleSizeBy(this Rect rect, float scale) - { - return rect.ScaleSizeBy(scale, rect.center); - } + public static Rect ScaleSizeBy(this Rect rect, float scale) + { + return rect.ScaleSizeBy(scale, rect.center); + } - public static Rect ScaleSizeBy(this Rect rect, float scale, Vector2 pivotPoint) - { - Rect result = rect; - result.x -= pivotPoint.x; - result.y -= pivotPoint.y; - result.xMin *= scale; - result.xMax *= scale; - result.yMin *= scale; - result.yMax *= scale; - result.x += pivotPoint.x; - result.y += pivotPoint.y; - return result; - } + public static Rect ScaleSizeBy(this Rect rect, float scale, Vector2 pivotPoint) + { + Rect result = rect; + result.x -= pivotPoint.x; + result.y -= pivotPoint.y; + result.xMin *= scale; + result.xMax *= scale; + result.yMin *= scale; + result.yMax *= scale; + result.x += pivotPoint.x; + result.y += pivotPoint.y; + return result; + } - public static Rect ScaleSizeBy(this Rect rect, Vector2 scale) - { - return rect.ScaleSizeBy(scale, rect.center); - } + public static Rect ScaleSizeBy(this Rect rect, Vector2 scale) + { + return rect.ScaleSizeBy(scale, rect.center); + } - public static Rect ScaleSizeBy(this Rect rect, Vector2 scale, Vector2 pivotPoint) - { - Rect result = rect; - result.x -= pivotPoint.x; - result.y -= pivotPoint.y; - result.xMin *= scale.x; - result.xMax *= scale.x; - result.yMin *= scale.y; - result.yMax *= scale.y; - result.x += pivotPoint.x; - result.y += pivotPoint.y; - return result; - } - } + public static Rect ScaleSizeBy(this Rect rect, Vector2 scale, Vector2 pivotPoint) + { + Rect result = rect; + result.x -= pivotPoint.x; + result.y -= pivotPoint.y; + result.xMin *= scale.x; + result.xMax *= scale.x; + result.yMin *= scale.y; + result.yMax *= scale.y; + result.x += pivotPoint.x; + result.y += pivotPoint.y; + return result; + } + } - public class EditorZoomArea - { - private const float kEditorWindowTabHeight = 21.0f; - private static Matrix4x4 _prevGuiMatrix; - - public static Rect Begin(float zoomScale, Rect screenCoordsArea) - { - GUI.EndGroup(); // End the group Unity begins automatically for an EditorWindow to clip out the window tab. This allows us to draw outside of the size of the EditorWindow. - - Rect clippedArea = screenCoordsArea.ScaleSizeBy(1.0f / zoomScale, screenCoordsArea.TopLeft()); - clippedArea.y += kEditorWindowTabHeight; - GUI.BeginGroup(clippedArea); - - _prevGuiMatrix = GUI.matrix; + public class EditorZoomArea + { + private const float kEditorWindowTabHeight = 21.0f; + private static Matrix4x4 _prevGuiMatrix; + + public static Rect Begin(float zoomScale, Rect screenCoordsArea) + { + GUI.EndGroup(); // End the group Unity begins automatically for an EditorWindow to clip out the window tab. This allows us to draw outside of the size of the EditorWindow. + + Rect clippedArea = screenCoordsArea.ScaleSizeBy(1.0f / zoomScale, screenCoordsArea.TopLeft()); + clippedArea.y += kEditorWindowTabHeight; + GUI.BeginGroup(clippedArea); + + _prevGuiMatrix = GUI.matrix; Matrix4x4 translation = Matrix4x4.TRS(clippedArea.TopLeft(), Quaternion.identity, Vector3.one); - Matrix4x4 scale = Matrix4x4.Scale(new Vector3(zoomScale, zoomScale, 1.0f)); - GUI.matrix = translation * scale * translation.inverse * GUI.matrix; + Matrix4x4 scale = Matrix4x4.Scale(new Vector3(zoomScale, zoomScale, 1.0f)); + GUI.matrix = translation * scale * translation.inverse * GUI.matrix; return clippedArea; - } - - public static void End() - { - GUI.matrix = _prevGuiMatrix; - GUI.EndGroup(); - GUI.BeginGroup(new Rect(0.0f, kEditorWindowTabHeight, Screen.width, Screen.height)); - } - } + } + + public static void End() + { + GUI.matrix = _prevGuiMatrix; + GUI.EndGroup(); + GUI.BeginGroup(new Rect(0.0f, kEditorWindowTabHeight, Screen.width, Screen.height)); + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Flowchart/Editor/EventHandlerEditor.cs b/Assets/Fungus/Flowchart/Editor/EventHandlerEditor.cs index fa4e9b08..89b085c7 100644 --- a/Assets/Fungus/Flowchart/Editor/EventHandlerEditor.cs +++ b/Assets/Fungus/Flowchart/Editor/EventHandlerEditor.cs @@ -12,57 +12,57 @@ using System.Collections.Generic; namespace Fungus { - [CustomEditor (typeof(EventHandler), true)] - public class EventHandlerEditor : Editor - { - /** - * Returns the class attribute info for an event handler class. - */ - public static EventHandlerInfoAttribute GetEventHandlerInfo(System.Type eventHandlerType) - { - object[] attributes = eventHandlerType.GetCustomAttributes(typeof(EventHandlerInfoAttribute), false); - foreach (object obj in attributes) - { - EventHandlerInfoAttribute eventHandlerInfoAttr = obj as EventHandlerInfoAttribute; - if (eventHandlerInfoAttr != null) - { - return eventHandlerInfoAttr; - } - } - - return null; - } + [CustomEditor (typeof(EventHandler), true)] + public class EventHandlerEditor : Editor + { + /** + * Returns the class attribute info for an event handler class. + */ + public static EventHandlerInfoAttribute GetEventHandlerInfo(System.Type eventHandlerType) + { + object[] attributes = eventHandlerType.GetCustomAttributes(typeof(EventHandlerInfoAttribute), false); + foreach (object obj in attributes) + { + EventHandlerInfoAttribute eventHandlerInfoAttr = obj as EventHandlerInfoAttribute; + if (eventHandlerInfoAttr != null) + { + return eventHandlerInfoAttr; + } + } + + return null; + } - public virtual void DrawInspectorGUI() - { - // Users should not be able to change the MonoScript for the command using the usual Script field. - // Doing so could cause block.commandList to contain null entries. - // To avoid this we manually display all properties, except for m_Script. - serializedObject.Update(); - SerializedProperty iterator = serializedObject.GetIterator(); - bool enterChildren = true; - while (iterator.NextVisible(enterChildren)) - { - enterChildren = false; - - if (iterator.name == "m_Script") - { - continue; - } - - EditorGUILayout.PropertyField(iterator, true, new GUILayoutOption[0]); - } + public virtual void DrawInspectorGUI() + { + // Users should not be able to change the MonoScript for the command using the usual Script field. + // Doing so could cause block.commandList to contain null entries. + // To avoid this we manually display all properties, except for m_Script. + serializedObject.Update(); + SerializedProperty iterator = serializedObject.GetIterator(); + bool enterChildren = true; + while (iterator.NextVisible(enterChildren)) + { + enterChildren = false; + + if (iterator.name == "m_Script") + { + continue; + } + + EditorGUILayout.PropertyField(iterator, true, new GUILayoutOption[0]); + } - EventHandler t = target as EventHandler; - EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(t.GetType()); - if (info != null && - info.HelpText.Length > 0) - { - EditorGUILayout.HelpBox(info.HelpText, MessageType.Info); - } + EventHandler t = target as EventHandler; + EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(t.GetType()); + if (info != null && + info.HelpText.Length > 0) + { + EditorGUILayout.HelpBox(info.HelpText, MessageType.Info); + } - serializedObject.ApplyModifiedProperties(); - } - } + serializedObject.ApplyModifiedProperties(); + } + } } diff --git a/Assets/Fungus/Flowchart/Editor/Extensions/SerializedPropertyExtensions.cs b/Assets/Fungus/Flowchart/Editor/Extensions/SerializedPropertyExtensions.cs index b4337b34..31f1fe2d 100644 --- a/Assets/Fungus/Flowchart/Editor/Extensions/SerializedPropertyExtensions.cs +++ b/Assets/Fungus/Flowchart/Editor/Extensions/SerializedPropertyExtensions.cs @@ -39,7 +39,7 @@ namespace Fungus case "ColorRGBA": element.colorValue = Color.black; break; - + default: if (element.type.StartsWith("PPtr")) element.objectReferenceValue = null; diff --git a/Assets/Fungus/Flowchart/Editor/FlowchartEditor.cs b/Assets/Fungus/Flowchart/Editor/FlowchartEditor.cs index d332a7ba..94c66893 100644 --- a/Assets/Fungus/Flowchart/Editor/FlowchartEditor.cs +++ b/Assets/Fungus/Flowchart/Editor/FlowchartEditor.cs @@ -14,288 +14,288 @@ using System.IO; namespace Fungus { - [CustomEditor (typeof(Flowchart))] - public class FlowchartEditor : Editor - { - protected class AddVariableInfo - { - public Flowchart flowchart; - public System.Type variableType; - } - - protected SerializedProperty descriptionProp; - protected SerializedProperty colorCommandsProp; - protected SerializedProperty hideComponentsProp; - protected SerializedProperty stepPauseProp; - protected SerializedProperty saveSelectionProp; - protected SerializedProperty localizationIdProp; - protected SerializedProperty variablesProp; - protected SerializedProperty showLineNumbersProp; + [CustomEditor (typeof(Flowchart))] + public class FlowchartEditor : Editor + { + protected class AddVariableInfo + { + public Flowchart flowchart; + public System.Type variableType; + } + + protected SerializedProperty descriptionProp; + protected SerializedProperty colorCommandsProp; + protected SerializedProperty hideComponentsProp; + protected SerializedProperty stepPauseProp; + protected SerializedProperty saveSelectionProp; + protected SerializedProperty localizationIdProp; + protected SerializedProperty variablesProp; + protected SerializedProperty showLineNumbersProp; protected SerializedProperty hideCommandsProp; protected SerializedProperty luaEnvironmentProp; protected SerializedProperty luaBindingNameProp; - protected Texture2D addTexture; - - protected virtual void OnEnable() - { - if (NullTargetCheck()) // Check for an orphaned editor instance - return; - - descriptionProp = serializedObject.FindProperty("description"); - colorCommandsProp = serializedObject.FindProperty("colorCommands"); - hideComponentsProp = serializedObject.FindProperty("hideComponents"); - stepPauseProp = serializedObject.FindProperty("stepPause"); - saveSelectionProp = serializedObject.FindProperty("saveSelection"); - localizationIdProp = serializedObject.FindProperty("localizationId"); - variablesProp = serializedObject.FindProperty("variables"); - showLineNumbersProp = serializedObject.FindProperty("showLineNumbers"); - hideCommandsProp = serializedObject.FindProperty("hideCommands"); + protected Texture2D addTexture; + + protected virtual void OnEnable() + { + if (NullTargetCheck()) // Check for an orphaned editor instance + return; + + descriptionProp = serializedObject.FindProperty("description"); + colorCommandsProp = serializedObject.FindProperty("colorCommands"); + hideComponentsProp = serializedObject.FindProperty("hideComponents"); + stepPauseProp = serializedObject.FindProperty("stepPause"); + saveSelectionProp = serializedObject.FindProperty("saveSelection"); + localizationIdProp = serializedObject.FindProperty("localizationId"); + variablesProp = serializedObject.FindProperty("variables"); + showLineNumbersProp = serializedObject.FindProperty("showLineNumbers"); + hideCommandsProp = serializedObject.FindProperty("hideCommands"); luaEnvironmentProp = serializedObject.FindProperty("luaEnvironment"); luaBindingNameProp = serializedObject.FindProperty("luaBindingName"); - addTexture = Resources.Load("Icons/add_small") as Texture2D; - } + addTexture = Resources.Load("Icons/add_small") as Texture2D; + } - public override void OnInspectorGUI() - { - serializedObject.Update(); + public override void OnInspectorGUI() + { + serializedObject.Update(); - Flowchart flowchart = target as Flowchart; + Flowchart flowchart = target as Flowchart; - flowchart.UpdateHideFlags(); + flowchart.UpdateHideFlags(); - EditorGUILayout.PropertyField(descriptionProp); - EditorGUILayout.PropertyField(colorCommandsProp); - EditorGUILayout.PropertyField(hideComponentsProp); - EditorGUILayout.PropertyField(stepPauseProp); - EditorGUILayout.PropertyField(saveSelectionProp); - EditorGUILayout.PropertyField(localizationIdProp); - EditorGUILayout.PropertyField(showLineNumbersProp); + EditorGUILayout.PropertyField(descriptionProp); + EditorGUILayout.PropertyField(colorCommandsProp); + EditorGUILayout.PropertyField(hideComponentsProp); + EditorGUILayout.PropertyField(stepPauseProp); + EditorGUILayout.PropertyField(saveSelectionProp); + EditorGUILayout.PropertyField(localizationIdProp); + EditorGUILayout.PropertyField(showLineNumbersProp); EditorGUILayout.PropertyField(luaEnvironmentProp); EditorGUILayout.PropertyField(luaBindingNameProp); - // Show list of commands to hide in Add Command menu - ReorderableListGUI.Title(new GUIContent(hideCommandsProp.displayName, hideCommandsProp.tooltip)); - ReorderableListGUI.ListField(hideCommandsProp); - - GUILayout.BeginHorizontal(); - GUILayout.FlexibleSpace(); - if (GUILayout.Button(new GUIContent("Open Flowchart Window", "Opens the Flowchart Window"))) - { - EditorWindow.GetWindow(typeof(FlowchartWindow), false, "Flowchart"); - } - - GUILayout.Space(10); - - if (GUILayout.Button(new GUIContent("Center View", "Centers the window view at the center of all blocks in the Flowchart"))) - { - // Reset the zoom so we don't have adjust the center position depending on zoom - flowchart.scrollPos = flowchart.centerPosition; - flowchart.zoom = FlowchartWindow.maxZoomValue; - } - GUILayout.FlexibleSpace(); - GUILayout.EndHorizontal(); - - serializedObject.ApplyModifiedProperties(); - } - - public virtual void DrawVariablesGUI() - { - serializedObject.Update(); - - Flowchart t = target as Flowchart; - - if (t.variables.Count == 0) - { - t.variablesExpanded = true; - } - - if (!t.variablesExpanded) - { - if (GUILayout.Button ("Variables (" + t.variables.Count + ")", GUILayout.Height(24))) - { - t.variablesExpanded = true; - } - - // Draw disclosure triangle - Rect lastRect = GUILayoutUtility.GetLastRect(); - lastRect.x += 5; - lastRect.y += 5; - EditorGUI.Foldout(lastRect, false, ""); - } - else - { - Rect listRect = new Rect(); - - if (t.variables.Count > 0) - { - // Remove any null variables from the list - // Can sometimes happen when upgrading to a new version of Fungus (if .meta GUID changes for a variable class) - for (int i = t.variables.Count - 1; i >= 0; i--) - { - if (t.variables[i] == null) - { - t.variables.RemoveAt(i); - } - } - - ReorderableListGUI.Title("Variables"); - VariableListAdaptor adaptor = new VariableListAdaptor(variablesProp, 0); - - ReorderableListFlags flags = ReorderableListFlags.DisableContextMenu | ReorderableListFlags.HideAddButton; - - ReorderableListControl.DrawControlFromState(adaptor, null, flags); - listRect = GUILayoutUtility.GetLastRect(); - } - else - { - GUILayoutUtility.GetRect(300, 24); - listRect = GUILayoutUtility.GetLastRect(); - listRect.y += 20; - } - - float plusWidth = 32; - float plusHeight = 24; - - Rect buttonRect = listRect; - float buttonHeight = 24; - buttonRect.x = 4; - buttonRect.y -= buttonHeight - 1; - buttonRect.height = buttonHeight; - if (!Application.isPlaying) - { - buttonRect.width -= 30; - } - - if (GUI.Button (buttonRect, "Variables")) - { - t.variablesExpanded = false; - } - - // Draw disclosure triangle - Rect lastRect = buttonRect; - lastRect.x += 5; - lastRect.y += 5; - EditorGUI.Foldout(lastRect, true, ""); - - Rect plusRect = listRect; - plusRect.x += plusRect.width - plusWidth; - plusRect.y -= plusHeight - 1; - plusRect.width = plusWidth; - plusRect.height = plusHeight; - - if (!Application.isPlaying && - GUI.Button(plusRect, addTexture)) - { - GenericMenu menu = new GenericMenu (); - List types = FindAllDerivedTypes(); - - // Add variable types without a category - foreach (System.Type type in types) - { - VariableInfoAttribute variableInfo = VariableEditor.GetVariableInfo(type); - if (variableInfo == null || - variableInfo.Category != "") - { - continue; - } - - AddVariableInfo addVariableInfo = new AddVariableInfo(); - addVariableInfo.flowchart = t; - addVariableInfo.variableType = type; - - GUIContent typeName = new GUIContent(variableInfo.VariableType); - - menu.AddItem(typeName, false, AddVariable, addVariableInfo); - } - - // Add types with a category - foreach (System.Type type in types) - { - VariableInfoAttribute variableInfo = VariableEditor.GetVariableInfo(type); - if (variableInfo == null || - variableInfo.Category == "") - { - continue; - } - - AddVariableInfo info = new AddVariableInfo(); - info.flowchart = t; - info.variableType = type; - - GUIContent typeName = new GUIContent(variableInfo.Category + "/" + variableInfo.VariableType); - - menu.AddItem(typeName, false, AddVariable, info); - } - - menu.ShowAsContext (); - } - } - - serializedObject.ApplyModifiedProperties(); - } - - protected virtual void AddVariable(object obj) - { - AddVariableInfo addVariableInfo = obj as AddVariableInfo; - if (addVariableInfo == null) - { - return; - } - - Flowchart flowchart = addVariableInfo.flowchart; - System.Type variableType = addVariableInfo.variableType; - - Undo.RecordObject(flowchart, "Add Variable"); - Variable newVariable = flowchart.gameObject.AddComponent(variableType) as Variable; - newVariable.key = flowchart.GetUniqueVariableKey(""); - flowchart.variables.Add(newVariable); - - // Because this is an async call, we need to force prefab instances to record changes - PrefabUtility.RecordPrefabInstancePropertyModifications(flowchart); - } - - public static List FindAllDerivedTypes() - { - return FindAllDerivedTypes(Assembly.GetAssembly(typeof(T))); - } - - public static List FindAllDerivedTypes(Assembly assembly) - { - var derivedType = typeof(T); - return assembly - .GetTypes() - .Where(t => - t != derivedType && - derivedType.IsAssignableFrom(t) - ).ToList(); - - } - - /** - * When modifying custom editor code you can occasionally end up with orphaned editor instances. - * When this happens, you'll get a null exception error every time the scene serializes / deserialized. - * Once this situation occurs, the only way to fix it is to restart the Unity editor. - * As a workaround, this function detects if this editor is an orphan and deletes it. - */ - protected virtual bool NullTargetCheck() - { - try - { - // The serializedObject accessor creates a new SerializedObject if needed. - // However, this will fail with a null exception if the target object no longer exists. - #pragma warning disable 0219 - SerializedObject so = serializedObject; - } - catch (System.NullReferenceException) - { - DestroyImmediate(this); - return true; - } - - return false; - } - } + // Show list of commands to hide in Add Command menu + ReorderableListGUI.Title(new GUIContent(hideCommandsProp.displayName, hideCommandsProp.tooltip)); + ReorderableListGUI.ListField(hideCommandsProp); + + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + if (GUILayout.Button(new GUIContent("Open Flowchart Window", "Opens the Flowchart Window"))) + { + EditorWindow.GetWindow(typeof(FlowchartWindow), false, "Flowchart"); + } + + GUILayout.Space(10); + + if (GUILayout.Button(new GUIContent("Center View", "Centers the window view at the center of all blocks in the Flowchart"))) + { + // Reset the zoom so we don't have adjust the center position depending on zoom + flowchart.scrollPos = flowchart.centerPosition; + flowchart.zoom = FlowchartWindow.maxZoomValue; + } + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + + serializedObject.ApplyModifiedProperties(); + } + + public virtual void DrawVariablesGUI() + { + serializedObject.Update(); + + Flowchart t = target as Flowchart; + + if (t.variables.Count == 0) + { + t.variablesExpanded = true; + } + + if (!t.variablesExpanded) + { + if (GUILayout.Button ("Variables (" + t.variables.Count + ")", GUILayout.Height(24))) + { + t.variablesExpanded = true; + } + + // Draw disclosure triangle + Rect lastRect = GUILayoutUtility.GetLastRect(); + lastRect.x += 5; + lastRect.y += 5; + EditorGUI.Foldout(lastRect, false, ""); + } + else + { + Rect listRect = new Rect(); + + if (t.variables.Count > 0) + { + // Remove any null variables from the list + // Can sometimes happen when upgrading to a new version of Fungus (if .meta GUID changes for a variable class) + for (int i = t.variables.Count - 1; i >= 0; i--) + { + if (t.variables[i] == null) + { + t.variables.RemoveAt(i); + } + } + + ReorderableListGUI.Title("Variables"); + VariableListAdaptor adaptor = new VariableListAdaptor(variablesProp, 0); + + ReorderableListFlags flags = ReorderableListFlags.DisableContextMenu | ReorderableListFlags.HideAddButton; + + ReorderableListControl.DrawControlFromState(adaptor, null, flags); + listRect = GUILayoutUtility.GetLastRect(); + } + else + { + GUILayoutUtility.GetRect(300, 24); + listRect = GUILayoutUtility.GetLastRect(); + listRect.y += 20; + } + + float plusWidth = 32; + float plusHeight = 24; + + Rect buttonRect = listRect; + float buttonHeight = 24; + buttonRect.x = 4; + buttonRect.y -= buttonHeight - 1; + buttonRect.height = buttonHeight; + if (!Application.isPlaying) + { + buttonRect.width -= 30; + } + + if (GUI.Button (buttonRect, "Variables")) + { + t.variablesExpanded = false; + } + + // Draw disclosure triangle + Rect lastRect = buttonRect; + lastRect.x += 5; + lastRect.y += 5; + EditorGUI.Foldout(lastRect, true, ""); + + Rect plusRect = listRect; + plusRect.x += plusRect.width - plusWidth; + plusRect.y -= plusHeight - 1; + plusRect.width = plusWidth; + plusRect.height = plusHeight; + + if (!Application.isPlaying && + GUI.Button(plusRect, addTexture)) + { + GenericMenu menu = new GenericMenu (); + List types = FindAllDerivedTypes(); + + // Add variable types without a category + foreach (System.Type type in types) + { + VariableInfoAttribute variableInfo = VariableEditor.GetVariableInfo(type); + if (variableInfo == null || + variableInfo.Category != "") + { + continue; + } + + AddVariableInfo addVariableInfo = new AddVariableInfo(); + addVariableInfo.flowchart = t; + addVariableInfo.variableType = type; + + GUIContent typeName = new GUIContent(variableInfo.VariableType); + + menu.AddItem(typeName, false, AddVariable, addVariableInfo); + } + + // Add types with a category + foreach (System.Type type in types) + { + VariableInfoAttribute variableInfo = VariableEditor.GetVariableInfo(type); + if (variableInfo == null || + variableInfo.Category == "") + { + continue; + } + + AddVariableInfo info = new AddVariableInfo(); + info.flowchart = t; + info.variableType = type; + + GUIContent typeName = new GUIContent(variableInfo.Category + "/" + variableInfo.VariableType); + + menu.AddItem(typeName, false, AddVariable, info); + } + + menu.ShowAsContext (); + } + } + + serializedObject.ApplyModifiedProperties(); + } + + protected virtual void AddVariable(object obj) + { + AddVariableInfo addVariableInfo = obj as AddVariableInfo; + if (addVariableInfo == null) + { + return; + } + + Flowchart flowchart = addVariableInfo.flowchart; + System.Type variableType = addVariableInfo.variableType; + + Undo.RecordObject(flowchart, "Add Variable"); + Variable newVariable = flowchart.gameObject.AddComponent(variableType) as Variable; + newVariable.key = flowchart.GetUniqueVariableKey(""); + flowchart.variables.Add(newVariable); + + // Because this is an async call, we need to force prefab instances to record changes + PrefabUtility.RecordPrefabInstancePropertyModifications(flowchart); + } + + public static List FindAllDerivedTypes() + { + return FindAllDerivedTypes(Assembly.GetAssembly(typeof(T))); + } + + public static List FindAllDerivedTypes(Assembly assembly) + { + var derivedType = typeof(T); + return assembly + .GetTypes() + .Where(t => + t != derivedType && + derivedType.IsAssignableFrom(t) + ).ToList(); + + } + + /** + * When modifying custom editor code you can occasionally end up with orphaned editor instances. + * When this happens, you'll get a null exception error every time the scene serializes / deserialized. + * Once this situation occurs, the only way to fix it is to restart the Unity editor. + * As a workaround, this function detects if this editor is an orphan and deletes it. + */ + protected virtual bool NullTargetCheck() + { + try + { + // The serializedObject accessor creates a new SerializedObject if needed. + // However, this will fail with a null exception if the target object no longer exists. + #pragma warning disable 0219 + SerializedObject so = serializedObject; + } + catch (System.NullReferenceException) + { + DestroyImmediate(this); + return true; + } + + return false; + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Flowchart/Editor/FlowchartMenuItems.cs b/Assets/Fungus/Flowchart/Editor/FlowchartMenuItems.cs index db149aed..d4afc13d 100644 --- a/Assets/Fungus/Flowchart/Editor/FlowchartMenuItems.cs +++ b/Assets/Fungus/Flowchart/Editor/FlowchartMenuItems.cs @@ -11,76 +11,76 @@ using System.Collections; namespace Fungus { - public class FlowchartMenuItems - { - [MenuItem("Tools/Fungus/Create/Flowchart", false, 0)] - static void CreateFlowchart() - { - GameObject go = SpawnPrefab("Flowchart"); - go.transform.position = Vector3.zero; + public class FlowchartMenuItems + { + [MenuItem("Tools/Fungus/Create/Flowchart", false, 0)] + static void CreateFlowchart() + { + GameObject go = SpawnPrefab("Flowchart"); + go.transform.position = Vector3.zero; - // This is the latest version of Flowchart, so no need to update. - Flowchart flowchart = go.GetComponent(); - if (flowchart != null) - { - flowchart.version = Flowchart.CURRENT_VERSION; - } + // This is the latest version of Flowchart, so no need to update. + Flowchart flowchart = go.GetComponent(); + if (flowchart != null) + { + flowchart.version = Flowchart.CURRENT_VERSION; + } - // Only the first created Flowchart in the scene should have a default GameStarted block - if (GameObject.FindObjectsOfType().Length > 1) - { - Block block = go.GetComponent(); - block.eventHandler = null; - GameObject.DestroyImmediate(block.eventHandler); - } - } + // Only the first created Flowchart in the scene should have a default GameStarted block + if (GameObject.FindObjectsOfType().Length > 1) + { + Block block = go.GetComponent(); + block.eventHandler = null; + GameObject.DestroyImmediate(block.eventHandler); + } + } - [MenuItem("Tools/Fungus/Create/Fungus Logo", false, 1000)] - static void CreateFungusLogo() - { - SpawnPrefab("FungusLogo"); - } + [MenuItem("Tools/Fungus/Create/Fungus Logo", false, 1000)] + static void CreateFungusLogo() + { + SpawnPrefab("FungusLogo"); + } - [MenuItem("Tools/Fungus/Utilities/Export Fungus Package")] - static void ExportFungusPackage() - { - string path = EditorUtility.SaveFilePanel("Export Fungus Package", "", "Fungus", "unitypackage"); - if(path.Length == 0) - { - return; - } + [MenuItem("Tools/Fungus/Utilities/Export Fungus Package")] + static void ExportFungusPackage() + { + string path = EditorUtility.SaveFilePanel("Export Fungus Package", "", "Fungus", "unitypackage"); + if(path.Length == 0) + { + return; + } - string[] folders = new string[] {"Assets/Fungus", "Assets/FungusExamples" }; + string[] folders = new string[] {"Assets/Fungus", "Assets/FungusExamples" }; - AssetDatabase.ExportPackage(folders, path, ExportPackageOptions.Recurse); - } + AssetDatabase.ExportPackage(folders, path, ExportPackageOptions.Recurse); + } - public static GameObject SpawnPrefab(string prefabName) - { - GameObject prefab = Resources.Load(prefabName); - if (prefab == null) - { - return null; - } + public static GameObject SpawnPrefab(string prefabName) + { + GameObject prefab = Resources.Load(prefabName); + if (prefab == null) + { + return null; + } - GameObject go = PrefabUtility.InstantiatePrefab(prefab) as GameObject; - PrefabUtility.DisconnectPrefabInstance(go); + GameObject go = PrefabUtility.InstantiatePrefab(prefab) as GameObject; + PrefabUtility.DisconnectPrefabInstance(go); - SceneView view = SceneView.lastActiveSceneView; - if (view != null) - { - Camera sceneCam = view.camera; - Vector3 pos = sceneCam.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 10f)); - pos.z = 0f; - go.transform.position = pos; - } + SceneView view = SceneView.lastActiveSceneView; + if (view != null) + { + Camera sceneCam = view.camera; + Vector3 pos = sceneCam.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 10f)); + pos.z = 0f; + go.transform.position = pos; + } - Selection.activeGameObject = go; - - Undo.RegisterCreatedObjectUndo(go, "Create Object"); + Selection.activeGameObject = go; + + Undo.RegisterCreatedObjectUndo(go, "Create Object"); - return go; - } - } + return go; + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Flowchart/Editor/FlowchartWindow.cs b/Assets/Fungus/Flowchart/Editor/FlowchartWindow.cs old mode 100755 new mode 100644 index 8ed6fe27..7e7e5c7d --- a/Assets/Fungus/Flowchart/Editor/FlowchartWindow.cs +++ b/Assets/Fungus/Flowchart/Editor/FlowchartWindow.cs @@ -13,836 +13,836 @@ using System.Collections.Generic; namespace Fungus { - public class FlowchartWindow : EditorWindow - { - public static List deleteList = new List(); - - protected List windowBlockMap = new List(); - - // The ReorderableList control doesn't drag properly when used with GUI.DragWindow(), - // so we just implement dragging ourselves. - protected int dragWindowId = -1; - protected Vector2 startDragPosition; - - public const float minZoomValue = 0.25f; - public const float maxZoomValue = 1f; - - protected GUIStyle nodeStyle = new GUIStyle(); - - protected static BlockInspector blockInspector; - - protected bool mouseOverVariables = false; - - protected int forceRepaintCount; - - protected Texture2D addTexture; - - [MenuItem("Tools/Fungus/Flowchart Window")] - static void Init() - { - GetWindow(typeof(FlowchartWindow), false, "Flowchart"); - } - - protected virtual void OnEnable() - { - // All block nodes use the same GUIStyle, but with a different background - nodeStyle.border.left = 20; - nodeStyle.border.right = 20; - nodeStyle.border.top = 5; - nodeStyle.border.bottom = 5; - nodeStyle.padding.left = 20; - nodeStyle.padding.right = 20; - nodeStyle.padding.top = 5; - nodeStyle.padding.bottom = 5; - nodeStyle.contentOffset = Vector2.zero; - nodeStyle.alignment = TextAnchor.MiddleCenter; - nodeStyle.wordWrap = true; - - addTexture = Resources.Load("Icons/add_small") as Texture2D; - } - - protected virtual void OnInspectorUpdate() - { - // Ensure the Block Inspector is always showing the currently selected block - Flowchart flowchart = GetFlowchart(); - if (flowchart == null) - { - return; - } - - if (Selection.activeGameObject == null && - flowchart.selectedBlock != null) - { - if (blockInspector == null) - { - ShowBlockInspector(flowchart); - } - blockInspector.block = flowchart.selectedBlock; - } - - forceRepaintCount--; - forceRepaintCount = Math.Max(0, forceRepaintCount); - - Repaint(); - } - - static public Flowchart GetFlowchart() - { - // Using a temp hidden object to track the active Flowchart across - // serialization / deserialization when playing the game in the editor. - FungusState fungusState = GameObject.FindObjectOfType(); - if (fungusState == null) - { - GameObject go = new GameObject("_FungusState"); - go.hideFlags = HideFlags.HideInHierarchy; - fungusState = go.AddComponent(); - } - - if (Selection.activeGameObject != null) - { - Flowchart fs = Selection.activeGameObject.GetComponent(); - if (fs != null) - { - fungusState.selectedFlowchart = fs; - } - } - - return fungusState.selectedFlowchart; - } - - protected virtual void OnGUI() - { - Flowchart flowchart = GetFlowchart(); - if (flowchart == null) - { - GUILayout.Label("No Flowchart scene object selected"); - return; - } - - // Delete any scheduled objects - foreach (Block deleteBlock in deleteList) - { - bool isSelected = (flowchart.selectedBlock == deleteBlock); - - foreach (Command command in deleteBlock.commandList) - { - Undo.DestroyObjectImmediate(command); - } - - Undo.DestroyObjectImmediate(deleteBlock); - flowchart.ClearSelectedCommands(); - - if (isSelected) - { - // Revert to showing properties for the Flowchart - Selection.activeGameObject = flowchart.gameObject; - } - } - deleteList.Clear(); - - DrawFlowchartView(flowchart); - DrawOverlay(flowchart); - - if (forceRepaintCount > 0) - { - // Redraw on next frame to get crisp refresh rate - Repaint(); - } - } - - protected virtual void DrawOverlay(Flowchart flowchart) - { - GUILayout.Space(8); - - GUILayout.BeginHorizontal(); - - GUILayout.Space(8); - - if (GUILayout.Button(new GUIContent(addTexture, "Add a new block"))) - { - Vector2 newNodePosition = new Vector2(50 - flowchart.scrollPos.x, - 50 - flowchart.scrollPos.y); - CreateBlock(flowchart, newNodePosition); - } - - GUILayout.Space(8); - - flowchart.zoom = GUILayout.HorizontalSlider(flowchart.zoom, minZoomValue, maxZoomValue, GUILayout.Width(100)); - - GUILayout.FlexibleSpace(); - - GUILayout.BeginVertical(); - GUILayout.Label(flowchart.name, EditorStyles.whiteBoldLabel); - if (flowchart.description.Length > 0) - { - GUILayout.Label(flowchart.description, EditorStyles.helpBox); - } - GUILayout.EndVertical(); - - GUILayout.EndHorizontal(); - - GUILayout.FlexibleSpace(); - - GUILayout.BeginHorizontal(); - - GUILayout.BeginVertical(GUILayout.Width(440)); - - GUILayout.FlexibleSpace(); - - flowchart.variablesScrollPos = GUILayout.BeginScrollView(flowchart.variablesScrollPos, GUILayout.MaxHeight(position.height * 0.75f)); - - GUILayout.FlexibleSpace(); - - GUILayout.Space(8); - - FlowchartEditor flowchartEditor = Editor.CreateEditor (flowchart) as FlowchartEditor; - flowchartEditor.DrawVariablesGUI(); - DestroyImmediate(flowchartEditor); - - Rect variableWindowRect = GUILayoutUtility.GetLastRect(); - if (flowchart.variablesExpanded && - flowchart.variables.Count > 0) - { - variableWindowRect.y -= 20; - variableWindowRect.height += 20; - } - if (Event.current.type == EventType.Repaint) - { - mouseOverVariables = variableWindowRect.Contains(Event.current.mousePosition); - } - - GUILayout.EndScrollView(); - - GUILayout.EndVertical(); - - GUILayout.FlexibleSpace(); - - GUILayout.EndHorizontal(); - } - - protected virtual void DrawFlowchartView(Flowchart flowchart) - { - Block[] blocks = flowchart.GetComponents(); - - foreach (Block block in blocks) - { - flowchart.scrollViewRect.xMin = Mathf.Min(flowchart.scrollViewRect.xMin, block.nodeRect.xMin - 400); - flowchart.scrollViewRect.xMax = Mathf.Max(flowchart.scrollViewRect.xMax, block.nodeRect.xMax + 400); - flowchart.scrollViewRect.yMin = Mathf.Min(flowchart.scrollViewRect.yMin, block.nodeRect.yMin - 400); - flowchart.scrollViewRect.yMax = Mathf.Max(flowchart.scrollViewRect.yMax, block.nodeRect.yMax + 400); - } - - // Calc rect for script view - Rect scriptViewRect = new Rect(0, 0, this.position.width / flowchart.zoom, this.position.height / flowchart.zoom); - - EditorZoomArea.Begin(flowchart.zoom, scriptViewRect); - - DrawGrid(flowchart); - - GLDraw.BeginGroup(scriptViewRect); - - if (Event.current.button == 0 && - Event.current.type == EventType.MouseDown && - !mouseOverVariables) - { - flowchart.selectedBlock = null; - if (!EditorGUI.actionKey) - { - flowchart.ClearSelectedCommands(); - } - Selection.activeGameObject = flowchart.gameObject; - } - - // The center of the Flowchart depends on the block positions and window dimensions, so we calculate it - // here in the FlowchartWindow class and store it on the Flowchart object for use later. - CalcFlowchartCenter(flowchart, blocks); - - // Draw connections - foreach (Block block in blocks) - { - DrawConnections(flowchart, block, false); - } - foreach (Block block in blocks) - { - DrawConnections(flowchart, block, true); - } - - GUIStyle windowStyle = new GUIStyle(); - windowStyle.stretchHeight = true; - - BeginWindows(); - - windowBlockMap.Clear(); - for (int i = 0; i < blocks.Length; ++i) - { - Block block = blocks[i]; - - float nodeWidthA = nodeStyle.CalcSize(new GUIContent(block.blockName)).x + 10; - float nodeWidthB = 0f; - if (block.eventHandler != null) - { - nodeWidthB = nodeStyle.CalcSize(new GUIContent(block.eventHandler.GetSummary())).x + 10; - } - - block.nodeRect.width = Mathf.Max(Mathf.Max(nodeWidthA, nodeWidthB), 120); - block.nodeRect.height = 40; - - if (Event.current.button == 0) - { - if (Event.current.type == EventType.MouseDrag && dragWindowId == i) - { - block.nodeRect.x += Event.current.delta.x; - block.nodeRect.y += Event.current.delta.y; - - forceRepaintCount = 6; - } - else if (Event.current.type == EventType.MouseUp && - dragWindowId == i) - { - Vector2 newPos = new Vector2(block.nodeRect.x, block.nodeRect.y); - - block.nodeRect.x = startDragPosition.x; - block.nodeRect.y = startDragPosition.y; - - Undo.RecordObject(block, "Node Position"); - - block.nodeRect.x = newPos.x; - block.nodeRect.y = newPos.y; - - dragWindowId = -1; - forceRepaintCount = 6; - } - } - - Rect windowRect = new Rect(block.nodeRect); - windowRect.x += flowchart.scrollPos.x; - windowRect.y += flowchart.scrollPos.y; - - GUILayout.Window(i, windowRect, DrawWindow, "", windowStyle); - - GUI.backgroundColor = Color.white; - - windowBlockMap.Add(block); - } - - EndWindows(); - - // Draw Event Handler labels - foreach (Block block in blocks) - { - if (block.eventHandler != null) - { - string handlerLabel = ""; - EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(block.eventHandler.GetType()); - if (info != null) - { - handlerLabel = "<" + info.EventHandlerName + "> "; - } - - GUIStyle handlerStyle = new GUIStyle(EditorStyles.whiteLabel); - handlerStyle.wordWrap = true; - handlerStyle.margin.top = 0; - handlerStyle.margin.bottom = 0; - handlerStyle.alignment = TextAnchor.MiddleCenter; - - Rect rect = new Rect(block.nodeRect); - rect.height = handlerStyle.CalcHeight(new GUIContent(handlerLabel), block.nodeRect.width); - rect.x += flowchart.scrollPos.x; - rect.y += flowchart.scrollPos.y - rect.height; - - GUI.Label(rect, handlerLabel, handlerStyle); - } - } - - - // Draw play icons beside all executing blocks - if (Application.isPlaying) - { - foreach (Block b in blocks) - { - if (b.IsExecuting()) - { - b.executingIconTimer = Time.realtimeSinceStartup + Block.executingIconFadeTime; - b.activeCommand.executingIconTimer = Time.realtimeSinceStartup + Block.executingIconFadeTime; - forceRepaintCount = 6; - } - - if (b.executingIconTimer > Time.realtimeSinceStartup) - { - Rect rect = new Rect(b.nodeRect); - - rect.x += flowchart.scrollPos.x - 37; - rect.y += flowchart.scrollPos.y + 3; - rect.width = 34; - rect.height = 34; - - if (!b.IsExecuting()) - { - float alpha = (b.executingIconTimer - Time.realtimeSinceStartup) / Block.executingIconFadeTime; - alpha = Mathf.Clamp01(alpha); - GUI.color = new Color(1f, 1f, 1f, alpha); - } - - if (GUI.Button(rect, FungusEditorResources.texPlayBig as Texture, new GUIStyle())) - { - SelectBlock(flowchart, b); - } - - GUI.color = Color.white; - } - } - } - - PanAndZoom(flowchart); - - GLDraw.EndGroup(); - - EditorZoomArea.End(); - } - - public virtual void CalcFlowchartCenter(Flowchart flowchart, Block[] blocks) - { - if (flowchart == null || - blocks.Count() == 0) - { - return; - } - - Vector2 min = blocks[0].nodeRect.min; - Vector2 max = blocks[0].nodeRect.max; - - foreach (Block block in blocks) - { - min.x = Mathf.Min(min.x, block.nodeRect.center.x); - min.y = Mathf.Min(min.y, block.nodeRect.center.y); - max.x = Mathf.Max(max.x, block.nodeRect.center.x); - max.y = Mathf.Max(max.y, block.nodeRect.center.y); - } - - Vector2 center = (min + max) * -0.5f; - - center.x += position.width * 0.5f; - center.y += position.height * 0.5f; - - flowchart.centerPosition = center; - } - - protected virtual void PanAndZoom(Flowchart flowchart) - { - // Right click to drag view - bool drag = false; - - // Pan tool - if (UnityEditor.Tools.current == Tool.View && UnityEditor.Tools.viewTool == ViewTool.Pan && - Event.current.button == 0 && Event.current.type == EventType.MouseDrag) - { - drag = true; - } - - // Right or middle button drag - if (Event.current.button > 0 && Event.current.type == EventType.MouseDrag) - { - drag = true; - } - - // Alt + left mouse drag - if (Event.current.alt && - Event.current.button == 0 && Event.current.type == EventType.MouseDrag) - { - drag = true; - } - - if (drag) - { - flowchart.scrollPos += Event.current.delta; - forceRepaintCount = 6; - } - - bool zoom = false; - - // Scroll wheel - if (Event.current.type == EventType.ScrollWheel) - { - zoom = true; - } - - // Zoom tool - if (UnityEditor.Tools.current == Tool.View && UnityEditor.Tools.viewTool == ViewTool.Zoom && - Event.current.button == 0 && Event.current.type == EventType.MouseDrag) - { - zoom = true; - } - - if (zoom) - { - flowchart.zoom -= Event.current.delta.y * 0.01f; - flowchart.zoom = Mathf.Clamp(flowchart.zoom, minZoomValue, maxZoomValue); - forceRepaintCount = 6; - } - } - - protected virtual void DrawGrid(Flowchart flowchart) - { - float width = this.position.width / flowchart.zoom; - float height = this.position.height / flowchart.zoom; - - // Match background color of scene view - if (EditorGUIUtility.isProSkin) - { - GUI.color = new Color32(71, 71, 71, 255); - } - else - { - GUI.color = new Color32(86, 86, 86, 255); - } - GUI.DrawTexture( new Rect(0,0, width, height), EditorGUIUtility.whiteTexture ); - - GUI.color = Color.white; - Color color = new Color32(96, 96, 96, 255); - - float gridSize = 128f; - - float x = flowchart.scrollPos.x % gridSize; - while (x < width) - { - GLDraw.DrawLine(new Vector2(x, 0), new Vector2(x, height), color, 1f); - x += gridSize; - } - - float y = (flowchart.scrollPos.y % gridSize); - while (y < height) - { - if (y >= 0) - { - GLDraw.DrawLine(new Vector2(0, y), new Vector2(width, y), color, 1f); - } - y += gridSize; - } - } - - protected virtual void SelectBlock(Flowchart flowchart, Block block) - { - // Select the block and also select currently executing command - ShowBlockInspector(flowchart); - flowchart.selectedBlock = block; - flowchart.ClearSelectedCommands(); - if (block.activeCommand != null) - { - flowchart.AddSelectedCommand(block.activeCommand); - } - } - - public static Block CreateBlock(Flowchart flowchart, Vector2 position) - { - Block newBlock = flowchart.CreateBlock(position); - Undo.RegisterCreatedObjectUndo(newBlock, "New Block"); - ShowBlockInspector(flowchart); - flowchart.selectedBlock = newBlock; - flowchart.ClearSelectedCommands(); - - return newBlock; - } - - protected virtual void DeleteBlock(Flowchart flowchart, Block block) - { - foreach (Command command in block.commandList) - { - Undo.DestroyObjectImmediate(command); - } - - Undo.DestroyObjectImmediate(block); - flowchart.ClearSelectedCommands(); - } - - protected virtual void DrawWindow(int windowId) - { - Block block = windowBlockMap[windowId]; - Flowchart flowchart = block.GetFlowchart(); - - if (flowchart == null) - { - return; - } + public class FlowchartWindow : EditorWindow + { + public static List deleteList = new List(); + + protected List windowBlockMap = new List(); + + // The ReorderableList control doesn't drag properly when used with GUI.DragWindow(), + // so we just implement dragging ourselves. + protected int dragWindowId = -1; + protected Vector2 startDragPosition; + + public const float minZoomValue = 0.25f; + public const float maxZoomValue = 1f; + + protected GUIStyle nodeStyle = new GUIStyle(); + + protected static BlockInspector blockInspector; + + protected bool mouseOverVariables = false; + + protected int forceRepaintCount; + + protected Texture2D addTexture; + + [MenuItem("Tools/Fungus/Flowchart Window")] + static void Init() + { + GetWindow(typeof(FlowchartWindow), false, "Flowchart"); + } + + protected virtual void OnEnable() + { + // All block nodes use the same GUIStyle, but with a different background + nodeStyle.border.left = 20; + nodeStyle.border.right = 20; + nodeStyle.border.top = 5; + nodeStyle.border.bottom = 5; + nodeStyle.padding.left = 20; + nodeStyle.padding.right = 20; + nodeStyle.padding.top = 5; + nodeStyle.padding.bottom = 5; + nodeStyle.contentOffset = Vector2.zero; + nodeStyle.alignment = TextAnchor.MiddleCenter; + nodeStyle.wordWrap = true; + + addTexture = Resources.Load("Icons/add_small") as Texture2D; + } + + protected virtual void OnInspectorUpdate() + { + // Ensure the Block Inspector is always showing the currently selected block + Flowchart flowchart = GetFlowchart(); + if (flowchart == null) + { + return; + } + + if (Selection.activeGameObject == null && + flowchart.selectedBlock != null) + { + if (blockInspector == null) + { + ShowBlockInspector(flowchart); + } + blockInspector.block = flowchart.selectedBlock; + } + + forceRepaintCount--; + forceRepaintCount = Math.Max(0, forceRepaintCount); + + Repaint(); + } + + static public Flowchart GetFlowchart() + { + // Using a temp hidden object to track the active Flowchart across + // serialization / deserialization when playing the game in the editor. + FungusState fungusState = GameObject.FindObjectOfType(); + if (fungusState == null) + { + GameObject go = new GameObject("_FungusState"); + go.hideFlags = HideFlags.HideInHierarchy; + fungusState = go.AddComponent(); + } + + if (Selection.activeGameObject != null) + { + Flowchart fs = Selection.activeGameObject.GetComponent(); + if (fs != null) + { + fungusState.selectedFlowchart = fs; + } + } + + return fungusState.selectedFlowchart; + } + + protected virtual void OnGUI() + { + Flowchart flowchart = GetFlowchart(); + if (flowchart == null) + { + GUILayout.Label("No Flowchart scene object selected"); + return; + } + + // Delete any scheduled objects + foreach (Block deleteBlock in deleteList) + { + bool isSelected = (flowchart.selectedBlock == deleteBlock); + + foreach (Command command in deleteBlock.commandList) + { + Undo.DestroyObjectImmediate(command); + } + + Undo.DestroyObjectImmediate(deleteBlock); + flowchart.ClearSelectedCommands(); + + if (isSelected) + { + // Revert to showing properties for the Flowchart + Selection.activeGameObject = flowchart.gameObject; + } + } + deleteList.Clear(); + + DrawFlowchartView(flowchart); + DrawOverlay(flowchart); + + if (forceRepaintCount > 0) + { + // Redraw on next frame to get crisp refresh rate + Repaint(); + } + } + + protected virtual void DrawOverlay(Flowchart flowchart) + { + GUILayout.Space(8); + + GUILayout.BeginHorizontal(); + + GUILayout.Space(8); + + if (GUILayout.Button(new GUIContent(addTexture, "Add a new block"))) + { + Vector2 newNodePosition = new Vector2(50 - flowchart.scrollPos.x, + 50 - flowchart.scrollPos.y); + CreateBlock(flowchart, newNodePosition); + } + + GUILayout.Space(8); + + flowchart.zoom = GUILayout.HorizontalSlider(flowchart.zoom, minZoomValue, maxZoomValue, GUILayout.Width(100)); + + GUILayout.FlexibleSpace(); + + GUILayout.BeginVertical(); + GUILayout.Label(flowchart.name, EditorStyles.whiteBoldLabel); + if (flowchart.description.Length > 0) + { + GUILayout.Label(flowchart.description, EditorStyles.helpBox); + } + GUILayout.EndVertical(); + + GUILayout.EndHorizontal(); + + GUILayout.FlexibleSpace(); + + GUILayout.BeginHorizontal(); + + GUILayout.BeginVertical(GUILayout.Width(440)); + + GUILayout.FlexibleSpace(); + + flowchart.variablesScrollPos = GUILayout.BeginScrollView(flowchart.variablesScrollPos, GUILayout.MaxHeight(position.height * 0.75f)); + + GUILayout.FlexibleSpace(); + + GUILayout.Space(8); + + FlowchartEditor flowchartEditor = Editor.CreateEditor (flowchart) as FlowchartEditor; + flowchartEditor.DrawVariablesGUI(); + DestroyImmediate(flowchartEditor); + + Rect variableWindowRect = GUILayoutUtility.GetLastRect(); + if (flowchart.variablesExpanded && + flowchart.variables.Count > 0) + { + variableWindowRect.y -= 20; + variableWindowRect.height += 20; + } + if (Event.current.type == EventType.Repaint) + { + mouseOverVariables = variableWindowRect.Contains(Event.current.mousePosition); + } + + GUILayout.EndScrollView(); + + GUILayout.EndVertical(); + + GUILayout.FlexibleSpace(); + + GUILayout.EndHorizontal(); + } + + protected virtual void DrawFlowchartView(Flowchart flowchart) + { + Block[] blocks = flowchart.GetComponents(); + + foreach (Block block in blocks) + { + flowchart.scrollViewRect.xMin = Mathf.Min(flowchart.scrollViewRect.xMin, block.nodeRect.xMin - 400); + flowchart.scrollViewRect.xMax = Mathf.Max(flowchart.scrollViewRect.xMax, block.nodeRect.xMax + 400); + flowchart.scrollViewRect.yMin = Mathf.Min(flowchart.scrollViewRect.yMin, block.nodeRect.yMin - 400); + flowchart.scrollViewRect.yMax = Mathf.Max(flowchart.scrollViewRect.yMax, block.nodeRect.yMax + 400); + } + + // Calc rect for script view + Rect scriptViewRect = new Rect(0, 0, this.position.width / flowchart.zoom, this.position.height / flowchart.zoom); + + EditorZoomArea.Begin(flowchart.zoom, scriptViewRect); + + DrawGrid(flowchart); + + GLDraw.BeginGroup(scriptViewRect); + + if (Event.current.button == 0 && + Event.current.type == EventType.MouseDown && + !mouseOverVariables) + { + flowchart.selectedBlock = null; + if (!EditorGUI.actionKey) + { + flowchart.ClearSelectedCommands(); + } + Selection.activeGameObject = flowchart.gameObject; + } + + // The center of the Flowchart depends on the block positions and window dimensions, so we calculate it + // here in the FlowchartWindow class and store it on the Flowchart object for use later. + CalcFlowchartCenter(flowchart, blocks); + + // Draw connections + foreach (Block block in blocks) + { + DrawConnections(flowchart, block, false); + } + foreach (Block block in blocks) + { + DrawConnections(flowchart, block, true); + } + + GUIStyle windowStyle = new GUIStyle(); + windowStyle.stretchHeight = true; + + BeginWindows(); + + windowBlockMap.Clear(); + for (int i = 0; i < blocks.Length; ++i) + { + Block block = blocks[i]; + + float nodeWidthA = nodeStyle.CalcSize(new GUIContent(block.blockName)).x + 10; + float nodeWidthB = 0f; + if (block.eventHandler != null) + { + nodeWidthB = nodeStyle.CalcSize(new GUIContent(block.eventHandler.GetSummary())).x + 10; + } + + block.nodeRect.width = Mathf.Max(Mathf.Max(nodeWidthA, nodeWidthB), 120); + block.nodeRect.height = 40; + + if (Event.current.button == 0) + { + if (Event.current.type == EventType.MouseDrag && dragWindowId == i) + { + block.nodeRect.x += Event.current.delta.x; + block.nodeRect.y += Event.current.delta.y; + + forceRepaintCount = 6; + } + else if (Event.current.type == EventType.MouseUp && + dragWindowId == i) + { + Vector2 newPos = new Vector2(block.nodeRect.x, block.nodeRect.y); + + block.nodeRect.x = startDragPosition.x; + block.nodeRect.y = startDragPosition.y; + + Undo.RecordObject(block, "Node Position"); + + block.nodeRect.x = newPos.x; + block.nodeRect.y = newPos.y; + + dragWindowId = -1; + forceRepaintCount = 6; + } + } + + Rect windowRect = new Rect(block.nodeRect); + windowRect.x += flowchart.scrollPos.x; + windowRect.y += flowchart.scrollPos.y; + + GUILayout.Window(i, windowRect, DrawWindow, "", windowStyle); + + GUI.backgroundColor = Color.white; + + windowBlockMap.Add(block); + } + + EndWindows(); + + // Draw Event Handler labels + foreach (Block block in blocks) + { + if (block.eventHandler != null) + { + string handlerLabel = ""; + EventHandlerInfoAttribute info = EventHandlerEditor.GetEventHandlerInfo(block.eventHandler.GetType()); + if (info != null) + { + handlerLabel = "<" + info.EventHandlerName + "> "; + } + + GUIStyle handlerStyle = new GUIStyle(EditorStyles.whiteLabel); + handlerStyle.wordWrap = true; + handlerStyle.margin.top = 0; + handlerStyle.margin.bottom = 0; + handlerStyle.alignment = TextAnchor.MiddleCenter; + + Rect rect = new Rect(block.nodeRect); + rect.height = handlerStyle.CalcHeight(new GUIContent(handlerLabel), block.nodeRect.width); + rect.x += flowchart.scrollPos.x; + rect.y += flowchart.scrollPos.y - rect.height; + + GUI.Label(rect, handlerLabel, handlerStyle); + } + } + + + // Draw play icons beside all executing blocks + if (Application.isPlaying) + { + foreach (Block b in blocks) + { + if (b.IsExecuting()) + { + b.executingIconTimer = Time.realtimeSinceStartup + Block.executingIconFadeTime; + b.activeCommand.executingIconTimer = Time.realtimeSinceStartup + Block.executingIconFadeTime; + forceRepaintCount = 6; + } + + if (b.executingIconTimer > Time.realtimeSinceStartup) + { + Rect rect = new Rect(b.nodeRect); + + rect.x += flowchart.scrollPos.x - 37; + rect.y += flowchart.scrollPos.y + 3; + rect.width = 34; + rect.height = 34; + + if (!b.IsExecuting()) + { + float alpha = (b.executingIconTimer - Time.realtimeSinceStartup) / Block.executingIconFadeTime; + alpha = Mathf.Clamp01(alpha); + GUI.color = new Color(1f, 1f, 1f, alpha); + } + + if (GUI.Button(rect, FungusEditorResources.texPlayBig as Texture, new GUIStyle())) + { + SelectBlock(flowchart, b); + } + + GUI.color = Color.white; + } + } + } + + PanAndZoom(flowchart); + + GLDraw.EndGroup(); + + EditorZoomArea.End(); + } + + public virtual void CalcFlowchartCenter(Flowchart flowchart, Block[] blocks) + { + if (flowchart == null || + blocks.Count() == 0) + { + return; + } + + Vector2 min = blocks[0].nodeRect.min; + Vector2 max = blocks[0].nodeRect.max; + + foreach (Block block in blocks) + { + min.x = Mathf.Min(min.x, block.nodeRect.center.x); + min.y = Mathf.Min(min.y, block.nodeRect.center.y); + max.x = Mathf.Max(max.x, block.nodeRect.center.x); + max.y = Mathf.Max(max.y, block.nodeRect.center.y); + } + + Vector2 center = (min + max) * -0.5f; + + center.x += position.width * 0.5f; + center.y += position.height * 0.5f; + + flowchart.centerPosition = center; + } + + protected virtual void PanAndZoom(Flowchart flowchart) + { + // Right click to drag view + bool drag = false; + + // Pan tool + if (UnityEditor.Tools.current == Tool.View && UnityEditor.Tools.viewTool == ViewTool.Pan && + Event.current.button == 0 && Event.current.type == EventType.MouseDrag) + { + drag = true; + } + + // Right or middle button drag + if (Event.current.button > 0 && Event.current.type == EventType.MouseDrag) + { + drag = true; + } + + // Alt + left mouse drag + if (Event.current.alt && + Event.current.button == 0 && Event.current.type == EventType.MouseDrag) + { + drag = true; + } + + if (drag) + { + flowchart.scrollPos += Event.current.delta; + forceRepaintCount = 6; + } + + bool zoom = false; + + // Scroll wheel + if (Event.current.type == EventType.ScrollWheel) + { + zoom = true; + } + + // Zoom tool + if (UnityEditor.Tools.current == Tool.View && UnityEditor.Tools.viewTool == ViewTool.Zoom && + Event.current.button == 0 && Event.current.type == EventType.MouseDrag) + { + zoom = true; + } + + if (zoom) + { + flowchart.zoom -= Event.current.delta.y * 0.01f; + flowchart.zoom = Mathf.Clamp(flowchart.zoom, minZoomValue, maxZoomValue); + forceRepaintCount = 6; + } + } + + protected virtual void DrawGrid(Flowchart flowchart) + { + float width = this.position.width / flowchart.zoom; + float height = this.position.height / flowchart.zoom; + + // Match background color of scene view + if (EditorGUIUtility.isProSkin) + { + GUI.color = new Color32(71, 71, 71, 255); + } + else + { + GUI.color = new Color32(86, 86, 86, 255); + } + GUI.DrawTexture( new Rect(0,0, width, height), EditorGUIUtility.whiteTexture ); + + GUI.color = Color.white; + Color color = new Color32(96, 96, 96, 255); + + float gridSize = 128f; + + float x = flowchart.scrollPos.x % gridSize; + while (x < width) + { + GLDraw.DrawLine(new Vector2(x, 0), new Vector2(x, height), color, 1f); + x += gridSize; + } + + float y = (flowchart.scrollPos.y % gridSize); + while (y < height) + { + if (y >= 0) + { + GLDraw.DrawLine(new Vector2(0, y), new Vector2(width, y), color, 1f); + } + y += gridSize; + } + } + + protected virtual void SelectBlock(Flowchart flowchart, Block block) + { + // Select the block and also select currently executing command + ShowBlockInspector(flowchart); + flowchart.selectedBlock = block; + flowchart.ClearSelectedCommands(); + if (block.activeCommand != null) + { + flowchart.AddSelectedCommand(block.activeCommand); + } + } + + public static Block CreateBlock(Flowchart flowchart, Vector2 position) + { + Block newBlock = flowchart.CreateBlock(position); + Undo.RegisterCreatedObjectUndo(newBlock, "New Block"); + ShowBlockInspector(flowchart); + flowchart.selectedBlock = newBlock; + flowchart.ClearSelectedCommands(); + + return newBlock; + } + + protected virtual void DeleteBlock(Flowchart flowchart, Block block) + { + foreach (Command command in block.commandList) + { + Undo.DestroyObjectImmediate(command); + } + + Undo.DestroyObjectImmediate(block); + flowchart.ClearSelectedCommands(); + } + + protected virtual void DrawWindow(int windowId) + { + Block block = windowBlockMap[windowId]; + Flowchart flowchart = block.GetFlowchart(); + + if (flowchart == null) + { + return; + } // Select block when node is clicked if (Event.current.button == 0 && - Event.current.type == EventType.MouseDown && - !mouseOverVariables) - { - // Check if might be start of a window drag - if (Event.current.button == 0 && - Event.current.alt == false) - { - dragWindowId = windowId; - startDragPosition.x = block.nodeRect.x; - startDragPosition.y = block.nodeRect.y; - } - - if (windowId < windowBlockMap.Count) - { - Undo.RecordObject(flowchart, "Select"); - - SelectBlock(flowchart, block); - - GUIUtility.keyboardControl = 0; // Fix for textarea not refeshing (change focus) - } - } - - bool selected = (flowchart.selectedBlock == block); - - GUIStyle nodeStyleCopy = new GUIStyle(nodeStyle); - - if (block.eventHandler != null) - { - nodeStyleCopy.normal.background = selected ? FungusEditorResources.texEventNodeOn : FungusEditorResources.texEventNodeOff; - } - else - { - // Count the number of unique connections (excluding self references) - List uniqueList = new List(); - List connectedBlocks = block.GetConnectedBlocks(); - foreach (Block connectedBlock in connectedBlocks) - { - if (connectedBlock == block || - uniqueList.Contains(connectedBlock)) - { - continue; - } - uniqueList.Add(connectedBlock); - } - - if (uniqueList.Count > 1) - { - nodeStyleCopy.normal.background = selected ? FungusEditorResources.texChoiceNodeOn : FungusEditorResources.texChoiceNodeOff; - } - else - { - nodeStyleCopy.normal.background = selected ? FungusEditorResources.texProcessNodeOn : FungusEditorResources.texProcessNodeOff; - } - } - - nodeStyleCopy.normal.textColor = Color.black; - - // Make sure node is wide enough to fit the node name text - float width = nodeStyleCopy.CalcSize(new GUIContent(block.blockName)).x; - block.nodeRect.width = Mathf.Max (block.nodeRect.width, width); - - GUI.backgroundColor = Color.white; - GUILayout.Box(block.blockName, nodeStyleCopy, GUILayout.Width(block.nodeRect.width), GUILayout.Height(block.nodeRect.height)); - - if (block.description.Length > 0) - { - GUIStyle descriptionStyle = new GUIStyle(EditorStyles.helpBox); - descriptionStyle.wordWrap = true; - GUILayout.Label(block.description, descriptionStyle); - } - - if (Event.current.type == EventType.ContextClick) - { - GenericMenu menu = new GenericMenu (); - - menu.AddItem(new GUIContent ("Duplicate"), false, DuplicateBlock, block); - menu.AddItem(new GUIContent ("Delete"), false, DeleteBlock, block); - - menu.ShowAsContext(); - } - } - - protected virtual void DrawConnections(Flowchart flowchart, Block block, bool highlightedOnly) - { - if (block == null) - { - return; - } - - List connectedBlocks = new List(); - - bool blockIsSelected = (flowchart.selectedBlock == block); - - foreach (Command command in block.commandList) - { - if (command == null) - { - continue; - } - - bool commandIsSelected = false; - foreach (Command selectedCommand in flowchart.selectedCommands) - { - if (selectedCommand == command) - { - commandIsSelected = true; - break; - } - } - - bool highlight = command.isExecuting || (blockIsSelected && commandIsSelected); - - if (highlightedOnly && !highlight || - !highlightedOnly && highlight) - { - continue; - } - - connectedBlocks.Clear(); - command.GetConnectedBlocks(ref connectedBlocks); - - foreach (Block blockB in connectedBlocks) - { - if (blockB == null || - block == blockB || - blockB.GetFlowchart() != flowchart) - { - continue; - } - - Rect startRect = new Rect(block.nodeRect); - startRect.x += flowchart.scrollPos.x; - startRect.y += flowchart.scrollPos.y; - - Rect endRect = new Rect(blockB.nodeRect); - endRect.x += flowchart.scrollPos.x; - endRect.y += flowchart.scrollPos.y; - - DrawRectConnection(startRect, endRect, highlight); - } - } - } - - protected virtual void DrawRectConnection(Rect rectA, Rect rectB, bool highlight) - { - Vector2[] pointsA = new Vector2[] { - new Vector2(rectA.xMin + 5, rectA.center.y), - new Vector2(rectA.xMin + rectA.width / 2, rectA.yMin + 2), - new Vector2(rectA.xMin + rectA.width / 2, rectA.yMax - 2), - new Vector2(rectA.xMax - 5, rectA.center.y) - }; - - Vector2[] pointsB = new Vector2[] { - new Vector2(rectB.xMin + 5, rectB.center.y), - new Vector2(rectB.xMin + rectB.width / 2, rectB.yMin + 2), - new Vector2(rectB.xMin + rectB.width / 2, rectB.yMax - 2), - new Vector2(rectB.xMax - 5, rectB.center.y) - }; - - Vector2 pointA = Vector2.zero; - Vector2 pointB = Vector2.zero; - float minDist = float.MaxValue; - - foreach (Vector2 a in pointsA) - { - foreach (Vector2 b in pointsB) - { - float d = Vector2.Distance(a, b); - if (d < minDist) - { - pointA = a; - pointB = b; - minDist = d; - } - } - } - - Color color = Color.grey; - if (highlight) - { - color = Color.green; - } - - GLDraw.DrawConnectingCurve(pointA, pointB, color, 1.025f); - - Rect dotARect = new Rect(pointA.x - 5, pointA.y - 5, 10, 10); - GUI.Label(dotARect, "", new GUIStyle("U2D.dragDotActive")); - - Rect dotBRect = new Rect(pointB.x - 5, pointB.y - 5, 10, 10); - GUI.Label(dotBRect, "", new GUIStyle("U2D.dragDotActive")); - } - - public static void DeleteBlock(object obj) - { - Block block = obj as Block; - FlowchartWindow.deleteList.Add(block); - } - - protected static void DuplicateBlock(object obj) - { - Flowchart flowchart = GetFlowchart(); - Block block = obj as Block; - - Vector2 newPosition = new Vector2(block.nodeRect.position.x + - block.nodeRect.width + 20, - block.nodeRect.y); - - Block oldBlock = block; - - Block newBlock = FlowchartWindow.CreateBlock(flowchart, newPosition); - newBlock.blockName = flowchart.GetUniqueBlockKey(oldBlock.blockName + " (Copy)"); - - Undo.RecordObject(newBlock, "Duplicate Block"); - - foreach (Command command in oldBlock.commandList) - { - if (ComponentUtility.CopyComponent(command)) - { - if (ComponentUtility.PasteComponentAsNew(flowchart.gameObject)) - { - Command[] commands = flowchart.GetComponents(); - Command pastedCommand = commands.Last(); - if (pastedCommand != null) - { - pastedCommand.itemId = flowchart.NextItemId(); - newBlock.commandList.Add (pastedCommand); - } - } - - // This stops the user pasting the command manually into another game object. - ComponentUtility.CopyComponent(flowchart.transform); - } - } - - if (oldBlock.eventHandler != null) - { - if (ComponentUtility.CopyComponent(oldBlock.eventHandler)) - { - if (ComponentUtility.PasteComponentAsNew(flowchart.gameObject)) - { - EventHandler[] eventHandlers = flowchart.GetComponents(); - EventHandler pastedEventHandler = eventHandlers.Last(); - if (pastedEventHandler != null) - { - pastedEventHandler.parentBlock = newBlock; - newBlock.eventHandler = pastedEventHandler; - } - } - } - } - } - - protected static void ShowBlockInspector(Flowchart flowchart) - { - if (blockInspector == null) - { - // Create a Scriptable Object with a custom editor which we can use to inspect the selected block. - // Editors for Scriptable Objects display using the full height of the inspector window. - blockInspector = ScriptableObject.CreateInstance() as BlockInspector; - blockInspector.hideFlags = HideFlags.DontSave; - } - - Selection.activeObject = blockInspector; - - EditorUtility.SetDirty(blockInspector); - } - - /** - * Displays a temporary text alert in the center of the Flowchart window. - */ - public static void ShowNotification(string notificationText) - { - EditorWindow window = EditorWindow.GetWindow(typeof(FlowchartWindow), false, "Flowchart"); - if (window != null) - { - window.ShowNotification(new GUIContent(notificationText)); - } - } - } + Event.current.type == EventType.MouseDown && + !mouseOverVariables) + { + // Check if might be start of a window drag + if (Event.current.button == 0 && + Event.current.alt == false) + { + dragWindowId = windowId; + startDragPosition.x = block.nodeRect.x; + startDragPosition.y = block.nodeRect.y; + } + + if (windowId < windowBlockMap.Count) + { + Undo.RecordObject(flowchart, "Select"); + + SelectBlock(flowchart, block); + + GUIUtility.keyboardControl = 0; // Fix for textarea not refeshing (change focus) + } + } + + bool selected = (flowchart.selectedBlock == block); + + GUIStyle nodeStyleCopy = new GUIStyle(nodeStyle); + + if (block.eventHandler != null) + { + nodeStyleCopy.normal.background = selected ? FungusEditorResources.texEventNodeOn : FungusEditorResources.texEventNodeOff; + } + else + { + // Count the number of unique connections (excluding self references) + List uniqueList = new List(); + List connectedBlocks = block.GetConnectedBlocks(); + foreach (Block connectedBlock in connectedBlocks) + { + if (connectedBlock == block || + uniqueList.Contains(connectedBlock)) + { + continue; + } + uniqueList.Add(connectedBlock); + } + + if (uniqueList.Count > 1) + { + nodeStyleCopy.normal.background = selected ? FungusEditorResources.texChoiceNodeOn : FungusEditorResources.texChoiceNodeOff; + } + else + { + nodeStyleCopy.normal.background = selected ? FungusEditorResources.texProcessNodeOn : FungusEditorResources.texProcessNodeOff; + } + } + + nodeStyleCopy.normal.textColor = Color.black; + + // Make sure node is wide enough to fit the node name text + float width = nodeStyleCopy.CalcSize(new GUIContent(block.blockName)).x; + block.nodeRect.width = Mathf.Max (block.nodeRect.width, width); + + GUI.backgroundColor = Color.white; + GUILayout.Box(block.blockName, nodeStyleCopy, GUILayout.Width(block.nodeRect.width), GUILayout.Height(block.nodeRect.height)); + + if (block.description.Length > 0) + { + GUIStyle descriptionStyle = new GUIStyle(EditorStyles.helpBox); + descriptionStyle.wordWrap = true; + GUILayout.Label(block.description, descriptionStyle); + } + + if (Event.current.type == EventType.ContextClick) + { + GenericMenu menu = new GenericMenu (); + + menu.AddItem(new GUIContent ("Duplicate"), false, DuplicateBlock, block); + menu.AddItem(new GUIContent ("Delete"), false, DeleteBlock, block); + + menu.ShowAsContext(); + } + } + + protected virtual void DrawConnections(Flowchart flowchart, Block block, bool highlightedOnly) + { + if (block == null) + { + return; + } + + List connectedBlocks = new List(); + + bool blockIsSelected = (flowchart.selectedBlock == block); + + foreach (Command command in block.commandList) + { + if (command == null) + { + continue; + } + + bool commandIsSelected = false; + foreach (Command selectedCommand in flowchart.selectedCommands) + { + if (selectedCommand == command) + { + commandIsSelected = true; + break; + } + } + + bool highlight = command.isExecuting || (blockIsSelected && commandIsSelected); + + if (highlightedOnly && !highlight || + !highlightedOnly && highlight) + { + continue; + } + + connectedBlocks.Clear(); + command.GetConnectedBlocks(ref connectedBlocks); + + foreach (Block blockB in connectedBlocks) + { + if (blockB == null || + block == blockB || + blockB.GetFlowchart() != flowchart) + { + continue; + } + + Rect startRect = new Rect(block.nodeRect); + startRect.x += flowchart.scrollPos.x; + startRect.y += flowchart.scrollPos.y; + + Rect endRect = new Rect(blockB.nodeRect); + endRect.x += flowchart.scrollPos.x; + endRect.y += flowchart.scrollPos.y; + + DrawRectConnection(startRect, endRect, highlight); + } + } + } + + protected virtual void DrawRectConnection(Rect rectA, Rect rectB, bool highlight) + { + Vector2[] pointsA = new Vector2[] { + new Vector2(rectA.xMin + 5, rectA.center.y), + new Vector2(rectA.xMin + rectA.width / 2, rectA.yMin + 2), + new Vector2(rectA.xMin + rectA.width / 2, rectA.yMax - 2), + new Vector2(rectA.xMax - 5, rectA.center.y) + }; + + Vector2[] pointsB = new Vector2[] { + new Vector2(rectB.xMin + 5, rectB.center.y), + new Vector2(rectB.xMin + rectB.width / 2, rectB.yMin + 2), + new Vector2(rectB.xMin + rectB.width / 2, rectB.yMax - 2), + new Vector2(rectB.xMax - 5, rectB.center.y) + }; + + Vector2 pointA = Vector2.zero; + Vector2 pointB = Vector2.zero; + float minDist = float.MaxValue; + + foreach (Vector2 a in pointsA) + { + foreach (Vector2 b in pointsB) + { + float d = Vector2.Distance(a, b); + if (d < minDist) + { + pointA = a; + pointB = b; + minDist = d; + } + } + } + + Color color = Color.grey; + if (highlight) + { + color = Color.green; + } + + GLDraw.DrawConnectingCurve(pointA, pointB, color, 1.025f); + + Rect dotARect = new Rect(pointA.x - 5, pointA.y - 5, 10, 10); + GUI.Label(dotARect, "", new GUIStyle("U2D.dragDotActive")); + + Rect dotBRect = new Rect(pointB.x - 5, pointB.y - 5, 10, 10); + GUI.Label(dotBRect, "", new GUIStyle("U2D.dragDotActive")); + } + + public static void DeleteBlock(object obj) + { + Block block = obj as Block; + FlowchartWindow.deleteList.Add(block); + } + + protected static void DuplicateBlock(object obj) + { + Flowchart flowchart = GetFlowchart(); + Block block = obj as Block; + + Vector2 newPosition = new Vector2(block.nodeRect.position.x + + block.nodeRect.width + 20, + block.nodeRect.y); + + Block oldBlock = block; + + Block newBlock = FlowchartWindow.CreateBlock(flowchart, newPosition); + newBlock.blockName = flowchart.GetUniqueBlockKey(oldBlock.blockName + " (Copy)"); + + Undo.RecordObject(newBlock, "Duplicate Block"); + + foreach (Command command in oldBlock.commandList) + { + if (ComponentUtility.CopyComponent(command)) + { + if (ComponentUtility.PasteComponentAsNew(flowchart.gameObject)) + { + Command[] commands = flowchart.GetComponents(); + Command pastedCommand = commands.Last(); + if (pastedCommand != null) + { + pastedCommand.itemId = flowchart.NextItemId(); + newBlock.commandList.Add (pastedCommand); + } + } + + // This stops the user pasting the command manually into another game object. + ComponentUtility.CopyComponent(flowchart.transform); + } + } + + if (oldBlock.eventHandler != null) + { + if (ComponentUtility.CopyComponent(oldBlock.eventHandler)) + { + if (ComponentUtility.PasteComponentAsNew(flowchart.gameObject)) + { + EventHandler[] eventHandlers = flowchart.GetComponents(); + EventHandler pastedEventHandler = eventHandlers.Last(); + if (pastedEventHandler != null) + { + pastedEventHandler.parentBlock = newBlock; + newBlock.eventHandler = pastedEventHandler; + } + } + } + } + } + + protected static void ShowBlockInspector(Flowchart flowchart) + { + if (blockInspector == null) + { + // Create a Scriptable Object with a custom editor which we can use to inspect the selected block. + // Editors for Scriptable Objects display using the full height of the inspector window. + blockInspector = ScriptableObject.CreateInstance() as BlockInspector; + blockInspector.hideFlags = HideFlags.DontSave; + } + + Selection.activeObject = blockInspector; + + EditorUtility.SetDirty(blockInspector); + } + + /** + * Displays a temporary text alert in the center of the Flowchart window. + */ + public static void ShowNotification(string notificationText) + { + EditorWindow window = EditorWindow.GetWindow(typeof(FlowchartWindow), false, "Flowchart"); + if (window != null) + { + window.ShowNotification(new GUIContent(notificationText)); + } + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Flowchart/Editor/FungusEditorResources.cs b/Assets/Fungus/Flowchart/Editor/FungusEditorResources.cs index f475da31..ed8021ad 100644 --- a/Assets/Fungus/Flowchart/Editor/FungusEditorResources.cs +++ b/Assets/Fungus/Flowchart/Editor/FungusEditorResources.cs @@ -9,183 +9,183 @@ using System; namespace Fungus { - - internal static class FungusEditorResources - { - - static FungusEditorResources() { - GenerateSpecialTextures(); - LoadResourceAssets(); - } - - private enum ResourceName - { - add_button = 0, - add_button_active, - title_background, - command_background, - choice_node_off, - choice_node_on, - process_node_off, - process_node_on, - event_node_off, - event_node_on, - play_big, - play_small - } - - private static string[] s_LightSkin = { - - "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" - }; - - private static string[] s_DarkSkin = { - "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" - }; - - public static Texture2D texAddButton - { - get { return s_Cached[(int)ResourceName.add_button]; } - } - - public static Texture2D texAddButtonActive - { - get { return s_Cached[(int)ResourceName.add_button_active]; } - } - - public static Texture2D texTitleBackground - { - get { return s_Cached[(int)ResourceName.title_background]; } - } - - public static Texture2D texCommandBackground - { - get { return s_Cached[(int)ResourceName.command_background]; } - } - - public static Texture2D texEventNodeOn - { - get { return s_Cached[(int)ResourceName.event_node_on]; } - } - - public static Texture2D texEventNodeOff - { - get { return s_Cached[(int)ResourceName.event_node_off]; } - } - - public static Texture2D texProcessNodeOn - { - get { return s_Cached[(int)ResourceName.process_node_on]; } - } - - public static Texture2D texProcessNodeOff - { - get { return s_Cached[(int)ResourceName.process_node_off]; } - } - - public static Texture2D texChoiceNodeOn - { - get { return s_Cached[(int)ResourceName.choice_node_on]; } - } - - public static Texture2D texChoiceNodeOff - { - get { return s_Cached[(int)ResourceName.choice_node_off]; } - } - - public static Texture2D texPlayBig - { - get { return s_Cached[(int)ResourceName.play_big]; } - } - - public static Texture2D texPlaySmall - { - get { return s_Cached[(int)ResourceName.play_small]; } - } - - public static Texture2D texItemSplitter { get; private set; } - - private static void GenerateSpecialTextures() - { - var splitterColor = EditorGUIUtility.isProSkin - ? new Color(1f, 1f, 1f, 0.14f) - : new Color(0.59f, 0.59f, 0.59f, 0.55f) - ; - texItemSplitter = CreatePixelTexture("(Generated) Item Splitter", splitterColor); - } - - public static Texture2D CreatePixelTexture(string name, Color color) - { - var tex = new Texture2D(1, 1, TextureFormat.ARGB32, false, true); - tex.name = name; - tex.hideFlags = HideFlags.HideAndDontSave; - tex.filterMode = FilterMode.Point; - tex.SetPixel(0, 0, color); - tex.Apply(); - return tex; - } - - private static Texture2D[] s_Cached; - - public static void LoadResourceAssets() - { - var skin = EditorGUIUtility.isProSkin ? s_DarkSkin : s_LightSkin; - s_Cached = new Texture2D[skin.Length]; - - for (int i = 0; i < s_Cached.Length; ++i) { - // Get image data (PNG) from base64 encoded strings. - byte[] imageData = Convert.FromBase64String(skin[i]); - - // Gather image size from image data. - int texWidth, texHeight; - GetImageSize(imageData, out texWidth, out texHeight); - - // Generate texture asset. - var tex = new Texture2D(texWidth, texHeight, TextureFormat.ARGB32, false, true); - tex.hideFlags = HideFlags.HideAndDontSave; - tex.name = "(Generated) ReorderableList:" + i; - tex.filterMode = FilterMode.Point; - tex.LoadImage(imageData); - - s_Cached[i] = tex; - } - - s_LightSkin = null; - s_DarkSkin = null; - } - - private static void GetImageSize(byte[] imageData, out int width, out int height) - { - width = ReadInt(imageData, 3 + 15); - height = ReadInt(imageData, 3 + 15 + 2 + 2); - } - - private static int ReadInt(byte[] imageData, int offset) - { - return (imageData[offset] << 8) | imageData[offset + 1]; - } - } + + internal static class FungusEditorResources + { + + static FungusEditorResources() { + GenerateSpecialTextures(); + LoadResourceAssets(); + } + + private enum ResourceName + { + add_button = 0, + add_button_active, + title_background, + command_background, + choice_node_off, + choice_node_on, + process_node_off, + process_node_on, + event_node_off, + event_node_on, + play_big, + play_small + } + + private static string[] s_LightSkin = { + + "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" + }; + + private static string[] s_DarkSkin = { + "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" + }; + + public static Texture2D texAddButton + { + get { return s_Cached[(int)ResourceName.add_button]; } + } + + public static Texture2D texAddButtonActive + { + get { return s_Cached[(int)ResourceName.add_button_active]; } + } + + public static Texture2D texTitleBackground + { + get { return s_Cached[(int)ResourceName.title_background]; } + } + + public static Texture2D texCommandBackground + { + get { return s_Cached[(int)ResourceName.command_background]; } + } + + public static Texture2D texEventNodeOn + { + get { return s_Cached[(int)ResourceName.event_node_on]; } + } + + public static Texture2D texEventNodeOff + { + get { return s_Cached[(int)ResourceName.event_node_off]; } + } + + public static Texture2D texProcessNodeOn + { + get { return s_Cached[(int)ResourceName.process_node_on]; } + } + + public static Texture2D texProcessNodeOff + { + get { return s_Cached[(int)ResourceName.process_node_off]; } + } + + public static Texture2D texChoiceNodeOn + { + get { return s_Cached[(int)ResourceName.choice_node_on]; } + } + + public static Texture2D texChoiceNodeOff + { + get { return s_Cached[(int)ResourceName.choice_node_off]; } + } + + public static Texture2D texPlayBig + { + get { return s_Cached[(int)ResourceName.play_big]; } + } + + public static Texture2D texPlaySmall + { + get { return s_Cached[(int)ResourceName.play_small]; } + } + + public static Texture2D texItemSplitter { get; private set; } + + private static void GenerateSpecialTextures() + { + var splitterColor = EditorGUIUtility.isProSkin + ? new Color(1f, 1f, 1f, 0.14f) + : new Color(0.59f, 0.59f, 0.59f, 0.55f) + ; + texItemSplitter = CreatePixelTexture("(Generated) Item Splitter", splitterColor); + } + + public static Texture2D CreatePixelTexture(string name, Color color) + { + var tex = new Texture2D(1, 1, TextureFormat.ARGB32, false, true); + tex.name = name; + tex.hideFlags = HideFlags.HideAndDontSave; + tex.filterMode = FilterMode.Point; + tex.SetPixel(0, 0, color); + tex.Apply(); + return tex; + } + + private static Texture2D[] s_Cached; + + public static void LoadResourceAssets() + { + var skin = EditorGUIUtility.isProSkin ? s_DarkSkin : s_LightSkin; + s_Cached = new Texture2D[skin.Length]; + + for (int i = 0; i < s_Cached.Length; ++i) { + // Get image data (PNG) from base64 encoded strings. + byte[] imageData = Convert.FromBase64String(skin[i]); + + // Gather image size from image data. + int texWidth, texHeight; + GetImageSize(imageData, out texWidth, out texHeight); + + // Generate texture asset. + var tex = new Texture2D(texWidth, texHeight, TextureFormat.ARGB32, false, true); + tex.hideFlags = HideFlags.HideAndDontSave; + tex.name = "(Generated) ReorderableList:" + i; + tex.filterMode = FilterMode.Point; + tex.LoadImage(imageData); + + s_Cached[i] = tex; + } + + s_LightSkin = null; + s_DarkSkin = null; + } + + private static void GetImageSize(byte[] imageData, out int width, out int height) + { + width = ReadInt(imageData, 3 + 15); + height = ReadInt(imageData, 3 + 15 + 2 + 2); + } + + private static int ReadInt(byte[] imageData, int offset) + { + return (imageData[offset] << 8) | imageData[offset + 1]; + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Flowchart/Editor/GLDraw.cs b/Assets/Fungus/Flowchart/Editor/GLDraw.cs old mode 100755 new mode 100644 index a9f49f23..e5d34937 --- a/Assets/Fungus/Flowchart/Editor/GLDraw.cs +++ b/Assets/Fungus/Flowchart/Editor/GLDraw.cs @@ -10,289 +10,289 @@ using System; namespace Fungus { - public class GLDraw - { - /* - * Clipping code: http://forum.unity3d.com/threads/17066-How-to-draw-a-GUI-2D-quot-line-quot?p=230386#post230386 - * Thick line drawing code: http://unifycommunity.com/wiki/index.php?title=VectorLine - */ - protected static bool clippingEnabled; - protected static Rect clippingBounds; - public static Material lineMaterial = null; - - /* @ Credit: "http://cs-people.bu.edu/jalon/cs480/Oct11Lab/clip.c" */ - protected static bool clip_test(float p, float q, ref float u1, ref float u2) - { - float r; - bool retval = true; - - if (p < 0.0) - { - r = q / p; - if (r > u2) - retval = false; - else if (r > u1) - u1 = r; - } - else if (p > 0.0) - { - r = q / p; - if (r < u1) - retval = false; - else if (r < u2) - u2 = r; - } - else if (q < 0.0) - retval = false; - - return retval; - } - - public static bool segment_rect_intersection(Rect bounds, ref Vector2 p1, ref Vector2 p2) - { - float u1 = 0.0f, u2 = 1.0f, dx = p2.x - p1.x, dy; - - if (clip_test(-dx, p1.x - bounds.xMin, ref u1, ref u2)) - { - if (clip_test(dx, bounds.xMax - p1.x, ref u1, ref u2)) - { - dy = p2.y - p1.y; - if (clip_test(-dy, p1.y - bounds.yMin, ref u1, ref u2)) - { - if (clip_test(dy, bounds.yMax - p1.y, ref u1, ref u2)) - { - if (u2 < 1.0) - { - p2.x = p1.x + u2 * dx; - p2.y = p1.y + u2 * dy; - } - - if (u1 > 0.0) - { - p1.x += u1 * dx; - p1.y += u1 * dy; - } - return true; - } - } - } - } - return false; - } - - public static void BeginGroup(Rect position) - { - clippingEnabled = true; - clippingBounds = new Rect(0, 0, position.width, position.height); - GUI.BeginGroup(position); - } - - public static void EndGroup() - { - GUI.EndGroup(); - clippingBounds = new Rect(0, 0, Screen.width, Screen.height); - clippingEnabled = false; - } - - public static Vector2 BeginScrollView(Rect position, Vector2 scrollPos, Rect viewRect, Rect clipRect) - { - clippingEnabled = true; - clippingBounds = clipRect; - return GUI.BeginScrollView(position, scrollPos, viewRect, GUIStyle.none, GUIStyle.none); - } - - public static void EndScrollView() - { - GUI.EndScrollView(); - clippingBounds = new Rect(0, 0, Screen.width, Screen.height); - clippingEnabled = false; - } - - public static void CreateMaterial() - { - if (lineMaterial != null) - return; - - lineMaterial = Resources.Load("GLLineDraw", typeof(Material)) as Material; - } - - public static void DrawLine(Vector2 start, Vector2 end, Color color, float width) - { - if (Event.current == null) - return; - if (Event.current.type != EventType.repaint) - return; - - if (clippingEnabled) - if (!segment_rect_intersection(clippingBounds, ref start, ref end)) - return; - - CreateMaterial(); - - lineMaterial.SetPass(0); - - Vector3 startPt; - Vector3 endPt; - - if (width == 1) - { - GL.Begin(GL.LINES); - GL.Color(color); - startPt = new Vector3(start.x, start.y, 0); - endPt = new Vector3(end.x, end.y, 0); - GL.Vertex(startPt); - GL.Vertex(endPt); - } - else - { - GL.Begin(GL.QUADS); - GL.Color(color); - startPt = new Vector3(end.y, start.x, 0); - endPt = new Vector3(start.y, end.x, 0); - Vector3 perpendicular = (startPt - endPt).normalized * width; - Vector3 v1 = new Vector3(start.x, start.y, 0); - Vector3 v2 = new Vector3(end.x, end.y, 0); - GL.Vertex(v1 - perpendicular); - GL.Vertex(v1 + perpendicular); - GL.Vertex(v2 + perpendicular); - GL.Vertex(v2 - perpendicular); - } - GL.End(); - } - - public static void DrawRect(Rect rect, Color color) - { - if (Event.current == null) - return; - if (Event.current.type != EventType.repaint) - return; - - CreateMaterial(); - // set the current material - lineMaterial.SetPass( 0 ); - GL.Begin( GL.QUADS ); - GL.Color( color ); - GL.Vertex3( rect.xMin, rect.yMin, 0 ); - GL.Vertex3( rect.xMax, rect.yMin, 0 ); - GL.Vertex3( rect.xMax, rect.yMax, 0 ); - GL.Vertex3( rect.xMin, rect.yMax, 0 ); - GL.End(); - } - - public static void DrawBox(Rect box, Color color, float width) - { - Vector2 p1 = new Vector2(box.xMin, box.yMin); - Vector2 p2 = new Vector2(box.xMax, box.yMin); - Vector2 p3 = new Vector2(box.xMax, box.yMax); - Vector2 p4 = new Vector2(box.xMin, box.yMax); - DrawLine(p1, p2, color, width); - DrawLine(p2, p3, color, width); - DrawLine(p3, p4, color, width); - DrawLine(p4, p1, color, width); - } - - public static void DrawBox(Vector2 topLeftCorner, Vector2 bottomRightCorner, Color color, float width) - { - Rect box = new Rect(topLeftCorner.x, topLeftCorner.y, bottomRightCorner.x - topLeftCorner.x, bottomRightCorner.y - topLeftCorner.y); - DrawBox(box, color, width); - } - - public static void DrawRoundedBox(Rect box, float radius, Color color, float width) - { - Vector2 p1, p2, p3, p4, p5, p6, p7, p8; - p1 = new Vector2(box.xMin + radius, box.yMin); - p2 = new Vector2(box.xMax - radius, box.yMin); - p3 = new Vector2(box.xMax, box.yMin + radius); - p4 = new Vector2(box.xMax, box.yMax - radius); - p5 = new Vector2(box.xMax - radius, box.yMax); - p6 = new Vector2(box.xMin + radius, box.yMax); - p7 = new Vector2(box.xMin, box.yMax - radius); - p8 = new Vector2(box.xMin, box.yMin + radius); - - DrawLine(p1, p2, color, width); - DrawLine(p3, p4, color, width); - DrawLine(p5, p6, color, width); - DrawLine(p7, p8, color, width); - - Vector2 t1, t2; - float halfRadius = radius / 2; - - t1 = new Vector2(p8.x, p8.y + halfRadius); - t2 = new Vector2(p1.x - halfRadius, p1.y); - DrawBezier(p8, t1, p1, t2, color, width); - - t1 = new Vector2(p2.x + halfRadius, p2.y); - t2 = new Vector2(p3.x, p3.y - halfRadius); - DrawBezier(p2, t1, p3, t2, color, width); - - t1 = new Vector2(p4.x, p4.y + halfRadius); - t2 = new Vector2(p5.x + halfRadius, p5.y); - DrawBezier(p4, t1, p5, t2, color, width); - - t1 = new Vector2(p6.x - halfRadius, p6.y); - t2 = new Vector2(p7.x, p7.y + halfRadius); - DrawBezier(p6, t1, p7, t2, color, width); - } - - public static void DrawConnectingCurve(Vector2 start, Vector2 end, Color color, float width) - { - Vector2 distance = start - end; - - Vector2 tangentA = start; - tangentA.x -= distance.x * 0.5f; - Vector2 tangentB = end; - tangentB.x += distance.x * 0.5f; - - int segments = Mathf.FloorToInt((distance.magnitude / 20) * 3); - - DrawBezier(start, tangentA, end, tangentB, color, width, segments); - - Vector2 pA = CubeBezier(start, tangentA, end, tangentB, 0.6f); - Vector2 pB = CubeBezier(start, tangentA, end, tangentB, 0.7f); - - float arrowHeadSize = 5; - - Vector2 arrowPosA = pB; - Vector2 arrowPosB = arrowPosA; - Vector2 arrowPosC = arrowPosA; - - Vector2 dir = (pB - pA).normalized; - - arrowPosB.x += dir.y * arrowHeadSize; - arrowPosB.y -= dir.x * arrowHeadSize; - arrowPosB -= dir * arrowHeadSize; - - arrowPosC.x -= dir.y * arrowHeadSize; - arrowPosC.y += dir.x * arrowHeadSize; - arrowPosC -= dir * arrowHeadSize; - - DrawLine(arrowPosA, arrowPosB, color, 1.025f); - DrawLine(arrowPosA, arrowPosC, color, 1.025f); - } - - public static void DrawBezier(Vector2 start, Vector2 startTangent, Vector2 end, Vector2 endTangent, Color color, float width) - { - int segments = Mathf.FloorToInt((start - end).magnitude / 20) * 3; // Three segments per distance of 20 - DrawBezier(start, startTangent, end, endTangent, color, width, segments); - } - - public static void DrawBezier(Vector2 start, Vector2 startTangent, Vector2 end, Vector2 endTangent, Color color, float width, int segments) - { - Vector2 startVector = CubeBezier(start, startTangent, end, endTangent, 0); - for (int i = 1; i <= segments; i++) - { - Vector2 endVector = CubeBezier(start, startTangent, end, endTangent, i / (float)segments); - DrawLine(startVector, endVector, color, width); - startVector = endVector; - } - } - - private static Vector2 CubeBezier(Vector2 s, Vector2 st, Vector2 e, Vector2 et, float t) - { - float rt = 1 - t; - float rtt = rt * t; - return rt * rt * rt * s + 3 * rt * rtt * st + 3 * rtt * t * et + t * t * t * e; - } - } + public class GLDraw + { + /* + * Clipping code: http://forum.unity3d.com/threads/17066-How-to-draw-a-GUI-2D-quot-line-quot?p=230386#post230386 + * Thick line drawing code: http://unifycommunity.com/wiki/index.php?title=VectorLine + */ + protected static bool clippingEnabled; + protected static Rect clippingBounds; + public static Material lineMaterial = null; + + /* @ Credit: "http://cs-people.bu.edu/jalon/cs480/Oct11Lab/clip.c" */ + protected static bool clip_test(float p, float q, ref float u1, ref float u2) + { + float r; + bool retval = true; + + if (p < 0.0) + { + r = q / p; + if (r > u2) + retval = false; + else if (r > u1) + u1 = r; + } + else if (p > 0.0) + { + r = q / p; + if (r < u1) + retval = false; + else if (r < u2) + u2 = r; + } + else if (q < 0.0) + retval = false; + + return retval; + } + + public static bool segment_rect_intersection(Rect bounds, ref Vector2 p1, ref Vector2 p2) + { + float u1 = 0.0f, u2 = 1.0f, dx = p2.x - p1.x, dy; + + if (clip_test(-dx, p1.x - bounds.xMin, ref u1, ref u2)) + { + if (clip_test(dx, bounds.xMax - p1.x, ref u1, ref u2)) + { + dy = p2.y - p1.y; + if (clip_test(-dy, p1.y - bounds.yMin, ref u1, ref u2)) + { + if (clip_test(dy, bounds.yMax - p1.y, ref u1, ref u2)) + { + if (u2 < 1.0) + { + p2.x = p1.x + u2 * dx; + p2.y = p1.y + u2 * dy; + } + + if (u1 > 0.0) + { + p1.x += u1 * dx; + p1.y += u1 * dy; + } + return true; + } + } + } + } + return false; + } + + public static void BeginGroup(Rect position) + { + clippingEnabled = true; + clippingBounds = new Rect(0, 0, position.width, position.height); + GUI.BeginGroup(position); + } + + public static void EndGroup() + { + GUI.EndGroup(); + clippingBounds = new Rect(0, 0, Screen.width, Screen.height); + clippingEnabled = false; + } + + public static Vector2 BeginScrollView(Rect position, Vector2 scrollPos, Rect viewRect, Rect clipRect) + { + clippingEnabled = true; + clippingBounds = clipRect; + return GUI.BeginScrollView(position, scrollPos, viewRect, GUIStyle.none, GUIStyle.none); + } + + public static void EndScrollView() + { + GUI.EndScrollView(); + clippingBounds = new Rect(0, 0, Screen.width, Screen.height); + clippingEnabled = false; + } + + public static void CreateMaterial() + { + if (lineMaterial != null) + return; + + lineMaterial = Resources.Load("GLLineDraw", typeof(Material)) as Material; + } + + public static void DrawLine(Vector2 start, Vector2 end, Color color, float width) + { + if (Event.current == null) + return; + if (Event.current.type != EventType.repaint) + return; + + if (clippingEnabled) + if (!segment_rect_intersection(clippingBounds, ref start, ref end)) + return; + + CreateMaterial(); + + lineMaterial.SetPass(0); + + Vector3 startPt; + Vector3 endPt; + + if (width == 1) + { + GL.Begin(GL.LINES); + GL.Color(color); + startPt = new Vector3(start.x, start.y, 0); + endPt = new Vector3(end.x, end.y, 0); + GL.Vertex(startPt); + GL.Vertex(endPt); + } + else + { + GL.Begin(GL.QUADS); + GL.Color(color); + startPt = new Vector3(end.y, start.x, 0); + endPt = new Vector3(start.y, end.x, 0); + Vector3 perpendicular = (startPt - endPt).normalized * width; + Vector3 v1 = new Vector3(start.x, start.y, 0); + Vector3 v2 = new Vector3(end.x, end.y, 0); + GL.Vertex(v1 - perpendicular); + GL.Vertex(v1 + perpendicular); + GL.Vertex(v2 + perpendicular); + GL.Vertex(v2 - perpendicular); + } + GL.End(); + } + + public static void DrawRect(Rect rect, Color color) + { + if (Event.current == null) + return; + if (Event.current.type != EventType.repaint) + return; + + CreateMaterial(); + // set the current material + lineMaterial.SetPass( 0 ); + GL.Begin( GL.QUADS ); + GL.Color( color ); + GL.Vertex3( rect.xMin, rect.yMin, 0 ); + GL.Vertex3( rect.xMax, rect.yMin, 0 ); + GL.Vertex3( rect.xMax, rect.yMax, 0 ); + GL.Vertex3( rect.xMin, rect.yMax, 0 ); + GL.End(); + } + + public static void DrawBox(Rect box, Color color, float width) + { + Vector2 p1 = new Vector2(box.xMin, box.yMin); + Vector2 p2 = new Vector2(box.xMax, box.yMin); + Vector2 p3 = new Vector2(box.xMax, box.yMax); + Vector2 p4 = new Vector2(box.xMin, box.yMax); + DrawLine(p1, p2, color, width); + DrawLine(p2, p3, color, width); + DrawLine(p3, p4, color, width); + DrawLine(p4, p1, color, width); + } + + public static void DrawBox(Vector2 topLeftCorner, Vector2 bottomRightCorner, Color color, float width) + { + Rect box = new Rect(topLeftCorner.x, topLeftCorner.y, bottomRightCorner.x - topLeftCorner.x, bottomRightCorner.y - topLeftCorner.y); + DrawBox(box, color, width); + } + + public static void DrawRoundedBox(Rect box, float radius, Color color, float width) + { + Vector2 p1, p2, p3, p4, p5, p6, p7, p8; + p1 = new Vector2(box.xMin + radius, box.yMin); + p2 = new Vector2(box.xMax - radius, box.yMin); + p3 = new Vector2(box.xMax, box.yMin + radius); + p4 = new Vector2(box.xMax, box.yMax - radius); + p5 = new Vector2(box.xMax - radius, box.yMax); + p6 = new Vector2(box.xMin + radius, box.yMax); + p7 = new Vector2(box.xMin, box.yMax - radius); + p8 = new Vector2(box.xMin, box.yMin + radius); + + DrawLine(p1, p2, color, width); + DrawLine(p3, p4, color, width); + DrawLine(p5, p6, color, width); + DrawLine(p7, p8, color, width); + + Vector2 t1, t2; + float halfRadius = radius / 2; + + t1 = new Vector2(p8.x, p8.y + halfRadius); + t2 = new Vector2(p1.x - halfRadius, p1.y); + DrawBezier(p8, t1, p1, t2, color, width); + + t1 = new Vector2(p2.x + halfRadius, p2.y); + t2 = new Vector2(p3.x, p3.y - halfRadius); + DrawBezier(p2, t1, p3, t2, color, width); + + t1 = new Vector2(p4.x, p4.y + halfRadius); + t2 = new Vector2(p5.x + halfRadius, p5.y); + DrawBezier(p4, t1, p5, t2, color, width); + + t1 = new Vector2(p6.x - halfRadius, p6.y); + t2 = new Vector2(p7.x, p7.y + halfRadius); + DrawBezier(p6, t1, p7, t2, color, width); + } + + public static void DrawConnectingCurve(Vector2 start, Vector2 end, Color color, float width) + { + Vector2 distance = start - end; + + Vector2 tangentA = start; + tangentA.x -= distance.x * 0.5f; + Vector2 tangentB = end; + tangentB.x += distance.x * 0.5f; + + int segments = Mathf.FloorToInt((distance.magnitude / 20) * 3); + + DrawBezier(start, tangentA, end, tangentB, color, width, segments); + + Vector2 pA = CubeBezier(start, tangentA, end, tangentB, 0.6f); + Vector2 pB = CubeBezier(start, tangentA, end, tangentB, 0.7f); + + float arrowHeadSize = 5; + + Vector2 arrowPosA = pB; + Vector2 arrowPosB = arrowPosA; + Vector2 arrowPosC = arrowPosA; + + Vector2 dir = (pB - pA).normalized; + + arrowPosB.x += dir.y * arrowHeadSize; + arrowPosB.y -= dir.x * arrowHeadSize; + arrowPosB -= dir * arrowHeadSize; + + arrowPosC.x -= dir.y * arrowHeadSize; + arrowPosC.y += dir.x * arrowHeadSize; + arrowPosC -= dir * arrowHeadSize; + + DrawLine(arrowPosA, arrowPosB, color, 1.025f); + DrawLine(arrowPosA, arrowPosC, color, 1.025f); + } + + public static void DrawBezier(Vector2 start, Vector2 startTangent, Vector2 end, Vector2 endTangent, Color color, float width) + { + int segments = Mathf.FloorToInt((start - end).magnitude / 20) * 3; // Three segments per distance of 20 + DrawBezier(start, startTangent, end, endTangent, color, width, segments); + } + + public static void DrawBezier(Vector2 start, Vector2 startTangent, Vector2 end, Vector2 endTangent, Color color, float width, int segments) + { + Vector2 startVector = CubeBezier(start, startTangent, end, endTangent, 0); + for (int i = 1; i <= segments; i++) + { + Vector2 endVector = CubeBezier(start, startTangent, end, endTangent, i / (float)segments); + DrawLine(startVector, endVector, color, width); + startVector = endVector; + } + } + + private static Vector2 CubeBezier(Vector2 s, Vector2 st, Vector2 e, Vector2 et, float t) + { + float rt = 1 - t; + float rtt = rt * t; + return rt * rt * rt * s + 3 * rt * rtt * st + 3 * rtt * t * et + t * t * t * e; + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Flowchart/Editor/IfEditor.cs b/Assets/Fungus/Flowchart/Editor/IfEditor.cs index 2ef6e6bf..e6dafd73 100644 --- a/Assets/Fungus/Flowchart/Editor/IfEditor.cs +++ b/Assets/Fungus/Flowchart/Editor/IfEditor.cs @@ -12,87 +12,87 @@ using System.Reflection; namespace Fungus { - [CustomEditor (typeof(If), true)] - public class IfEditor : CommandEditor - { - protected SerializedProperty variableProp; - protected SerializedProperty compareOperatorProp; - protected SerializedProperty booleanDataProp; - protected SerializedProperty integerDataProp; - protected SerializedProperty floatDataProp; - protected SerializedProperty stringDataProp; - - protected virtual void OnEnable() - { - if (NullTargetCheck()) // Check for an orphaned editor instance - return; - - variableProp = serializedObject.FindProperty("variable"); - compareOperatorProp = serializedObject.FindProperty("compareOperator"); - booleanDataProp = serializedObject.FindProperty("booleanData"); - integerDataProp = serializedObject.FindProperty("integerData"); - floatDataProp = serializedObject.FindProperty("floatData"); - stringDataProp = serializedObject.FindProperty("stringData"); - } - - public override void DrawCommandGUI() - { - serializedObject.Update(); - - If t = target as If; - - Flowchart flowchart = t.GetFlowchart(); - if (flowchart == null) - { - return; - } - - EditorGUILayout.PropertyField(variableProp); - - if (variableProp.objectReferenceValue == null) - { - serializedObject.ApplyModifiedProperties(); - return; - } - - Variable selectedVariable = variableProp.objectReferenceValue as Variable; - System.Type variableType = selectedVariable.GetType(); - - List operatorList = new List(); - operatorList.Add(new GUIContent("==")); - operatorList.Add(new GUIContent("!=")); - if (variableType == typeof(IntegerVariable) || - variableType == typeof(FloatVariable)) - { - operatorList.Add(new GUIContent("<")); - operatorList.Add(new GUIContent(">")); - operatorList.Add(new GUIContent("<=")); - operatorList.Add(new GUIContent(">=")); - } - - compareOperatorProp.enumValueIndex = EditorGUILayout.Popup(new GUIContent("Compare", "The comparison operator to use when comparing values"), - compareOperatorProp.enumValueIndex, - operatorList.ToArray()); - - if (variableType == typeof(BooleanVariable)) - { - EditorGUILayout.PropertyField(booleanDataProp); - } - else if (variableType == typeof(IntegerVariable)) - { - EditorGUILayout.PropertyField(integerDataProp); - } - else if (variableType == typeof(FloatVariable)) - { - EditorGUILayout.PropertyField(floatDataProp); - } - else if (variableType == typeof(StringVariable)) - { - EditorGUILayout.PropertyField(stringDataProp); - } - - serializedObject.ApplyModifiedProperties(); - } - } + [CustomEditor (typeof(If), true)] + public class IfEditor : CommandEditor + { + protected SerializedProperty variableProp; + protected SerializedProperty compareOperatorProp; + protected SerializedProperty booleanDataProp; + protected SerializedProperty integerDataProp; + protected SerializedProperty floatDataProp; + protected SerializedProperty stringDataProp; + + protected virtual void OnEnable() + { + if (NullTargetCheck()) // Check for an orphaned editor instance + return; + + variableProp = serializedObject.FindProperty("variable"); + compareOperatorProp = serializedObject.FindProperty("compareOperator"); + booleanDataProp = serializedObject.FindProperty("booleanData"); + integerDataProp = serializedObject.FindProperty("integerData"); + floatDataProp = serializedObject.FindProperty("floatData"); + stringDataProp = serializedObject.FindProperty("stringData"); + } + + public override void DrawCommandGUI() + { + serializedObject.Update(); + + If t = target as If; + + Flowchart flowchart = t.GetFlowchart(); + if (flowchart == null) + { + return; + } + + EditorGUILayout.PropertyField(variableProp); + + if (variableProp.objectReferenceValue == null) + { + serializedObject.ApplyModifiedProperties(); + return; + } + + Variable selectedVariable = variableProp.objectReferenceValue as Variable; + System.Type variableType = selectedVariable.GetType(); + + List operatorList = new List(); + operatorList.Add(new GUIContent("==")); + operatorList.Add(new GUIContent("!=")); + if (variableType == typeof(IntegerVariable) || + variableType == typeof(FloatVariable)) + { + operatorList.Add(new GUIContent("<")); + operatorList.Add(new GUIContent(">")); + operatorList.Add(new GUIContent("<=")); + operatorList.Add(new GUIContent(">=")); + } + + compareOperatorProp.enumValueIndex = EditorGUILayout.Popup(new GUIContent("Compare", "The comparison operator to use when comparing values"), + compareOperatorProp.enumValueIndex, + operatorList.ToArray()); + + if (variableType == typeof(BooleanVariable)) + { + EditorGUILayout.PropertyField(booleanDataProp); + } + else if (variableType == typeof(IntegerVariable)) + { + EditorGUILayout.PropertyField(integerDataProp); + } + else if (variableType == typeof(FloatVariable)) + { + EditorGUILayout.PropertyField(floatDataProp); + } + else if (variableType == typeof(StringVariable)) + { + EditorGUILayout.PropertyField(stringDataProp); + } + + serializedObject.ApplyModifiedProperties(); + } + } } diff --git a/Assets/Fungus/Flowchart/Editor/InvokeEventEditor.cs b/Assets/Fungus/Flowchart/Editor/InvokeEventEditor.cs index 1daa0485..4e791e59 100644 --- a/Assets/Fungus/Flowchart/Editor/InvokeEventEditor.cs +++ b/Assets/Fungus/Flowchart/Editor/InvokeEventEditor.cs @@ -10,70 +10,70 @@ using System.Collections.Generic; namespace Fungus { - [CustomEditor (typeof(InvokeEvent))] - public class InvokeEventEditor : CommandEditor - { - protected SerializedProperty delayProp; - protected SerializedProperty invokeTypeProp; - protected SerializedProperty staticEventProp; - protected SerializedProperty booleanParameterProp; - protected SerializedProperty booleanEventProp; - protected SerializedProperty integerParameterProp; - protected SerializedProperty integerEventProp; - protected SerializedProperty floatParameterProp; - protected SerializedProperty floatEventProp; - protected SerializedProperty stringParameterProp; - protected SerializedProperty stringEventProp; + [CustomEditor (typeof(InvokeEvent))] + public class InvokeEventEditor : CommandEditor + { + protected SerializedProperty delayProp; + protected SerializedProperty invokeTypeProp; + protected SerializedProperty staticEventProp; + protected SerializedProperty booleanParameterProp; + protected SerializedProperty booleanEventProp; + protected SerializedProperty integerParameterProp; + protected SerializedProperty integerEventProp; + protected SerializedProperty floatParameterProp; + protected SerializedProperty floatEventProp; + protected SerializedProperty stringParameterProp; + protected SerializedProperty stringEventProp; - protected virtual void OnEnable() - { - if (NullTargetCheck()) // Check for an orphaned editor instance - return; + protected virtual void OnEnable() + { + if (NullTargetCheck()) // Check for an orphaned editor instance + return; - delayProp = serializedObject.FindProperty("delay"); - invokeTypeProp = serializedObject.FindProperty("invokeType"); - staticEventProp = serializedObject.FindProperty("staticEvent"); - booleanParameterProp = serializedObject.FindProperty("booleanParameter"); - booleanEventProp = serializedObject.FindProperty("booleanEvent"); - integerParameterProp = serializedObject.FindProperty("integerParameter"); - integerEventProp = serializedObject.FindProperty("integerEvent"); - floatParameterProp = serializedObject.FindProperty("floatParameter"); - floatEventProp = serializedObject.FindProperty("floatEvent"); - stringParameterProp = serializedObject.FindProperty("stringParameter"); - stringEventProp = serializedObject.FindProperty("stringEvent"); - } + delayProp = serializedObject.FindProperty("delay"); + invokeTypeProp = serializedObject.FindProperty("invokeType"); + staticEventProp = serializedObject.FindProperty("staticEvent"); + booleanParameterProp = serializedObject.FindProperty("booleanParameter"); + booleanEventProp = serializedObject.FindProperty("booleanEvent"); + integerParameterProp = serializedObject.FindProperty("integerParameter"); + integerEventProp = serializedObject.FindProperty("integerEvent"); + floatParameterProp = serializedObject.FindProperty("floatParameter"); + floatEventProp = serializedObject.FindProperty("floatEvent"); + stringParameterProp = serializedObject.FindProperty("stringParameter"); + stringEventProp = serializedObject.FindProperty("stringEvent"); + } - public override void DrawCommandGUI() - { - serializedObject.Update(); + public override void DrawCommandGUI() + { + serializedObject.Update(); - EditorGUILayout.PropertyField(delayProp); - EditorGUILayout.PropertyField(invokeTypeProp); + EditorGUILayout.PropertyField(delayProp); + EditorGUILayout.PropertyField(invokeTypeProp); - switch ((InvokeEvent.InvokeType)invokeTypeProp.enumValueIndex) - { - case InvokeEvent.InvokeType.Static: - EditorGUILayout.PropertyField(staticEventProp); - break; - case InvokeEvent.InvokeType.DynamicBoolean: - EditorGUILayout.PropertyField(booleanEventProp); - EditorGUILayout.PropertyField(booleanParameterProp); - break; - case InvokeEvent.InvokeType.DynamicInteger: - EditorGUILayout.PropertyField(integerEventProp); - EditorGUILayout.PropertyField(integerParameterProp); - break; - case InvokeEvent.InvokeType.DynamicFloat: - EditorGUILayout.PropertyField(floatEventProp); - EditorGUILayout.PropertyField(floatParameterProp); - break; - case InvokeEvent.InvokeType.DynamicString: - EditorGUILayout.PropertyField(stringEventProp); - EditorGUILayout.PropertyField(stringParameterProp); - break; - } + switch ((InvokeEvent.InvokeType)invokeTypeProp.enumValueIndex) + { + case InvokeEvent.InvokeType.Static: + EditorGUILayout.PropertyField(staticEventProp); + break; + case InvokeEvent.InvokeType.DynamicBoolean: + EditorGUILayout.PropertyField(booleanEventProp); + EditorGUILayout.PropertyField(booleanParameterProp); + break; + case InvokeEvent.InvokeType.DynamicInteger: + EditorGUILayout.PropertyField(integerEventProp); + EditorGUILayout.PropertyField(integerParameterProp); + break; + case InvokeEvent.InvokeType.DynamicFloat: + EditorGUILayout.PropertyField(floatEventProp); + EditorGUILayout.PropertyField(floatParameterProp); + break; + case InvokeEvent.InvokeType.DynamicString: + EditorGUILayout.PropertyField(stringEventProp); + EditorGUILayout.PropertyField(stringParameterProp); + break; + } - serializedObject.ApplyModifiedProperties(); - } - } + serializedObject.ApplyModifiedProperties(); + } + } } diff --git a/Assets/Fungus/Flowchart/Editor/InvokeMethodEditor.cs b/Assets/Fungus/Flowchart/Editor/InvokeMethodEditor.cs old mode 100755 new mode 100644 index 92014333..c654d5e2 --- a/Assets/Fungus/Flowchart/Editor/InvokeMethodEditor.cs +++ b/Assets/Fungus/Flowchart/Editor/InvokeMethodEditor.cs @@ -15,433 +15,433 @@ using System.Text; namespace Fungus { - [CustomEditor(typeof(InvokeMethod))] - public class InvokeMethodEditor : CommandEditor - { - InvokeMethod targetMethod; - - public override void DrawCommandGUI() - { - base.DrawCommandGUI(); - - targetMethod = target as InvokeMethod; - - if (targetMethod == null || targetMethod.targetObject == null) - return; - - SerializedObject objSerializedTarget = new SerializedObject(targetMethod); - - string component = ShowComponents(objSerializedTarget, targetMethod.targetObject); - - // show component methods if selected - if (!string.IsNullOrEmpty(component)) - { - var method = ShowMethods(objSerializedTarget, targetMethod.targetObject, component); - - // show method parameters if selected - if (method != null) - { - objSerializedTarget.ApplyModifiedProperties(); - ShowParameters(objSerializedTarget, targetMethod.targetObject, method); - ShowReturnValue(objSerializedTarget, method); - } - } - } - - private string ShowComponents(SerializedObject objTarget, GameObject gameObject) - { - var targetComponentAssemblyName = objTarget.FindProperty("targetComponentAssemblyName"); - var targetComponentFullname = objTarget.FindProperty("targetComponentFullname"); - var targetComponentText = objTarget.FindProperty("targetComponentText"); - var objComponents = gameObject.GetComponents(); - var objTypesAssemblynames = (from objComp in objComponents select objComp.GetType().AssemblyQualifiedName).ToList(); - var objTypesName = (from objComp in objComponents select objComp.GetType().Name).ToList(); - - int index = objTypesAssemblynames.IndexOf(targetComponentAssemblyName.stringValue); - - index = EditorGUILayout.Popup("Target Component", index, objTypesName.ToArray()); - - if (index != -1) - { - targetComponentAssemblyName.stringValue = objTypesAssemblynames[index]; - targetComponentFullname.stringValue = objComponents.GetType().FullName; - targetComponentText.stringValue = objTypesName[index]; - } - else - { - targetComponentAssemblyName.stringValue = null; - } - - objTarget.ApplyModifiedProperties(); - - return targetComponentAssemblyName.stringValue; - } - - private MethodInfo ShowMethods(SerializedObject objTarget, GameObject gameObject, string component) - { - MethodInfo result = null; - - var targetMethodProp = objTarget.FindProperty("targetMethod"); - var targetMethodTextProp = objTarget.FindProperty("targetMethodText"); - var methodParametersProp = objTarget.FindProperty("methodParameters"); - var showInheritedProp = objTarget.FindProperty("showInherited"); - var saveReturnValueProp = objTarget.FindProperty("saveReturnValue"); - var returnValueKeyProp = objTarget.FindProperty("returnValueVariableKey"); - - var objComponent = gameObject.GetComponent(ReflectionHelper.GetType(component)); - var bindingFlags = BindingFlags.Default | BindingFlags.Public | BindingFlags.Instance; - - if (!showInheritedProp.boolValue) - { - bindingFlags |= BindingFlags.DeclaredOnly; - } - - if (objComponent != null) - { - var objMethods = objComponent.GetType().GetMethods(bindingFlags); - var methods = (from objMethod in objMethods where !objMethod.IsSpecialName select objMethod).ToList(); // filter out the getter/setter methods - var methodText = (from objMethod in methods select objMethod.Name + FormatParameters(objMethod.GetParameters()) + ": " + objMethod.ReturnType.Name).ToList(); - int index = methodText.IndexOf(targetMethodTextProp.stringValue); - - index = EditorGUILayout.Popup("Target Method", index, methodText.ToArray()); - - EditorGUILayout.PropertyField(showInheritedProp); - - if (index != -1) - { - if (targetMethodTextProp.stringValue != methodText[index]) - { - // reset - methodParametersProp.ClearArray(); - methodParametersProp.arraySize = methods[index].GetParameters().Length; - - saveReturnValueProp.boolValue = false; - returnValueKeyProp.stringValue = null; - } - - targetMethodTextProp.stringValue = methodText[index]; - targetMethodProp.stringValue = methods[index].Name; - - result = methods[index]; - } - else - { - targetMethodTextProp.stringValue = null; - targetMethodProp.stringValue = null; - } - - objTarget.ApplyModifiedProperties(); - } - - return result; - } - - private void ShowParameters(SerializedObject objTarget, GameObject gameObject, MethodInfo method) - { - var methodParametersProp = objTarget.FindProperty("methodParameters"); - var objParams = method.GetParameters(); - - if (objParams.Length > 0) - { - GUILayout.Space(20); - EditorGUILayout.LabelField("Parameters", EditorStyles.boldLabel); - EditorGUILayout.BeginVertical(EditorStyles.helpBox); - - for (int i = 0; i < objParams.Length; i++) - { - var objParam = objParams[i]; - - GUILayout.BeginHorizontal(); - string labelFormat = string.Format("{0}: {1}", objParam.ParameterType.Name, objParam.Name); - - var objItemProp = methodParametersProp.GetArrayElementAtIndex(i); - var serObjValueProp = objItemProp.FindPropertyRelative("objValue"); - var serVariableKeyProp = objItemProp.FindPropertyRelative("variableKey"); - var serValueTypeAssemblynameProp = serObjValueProp.FindPropertyRelative("typeAssemblyname"); - var serValueTypeFullnameProp = serObjValueProp.FindPropertyRelative("typeFullname"); - - serValueTypeAssemblynameProp.stringValue = objParam.ParameterType.AssemblyQualifiedName; - serValueTypeFullnameProp.stringValue = objParam.ParameterType.FullName; - - bool isDrawn = true; - - if (string.IsNullOrEmpty(serVariableKeyProp.stringValue)) - { - isDrawn = DrawTypedPropertyInput(labelFormat, serObjValueProp, objParam.ParameterType); - } - - if (isDrawn) - { - var vars = GetFungusVariablesByType(targetMethod.GetFlowchart().variables, objParam.ParameterType); - var values = new string[] { "" }; - var displayValue = values.Concat(vars).ToList(); - - int index = displayValue.IndexOf(serVariableKeyProp.stringValue); - - if (index == -1) - { - index = 0; - } - - if (string.IsNullOrEmpty(serVariableKeyProp.stringValue)) - { - index = EditorGUILayout.Popup(index, displayValue.ToArray(), GUILayout.MaxWidth(80)); - } - else - { - index = EditorGUILayout.Popup(labelFormat, index, displayValue.ToArray()); - } - - if (index > 0) - { - serVariableKeyProp.stringValue = displayValue[index]; - } - else - { - serVariableKeyProp.stringValue = null; - } - } - else - { - var style = EditorStyles.label; - var prevColor = style.normal.textColor; - style.normal.textColor = Color.red; - EditorGUILayout.LabelField(new GUIContent(objParam.ParameterType.Name + " cannot be drawn, don´t use this method in the flowchart."), style); - style.normal.textColor = prevColor; - } - - GUILayout.EndHorizontal(); - } - EditorGUILayout.EndVertical(); - - objTarget.ApplyModifiedProperties(); - } - } - - private void ShowReturnValue(SerializedObject objTarget, MethodInfo method) - { - var saveReturnValueProp = objTarget.FindProperty("saveReturnValue"); - var returnValueKeyProp = objTarget.FindProperty("returnValueVariableKey"); - var returnValueTypeProp = objTarget.FindProperty("returnValueType"); - var callModeProp = objTarget.FindProperty("callMode"); - - returnValueTypeProp.stringValue = method.ReturnType.FullName; - - if (method.ReturnType == typeof(IEnumerator)) - { - GUILayout.Space(20); - EditorGUILayout.PropertyField(callModeProp); - } - else if (method.ReturnType != typeof(void)) - { - GUILayout.Space(20); - EditorGUILayout.LabelField("Return Value", EditorStyles.boldLabel); - EditorGUILayout.BeginVertical(EditorStyles.helpBox); - saveReturnValueProp.boolValue = EditorGUILayout.Toggle("Save return value", saveReturnValueProp.boolValue); - - if (saveReturnValueProp.boolValue) - { - var vars = GetFungusVariablesByType(targetMethod.GetFlowchart().variables, method.ReturnType).ToList(); - int index = vars.IndexOf(returnValueKeyProp.stringValue); - index = EditorGUILayout.Popup(method.ReturnType.Name, index, vars.ToArray()); - - if (index != -1) - { - returnValueKeyProp.stringValue = vars[index]; - } - } - - EditorGUILayout.EndVertical(); - } - else - { - saveReturnValueProp.boolValue = false; - } - - objTarget.ApplyModifiedProperties(); - } - - private bool DrawTypedPropertyInput(string label, SerializedProperty objProperty, Type type) - { - SerializedProperty objectValue = null; - - if (type == typeof(int)) - { - objectValue = objProperty.FindPropertyRelative("intValue"); - objectValue.intValue = EditorGUILayout.IntField(new GUIContent(label), objectValue.intValue); - - return true; - } - else if (type == typeof(bool)) - { - objectValue = objProperty.FindPropertyRelative("boolValue"); - objectValue.boolValue = EditorGUILayout.Toggle(new GUIContent(label), objectValue.boolValue); - - return true; - } - else if (type == typeof(float)) - { - objectValue = objProperty.FindPropertyRelative("floatValue"); - objectValue.floatValue = EditorGUILayout.FloatField(new GUIContent(label), objectValue.floatValue); - - return true; - } - else if (type == typeof(string)) - { - objectValue = objProperty.FindPropertyRelative("stringValue"); - objectValue.stringValue = EditorGUILayout.TextField(new GUIContent(label), objectValue.stringValue); - - return true; - } - else if (type == typeof(Color)) - { - objectValue = objProperty.FindPropertyRelative("colorValue"); - objectValue.colorValue = EditorGUILayout.ColorField(new GUIContent(label), objectValue.colorValue); - - return true; - } - else if (type == typeof(UnityEngine.GameObject)) - { - objectValue = objProperty.FindPropertyRelative("gameObjectValue"); - objectValue.objectReferenceValue = EditorGUILayout.ObjectField(new GUIContent(label), objectValue.objectReferenceValue, typeof(UnityEngine.GameObject), true); - - return true; - } - else if (type == typeof(UnityEngine.Material)) - { - objectValue = objProperty.FindPropertyRelative("materialValue"); - objectValue.objectReferenceValue = EditorGUILayout.ObjectField(new GUIContent(label), objectValue.objectReferenceValue, typeof(UnityEngine.Material), true); - - return true; - } - else if (type == typeof(UnityEngine.Sprite)) - { - objectValue = objProperty.FindPropertyRelative("spriteValue"); - objectValue.objectReferenceValue = EditorGUILayout.ObjectField(new GUIContent(label), objectValue.objectReferenceValue, typeof(UnityEngine.Sprite), true); - - return true; - } - else if (type == typeof(UnityEngine.Texture)) - { - objectValue = objProperty.FindPropertyRelative("textureValue"); - objectValue.objectReferenceValue = EditorGUILayout.ObjectField(new GUIContent(label), objectValue.objectReferenceValue, typeof(UnityEngine.Texture), true); - - return true; - } - else if (type == typeof(UnityEngine.Vector2)) - { - objectValue = objProperty.FindPropertyRelative("vector2Value"); - objectValue.vector2Value = EditorGUILayout.Vector2Field(new GUIContent(label), objectValue.vector2Value); - - return true; - } - else if (type == typeof(UnityEngine.Vector3)) - { - objectValue = objProperty.FindPropertyRelative("vector3Value"); - objectValue.vector3Value = EditorGUILayout.Vector3Field(new GUIContent(label), objectValue.vector3Value); - - return true; - } - else if (type.IsSubclassOf(typeof(UnityEngine.Object))) - { - objectValue = objProperty.FindPropertyRelative("objectValue"); - objectValue.objectReferenceValue = EditorGUILayout.ObjectField(new GUIContent(label), objectValue.objectReferenceValue, type, true); - - return true; - } - else if (type.IsEnum) - { - var enumNames = Enum.GetNames(type); - objectValue = objProperty.FindPropertyRelative("intValue"); - objectValue.intValue = EditorGUILayout.Popup(label, objectValue.intValue, enumNames); - - return true; - } - - return false; - } - - private string[] GetFungusVariablesByType(List variables, Type type) - { - string[] result = new string[0]; - - if (type == typeof(int)) - { - result = (from v in variables where v.GetType() == typeof(IntegerVariable) select v.key).ToArray(); - } - else if (type == typeof(bool)) - { - result = (from v in variables where v.GetType() == typeof(BooleanVariable) select v.key).ToArray(); - } - else if (type == typeof(float)) - { - result = (from v in variables where v.GetType() == typeof(FloatVariable) select v.key).ToArray(); - } - else if (type == typeof(string)) - { - result = (from v in variables where v.GetType() == typeof(StringVariable) select v.key).ToArray(); - } - else if (type == typeof(Color)) - { - result = (from v in variables where v.GetType() == typeof(ColorVariable) select v.key).ToArray(); - } - else if (type == typeof(UnityEngine.GameObject)) - { - result = (from v in variables where v.GetType() == typeof(GameObjectVariable) select v.key).ToArray(); - } - else if (type == typeof(UnityEngine.Material)) - { - result = (from v in variables where v.GetType() == typeof(MaterialVariable) select v.key).ToArray(); - } - else if (type == typeof(UnityEngine.Sprite)) - { - result = (from v in variables where v.GetType() == typeof(SpriteVariable) select v.key).ToArray(); - } - else if (type == typeof(UnityEngine.Texture)) - { - result = (from v in variables where v.GetType() == typeof(TextureVariable) select v.key).ToArray(); - } - else if (type == typeof(UnityEngine.Vector2)) - { - result = (from v in variables where v.GetType() == typeof(Vector2Variable) select v.key).ToArray(); - } - else if (type == typeof(UnityEngine.Vector3)) - { - result = (from v in variables where v.GetType() == typeof(Vector3Variable) select v.key).ToArray(); - } - else if (type.IsSubclassOf(typeof(UnityEngine.Object))) - { - result = (from v in variables where v.GetType() == typeof(ObjectVariable) select v.key).ToArray(); - } - - return result; - } - - private string FormatParameters(ParameterInfo[] paramInfo) - { - string result = " ("; - - for (int i = 0; i < paramInfo.Length; i++) - { - var pi = paramInfo[i]; - result += pi.ParameterType.Name; // " arg" + (i + 1); - - if (i < paramInfo.Length - 1) - { - result += ", "; - } - } - - return result + ")"; - } - - private object GetDefaultValue(Type t) - { - if (t.IsValueType) - return Activator.CreateInstance(t); - - return null; - } - } + [CustomEditor(typeof(InvokeMethod))] + public class InvokeMethodEditor : CommandEditor + { + InvokeMethod targetMethod; + + public override void DrawCommandGUI() + { + base.DrawCommandGUI(); + + targetMethod = target as InvokeMethod; + + if (targetMethod == null || targetMethod.targetObject == null) + return; + + SerializedObject objSerializedTarget = new SerializedObject(targetMethod); + + string component = ShowComponents(objSerializedTarget, targetMethod.targetObject); + + // show component methods if selected + if (!string.IsNullOrEmpty(component)) + { + var method = ShowMethods(objSerializedTarget, targetMethod.targetObject, component); + + // show method parameters if selected + if (method != null) + { + objSerializedTarget.ApplyModifiedProperties(); + ShowParameters(objSerializedTarget, targetMethod.targetObject, method); + ShowReturnValue(objSerializedTarget, method); + } + } + } + + private string ShowComponents(SerializedObject objTarget, GameObject gameObject) + { + var targetComponentAssemblyName = objTarget.FindProperty("targetComponentAssemblyName"); + var targetComponentFullname = objTarget.FindProperty("targetComponentFullname"); + var targetComponentText = objTarget.FindProperty("targetComponentText"); + var objComponents = gameObject.GetComponents(); + var objTypesAssemblynames = (from objComp in objComponents select objComp.GetType().AssemblyQualifiedName).ToList(); + var objTypesName = (from objComp in objComponents select objComp.GetType().Name).ToList(); + + int index = objTypesAssemblynames.IndexOf(targetComponentAssemblyName.stringValue); + + index = EditorGUILayout.Popup("Target Component", index, objTypesName.ToArray()); + + if (index != -1) + { + targetComponentAssemblyName.stringValue = objTypesAssemblynames[index]; + targetComponentFullname.stringValue = objComponents.GetType().FullName; + targetComponentText.stringValue = objTypesName[index]; + } + else + { + targetComponentAssemblyName.stringValue = null; + } + + objTarget.ApplyModifiedProperties(); + + return targetComponentAssemblyName.stringValue; + } + + private MethodInfo ShowMethods(SerializedObject objTarget, GameObject gameObject, string component) + { + MethodInfo result = null; + + var targetMethodProp = objTarget.FindProperty("targetMethod"); + var targetMethodTextProp = objTarget.FindProperty("targetMethodText"); + var methodParametersProp = objTarget.FindProperty("methodParameters"); + var showInheritedProp = objTarget.FindProperty("showInherited"); + var saveReturnValueProp = objTarget.FindProperty("saveReturnValue"); + var returnValueKeyProp = objTarget.FindProperty("returnValueVariableKey"); + + var objComponent = gameObject.GetComponent(ReflectionHelper.GetType(component)); + var bindingFlags = BindingFlags.Default | BindingFlags.Public | BindingFlags.Instance; + + if (!showInheritedProp.boolValue) + { + bindingFlags |= BindingFlags.DeclaredOnly; + } + + if (objComponent != null) + { + var objMethods = objComponent.GetType().GetMethods(bindingFlags); + var methods = (from objMethod in objMethods where !objMethod.IsSpecialName select objMethod).ToList(); // filter out the getter/setter methods + var methodText = (from objMethod in methods select objMethod.Name + FormatParameters(objMethod.GetParameters()) + ": " + objMethod.ReturnType.Name).ToList(); + int index = methodText.IndexOf(targetMethodTextProp.stringValue); + + index = EditorGUILayout.Popup("Target Method", index, methodText.ToArray()); + + EditorGUILayout.PropertyField(showInheritedProp); + + if (index != -1) + { + if (targetMethodTextProp.stringValue != methodText[index]) + { + // reset + methodParametersProp.ClearArray(); + methodParametersProp.arraySize = methods[index].GetParameters().Length; + + saveReturnValueProp.boolValue = false; + returnValueKeyProp.stringValue = null; + } + + targetMethodTextProp.stringValue = methodText[index]; + targetMethodProp.stringValue = methods[index].Name; + + result = methods[index]; + } + else + { + targetMethodTextProp.stringValue = null; + targetMethodProp.stringValue = null; + } + + objTarget.ApplyModifiedProperties(); + } + + return result; + } + + private void ShowParameters(SerializedObject objTarget, GameObject gameObject, MethodInfo method) + { + var methodParametersProp = objTarget.FindProperty("methodParameters"); + var objParams = method.GetParameters(); + + if (objParams.Length > 0) + { + GUILayout.Space(20); + EditorGUILayout.LabelField("Parameters", EditorStyles.boldLabel); + EditorGUILayout.BeginVertical(EditorStyles.helpBox); + + for (int i = 0; i < objParams.Length; i++) + { + var objParam = objParams[i]; + + GUILayout.BeginHorizontal(); + string labelFormat = string.Format("{0}: {1}", objParam.ParameterType.Name, objParam.Name); + + var objItemProp = methodParametersProp.GetArrayElementAtIndex(i); + var serObjValueProp = objItemProp.FindPropertyRelative("objValue"); + var serVariableKeyProp = objItemProp.FindPropertyRelative("variableKey"); + var serValueTypeAssemblynameProp = serObjValueProp.FindPropertyRelative("typeAssemblyname"); + var serValueTypeFullnameProp = serObjValueProp.FindPropertyRelative("typeFullname"); + + serValueTypeAssemblynameProp.stringValue = objParam.ParameterType.AssemblyQualifiedName; + serValueTypeFullnameProp.stringValue = objParam.ParameterType.FullName; + + bool isDrawn = true; + + if (string.IsNullOrEmpty(serVariableKeyProp.stringValue)) + { + isDrawn = DrawTypedPropertyInput(labelFormat, serObjValueProp, objParam.ParameterType); + } + + if (isDrawn) + { + var vars = GetFungusVariablesByType(targetMethod.GetFlowchart().variables, objParam.ParameterType); + var values = new string[] { "" }; + var displayValue = values.Concat(vars).ToList(); + + int index = displayValue.IndexOf(serVariableKeyProp.stringValue); + + if (index == -1) + { + index = 0; + } + + if (string.IsNullOrEmpty(serVariableKeyProp.stringValue)) + { + index = EditorGUILayout.Popup(index, displayValue.ToArray(), GUILayout.MaxWidth(80)); + } + else + { + index = EditorGUILayout.Popup(labelFormat, index, displayValue.ToArray()); + } + + if (index > 0) + { + serVariableKeyProp.stringValue = displayValue[index]; + } + else + { + serVariableKeyProp.stringValue = null; + } + } + else + { + var style = EditorStyles.label; + var prevColor = style.normal.textColor; + style.normal.textColor = Color.red; + EditorGUILayout.LabelField(new GUIContent(objParam.ParameterType.Name + " cannot be drawn, don´t use this method in the flowchart."), style); + style.normal.textColor = prevColor; + } + + GUILayout.EndHorizontal(); + } + EditorGUILayout.EndVertical(); + + objTarget.ApplyModifiedProperties(); + } + } + + private void ShowReturnValue(SerializedObject objTarget, MethodInfo method) + { + var saveReturnValueProp = objTarget.FindProperty("saveReturnValue"); + var returnValueKeyProp = objTarget.FindProperty("returnValueVariableKey"); + var returnValueTypeProp = objTarget.FindProperty("returnValueType"); + var callModeProp = objTarget.FindProperty("callMode"); + + returnValueTypeProp.stringValue = method.ReturnType.FullName; + + if (method.ReturnType == typeof(IEnumerator)) + { + GUILayout.Space(20); + EditorGUILayout.PropertyField(callModeProp); + } + else if (method.ReturnType != typeof(void)) + { + GUILayout.Space(20); + EditorGUILayout.LabelField("Return Value", EditorStyles.boldLabel); + EditorGUILayout.BeginVertical(EditorStyles.helpBox); + saveReturnValueProp.boolValue = EditorGUILayout.Toggle("Save return value", saveReturnValueProp.boolValue); + + if (saveReturnValueProp.boolValue) + { + var vars = GetFungusVariablesByType(targetMethod.GetFlowchart().variables, method.ReturnType).ToList(); + int index = vars.IndexOf(returnValueKeyProp.stringValue); + index = EditorGUILayout.Popup(method.ReturnType.Name, index, vars.ToArray()); + + if (index != -1) + { + returnValueKeyProp.stringValue = vars[index]; + } + } + + EditorGUILayout.EndVertical(); + } + else + { + saveReturnValueProp.boolValue = false; + } + + objTarget.ApplyModifiedProperties(); + } + + private bool DrawTypedPropertyInput(string label, SerializedProperty objProperty, Type type) + { + SerializedProperty objectValue = null; + + if (type == typeof(int)) + { + objectValue = objProperty.FindPropertyRelative("intValue"); + objectValue.intValue = EditorGUILayout.IntField(new GUIContent(label), objectValue.intValue); + + return true; + } + else if (type == typeof(bool)) + { + objectValue = objProperty.FindPropertyRelative("boolValue"); + objectValue.boolValue = EditorGUILayout.Toggle(new GUIContent(label), objectValue.boolValue); + + return true; + } + else if (type == typeof(float)) + { + objectValue = objProperty.FindPropertyRelative("floatValue"); + objectValue.floatValue = EditorGUILayout.FloatField(new GUIContent(label), objectValue.floatValue); + + return true; + } + else if (type == typeof(string)) + { + objectValue = objProperty.FindPropertyRelative("stringValue"); + objectValue.stringValue = EditorGUILayout.TextField(new GUIContent(label), objectValue.stringValue); + + return true; + } + else if (type == typeof(Color)) + { + objectValue = objProperty.FindPropertyRelative("colorValue"); + objectValue.colorValue = EditorGUILayout.ColorField(new GUIContent(label), objectValue.colorValue); + + return true; + } + else if (type == typeof(UnityEngine.GameObject)) + { + objectValue = objProperty.FindPropertyRelative("gameObjectValue"); + objectValue.objectReferenceValue = EditorGUILayout.ObjectField(new GUIContent(label), objectValue.objectReferenceValue, typeof(UnityEngine.GameObject), true); + + return true; + } + else if (type == typeof(UnityEngine.Material)) + { + objectValue = objProperty.FindPropertyRelative("materialValue"); + objectValue.objectReferenceValue = EditorGUILayout.ObjectField(new GUIContent(label), objectValue.objectReferenceValue, typeof(UnityEngine.Material), true); + + return true; + } + else if (type == typeof(UnityEngine.Sprite)) + { + objectValue = objProperty.FindPropertyRelative("spriteValue"); + objectValue.objectReferenceValue = EditorGUILayout.ObjectField(new GUIContent(label), objectValue.objectReferenceValue, typeof(UnityEngine.Sprite), true); + + return true; + } + else if (type == typeof(UnityEngine.Texture)) + { + objectValue = objProperty.FindPropertyRelative("textureValue"); + objectValue.objectReferenceValue = EditorGUILayout.ObjectField(new GUIContent(label), objectValue.objectReferenceValue, typeof(UnityEngine.Texture), true); + + return true; + } + else if (type == typeof(UnityEngine.Vector2)) + { + objectValue = objProperty.FindPropertyRelative("vector2Value"); + objectValue.vector2Value = EditorGUILayout.Vector2Field(new GUIContent(label), objectValue.vector2Value); + + return true; + } + else if (type == typeof(UnityEngine.Vector3)) + { + objectValue = objProperty.FindPropertyRelative("vector3Value"); + objectValue.vector3Value = EditorGUILayout.Vector3Field(new GUIContent(label), objectValue.vector3Value); + + return true; + } + else if (type.IsSubclassOf(typeof(UnityEngine.Object))) + { + objectValue = objProperty.FindPropertyRelative("objectValue"); + objectValue.objectReferenceValue = EditorGUILayout.ObjectField(new GUIContent(label), objectValue.objectReferenceValue, type, true); + + return true; + } + else if (type.IsEnum) + { + var enumNames = Enum.GetNames(type); + objectValue = objProperty.FindPropertyRelative("intValue"); + objectValue.intValue = EditorGUILayout.Popup(label, objectValue.intValue, enumNames); + + return true; + } + + return false; + } + + private string[] GetFungusVariablesByType(List variables, Type type) + { + string[] result = new string[0]; + + if (type == typeof(int)) + { + result = (from v in variables where v.GetType() == typeof(IntegerVariable) select v.key).ToArray(); + } + else if (type == typeof(bool)) + { + result = (from v in variables where v.GetType() == typeof(BooleanVariable) select v.key).ToArray(); + } + else if (type == typeof(float)) + { + result = (from v in variables where v.GetType() == typeof(FloatVariable) select v.key).ToArray(); + } + else if (type == typeof(string)) + { + result = (from v in variables where v.GetType() == typeof(StringVariable) select v.key).ToArray(); + } + else if (type == typeof(Color)) + { + result = (from v in variables where v.GetType() == typeof(ColorVariable) select v.key).ToArray(); + } + else if (type == typeof(UnityEngine.GameObject)) + { + result = (from v in variables where v.GetType() == typeof(GameObjectVariable) select v.key).ToArray(); + } + else if (type == typeof(UnityEngine.Material)) + { + result = (from v in variables where v.GetType() == typeof(MaterialVariable) select v.key).ToArray(); + } + else if (type == typeof(UnityEngine.Sprite)) + { + result = (from v in variables where v.GetType() == typeof(SpriteVariable) select v.key).ToArray(); + } + else if (type == typeof(UnityEngine.Texture)) + { + result = (from v in variables where v.GetType() == typeof(TextureVariable) select v.key).ToArray(); + } + else if (type == typeof(UnityEngine.Vector2)) + { + result = (from v in variables where v.GetType() == typeof(Vector2Variable) select v.key).ToArray(); + } + else if (type == typeof(UnityEngine.Vector3)) + { + result = (from v in variables where v.GetType() == typeof(Vector3Variable) select v.key).ToArray(); + } + else if (type.IsSubclassOf(typeof(UnityEngine.Object))) + { + result = (from v in variables where v.GetType() == typeof(ObjectVariable) select v.key).ToArray(); + } + + return result; + } + + private string FormatParameters(ParameterInfo[] paramInfo) + { + string result = " ("; + + for (int i = 0; i < paramInfo.Length; i++) + { + var pi = paramInfo[i]; + result += pi.ParameterType.Name; // " arg" + (i + 1); + + if (i < paramInfo.Length - 1) + { + result += ", "; + } + } + + return result + ")"; + } + + private object GetDefaultValue(Type t) + { + if (t.IsValueType) + return Activator.CreateInstance(t); + + return null; + } + } } \ No newline at end of file diff --git a/Assets/Fungus/Flowchart/Editor/LabelEditor.cs b/Assets/Fungus/Flowchart/Editor/LabelEditor.cs index abbb865e..3656e3c8 100644 --- a/Assets/Fungus/Flowchart/Editor/LabelEditor.cs +++ b/Assets/Fungus/Flowchart/Editor/LabelEditor.cs @@ -13,74 +13,74 @@ using System.Linq; namespace Fungus { - [CustomEditor (typeof(Label))] - public class LabelEditor : CommandEditor - { - protected SerializedProperty keyProp; - - static public void LabelField(SerializedProperty property, - GUIContent labelText, - Block block) - { - List labelKeys = new List(); - List