Browse Source

Added MenuShuffle Fungus Command and a demo scene

-refactor menudialogue to track items that are hidden
Menu Fungus Command now has a hideThisOption boolean
Updated narrative_commands doco to match
master
desktop-maesty/steve 7 years ago
parent
commit
ffa5b2d189
  1. 14
      Assets/Fungus/Scripts/Commands/Menu.cs
  2. 59
      Assets/Fungus/Scripts/Commands/MenuShuffle.cs
  3. 8
      Assets/Fungus/Scripts/Commands/MenuShuffle.cs.meta
  4. 292
      Assets/Fungus/Scripts/Components/MenuDialog.cs
  5. 5
      Assets/Fungus/Scripts/Editor/MenuEditor.cs
  6. 9
      Assets/FungusExamples/MenuShuffle.meta
  7. 2046
      Assets/FungusExamples/MenuShuffle/MenuShuffle.unity
  8. 8
      Assets/FungusExamples/MenuShuffle/MenuShuffle.unity.meta
  9. 11
      Docs/command_ref/narrative_commands.md

14
Assets/Fungus/Scripts/Commands/Menu.cs

@ -35,6 +35,9 @@ namespace Fungus
[Tooltip("A custom Menu Dialog to use to display this menu. All subsequent Menu commands will use this dialog.")]
[SerializeField] protected MenuDialog setMenuDialog;
[Tooltip("If true, this option will be passed to the Menu Dialogue but marked as hidden, this can be used to hide options while maintaining a Menu Shuffle.")]
[SerializeField] protected BooleanData hideThisOption = new BooleanData(false);
#region Public members
public MenuDialog SetMenuDialog { get { return setMenuDialog; } set { setMenuDialog = value; } }
@ -47,11 +50,9 @@ namespace Fungus
MenuDialog.ActiveMenuDialog = setMenuDialog;
}
bool hideOption = (hideIfVisited && targetBlock != null && targetBlock.GetExecutionCount() > 0);
bool hideOption = (hideIfVisited && targetBlock != null && targetBlock.GetExecutionCount() > 0) || hideThisOption.Value;
if (!hideOption)
{
var menuDialog = MenuDialog.GetMenuDialog();
var menuDialog = MenuDialog.GetMenuDialog();
if (menuDialog != null)
{
menuDialog.SetActive(true);
@ -59,10 +60,9 @@ namespace Fungus
var flowchart = GetFlowchart();
string displayText = flowchart.SubstituteVariables(text);
menuDialog.AddOption(displayText, interactable, targetBlock);
menuDialog.AddOption(displayText, interactable, hideOption, targetBlock);
}
}
Continue();
}

59
Assets/Fungus/Scripts/Commands/MenuShuffle.cs

@ -0,0 +1,59 @@
// This code is part of the Fungus library (http://fungusgames.com) maintained by Chris Gregan (http://twitter.com/gofungus).
// It is released for free under the MIT open source license (https://github.com/snozbot/fungus/blob/master/LICENSE)
using UnityEngine;
using UnityEngine.Serialization;
using System.Collections.Generic;
namespace Fungus
{
/// <summary>
/// Shuffle the order of the items in a Fungus Menu
/// </summary>
[CommandInfo("Narrative",
"Menu Shuffle",
"Shuffle the order of the items in a Fungus Menu")]
[AddComponentMenu("")]
[ExecuteInEditMode]
public class MenuShuffle : Command
{
public enum Mode
{
Every,
Once
}
[SerializeField]
[Tooltip("Determines if the order is shuffled everytime this command is it (Every) or if it is consistent when returned to but random (Once)")]
protected Mode shuffleMode = Mode.Once;
private int seed = -1;
public override void OnEnter()
{
var menuDialog = MenuDialog.GetMenuDialog();
//if we shuffle every time or we haven't shuffled yet
if(shuffleMode == Mode.Every || seed == -1)
{
seed = Random.Range(0,1000000);
}
if (menuDialog != null)
{
menuDialog.Shuffle(new System.Random(seed));
}
Continue();
}
public override string GetSummary()
{
return shuffleMode.ToString();
}
public override Color GetButtonColor()
{
return new Color32(184, 210, 235, 255);
}
}
}

8
Assets/Fungus/Scripts/Commands/MenuShuffle.cs.meta

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4d88125c88dab4a44851835e94ff4d90
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

292
Assets/Fungus/Scripts/Components/MenuDialog.cs

