From 7420ffeefb6e4716b0ff0f7abda2b0d2fd2c3be8 Mon Sep 17 00:00:00 2001 From: lealeelu Date: Sun, 26 Jun 2016 13:26:38 -0400 Subject: [PATCH 1/7] Create Conversation Function in LuaUtils --- Assets/Fungus/Narrative/Scripts/Stage.cs | 2 +- .../FungusLua/Resources/Lua/fungus.txt | 7 ++ .../FungusLua/Scripts/ConversationManager.cs | 112 +++++++++++++++++ .../Scripts/ConversationManager.cs.meta | 12 ++ .../Thirdparty/FungusLua/Scripts/LuaUtils.cs | 13 ++ .../Narrative/PortaitControlExample.unity | 117 +++++++++++++++--- 6 files changed, 248 insertions(+), 15 deletions(-) create mode 100644 Assets/Fungus/Thirdparty/FungusLua/Scripts/ConversationManager.cs create mode 100644 Assets/Fungus/Thirdparty/FungusLua/Scripts/ConversationManager.cs.meta diff --git a/Assets/Fungus/Narrative/Scripts/Stage.cs b/Assets/Fungus/Narrative/Scripts/Stage.cs index a73dedc8..50852da6 100644 --- a/Assets/Fungus/Narrative/Scripts/Stage.cs +++ b/Assets/Fungus/Narrative/Scripts/Stage.cs @@ -69,7 +69,7 @@ namespace Fungus foreach (RectTransform position in positions) { - if ( String.Compare(position.name, position_string, true) == 0 ) + if ( String.Compare(position.name.Replace(" ", ""), position_string.Replace(" ", ""), true) == 0 ) { return position; } diff --git a/Assets/Fungus/Thirdparty/FungusLua/Resources/Lua/fungus.txt b/Assets/Fungus/Thirdparty/FungusLua/Resources/Lua/fungus.txt index 69835871..87ab134c 100644 --- a/Assets/Fungus/Thirdparty/FungusLua/Resources/Lua/fungus.txt +++ b/Assets/Fungus/Thirdparty/FungusLua/Resources/Lua/fungus.txt @@ -197,6 +197,13 @@ function M.say(text, voiceclip) M.runwait(e) end +-- Say series of lines +-- conv: A string of conversational lines +function M.conversation(conv) + local e = luautils.Conversation(conv) + M.runwait(e) +end + -------------- -- Menu Dialog -------------- diff --git a/Assets/Fungus/Thirdparty/FungusLua/Scripts/ConversationManager.cs b/Assets/Fungus/Thirdparty/FungusLua/Scripts/ConversationManager.cs new file mode 100644 index 00000000..24fa58e1 --- /dev/null +++ b/Assets/Fungus/Thirdparty/FungusLua/Scripts/ConversationManager.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using UnityEngine; + +namespace Fungus +{ + public class ConversationManager + { + protected Stage stage; + + protected CharacterItem[] characters; + + protected struct CharacterItem + { + public string name; + public string nameText; + public Character character; + + public bool CharacterMatch(string matchString) + { + return name.StartsWith(matchString, true, System.Globalization.CultureInfo.CurrentCulture) + || nameText.StartsWith(matchString, true, System.Globalization.CultureInfo.CurrentCulture); + } + } + + protected bool exitSayWait; + + public ConversationManager () { + stage = Stage.activeStages[0]; + + Character[] characterObjects = GameObject.FindObjectsOfType(); + characters = new CharacterItem[characterObjects.Length]; + + for (int i = 0; i < characterObjects.Length; i++) + { + characters[i].name = characterObjects[i].name; + characters[i].nameText = characterObjects[i].nameText; + characters[i].character = characterObjects[i]; + } + } + + /// + /// Parse and execute a conversation string + /// + /// + public IEnumerator Conversation(string conv) + { + if (string.IsNullOrEmpty(conv)) + { + yield break; + } + + SayDialog sayDialog = SayDialog.GetSayDialog(); + + if (sayDialog == null) + { + yield break; + } + + //find SimpleScript say strings with portrait options + //You can test regex matches here: http://regexstorm.net/tester + var sayRegex = new Regex(@"((?\w*)( ?(?\w*)( ?(?\w*)))?:)?(?.*\r*\n)"); + var sayMatches = sayRegex.Matches(conv); + + foreach (Match match in sayMatches) + { + string characterKey = match.Groups["character"].Value; + string portrait = match.Groups["portrait"].Value; + string position = match.Groups["position"].Value; + string text = match.Groups["text"].Value; + Character character = null; + + sayDialog.gameObject.SetActive(true); + + if (!string.IsNullOrEmpty(characterKey)) + { + for (int i = 0; i < characters.Length; i++) + { + if( characters[i].CharacterMatch(characterKey)) { + character = characters[i].character; + break; + } + } + } + //We'll assume that if no character is specified, we'll leave it as the last one. + //It's probably a continuation of the last character's dialog + if (character != null) + { + sayDialog.SetCharacter(character); + + string currentPosition = null; + if (character.state.position != null) currentPosition = character.state.position.name; + if (stage != null) stage.Show(character, portrait, currentPosition ?? position, position); + } + + exitSayWait = false; + sayDialog.Say(text, true, true, true, false, null, () => { + exitSayWait = true; + }); + + while (!exitSayWait) + { + yield return null; + } + exitSayWait = false; + } + } + } +} + \ No newline at end of file diff --git a/Assets/Fungus/Thirdparty/FungusLua/Scripts/ConversationManager.cs.meta b/Assets/Fungus/Thirdparty/FungusLua/Scripts/ConversationManager.cs.meta new file mode 100644 index 00000000..cda12b94 --- /dev/null +++ b/Assets/Fungus/Thirdparty/FungusLua/Scripts/ConversationManager.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 6ee240b2e8b559946a44b33a90cca00f +timeCreated: 1467644516 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Fungus/Thirdparty/FungusLua/Scripts/LuaUtils.cs b/Assets/Fungus/Thirdparty/FungusLua/Scripts/LuaUtils.cs index 4e27b6fe..0dd85716 100644 --- a/Assets/Fungus/Thirdparty/FungusLua/Scripts/LuaUtils.cs +++ b/Assets/Fungus/Thirdparty/FungusLua/Scripts/LuaUtils.cs @@ -72,6 +72,8 @@ namespace Fungus protected StringSubstituter stringSubstituter; + protected ConversationManager conversationManager; + /// /// Called by LuaEnvironment when initializing. /// @@ -283,6 +285,8 @@ namespace Fungus stringSubstituter = new StringSubstituter(); + conversationManager = new ConversationManager(); + if (fungusModule == FungusModuleOptions.UseGlobalVariables) { // Copy all items from the Fungus table to global variables @@ -466,6 +470,15 @@ namespace Fungus } return null; } + + /// + /// Use the conversation manager to play out a conversation + /// + /// + public virtual IEnumerator Conversation(string conv) + { + return conversationManager.Conversation(conv); + } } } \ No newline at end of file diff --git a/Assets/FungusExamples/FungusLua/Narrative/PortaitControlExample.unity b/Assets/FungusExamples/FungusLua/Narrative/PortaitControlExample.unity index 52cc9c86..b5009224 100644 --- a/Assets/FungusExamples/FungusLua/Narrative/PortaitControlExample.unity +++ b/Assets/FungusExamples/FungusLua/Narrative/PortaitControlExample.unity @@ -168,7 +168,6 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -10} m_LocalScale: {x: 1, y: 1, z: 1} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 0 @@ -185,6 +184,7 @@ GameObject: - 114: {fileID: 574033267} - 114: {fileID: 574033272} - 114: {fileID: 574033266} + - 114: {fileID: 574033273} m_Layer: 0 m_Name: Flowchart m_TagString: Untagged @@ -289,6 +289,7 @@ MonoBehaviour: description: eventHandler: {fileID: 574033267} commandList: + - {fileID: 574033273} - {fileID: 574033272} - {fileID: 574033266} --- !u!114 &574033269 @@ -317,7 +318,7 @@ MonoBehaviour: height: 859 selectedBlock: {fileID: 574033268} selectedCommands: - - {fileID: 574033266} + - {fileID: 574033273} variables: [] description: stepPause: 0 @@ -336,10 +337,9 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 4 + m_RootOrder: 5 --- !u!114 &574033272 MonoBehaviour: m_ObjectHideFlags: 2 @@ -383,6 +383,28 @@ MonoBehaviour: runAsCoroutine: 1 waitUntilFinished: 1 returnVariable: {fileID: 0} +--- !u!114 &574033273 +MonoBehaviour: + m_ObjectHideFlags: 2 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 574033265} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 71f455683d4ba4405b8dbba457159620, type: 3} + m_Name: + m_EditorClassIdentifier: + itemId: 6 + errorMessage: + indentLevel: 0 + luaEnvironment: {fileID: 1332690758} + luaFile: {fileID: 0} + luaScript: "conversation [[\r\nWhat is that smell??\nsherlock: Ugh.\r\nS bored:ew!\r\nwatson + annoyed left: That was me.\r\nW confident right: I'm not Sorry!\r\nW confident + offscreenright: GOOD DAY!\n]]" + runAsCoroutine: 1 + waitUntilFinished: 1 + returnVariable: {fileID: 0} --- !u!1 &653560666 stripped GameObject: m_PrefabParentObject: {fileID: 110274, guid: c6289d5f8fa843145a2355af9cb09719, type: 2} @@ -470,10 +492,9 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 1 + m_RootOrder: 3 --- !u!1 &856587351 GameObject: m_ObjectHideFlags: 0 @@ -545,10 +566,81 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 7 + m_RootOrder: 8 +--- !u!1 &1332690755 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 100640, guid: 49031c561e16d4fcf91c12153f8e0b25, type: 2} + m_PrefabInternal: {fileID: 0} + serializedVersion: 4 + m_Component: + - 4: {fileID: 1332690759} + - 114: {fileID: 1332690756} + - 114: {fileID: 1332690758} + - 114: {fileID: 1332690757} + m_Layer: 0 + m_Name: LuaEnvironment + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1332690756 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1332690755} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3e93d27e4a7119643a1592b568a905df, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!114 &1332690757 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 11486636, guid: 49031c561e16d4fcf91c12153f8e0b25, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1332690755} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c10f0b861365b42b0928858f7b086ff3, type: 3} + m_Name: + m_EditorClassIdentifier: + fungusModule: 0 + activeLanguage: en + stringTables: [] + registerTypes: + - {fileID: 4900000, guid: 9c3ab7a98d51241bbb499643399fa761, type: 3} + - {fileID: 4900000, guid: 93fddea8208764a2dbb189cc238aed40, type: 3} +--- !u!114 &1332690758 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 11493126, guid: 49031c561e16d4fcf91c12153f8e0b25, + type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1332690755} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: ba19c26c1ba7243d2b57ebc4329cc7c6, type: 3} + m_Name: + m_EditorClassIdentifier: + remoteDebugger: 0 +--- !u!4 &1332690759 +Transform: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 495584, guid: 49031c561e16d4fcf91c12153f8e0b25, type: 2} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1332690755} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 --- !u!1 &1756024128 GameObject: m_ObjectHideFlags: 1 @@ -586,10 +678,9 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 3 + m_RootOrder: 4 --- !u!1 &1818980091 GameObject: m_ObjectHideFlags: 0 @@ -637,10 +728,9 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 5 + m_RootOrder: 6 --- !u!1 &1876668763 GameObject: m_ObjectHideFlags: 0 @@ -688,10 +778,9 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 6 + m_RootOrder: 7 --- !u!1001 &2059718440 Prefab: m_ObjectHideFlags: 0 From 4992ea9925cb68d62944cf46c5d36a53f4848267 Mon Sep 17 00:00:00 2001 From: lealeelu Date: Mon, 18 Jul 2016 10:00:19 -0400 Subject: [PATCH 2/7] Cache stage and portrait info and add ability to rearrange params --- Assets/Fungus/Narrative/Scripts/Character.cs | 30 ++-- .../Narrative/Scripts/PortraitController.cs | 34 +++-- Assets/Fungus/Narrative/Scripts/Stage.cs | 21 +-- .../FungusLua/Scripts/ConversationManager.cs | 135 +++++++++++------- .../Narrative/PortaitControlExample.unity | 13 +- 5 files changed, 152 insertions(+), 81 deletions(-) diff --git a/Assets/Fungus/Narrative/Scripts/Character.cs b/Assets/Fungus/Narrative/Scripts/Character.cs index 4f865033..917c6ab3 100644 --- a/Assets/Fungus/Narrative/Scripts/Character.cs +++ b/Assets/Fungus/Narrative/Scripts/Character.cs @@ -20,6 +20,7 @@ namespace Fungus public AudioClip soundEffect; public Sprite profileSprite; public List portraits; + public Sprite[] cachedPortraits; public FacingDirection portraitsFace; public PortraitState state; @@ -38,6 +39,12 @@ namespace Fungus { activeCharacters.Add(this); } + + if (cachedPortraits == null) + { + cachedPortraits = new Sprite[portraits.Count]; + portraits.CopyTo(cachedPortraits); + } } protected virtual void OnDisable() @@ -59,30 +66,33 @@ namespace Fungus nameText = standardText; } + public bool NameStartsWith(string matchString) + { + return name.StartsWith(matchString, true, System.Globalization.CultureInfo.CurrentCulture) + || nameText.StartsWith(matchString, true, System.Globalization.CultureInfo.CurrentCulture); + } + /// /// Looks for a portrait by name on a character /// If none is found, give a warning and return a blank sprite /// /// /// Character portrait sprite - public virtual Sprite GetPortrait(string portrait_string) + public virtual Sprite GetPortrait(string portrait_string) { - if (portrait_string == null) + if (String.IsNullOrEmpty(portrait_string)) { - Debug.LogWarning("No portrait specifed for character " + name); - //Would be nice to have a sprite show up instead - return new Sprite(); + return null; } - foreach (Sprite portrait in portraits) + for (int i = 0; i < cachedPortraits.Length; i++) { - if ( String.Compare(portrait.name, portrait_string, true) == 0) + if ( String.Compare(cachedPortraits[i].name, portrait_string, true) == 0) { - return portrait; + return cachedPortraits[i]; } } - Debug.LogWarning("No portrait \"" + portrait_string + "\" found for character \"" + name + "\""); - return new Sprite(); + return null; } public virtual string GetDescription() diff --git a/Assets/Fungus/Narrative/Scripts/PortraitController.cs b/Assets/Fungus/Narrative/Scripts/PortraitController.cs index fbe03a47..217c0c3c 100644 --- a/Assets/Fungus/Narrative/Scripts/PortraitController.cs +++ b/Assets/Fungus/Narrative/Scripts/PortraitController.cs @@ -53,7 +53,7 @@ namespace Fungus waitUntilFinished = false; onComplete = null; - // Special values that can be overriden + // Special values that can be overridden fadeDuration = 0.5f; moveDuration = 1f; this.useDefaultSettings = useDefaultSettings; @@ -112,6 +112,7 @@ namespace Fungus void Awake() { stage = GetComponentInParent(); + stage.CachePositions(); } /// @@ -394,11 +395,13 @@ namespace Fungus options.moveDuration = moveDuration; options.waitUntilFinished = waitUntilFinished; - DoMoveTween(CleanPortraitOptions(options)); + DoMoveTween(options); } public void DoMoveTween(PortraitOptions options) { + CleanPortraitOptions(options); + // LeanTween doesn't handle 0 duration properly float duration = (options.moveDuration > 0f) ? options.moveDuration : float.Epsilon; @@ -422,7 +425,7 @@ namespace Fungus options.character = character; options.fromPosition = options.toPosition = stage.GetPosition(position); - Show(CleanPortraitOptions(options)); + Show(options); } /// @@ -441,7 +444,7 @@ namespace Fungus options.toPosition = stage.GetPosition(toPosition); options.move = true; - Show(CleanPortraitOptions(options)); + Show(options); } /// @@ -453,7 +456,7 @@ namespace Fungus /// Moonsharp Table public void Show(Table optionsTable) { - Show(CleanPortraitOptions(PortraitUtil.ConvertTableToPortraitOptions(optionsTable, stage))); + Show(PortraitUtil.ConvertTableToPortraitOptions(optionsTable, stage)); } /// @@ -462,6 +465,8 @@ namespace Fungus /// public void Show(PortraitOptions options) { + options = CleanPortraitOptions(options); + if (options.shiftIntoPlace) { options.fromPosition = Instantiate(options.toPosition) as RectTransform; @@ -507,9 +512,12 @@ namespace Fungus } // Fade in the new sprite image - options.character.state.portraitImage.sprite = options.portrait; - options.character.state.portraitImage.color = new Color(1f, 1f, 1f, 0f); - LeanTween.alpha(options.character.state.portraitImage.rectTransform, 1f, duration).setEase(stage.fadeEaseType); + if (options.character.state.portraitImage.sprite != options.portrait) + { + options.character.state.portraitImage.sprite = options.portrait; + options.character.state.portraitImage.color = new Color(1f, 1f, 1f, 0f); + LeanTween.alpha(options.character.state.portraitImage.rectTransform, 1f, duration).setEase(stage.fadeEaseType); + } DoMoveTween(options); @@ -548,7 +556,7 @@ namespace Fungus options.fromPosition = options.toPosition = character.state.position; } - Show(CleanPortraitOptions(options)); + Show(options); } /// @@ -560,7 +568,7 @@ namespace Fungus PortraitOptions options = new PortraitOptions(true); options.character = character; - Hide(CleanPortraitOptions(options)); + Hide(options); } /// @@ -575,7 +583,7 @@ namespace Fungus options.toPosition = stage.GetPosition(toPosition); options.move = true; - Hide(CleanPortraitOptions(options)); + Hide(options); } /// @@ -587,7 +595,7 @@ namespace Fungus /// Moonsharp Table public void Hide(Table optionsTable) { - Hide(CleanPortraitOptions(PortraitUtil.ConvertTableToPortraitOptions(optionsTable, stage))); + Hide(PortraitUtil.ConvertTableToPortraitOptions(optionsTable, stage)); } /// @@ -596,6 +604,8 @@ namespace Fungus /// public void Hide(PortraitOptions options) { + CleanPortraitOptions(options); + if (options.character.state.display == DisplayType.None) { return; diff --git a/Assets/Fungus/Narrative/Scripts/Stage.cs b/Assets/Fungus/Narrative/Scripts/Stage.cs index 50852da6..dabc0424 100644 --- a/Assets/Fungus/Narrative/Scripts/Stage.cs +++ b/Assets/Fungus/Narrative/Scripts/Stage.cs @@ -25,6 +25,7 @@ namespace Fungus public Vector2 shiftOffset; public Image defaultPosition; public List positions; + public RectTransform[] cachedPositions; public List charactersOnStage = new List(); [HideInInspector] @@ -38,6 +39,12 @@ namespace Fungus } } + public void CachePositions() + { + cachedPositions = new RectTransform[positions.Count]; + positions.CopyTo(cachedPositions); + } + protected virtual void OnDisable() { activeStages.Remove(this); @@ -61,21 +68,19 @@ namespace Fungus /// public RectTransform GetPosition(String position_string) { - if (position_string == null) + if (string.IsNullOrEmpty(position_string)) { - Debug.LogWarning("Missing stage position."); - return new RectTransform(); + return null; } - foreach (RectTransform position in positions) + for (int i = 0; i < cachedPositions.Length; i++) { - if ( String.Compare(position.name.Replace(" ", ""), position_string.Replace(" ", ""), true) == 0 ) + if ( String.Compare(cachedPositions[i].name, position_string, true) == 0 ) { - return position; + return cachedPositions[i]; } } - Debug.LogWarning("Unidentified stage position: " + position_string); - return new RectTransform(); + return null; } } } diff --git a/Assets/Fungus/Thirdparty/FungusLua/Scripts/ConversationManager.cs b/Assets/Fungus/Thirdparty/FungusLua/Scripts/ConversationManager.cs index 24fa58e1..7cdbe9aa 100644 --- a/Assets/Fungus/Thirdparty/FungusLua/Scripts/ConversationManager.cs +++ b/Assets/Fungus/Thirdparty/FungusLua/Scripts/ConversationManager.cs @@ -9,36 +9,24 @@ namespace Fungus public class ConversationManager { protected Stage stage; + protected Character[] characters; - protected CharacterItem[] characters; + protected Character currentCharacter; + protected Sprite currentPortrait; + protected RectTransform currentPosition; - protected struct CharacterItem - { - public string name; - public string nameText; - public Character character; - - public bool CharacterMatch(string matchString) - { - return name.StartsWith(matchString, true, System.Globalization.CultureInfo.CurrentCulture) - || nameText.StartsWith(matchString, true, System.Globalization.CultureInfo.CurrentCulture); - } - } + protected Character previousCharacter; + protected Sprite previousPortrait; + protected RectTransform previousPosition; protected bool exitSayWait; - public ConversationManager () { + public ConversationManager () + { stage = Stage.activeStages[0]; - - Character[] characterObjects = GameObject.FindObjectsOfType(); - characters = new CharacterItem[characterObjects.Length]; - - for (int i = 0; i < characterObjects.Length; i++) - { - characters[i].name = characterObjects[i].name; - characters[i].nameText = characterObjects[i].nameText; - characters[i].character = characterObjects[i]; - } + + // cache characters for faster lookup + characters = GameObject.FindObjectsOfType(); } /// @@ -61,40 +49,59 @@ namespace Fungus //find SimpleScript say strings with portrait options //You can test regex matches here: http://regexstorm.net/tester - var sayRegex = new Regex(@"((?\w*)( ?(?\w*)( ?(?\w*)))?:)?(?.*\r*\n)"); - var sayMatches = sayRegex.Matches(conv); + Regex sayRegex = new Regex(@"((?[\w ,]*):)?(?.*\r*\n)"); + MatchCollection sayMatches = sayRegex.Matches(conv); - foreach (Match match in sayMatches) + for (int i = 0; i < sayMatches.Count; i++) { - string characterKey = match.Groups["character"].Value; - string portrait = match.Groups["portrait"].Value; - string position = match.Groups["position"].Value; - string text = match.Groups["text"].Value; - Character character = null; - - sayDialog.gameObject.SetActive(true); + previousCharacter = currentCharacter; + previousPortrait = currentPortrait; + previousPosition = currentPosition; - if (!string.IsNullOrEmpty(characterKey)) + string sayParams = sayMatches[i].Groups["sayParams"].Value; + if (!string.IsNullOrEmpty(sayParams)) { - for (int i = 0; i < characters.Length; i++) + string[] separateParams; + if (sayParams.Contains(",")) { - if( characters[i].CharacterMatch(characterKey)) { - character = characters[i].character; - break; + separateParams = sayParams.Split(','); + for (int j = 0; j < separateParams.Length; j++) + { + separateParams[j] = separateParams[j].Trim(); } } + else + { + separateParams = sayParams.Split(' '); + } + + SetParams(separateParams); } - //We'll assume that if no character is specified, we'll leave it as the last one. - //It's probably a continuation of the last character's dialog - if (character != null) + else { - sayDialog.SetCharacter(character); + //no params! Use previous settings? + } + + string text = sayMatches[i].Groups["text"].Value; + + sayDialog.gameObject.SetActive(true); - string currentPosition = null; - if (character.state.position != null) currentPosition = character.state.position.name; - if (stage != null) stage.Show(character, portrait, currentPosition ?? position, position); + if (currentCharacter != null && currentCharacter != previousCharacter) + { + sayDialog.SetCharacter(currentCharacter); } + if (stage != null && currentCharacter != null && (currentPortrait != previousPortrait || currentPosition != previousPosition)) + { + PortraitOptions portraitOptions = new PortraitOptions(true); + portraitOptions.character = currentCharacter; + portraitOptions.fromPosition = currentCharacter.state.position ?? previousPosition; + portraitOptions.toPosition = currentPosition; + portraitOptions.portrait = currentPortrait; + + stage.Show(portraitOptions); + } + exitSayWait = false; sayDialog.Say(text, true, true, true, false, null, () => { exitSayWait = true; @@ -106,7 +113,39 @@ namespace Fungus } exitSayWait = false; } + + } + + /// + /// Using the string of say parameters before the ':', + /// set the current character, position and portrait if provided. + /// + /// The list of say parameters + private void SetParams(string[] sayParams) + { + Sprite portrait = null; + RectTransform position = null; + + //find the character first, since we need to get its portrait + for (int i = 0; i < sayParams.Length; i++) + { + for (int j = 0; j < characters.Length; j++) + { + if (characters[j].NameStartsWith(sayParams[i])) + { + currentCharacter = characters[j]; + break; + } + } + } + + for (int i = 0; i < sayParams.Length; i++) + { + if (portrait == null) portrait = currentCharacter.GetPortrait(sayParams[i]); + if (position == null) position = stage.GetPosition(sayParams[i]); + } + currentPosition = position; + currentPortrait = portrait; } } -} - \ No newline at end of file +} \ No newline at end of file diff --git a/Assets/FungusExamples/FungusLua/Narrative/PortaitControlExample.unity b/Assets/FungusExamples/FungusLua/Narrative/PortaitControlExample.unity index b5009224..8d93e3df 100644 --- a/Assets/FungusExamples/FungusLua/Narrative/PortaitControlExample.unity +++ b/Assets/FungusExamples/FungusLua/Narrative/PortaitControlExample.unity @@ -399,9 +399,10 @@ MonoBehaviour: indentLevel: 0 luaEnvironment: {fileID: 1332690758} luaFile: {fileID: 0} - luaScript: "conversation [[\r\nWhat is that smell??\nsherlock: Ugh.\r\nS bored:ew!\r\nwatson - annoyed left: That was me.\r\nW confident right: I'm not Sorry!\r\nW confident - offscreenright: GOOD DAY!\n]]" + luaScript: "conversation [[\r\nsherlock right: Character+position.\r\nThis is a + continuation of the last line.\nS bored left:All three options with name shortened.\nwatson + annoyed right: Adding a new character on the right.\r\nW, confident, offscreen + right: Move Watson offscreen using commas.\r\n]]" runAsCoroutine: 1 waitUntilFinished: 1 returnVariable: {fileID: 0} @@ -716,6 +717,9 @@ MonoBehaviour: portraits: - {fileID: 21300000, guid: a92b08a118b7d46f59dd091acb2e4102, type: 3} - {fileID: 21300000, guid: 03bc547cc0049594bae51f00903eedef, type: 3} + cachedPortraits: + - {fileID: 21300000, guid: a92b08a118b7d46f59dd091acb2e4102, type: 3} + - {fileID: 21300000, guid: 03bc547cc0049594bae51f00903eedef, type: 3} portraitsFace: 0 setSayDialog: {fileID: 0} description: @@ -766,6 +770,9 @@ MonoBehaviour: portraits: - {fileID: 21300000, guid: 8f5b5b110e73a414eb229eeead86200f, type: 3} - {fileID: 21300000, guid: c779e34c6eb8e45da98c70cf2802a54c, type: 3} + cachedPortraits: + - {fileID: 21300000, guid: 8f5b5b110e73a414eb229eeead86200f, type: 3} + - {fileID: 21300000, guid: c779e34c6eb8e45da98c70cf2802a54c, type: 3} portraitsFace: 0 setSayDialog: {fileID: 0} description: From 19cbab71122af947b367f61e3d9a291db6b5e36a Mon Sep 17 00:00:00 2001 From: Christopher Date: Wed, 20 Jul 2016 17:15:09 +0100 Subject: [PATCH 3/7] Changed PortraitOptions and PortraitState to use class instead of struct. MoonSharp doesn't work well with structs. --- Assets/Fungus/Narrative/Scripts/PortraitController.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Assets/Fungus/Narrative/Scripts/PortraitController.cs b/Assets/Fungus/Narrative/Scripts/PortraitController.cs index 217c0c3c..e3b256ed 100644 --- a/Assets/Fungus/Narrative/Scripts/PortraitController.cs +++ b/Assets/Fungus/Narrative/Scripts/PortraitController.cs @@ -13,7 +13,7 @@ using MoonSharp.Interpreter; namespace Fungus { - public struct PortraitOptions + public class PortraitOptions { public Character character; public Character replacedCharacter; @@ -38,7 +38,6 @@ namespace Fungus /// Will use stage default times for animation and fade public PortraitOptions(bool useDefaultSettings = true) { - // Defaults usually assigned on constructing a struct character = null; replacedCharacter = null; portrait = null; @@ -65,7 +64,7 @@ namespace Fungus } } - public struct PortraitState + public class PortraitState { public bool onScreen; public bool dimmed; From 3234aecee86acd61f9d0bf511335ea581f24f597 Mon Sep 17 00:00:00 2001 From: Christopher Date: Wed, 20 Jul 2016 17:17:30 +0100 Subject: [PATCH 4/7] Removed cached portraits --- Assets/Fungus/Narrative/Scripts/Character.cs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/Assets/Fungus/Narrative/Scripts/Character.cs b/Assets/Fungus/Narrative/Scripts/Character.cs index 917c6ab3..798dab7d 100644 --- a/Assets/Fungus/Narrative/Scripts/Character.cs +++ b/Assets/Fungus/Narrative/Scripts/Character.cs @@ -20,9 +20,8 @@ namespace Fungus public AudioClip soundEffect; public Sprite profileSprite; public List portraits; - public Sprite[] cachedPortraits; public FacingDirection portraitsFace; - public PortraitState state; + public PortraitState state = new PortraitState(); [Tooltip("Sets the active Say dialog with a reference to a Say Dialog object in the scene. All story text will now display using this Say Dialog.")] public SayDialog setSayDialog; @@ -39,12 +38,6 @@ namespace Fungus { activeCharacters.Add(this); } - - if (cachedPortraits == null) - { - cachedPortraits = new Sprite[portraits.Count]; - portraits.CopyTo(cachedPortraits); - } } protected virtual void OnDisable() @@ -85,11 +78,11 @@ namespace Fungus return null; } - for (int i = 0; i < cachedPortraits.Length; i++) + for (int i = 0; i < portraits.Count; i++) { - if ( String.Compare(cachedPortraits[i].name, portrait_string, true) == 0) + if ( String.Compare(portraits[i].name, portrait_string, true) == 0) { - return cachedPortraits[i]; + return portraits[i]; } } return null; From 057772cf2881b67af86920a338e6045b226828da Mon Sep 17 00:00:00 2001 From: Christopher Date: Wed, 20 Jul 2016 17:17:57 +0100 Subject: [PATCH 5/7] Fix if no EventSystem present in scene --- Assets/Fungus/Narrative/Scripts/DialogInput.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Assets/Fungus/Narrative/Scripts/DialogInput.cs b/Assets/Fungus/Narrative/Scripts/DialogInput.cs index c21fb713..37e6224e 100644 --- a/Assets/Fungus/Narrative/Scripts/DialogInput.cs +++ b/Assets/Fungus/Narrative/Scripts/DialogInput.cs @@ -82,6 +82,11 @@ namespace Fungus protected virtual void Update() { + if (EventSystem.current == null) + { + return; + } + if (currentStandaloneInputModule == null) { if (EventSystem.current == null) @@ -155,4 +160,4 @@ namespace Fungus } } - \ No newline at end of file + From 86855857d84b01ee482751b874377b93353197f6 Mon Sep 17 00:00:00 2001 From: Christopher Date: Wed, 20 Jul 2016 17:19:32 +0100 Subject: [PATCH 6/7] Safely get active stage, handles case where no stage present. Ignore blank lines and Lua style comments in conversation string. SetParams checks input params in order of priority, handles some cases of missing params (not all yet) --- .../FungusLua/Scripts/ConversationManager.cs | 94 ++++++++++++++++--- 1 file changed, 81 insertions(+), 13 deletions(-) diff --git a/Assets/Fungus/Thirdparty/FungusLua/Scripts/ConversationManager.cs b/Assets/Fungus/Thirdparty/FungusLua/Scripts/ConversationManager.cs index 7cdbe9aa..5b61c988 100644 --- a/Assets/Fungus/Thirdparty/FungusLua/Scripts/ConversationManager.cs +++ b/Assets/Fungus/Thirdparty/FungusLua/Scripts/ConversationManager.cs @@ -8,7 +8,6 @@ namespace Fungus { public class ConversationManager { - protected Stage stage; protected Character[] characters; protected Character currentCharacter; @@ -23,12 +22,21 @@ namespace Fungus public ConversationManager () { - stage = Stage.activeStages[0]; - // cache characters for faster lookup characters = GameObject.FindObjectsOfType(); } + protected Stage GetActiveStage() + { + if (Stage.activeStages == null || + Stage.activeStages.Count == 0) + { + return null; + } + + return Stage.activeStages[0]; + } + /// /// Parse and execute a conversation string /// @@ -91,6 +99,8 @@ namespace Fungus sayDialog.SetCharacter(currentCharacter); } + Stage stage = GetActiveStage(); + if (stage != null && currentCharacter != null && (currentPortrait != previousPortrait || currentPosition != previousPosition)) { PortraitOptions portraitOptions = new PortraitOptions(true); @@ -102,6 +112,12 @@ namespace Fungus stage.Show(portraitOptions); } + // Ignore Lua style comments and blank lines + if (text.StartsWith("--") || text.Trim() == "") + { + continue; + } + exitSayWait = false; sayDialog.Say(text, true, true, true, false, null, () => { exitSayWait = true; @@ -123,29 +139,81 @@ namespace Fungus /// The list of say parameters private void SetParams(string[] sayParams) { + Character character = null; Sprite portrait = null; RectTransform position = null; - //find the character first, since we need to get its portrait - for (int i = 0; i < sayParams.Length; i++) + // try to find the character first, since we need to get its portrait + int characterIndex = -1; + for (int i = 0; character == null && i < sayParams.Length; i++) { for (int j = 0; j < characters.Length; j++) { if (characters[j].NameStartsWith(sayParams[i])) { - currentCharacter = characters[j]; + characterIndex = i; + character = characters[j]; break; } } } - for (int i = 0; i < sayParams.Length; i++) - { - if (portrait == null) portrait = currentCharacter.GetPortrait(sayParams[i]); - if (position == null) position = stage.GetPosition(sayParams[i]); - } - currentPosition = position; - currentPortrait = portrait; + // Assume last used character if none is specified now + if (character == null) + { + character = currentCharacter; + } + + // Next see if we can find a portrait for this character + int portraitIndex = -1; + if (character != null) + { + for (int i = 0; i < sayParams.Length; i++) + { + if (portrait == null && + character != null && + i != characterIndex) + { + Sprite s = character.GetPortrait(sayParams[i]); + if (s != null) + { + portraitIndex = i; + portrait = s; + break; + } + } + } + } + + // Next check if there's a position parameter + Stage stage = GetActiveStage(); + if (stage != null) + { + for (int i = 0; i < sayParams.Length; i++) + { + if (i != characterIndex && + i != portraitIndex) + { + position = stage.GetPosition(sayParams[i]); + break; + } + } + } + + if (character != null) + { + currentCharacter = character; + } + + if (portrait != null) + { + currentPortrait = portrait; + } + + if (position != null) + { + currentPosition = position; + } } } } \ No newline at end of file From cd86fe49701d62a681e28d12abeb42b12f90d8a2 Mon Sep 17 00:00:00 2001 From: Christopher Date: Wed, 20 Jul 2016 17:20:09 +0100 Subject: [PATCH 7/7] Extended example to show omitting parameters. --- .../Narrative/PortaitControlExample.unity | 59 +++++++++++++++++-- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/Assets/FungusExamples/FungusLua/Narrative/PortaitControlExample.unity b/Assets/FungusExamples/FungusLua/Narrative/PortaitControlExample.unity index 8d93e3df..fbaa06da 100644 --- a/Assets/FungusExamples/FungusLua/Narrative/PortaitControlExample.unity +++ b/Assets/FungusExamples/FungusLua/Narrative/PortaitControlExample.unity @@ -168,6 +168,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -10} m_LocalScale: {x: 1, y: 1, z: 1} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 0 @@ -185,6 +186,7 @@ GameObject: - 114: {fileID: 574033272} - 114: {fileID: 574033266} - 114: {fileID: 574033273} + - 114: {fileID: 574033271} m_Layer: 0 m_Name: Flowchart m_TagString: Untagged @@ -280,8 +282,8 @@ MonoBehaviour: m_EditorClassIdentifier: nodeRect: serializedVersion: 2 - x: 67 - y: 70 + x: 69.066124 + y: 68.96694 width: 120 height: 40 itemId: 0 @@ -289,6 +291,7 @@ MonoBehaviour: description: eventHandler: {fileID: 574033267} commandList: + - {fileID: 574033271} - {fileID: 574033273} - {fileID: 574033272} - {fileID: 574033266} @@ -309,7 +312,7 @@ MonoBehaviour: variablesScrollPos: {x: 0, y: 0} variablesExpanded: 1 blockViewHeight: 214 - zoom: 1 + zoom: 0.967998 scrollViewRect: serializedVersion: 2 x: -343 @@ -318,7 +321,7 @@ MonoBehaviour: height: 859 selectedBlock: {fileID: 574033268} selectedCommands: - - {fileID: 574033273} + - {fileID: 574033271} variables: [] description: stepPause: 0 @@ -337,9 +340,51 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 5 +--- !u!114 &574033271 +MonoBehaviour: + m_ObjectHideFlags: 2 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 574033265} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 71f455683d4ba4405b8dbba457159620, type: 3} + m_Name: + m_EditorClassIdentifier: + itemId: 7 + errorMessage: + indentLevel: 0 + luaEnvironment: {fileID: 0} + luaFile: {fileID: 0} + luaScript: 'conversation[[ + + + watson annoyed left: What the deuce! + + confident middle: I say! + + annoyed: Well I never! + + I exclaim! + + + sherlock bored right: ahem! + + right: I concur. + + angry: Wait, no I don''t! + + + ]] + +' + runAsCoroutine: 1 + waitUntilFinished: 1 + returnVariable: {fileID: 0} --- !u!114 &574033272 MonoBehaviour: m_ObjectHideFlags: 2 @@ -493,6 +538,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 3 @@ -567,6 +613,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 8 @@ -639,6 +686,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 1 @@ -679,6 +727,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 4 @@ -732,6 +781,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 6 @@ -785,6 +835,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_Children: [] m_Father: {fileID: 0} m_RootOrder: 7