diff --git a/Assets/Fungus/Prefabs/PageStyle1.prefab b/Assets/Fungus/Prefabs/PageStyle1.prefab index dcf134aa..58d9dbe3 100644 Binary files a/Assets/Fungus/Prefabs/PageStyle1.prefab and b/Assets/Fungus/Prefabs/PageStyle1.prefab differ diff --git a/Assets/Fungus/Prefabs/PageStyle2.prefab b/Assets/Fungus/Prefabs/PageStyle2.prefab index 3d1532f9..57fc1381 100644 Binary files a/Assets/Fungus/Prefabs/PageStyle2.prefab and b/Assets/Fungus/Prefabs/PageStyle2.prefab differ diff --git a/Assets/Fungus/Scripts/Button.cs b/Assets/Fungus/Scripts/Button.cs index 0251fa1f..549f1dae 100644 --- a/Assets/Fungus/Scripts/Button.cs +++ b/Assets/Fungus/Scripts/Button.cs @@ -1,4 +1,4 @@ -using UnityEngine; +using UnityEngine; using System; using System.Collections; using Fungus; @@ -80,7 +80,7 @@ namespace Fungus if (autoHide) { if (showButton && - Game.GetInstance().IsGameIdle()) + Game.GetInstance().ShowAutoButtons()) { targetAlpha = 1f; @@ -104,18 +104,28 @@ namespace Fungus UpdateTargetAlpha(); SpriteRenderer spriteRenderer = renderer as SpriteRenderer; - Color color = spriteRenderer.color; float fadeSpeed = (1f / Game.GetInstance().buttonFadeDuration); - color.a = Mathf.MoveTowards(color.a, targetAlpha, Time.deltaTime * fadeSpeed); - spriteRenderer.color = color; + + float alpha = Mathf.MoveTowards(spriteRenderer.color.a, targetAlpha, Time.deltaTime * fadeSpeed);; + + // Set alpha for this sprite and any child sprites + SpriteRenderer[] children = spriteRenderer.gameObject.GetComponentsInChildren(); + foreach (SpriteRenderer child in children) + { + Color color = child.color; + color.a = alpha; + child.color = color; + } } void OnMouseUpAsButton() { SpriteRenderer spriteRenderer = renderer as SpriteRenderer; - // Ignore button press if sprite is not fully visible - if (spriteRenderer.color.a != 1f) + // Ignore button press if sprite is not fully visible or + // if the game is not in an idle state + if (spriteRenderer.color.a != 1f || + !Game.GetInstance().ShowAutoButtons()) { return; } diff --git a/Assets/Fungus/Scripts/Commands.cs b/Assets/Fungus/Scripts/Commands.cs index 022935f3..2bf009a6 100644 --- a/Assets/Fungus/Scripts/Commands.cs +++ b/Assets/Fungus/Scripts/Commands.cs @@ -184,13 +184,13 @@ namespace Fungus } /** - * Sets the title text displayed at the top of the active page. + * Sets the header text displayed at the top of the active page. */ - public class TitleCommand : CommandQueue.Command + public class HeaderCommand : CommandQueue.Command { string titleText; - public TitleCommand(string _titleText) + public HeaderCommand(string _titleText) { titleText = _titleText; } @@ -204,7 +204,37 @@ namespace Fungus } else { - page.SetTitle(titleText); + page.SetHeader(titleText); + } + if (onComplete != null) + { + onComplete(); + } + } + } + + /** + * Sets the footer text displayed at the top of the active page. + */ + public class FooterCommand : CommandQueue.Command + { + string titleText; + + public FooterCommand(string _titleText) + { + titleText = _titleText; + } + + public override void Execute(CommandQueue commandQueue, Action onComplete) + { + Page page = Game.GetInstance().activePage; + if (page == null) + { + Debug.LogError("Active page must not be null"); + } + else + { + page.SetFooter(titleText); } if (onComplete != null) { @@ -326,7 +356,7 @@ namespace Fungus // Fade out screen game.cameraController.Fade(0f, game.roomFadeDuration / 2f, delegate { - + game.activeRoom = room; // Notify room script that the Room is being entered diff --git a/Assets/Fungus/Scripts/Game.cs b/Assets/Fungus/Scripts/Game.cs index 182b2c96..a26b8321 100644 --- a/Assets/Fungus/Scripts/Game.cs +++ b/Assets/Fungus/Scripts/Game.cs @@ -63,6 +63,10 @@ namespace Fungus */ public AudioClip buttonClickClip; + public float autoHideButtonDuration = 5f; + + float autoHideButtonTimer; + /** * Global dictionary of integer values for storing game state. */ @@ -134,6 +138,17 @@ namespace Fungus } } + public virtual void Update() + { + autoHideButtonTimer -= Time.deltaTime; + autoHideButtonTimer = Mathf.Max(autoHideButtonTimer, 0f); + + if (Input.GetMouseButtonDown(0)) + { + autoHideButtonTimer = autoHideButtonDuration; + } + } + /** * Plays the button clicked sound effect */ @@ -147,10 +162,10 @@ namespace Fungus } /** - * Returns true if the game is in an idle state. - * The game is in and idle state if the active page is not currently displaying story text/options, and no Wait command is in progress + * Returns true if the game should display 'auto hide' buttons. + * Buttons will be displayed if the active page is not currently displaying story text/options, and no Wait command is in progress. */ - public bool IsGameIdle() + public bool ShowAutoButtons() { if (waiting) { @@ -160,7 +175,7 @@ namespace Fungus if (activePage == null || activePage.mode == Page.Mode.Idle) { - return true; + return (autoHideButtonTimer > 0f); } return false; diff --git a/Assets/Fungus/Scripts/GameController.cs b/Assets/Fungus/Scripts/GameController.cs index ba272ee1..8457ce3d 100644 --- a/Assets/Fungus/Scripts/GameController.cs +++ b/Assets/Fungus/Scripts/GameController.cs @@ -159,15 +159,36 @@ namespace Fungus } /** - * Sets the title text displayed at the top of the active Page. - * The title text is only displayed when there is some story text or options to be shown. - * This method returns immediately but it queues an asynchronous command for later execution. - * @param titleText The text to display as the title of the Page. + * Obsolete! Use Header() instead. */ + [System.Obsolete("use Header() instead")] public static void Title(string titleText) + { + Header(titleText); + } + + /** + * Sets the header text displayed at the top of the active Page. + * The header text is only displayed when there is some story text or options to be shown. + * This method returns immediately but it queues an asynchronous command for later execution. + * @param footerText The text to display as the header of the Page. + */ + public static void Header(string headerText) + { + CommandQueue commandQueue = Game.GetInstance().commandQueue; + commandQueue.AddCommand(new Command.HeaderCommand(headerText)); + } + + /** + * Sets the footer text displayed at the top of the active Page. + * The footer text is only displayed when there is some story text or options to be shown. + * This method returns immediately but it queues an asynchronous command for later execution. + * @param footerText The text to display as the footer of the Page. + */ + public static void Footer(string footerText) { CommandQueue commandQueue = Game.GetInstance().commandQueue; - commandQueue.AddCommand(new Command.TitleCommand(titleText)); + commandQueue.AddCommand(new Command.FooterCommand(footerText)); } /** @@ -375,7 +396,6 @@ namespace Fungus /** * Makes a sprite stop behaving as a clickable button. - * Removes the Button component from the sprite object. * This method returns immediately but it queues an asynchronous command for later execution. * @param spriteRenderer The sprite to be made non-clickable */ diff --git a/Assets/Fungus/Scripts/Page.cs b/Assets/Fungus/Scripts/Page.cs index be9befad..a51fd805 100644 --- a/Assets/Fungus/Scripts/Page.cs +++ b/Assets/Fungus/Scripts/Page.cs @@ -27,10 +27,11 @@ namespace Fungus public VerticalAlign verticalAlign = VerticalAlign.Middle; - string titleText = ""; + string headerText = ""; + string footerText = ""; - string originalStoryText = ""; string displayedStoryText = ""; + string originalStoryText = ""; Action deferredAction; Action continueAction; @@ -42,6 +43,7 @@ namespace Fungus Choose }; + [HideInInspector] public Mode mode = Mode.Idle; class Option @@ -69,9 +71,14 @@ namespace Fungus } } - public void SetTitle(string _titleText) + public void SetHeader(string _headerText) { - titleText = _titleText; + headerText = _headerText; + } + + public void SetFooter(string _footerText) + { + footerText = _footerText; } public void Say(string sayText, Action sayAction) @@ -106,23 +113,18 @@ namespace Fungus return; } - GUIStyle sayStyle = pageStyle.GetScaledSayStyle(); - // Disable quick continue for a short period to prevent accidental taps quickContinueTimer = 0.8f; + originalStoryText = storyText; + // Hack to avoid displaying partial color tag text if (storyText.Contains("<")) { - originalStoryText = storyText; displayedStoryText = storyText; } else { - float textWidth = CalcInnerRect(GetScreenRect()).width; - originalStoryText = InsertLineBreaks(storyText, sayStyle, textWidth); - displayedStoryText = ""; - // Use a coroutine to write the story text out over time StartCoroutine(WriteStoryInternal()); } @@ -134,34 +136,47 @@ namespace Fungus int charactersPerSecond = Game.GetInstance().charactersPerSecond; // Zero CPS means write instantly - if (charactersPerSecond <= 0) + if (charactersPerSecond == 0) { displayedStoryText = originalStoryText; yield break; } displayedStoryText = ""; - float writeDelay = 1f / (float)charactersPerSecond; + + // Make one character visible at a time + float writeDelay = (1f / (float)charactersPerSecond); float timeAccumulator = 0f; + int i = 0; - while (displayedStoryText.Length < originalStoryText.Length) + while (true) { timeAccumulator += Time.deltaTime; - while (timeAccumulator > 0f) + while (timeAccumulator > writeDelay) { + i++; timeAccumulator -= writeDelay; + } - if (displayedStoryText.Length < originalStoryText.Length) - { - displayedStoryText += originalStoryText.Substring(displayedStoryText.Length, 1); - } + if (i >= originalStoryText.Length) + { + displayedStoryText = originalStoryText; + break; + } + else + { + string left = originalStoryText.Substring(0, i + 1); + string right = originalStoryText.Substring(i + 1); + + displayedStoryText = left; + displayedStoryText += ""; + displayedStoryText += right; + displayedStoryText += ""; } yield return null; } - - displayedStoryText = originalStoryText; } public virtual void OnGUI() @@ -178,21 +193,23 @@ namespace Fungus } GUIStyle boxStyle = pageStyle.boxStyle; - GUIStyle titleStyle = pageStyle.GetScaledTitleStyle(); + GUIStyle headerStyle = pageStyle.GetScaledHeaderStyle(); + GUIStyle footerStyle = pageStyle.GetScaledFooterStyle(); GUIStyle sayStyle = pageStyle.GetScaledSayStyle(); GUIStyle optionStyle = pageStyle.GetScaledOptionStyle(); GUIStyle optionAlternateStyle = pageStyle.GetScaledOptionAlternateStyle(); GUIStyle continueStyle = pageStyle.GetScaledContinueStyle(); Rect pageRect = GetScreenRect(); - Rect outerRect = pageRect; + Rect outerRect = FitRectToScreen(pageRect); Rect innerRect = CalcInnerRect(outerRect); // Calculate height of each section - float titleHeight = CalcTitleHeight(innerRect.width); + float headerHeight = CalcHeaderHeight(innerRect.width); + float footerHeight = CalcFooterHeight(innerRect.width); float storyHeight = CalcStoryHeight(innerRect.width); float optionsHeight = CalcOptionsHeight(innerRect.width); - float contentHeight = titleHeight + storyHeight + optionsHeight; + float contentHeight = headerHeight + footerHeight + storyHeight + optionsHeight; // Adjust outer rect position based on alignment settings switch (verticalAlign) @@ -213,10 +230,7 @@ namespace Fungus // Force outer rect to always be on-screen // If the rect is bigger than the screen, then the top-left corner will always be visible - outerRect.x = Mathf.Min(outerRect.x, Screen.width - outerRect.width); - outerRect.y = Mathf.Min(outerRect.y, Screen.height - outerRect.height); - outerRect.x = Mathf.Max(0, outerRect.x); - outerRect.y = Mathf.Max(0, outerRect.y); + outerRect = FitRectToScreen(outerRect); innerRect = CalcInnerRect(outerRect); @@ -225,17 +239,29 @@ namespace Fungus boxRect.height = contentHeight + (boxStyle.padding.top + boxStyle.padding.bottom); GUI.Box(boxRect, "", boxStyle); - // Draw title label - Rect titleRect = innerRect; - titleRect.height = titleHeight; - GUI.Label(titleRect, titleText, titleStyle); + // Draw header label + Rect headerRect = innerRect; + headerRect.height = headerHeight; + if (headerHeight > 0) + { + GUI.Label(headerRect, headerText, headerStyle); + } // Draw say label Rect storyRect = innerRect; - storyRect.y += titleHeight; + storyRect.y += headerHeight; storyRect.height = storyHeight; GUI.Label(storyRect, displayedStoryText, sayStyle); + // Draw footer label + Rect footerRect = innerRect; + footerRect.y += storyHeight; + footerRect.height = footerHeight; + if (footerHeight > 0) + { + GUI.Label(footerRect, footerText, footerStyle); + } + bool finishedWriting = (displayedStoryText.Length == originalStoryText.Length); if (!finishedWriting) { @@ -259,7 +285,7 @@ namespace Fungus { // Draw option buttons Rect buttonRect = innerRect; - buttonRect.y += titleHeight + storyHeight; + buttonRect.y += headerHeight + storyHeight; bool alternateRow = false; foreach (Option option in options) { @@ -329,21 +355,38 @@ namespace Fungus } } - float CalcTitleHeight(float boxWidth) + float CalcHeaderHeight(float boxWidth) { PageStyle pageStyle = Game.GetInstance().activePageStyle; if (pageStyle == null || mode == Mode.Idle || - titleText.Length == 0) + headerText.Length == 0) { return 0; } - GUIStyle titleStyle = pageStyle.GetScaledTitleStyle(); + GUIStyle headerStyle = pageStyle.GetScaledHeaderStyle(); - GUIContent titleContent = new GUIContent(titleText); - return titleStyle.CalcHeight(titleContent, boxWidth); + GUIContent headerContent = new GUIContent(headerText); + return headerStyle.CalcHeight(headerContent, boxWidth); + } + + float CalcFooterHeight(float boxWidth) + { + PageStyle pageStyle = Game.GetInstance().activePageStyle; + + if (pageStyle == null || + mode == Mode.Idle || + footerText.Length == 0) + { + return 0; + } + + GUIStyle footerStyle = pageStyle.GetScaledFooterStyle(); + + GUIContent headerContent = new GUIContent(headerText); + return footerStyle.CalcHeight(headerContent, boxWidth); } float CalcStoryHeight(float boxWidth) @@ -392,6 +435,20 @@ namespace Fungus return totalHeight; } + // Force rect to always be on-screen + Rect FitRectToScreen(Rect rect) + { + Rect fittedRect = new Rect(); + fittedRect.width = Mathf.Min(rect.width, Screen.width); + fittedRect.height = Mathf.Min(rect.height, Screen.height); + fittedRect.x = Mathf.Min(rect.x, Screen.width - rect.width); + fittedRect.y = Mathf.Min(rect.y, Screen.height - rect.height); + fittedRect.x = Mathf.Max(0, fittedRect.x); + fittedRect.y = Mathf.Max(0, fittedRect.y); + + return fittedRect; + } + // Returns smaller internal box rect with padding style applied Rect CalcInnerRect(Rect outerRect) { @@ -404,10 +461,12 @@ namespace Fungus GUIStyle boxStyle = pageStyle.boxStyle; - return new Rect(outerRect.x + boxStyle.padding.left, - outerRect.y + boxStyle.padding.top, - outerRect.width - (boxStyle.padding.left + boxStyle.padding.right), - outerRect.height - (boxStyle.padding.top + boxStyle.padding.bottom)); + Rect innerRect = new Rect(outerRect.x + boxStyle.padding.left, + outerRect.y + boxStyle.padding.top, + outerRect.width - (boxStyle.padding.left + boxStyle.padding.right), + outerRect.height - (boxStyle.padding.top + boxStyle.padding.bottom)); + + return innerRect; } Rect CalcContinueRect(Rect outerRect) @@ -431,8 +490,10 @@ namespace Fungus return new Rect(x, y, width, height); } - // Returns the page rect in screen space coords - Rect GetScreenRect() + /** + * Returns the page rect in screen space coords + */ + public Rect GetScreenRect() { // Y decreases up the screen in GUI space, so top left is rect origin @@ -448,31 +509,9 @@ namespace Fungus Vector2 tl = mainCamera.WorldToScreenPoint(topLeft); Vector2 br = mainCamera.WorldToScreenPoint(bottomRight); - return new Rect(tl.x, Screen.height - tl.y, br.x - tl.x, tl.y - br.y); - } + Rect pageRect = new Rect(tl.x, Screen.height - tl.y, br.x - tl.x, tl.y - br.y); - // Inserts extra line breaks to avoid partial words 'jumping' to next line due to word wrap - string InsertLineBreaks(string text, GUIStyle style, float maxWidth) - { - string output = ""; - string[] parts = Regex.Split(text, @"(?=\s)"); - foreach (string word in parts) - { - float oldHeight = style.CalcHeight(new GUIContent(output), maxWidth); - float newHeight = style.CalcHeight(new GUIContent(output + word), maxWidth); - - if (oldHeight > 0 && - newHeight > oldHeight) - { - output += "\n" + word.TrimStart(); - } - else - { - output += word; - } - } - - return output; + return FitRectToScreen(pageRect); } } } \ No newline at end of file diff --git a/Assets/Fungus/Scripts/PageStyle.cs b/Assets/Fungus/Scripts/PageStyle.cs index 51453811..3ff077c9 100644 --- a/Assets/Fungus/Scripts/PageStyle.cs +++ b/Assets/Fungus/Scripts/PageStyle.cs @@ -12,20 +12,26 @@ namespace Fungus // The font size for title, say and option text is calculated by multiplying the screen height // by the corresponding font scale. Text appears the same size across all device resolutions. - /// Title font size as a fraction of screen height. - public float titleFontScale = 1f / 20f; + /// Header font size as a fraction of screen height. + public float headerFontScale = 1f / 20f; /// Say font size as a fraction of screen height. public float sayFontScale = 1f / 25f; + /// Header font size as a fraction of screen height. + public float footerFontScale = 1f / 20f; + /// Continue font size as a fraction of screen height. public float continueFontScale = 1f / 30f; /// Option font size as a fraction of screen height. public float optionFontScale = 1f / 25f; - /// Style for title text - public GUIStyle titleStyle; + /// Style for header text + public GUIStyle headerStyle; + + /// Style for header text + public GUIStyle footerStyle; /// Style for say text public GUIStyle sayStyle; @@ -43,14 +49,26 @@ namespace Fungus public GUIStyle boxStyle; /** - * Returns the style for Title text. + * Returns the style for Header text. + * Overrides the font size to compensate for varying device resolution. + * Font size is calculated as a fraction of the current screen height. + */ + public GUIStyle GetScaledHeaderStyle() + { + GUIStyle style = new GUIStyle(headerStyle); + style.fontSize = Mathf.RoundToInt((float)Screen.height * headerFontScale); + return style; + } + + /** + * Returns the style for Footer text. * Overrides the font size to compensate for varying device resolution. * Font size is calculated as a fraction of the current screen height. */ - public GUIStyle GetScaledTitleStyle() + public GUIStyle GetScaledFooterStyle() { - GUIStyle style = new GUIStyle(titleStyle); - style.fontSize = Mathf.RoundToInt((float)Screen.height * titleFontScale); + GUIStyle style = new GUIStyle(footerStyle); + style.fontSize = Mathf.RoundToInt((float)Screen.height * footerFontScale); return style; } diff --git a/Assets/FungusExample/Scenes/Example.unity b/Assets/FungusExample/Scenes/Example.unity index 32de0143..8fcb3576 100644 Binary files a/Assets/FungusExample/Scenes/Example.unity and b/Assets/FungusExample/Scenes/Example.unity differ diff --git a/Assets/FungusExample/Scripts/PageRoom.cs b/Assets/FungusExample/Scripts/PageRoom.cs index 8e1ea621..c83b7c77 100644 --- a/Assets/FungusExample/Scripts/PageRoom.cs +++ b/Assets/FungusExample/Scripts/PageRoom.cs @@ -16,8 +16,8 @@ public class PageRoom : Room // You can also use the OnLeave() method to handle when the player leaves the room. void OnEnter() { - // Sets the title text on the page - Title("The Mushroom"); + // Sets the header text on the page + Header("The Mushroom"); // Each Say() command writes one line of text, followed by a continue button Say("One day in the forest, a mushroom grew."); @@ -27,8 +27,8 @@ public class PageRoom : Room // Wait for a few seconds Wait(3); - // Set the title text to the empty string to remove the page title - Title(""); + // Set the header text to the empty string to remove the page title + Header(""); Say("..."); Say("Hmmm. Nothing seems to be happening.");