@ -21,6 +21,67 @@ namespace Fungus
protected Button[] cachedButtons;
protected Slider cachedSlider;
private int nextOptionIndex;
#region Public members
/// <summary>
/// Currently active Menu Dialog used to display Menu options
/// </summary>
public static MenuDialog ActiveMenuDialog { get; set; }
/// <summary>
/// A cached list of button objects in the menu dialog.
/// </summary>
/// <value>The cached buttons.</value>
public virtual Button[] CachedButtons { get { return cachedButtons; } }
/// <summary>
/// A cached slider object used for the timer in the menu dialog.
/// </summary>
/// <value>The cached slider.</value>
public virtual Slider CachedSlider { get { return cachedSlider; } }
/// <summary>
/// Sets the active state of the Menu Dialog gameobject.
/// </summary>
public virtual void SetActive(bool state)
{
gameObject.SetActive(state);
}
/// <summary>
/// Returns a menu dialog by searching for one in the scene or creating one if none exists.
/// </summary>
public static MenuDialog GetMenuDialog()
{
if (ActiveMenuDialog == null)
{
// Use first Menu Dialog found in the scene (if any)
var md = GameObject.FindObjectOfType<MenuDialog>();
if (md != null)
{
ActiveMenuDialog = md;
}
if (ActiveMenuDialog == null)
{
// Auto spawn a menu dialog object from the prefab
GameObject prefab = Resources.Load<GameObject>("Prefabs/MenuDialog");
if (prefab != null)
{
GameObject go = Instantiate(prefab) as GameObject;
go.SetActive(false);
go.name = "MenuDialog";
ActiveMenuDialog = go.GetComponent<MenuDialog>();
}
}
}
return ActiveMenuDialog;
}
protected virtual void Awake()
{
@ -66,9 +127,9 @@ namespace Fungus
protected virtual IEnumerator WaitForTimeout(float timeoutDuration, Block targetBlock)
{
float elapsedTime = 0;
Slider timeoutSlider = GetComponentInChildren<Slider>();
Slider timeoutSlider = CachedSlider;
while (elapsedTime < timeoutDuration)
{
if (timeoutSlider != null)
@ -76,12 +137,12 @@ namespace Fungus
float t = 1f - elapsedTime / timeoutDuration;
timeoutSlider.value = t;
}
elapsedTime += Time.deltaTime;
yield return null;
}
Clear();
gameObject.SetActive(false);
@ -108,64 +169,6 @@ namespace Fungus
}
}
#region Public members
/// <summary>
/// Currently active Menu Dialog used to display Menu options
/// </summary>
public static MenuDialog ActiveMenuDialog { get; set; }
/// <summary>
/// Returns a menu dialog by searching for one in the scene or creating one if none exists.
/// </summary>
public static MenuDialog GetMenuDialog()
{
if (ActiveMenuDialog == null)
{
// Use first Menu Dialog found in the scene (if any)
var md = GameObject.FindObjectOfType<MenuDialog>();
if (md != null)
{
ActiveMenuDialog = md;
}
if (ActiveMenuDialog == null)
{
// Auto spawn a menu dialog object from the prefab
GameObject prefab = Resources.Load<GameObject>("Prefabs/MenuDialog");
if (prefab != null)
{
GameObject go = Instantiate(prefab) as GameObject;
go.SetActive(false);
go.name = "MenuDialog";
ActiveMenuDialog = go.GetComponent<MenuDialog>();
}
}
}
return ActiveMenuDialog;
}
/// <summary>
/// A cached list of button objects in the menu dialog.
/// </summary>
/// <value>The cached buttons.</value>
public virtual Button[] CachedButtons { get { return cachedButtons; } }
/// <summary>
/// A cached slider object used for the timer in the menu dialog.
/// </summary>
/// <value>The cached slider.</value>
public virtual Slider CachedSlider { get { return cachedSlider; } }
/// <summary>
/// Sets the active state of the Menu Dialog gameobject.
/// </summary>
public virtual void SetActive(bool state)
{
gameObject.SetActive(state);
}
/// <summary>
/// Clear all displayed options in the Menu Dialog.
/// </summary>
@ -173,7 +176,9 @@ namespace Fungus
{
StopAllCoroutines();
var optionButtons = GetComponentsInChildren<Button>();
nextOptionIndex = 0;
var optionButtons = CachedButtons;
for (int i = 0; i < optionButtons.Length; i++)
{
var button = optionButtons[i];
@ -185,11 +190,12 @@ namespace Fungus
var button = optionButtons[i];
if (button != null)
{
button.transform.SetSiblingIndex(i);
button.gameObject.SetActive(false);
}
}
Slider timeoutSlider = GetComponentInChildren<Slider>();
Slider timeoutSlider = CachedSlider;
if (timeoutSlider != null)
{
timeoutSlider.gameObject.SetActive(false);
@ -215,53 +221,33 @@ namespace Fungus
/// <returns><c>true</c>, if the option was added successfully.</returns>
/// <param name="text">The option text to display on the button.</param>
/// <param name="interactable">If false, the option is displayed but is not selectable.</param>
/// <param name="hideOption">If true, the option is not displayed but the menu knows that option can or did exist</param>
/// <param name="targetBlock">Block to execute when the option is selected.</param>
public virtual bool AddOption(string text, bool interactable, Block targetBlock)
public virtual bool AddOption(string text, bool interactable, bool hideOption, Block targetBlock)
{
bool addedOption = false;
for (int i = 0; i < cachedButtons.Length; i++)
var block = targetBlock;
UnityEngine.Events.UnityAction action = delegate
{
var button = cachedButtons[i];
if (!button.gameObject.activeSelf)
EventSystem.current.SetSelectedGameObject(null);
StopAllCoroutines();
// Stop timeout
Clear();
HideSayDialog();
if (block != null)
{
button.gameObject.SetActive(true);
button.interactable = interactable;
if (interactable && autoSelectFirstButton && !cachedButtons.Select(x => x.gameObject).Contains(EventSystem.current.currentSelectedGameObject))
{
EventSystem.current.SetSelectedGameObject(button.gameObject);
}
Text textComponent = button.GetComponentInChildren<Text>();
if (textComponent != null)
{
textComponent.text = text;
}
var block = targetBlock;
button.onClick.AddListener(delegate
{
EventSystem.current.SetSelectedGameObject(null);
StopAllCoroutines();
// Stop timeout
Clear();
HideSayDialog();
if (block != null)
{
var flowchart = block.GetFlowchart();
#if UNITY_EDITOR
// Select the new target block in the Flowchart window
flowchart.SelectedBlock = block;
#endif
gameObject.SetActive(false);
// Use a coroutine to call the block on the next frame
// Have to use the Flowchart gameobject as the MenuDialog is now inactive
flowchart.StartCoroutine(CallBlock(block));
}
});
addedOption = true;
break;
var flowchart = block.GetFlowchart();
#if UNITY_EDITOR
// Select the new target block in the Flowchart window
flowchart.SelectedBlock = block;
#endif
gameObject.SetActive(false);
// Use a coroutine to call the block on the next frame
// Have to use the Flowchart gameobject as the MenuDialog is now inactive
flowchart.StartCoroutine(CallBlock(block));
}
}
};
return addedOption;
return AddOption(text, interactable, hideOption, action);
}
/// <summary>
@ -276,39 +262,60 @@ namespace Fungus
gameObject.SetActive(true);
}
bool addedOption = false;
for (int i = 0; i < CachedButtons.Length; i++)
// Copy to local variables
LuaEnvironment env = luaEnv;
Closure call = callBack;
UnityEngine.Events.UnityAction action = delegate
{
var button = CachedButtons[i];
if (!button.gameObject.activeSelf)
{
button.gameObject.SetActive(true);
button.interactable = interactable;
var textComponent = button.GetComponentInChildren<Text>();
if (textComponent != null)
{
textComponent.text = text;
}
StopAllCoroutines();
// Stop timeout
Clear();
HideSayDialog();
// Use a coroutine to call the callback on the next frame
StartCoroutine(CallLuaClosure(env, call));
};
// Copy to local variables
LuaEnvironment env = luaEnv;
Closure call = callBack;
button.onClick.AddListener(delegate
{
StopAllCoroutines();
// Stop timeout
Clear();
HideSayDialog();
// Use a coroutine to call the callback on the next frame
StartCoroutine(CallLuaClosure(env, call));
});
addedOption = true;
break;
}
return AddOption(text, interactable, false, action);
}
/// <summary>
/// Adds the option to the list of displayed options. Calls a Block when selected.
/// Will cause the Menu dialog to become visible if it is not already visible.
/// </summary>
/// <returns><c>true</c>, if the option was added successfully.</returns>
/// <param name="text">The option text to display on the button.</param>
/// <param name="interactable">If false, the option is displayed but is not selectable.</param>
/// <param name="hideOption">If true, the option is not displayed but the menu knows that option can or did exist</param>
/// <param name="action">Action attached to the button on the menu item</param>
private bool AddOption(string text, bool interactable, bool hideOption, UnityEngine.Events.UnityAction action)
{
if (nextOptionIndex >= CachedButtons.Length)
return false;
var button = cachedButtons[nextOptionIndex];
//move forward for next call
nextOptionIndex++;
//don't need to set anything on it
if (hideOption)
return true;
button.gameObject.SetActive(true);
button.interactable = interactable;
if (interactable && autoSelectFirstButton && !cachedButtons.Select(x => x.gameObject).Contains(EventSystem.current.currentSelectedGameObject))
{
EventSystem.current.SetSelectedGameObject(button.gameObject);
}
Text textComponent = button.GetComponentInChildren<Text>();
if (textComponent != null)
{
textComponent.text = text;
}
button.onClick.AddListener(action);
return addedOption;
return true;
}
/// <summary>
@ -342,7 +349,7 @@ namespace Fungus
StopAllCoroutines();
float elapsedTime = 0;
Slider timeoutSlider = GetComponentInChildren<Slider>();
Slider timeoutSlider = CachedSlider;
while (elapsedTime < duration)
{
@ -394,6 +401,17 @@ namespace Fungus
}
}
/// <summary>
/// Shuffle the parent order of the cached buttons, allows for randomising button order, buttons are auto reordered when cleared
/// </summary>
public void Shuffle(System.Random r)
{
for (int i = 0; i < CachedButtons.Length; i++)
{
CachedButtons[i].transform.SetSiblingIndex(r.Next(CachedButtons.Length));
}
}
#endregion
}
}

