Created
December 5, 2023 23:09
-
-
Save samsheffield/392d4fc628c07e4002238e2fc73315d9 to your computer and use it in GitHub Desktop.
Ink + Unity demo files (Narrative Design F23)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using UnityEngine; | |
public class InkInteraction : MonoBehaviour | |
{ | |
// This is the ink JSON asset | |
[SerializeField] private TextAsset inkJSON; | |
// This is a reference to the Ink Manager script designed to run the story | |
// Note: You could also make the Ink Manager a singleton and avoid having to create a variable here | |
[SerializeField] private InkManager inkManager; | |
// This indicates whether we are able to interact with something and start the Ink story | |
// Note: In this example you will need to set it in the Inspector to true, but a practical use would be using a trigger collider | |
[SerializeField] private bool canInteract; | |
void Update() | |
{ | |
// Can we interact? Is the story NOT already playing? Is it ready? | |
if (canInteract && !inkManager.inkIsPlaying && inkManager.isReady) | |
{ | |
// Start the story with the referenced Ink JSON asset using an input action | |
if (Input.GetKeyDown(KeyCode.Return)) | |
{ | |
StartCoroutine(StartAfterDelay()); | |
} | |
} | |
} | |
// Need a little cooldown since we are using the same input action for interaction and story progression | |
private IEnumerator StartAfterDelay() | |
{ | |
yield return new WaitForSeconds(.2f); | |
inkManager.StartStory(inkJSON); | |
} | |
// Optional: trigger the start of the interaction | |
private void OnTriggerEnter(Collider other) | |
{ | |
canInteract = true; | |
} | |
private void OnTriggerExit(Collider other) | |
{ | |
canInteract = false; | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using Ink.Runtime; | |
using TMPro; | |
using UnityEngine.EventSystems; | |
using UnityEngine.Serialization; | |
public class InkManager : MonoBehaviour | |
{ | |
// This holds our current Ink story | |
[SerializeField] private Story currentStory; | |
// This is a reference to the UI panel containing the story test | |
[SerializeField] private GameObject storyPanel; | |
// This is a reference to the text used to render the story | |
[SerializeField] private TextMeshProUGUI storyText; | |
// This is an array which holds choices from our story | |
[SerializeField] private GameObject[] choices; | |
// This is the text used to render the choices | |
private TextMeshProUGUI[] choicesText; | |
// This is used to keep track of whether the story is already playing | |
// Note: It is public because it is being used by the scripts enabling interaction | |
public bool inkIsPlaying; | |
public bool isReady = true; | |
// Create variables for each Ink tag. This is a constant variable so that it can be used with switch() | |
// Note: Const variables can't be modified at runtime | |
private const string SPEAKER = "speaker"; | |
void Start() | |
{ | |
// Disable UI | |
storyPanel.SetActive(false); | |
// Set the length of the choices text array to match the maximum number of buttons | |
choicesText = new TextMeshProUGUI[choices.Length]; | |
// Then get references to the text component of each one | |
int index = 0; | |
foreach (GameObject choice in choices) | |
{ | |
choicesText[index] = choice.GetComponentInChildren<TextMeshProUGUI>(); | |
index++; | |
} | |
} | |
void Update() | |
{ | |
// Only continue past this point if ink is playing | |
if (!inkIsPlaying) | |
{ | |
return; | |
} | |
// Use an input action to progress the story | |
if (Input.GetKeyDown(KeyCode.Return) ) | |
{ | |
ContinueStory(); | |
} | |
} | |
// Starts the story and activates the UI | |
public void StartStory(TextAsset inkJSON) | |
{ | |
// Set the story to the selected ink JSON file | |
currentStory = new Story(inkJSON.text); | |
// The ink story is now playing | |
inkIsPlaying = true; | |
// Enable the UI | |
storyPanel.SetActive(true); | |
isReady = false; | |
// Bind the Ink Functions: | |
// Note about Actions | |
currentStory.BindExternalFunction("hiUnity", (string hiMessage) => { | |
Debug.Log(hiMessage); | |
}); | |
// Begin playing story | |
ContinueStory(); | |
} | |
// Progresses the story while possible and signals exit when done | |
private void ContinueStory() | |
{ | |
if (currentStory.canContinue) | |
{ | |
// Set the text to the current block of the ink story | |
storyText.text = currentStory.Continue(); | |
// If there are choices, display them | |
DisplayChoices(); | |
// Handle tags | |
HandleTags(currentStory.currentTags); | |
} | |
else | |
{ | |
ExitStory(); | |
} | |
} | |
// Handles what happens when the story is exited | |
private void ExitStory() | |
{ | |
// Optional: Reset the story once completed | |
currentStory.ResetState(); | |
// Unbind any Ink functions | |
currentStory.UnbindExternalFunction("hiUnity"); | |
// Ink is no longer playing | |
inkIsPlaying = false; | |
// Disable the UI | |
storyPanel.SetActive(false); | |
StartCoroutine(MakeReady()); | |
} | |
// Display choices when they are available | |
private void DisplayChoices() | |
{ | |
// This is a List which is populated with any current choices | |
List<Choice> currentChoices = currentStory.currentChoices; | |
// If there ae more choices in your ink story than buttons that you've set up, this will throw an error | |
if (currentChoices.Count > choices.Length) | |
{ | |
Debug.LogError("More choices given than the UI can support"); | |
} | |
// For each choice detected... | |
int index = 0; | |
foreach (Choice choice in currentChoices) | |
{ | |
// Activate the choice button | |
choices[index].gameObject.SetActive(true); | |
// Set the text of the button the choice text in the ink story | |
choicesText[index].text = choice.text; | |
index++; | |
} | |
// Deactivate any choice buttons not being used | |
// Note: the count starts at the current value of index started above, not 0 | |
for (int i = index; i < choices.Length; i++) | |
{ | |
choices[i].gameObject.SetActive(false); | |
} | |
// Automatically select the first button so that a player can use input actions to navigate the UI | |
EventSystem.current.SetSelectedGameObject (choices[0]); | |
} | |
// This is used to manage choices | |
// Note: This is public because it need to be added as a button's OnClick Event | |
// Note: choiceIndex needs to be set in the OnClick event to match the button order (0,1,2, etc) | |
public void MakeChoice(int choiceIndex) | |
{ | |
currentStory.ChooseChoiceIndex(choiceIndex); | |
} | |
// Need a little cooldown since we are using the same input action for interaction and story progression | |
private IEnumerator MakeReady() | |
{ | |
yield return new WaitForSeconds(.2f); | |
isReady = true; | |
} | |
// Handle all found Ink tags | |
private void HandleTags(List<string> currentTags) | |
{ | |
// Parse each set of tags found | |
foreach (string tag in currentTags) | |
{ | |
// The tags arrive formatted like this: key:value. This needs to be split into separate key and value strings at the : | |
string[] splitTag = tag.Split(':'); | |
// Check the split tags to make sure it consists of two parts and throw an error if it doesn't | |
if(splitTag.Length != 2) | |
{ | |
Debug.LogError("Tag could not be parsed: " + tag); | |
} | |
// Trim can be used to remove any trailing white space | |
string tagKey = splitTag[0].Trim(); | |
string tagValue = splitTag[1].Trim(); | |
// Do something when a particular tag is found | |
switch(tagKey) | |
{ | |
// Add a case for each tag | |
case SPEAKER: | |
if(tagValue == "one") | |
{ | |
Debug.Log("This is speaker one"); | |
} | |
break; | |
default: | |
// Give a warning if an unknown key was found | |
Debug.LogWarning("An unknown key was found: " + tagKey); | |
break; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment