using UnityEngine; using System.Collections; using System.Collections.Generic; using System.Linq; using System; using System.Runtime.Serialization.Formatters.Binary; using System.IO; /** * @package Fungus An open source library for Unity 3D for creating graphic interactive fiction games. */ namespace Fungus { /** * Manages global game state and movement between rooms. */ public class Game : GameController { /** * The currently active Room. * Only one Room may be active at a time. */ public Room activeRoom; /** * The style to apply when displaying Pages. */ public PageStyle activePageStyle; /** * Automatically display links between connected Rooms. */ public bool showLinks = true; /** * Writing speed for page text. */ public int charactersPerSecond = 60; /** * Fixed Z coordinate of main camera. */ public float cameraZ = - 10f; /** * Time for fade transition to complete when moving to a different Room. */ public float roomFadeDuration = 1f; /** * Time for fade transition to complete when hiding/showing buttons. */ public float buttonFadeDuration = 0.25f; /** * Time which must elapse before buttons will automatically hide. */ public float autoHideButtonDuration = 5f; /** * Full screen texture used for screen fade effect. */ public Texture2D screenFadeTexture; /** * Position of continue and swipe icons in normalized screen space coords. * (0,0) = top left, (1,1) = bottom right */ public Vector2 iconPosition = new Vector2(1,1); /** * Icon to display when waiting for player input to continue */ public Texture2D continueIcon; /** * Icon to display when swipe pan mode is active. */ public Texture2D swipePanIcon; /** * Sound effect to play when buttons are clicked. */ public AudioClip buttonClickClip; /** * Default screen position for Page when player enters a Room. */ public PageController.PagePosition defaultPagePosition; /** * Default width and height of Page as a fraction of screen height [0..1] */ public Vector2 defaultPageScale = new Vector2(0.75f, 0.25f); /** * Automatically center the Page when player is choosing from multiple options. */ public bool centerChooseMenu = true; /** * Width of Page as a fraction of screen width [0..1] when automatically centering a Choose menu. * This setting only has an effect when centerChooseMenu is enabled. */ public float chooseMenuWidth = 0.5f; /** * Global dictionary of integer values for storing game state. */ [HideInInspector] static public Dictionary values = new Dictionary(); [HideInInspector] static public StringTable stringTable = new StringTable(); [HideInInspector] public CommandQueue commandQueue; [HideInInspector] public CameraController cameraController; [HideInInspector] public PageController pageController; /** * True when executing a Wait() or WaitForTap() command */ [HideInInspector] public bool waiting; [HideInInspector] public bool swipePanActive; [HideInInspector] public float fadeAlpha = 0f; float autoHideButtonTimer; static Game instance; /** * Returns the singleton instance for the Game class */ public static Game GetInstance() { if (!instance) { instance = GameObject.FindObjectOfType(typeof(Game)) as Game; if (!instance) { Debug.LogError("There must be one active Game object in your scene."); } } return instance; } public virtual void Start() { // Add components for additional game functionality commandQueue = gameObject.AddComponent(); cameraController = gameObject.AddComponent(); pageController = gameObject.AddComponent(); AudioSource audioSource = gameObject.AddComponent(); audioSource.playOnAwake = false; audioSource.loop = true; // Auto-hide buttons should be visible at start of game autoHideButtonTimer = autoHideButtonDuration; if (activeRoom == null) { // Pick first room found if none is specified activeRoom = GameObject.FindObjectOfType(typeof(Room)) as Room; } if (activeRoom != null) { // Move to the active room commandQueue.Clear(); commandQueue.AddCommand(new Command.MoveToRoom(activeRoom)); commandQueue.Execute(); } } public virtual void Update() { autoHideButtonTimer -= Time.deltaTime; autoHideButtonTimer = Mathf.Max(autoHideButtonTimer, 0f); if (Input.GetMouseButtonDown(0)) { autoHideButtonTimer = autoHideButtonDuration; } } void OnGUI() { if (swipePanActive) { // Draw the swipe panning icon if (swipePanIcon) { float x = Screen.width * iconPosition.x; float y = Screen.height * iconPosition.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); } } if (pageController.mode == PageController.Mode.Say && pageController.FinishedWriting()) { // Draw the continue icon if (continueIcon) { float x = Screen.width * iconPosition.x; float y = Screen.height * iconPosition.y; float width = continueIcon.width; float height = continueIcon.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, continueIcon); } } // Draw full screen fade texture if (fadeAlpha < 1f) { // 1 = scene fully visible // 0 = scene fully obscured GUI.color = new Color(1,1,1, 1f - fadeAlpha); GUI.depth = -1000; GUI.DrawTexture(new Rect(0, 0, Screen.width, Screen.height), screenFadeTexture); } } /** * Plays the button clicked sound effect */ public void PlayButtonClick() { if (buttonClickClip == null) { return; } audio.PlayOneShot(buttonClickClip); } /** * Returns true if the game should display 'auto hide' buttons. * Buttons will be displayed if the game is not currently displaying story text/options, and no Wait command is in progress. */ public bool GetShowAutoButtons() { if (waiting) { return false; } if (pageController == null || pageController.mode == PageController.Mode.Idle) { return (autoHideButtonTimer > 0f); } return false; } /** * Sets a globally accessible game state value. * @param key The key of the value. * @param value The integer value to store. */ public void SetGameValue(string key, int value) { values[key] = value; } /** * Gets a globally accessible game state value. * @param key The key of the value. * @return The integer value for the specified key, or 0 if the key does not exist. */ public int GetGameValue(string key) { if (values.ContainsKey(key)) { return values[key]; } return 0; } /** * Returns a parallax offset vector based on the camera position relative to the active Room. * Higher values for the parallaxFactor yield a larger offset (appears closer to camera). * Suggested range for good parallax effect is [0.1..0.5]. * @param parallaxFactor Horizontal and vertical scale factors to apply to camera offset vector. * @return A parallax offset vector based on camera positon relative to current room and the parallaxFactor. */ public Vector3 GetParallaxOffset(Vector2 parallaxFactor) { if (activeRoom == null) { return Vector3.zero; } Vector3 a = activeRoom.transform.position; Vector3 b = cameraController.GetCameraPosition(); Vector3 offset = (a - b); offset.x *= parallaxFactor.x; offset.y *= parallaxFactor.y; return offset; } /** * Loads a new scene. * This is useful for splitting a large Fungus game across multiple scene files to reduce peak memory usage. * All previously loaded assets (including textures) will be released. * @param sceneName The filename of the scene to load. * @param saveGame Automatically save the current game state as a checkpoint. */ public void LoadScene(string sceneName, bool saveGame) { Application.LoadLevel(sceneName); if (saveGame) { SaveGame("Fungus.Save"); } // Free up memory used by textures in previous scene Resources.UnloadUnusedAssets(); } /** * Save the current game state to persistant storage. * Only the values, string table and current scene are stored. * @param saveName The name of the saved game data. */ public void SaveGame(string saveName) { // Store loaded scene name in save data SetString("Fungus.Scene", Application.loadedLevelName); var b = new BinaryFormatter(); var m = new MemoryStream(); // Save values { b.Serialize(m, values); string s = Convert.ToBase64String(m.GetBuffer()); PlayerPrefs.SetString(saveName + ".values", s); } // Save string table { b.Serialize(m, stringTable.stringDict); string s = Convert.ToBase64String(m.GetBuffer()); PlayerPrefs.SetString(saveName + ".stringTable", s); } } /** * Loads the current game state from persistant storage. * This will cause the scene specified in the "Fungus.Scene" string to be loaded. * Each scene in your game should contain the necessary code to restore the current game state based on saved data. * @param saveName The name of the saved game data. */ public void LoadGame(string saveName) { // Load values { var data = PlayerPrefs.GetString(saveName + ".values"); if (!string.IsNullOrEmpty(data)) { var b = new BinaryFormatter(); var m = new MemoryStream(Convert.FromBase64String(data)); values = (Dictionary)b.Deserialize(m); } } // Load string table { var data = PlayerPrefs.GetString(saveName + ".stringTable"); if (!string.IsNullOrEmpty(data)) { var b = new BinaryFormatter(); var m = new MemoryStream(Convert.FromBase64String(data)); stringTable.stringDict = b.Deserialize(m) as Dictionary; } } string sceneName = GetString("Fungus.Scene"); if (sceneName.Length > 0) { LoadScene(sceneName, true); } } } }