5
Assets/Fungus/Scripts/Editor/MenuEditor.cs

@ -15,6 +15,7 @@ namespace Fungus.EditorUtils
protected SerializedProperty hideIfVisitedProp;
protected SerializedProperty interactableProp;
protected SerializedProperty setMenuDialogProp;
protected SerializedProperty hideThisOptionProp;
protected virtual void OnEnable()
{
@ -27,6 +28,7 @@ namespace Fungus.EditorUtils
hideIfVisitedProp = serializedObject.FindProperty("hideIfVisited");
interactableProp = serializedObject.FindProperty("interactable");
setMenuDialogProp = serializedObject.FindProperty("setMenuDialog");
hideThisOptionProp = serializedObject.FindProperty("hideThisOption");
}
public override void DrawCommandGUI()
@ -51,7 +53,8 @@ namespace Fungus.EditorUtils
EditorGUILayout.PropertyField(hideIfVisitedProp);
EditorGUILayout.PropertyField(interactableProp);
EditorGUILayout.PropertyField(setMenuDialogProp);
EditorGUILayout.PropertyField(hideThisOptionProp);
serializedObject.ApplyModifiedProperties();
}
}

9
Assets/FungusExamples/MenuShuffle.meta

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 7940d278a16c8a440a2f7a874f0e6c3b
folderAsset: yes
timeCreated: 1506117269
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

2046
Assets/FungusExamples/MenuShuffle/MenuShuffle.unity

File diff suppressed because it is too large Load Diff

8
Assets/FungusExamples/MenuShuffle/MenuShuffle.unity.meta

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 049ab00d3819dc94db2b4b5b63814f54
timeCreated: 1469542890
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

11
Docs/command_ref/narrative_commands.md

@ -41,6 +41,17 @@ Target Block | Fungus.Block | Block to execute when this option is selected
Hide If Visited | System.Boolean | Hide this option if the target block has been executed previously
Interactable | Fungus.BooleanData | If false, the menu option will be displayed but will not be selectable
Set Menu Dialog | Fungus.MenuDialog | A custom Menu Dialog to use to display this menu. All subsequent Menu commands will use this dialog.
hideThisOption | Fungus.BooleanData | If true, this option will be passed to the Menu Dialogue but marked as hidden, this can be used to hide options while maintaining a Menu Shuffle.
# Menu Shuffle # {#MenuShuffle}
Shuffle the order of the items in a Fungus Menu
Defined in Fungus.MenuShuffle
Property | Type | Description
--- | --- | ---
shuffleMode | System.Enum | Determines if the order is shuffled everytime this command is it (Every) or if it is consistent when returned to but random (Once)
# Menu Timer # {#MenuTimer}
Displays a timer bar and executes a target block if the player fails to select a menu option in time.

Loading…
Cancel
Save