Last active
March 4, 2024 17:39
-
-
Save ssshake/576fa871870e61a8c5bcda42a3ea34f9 to your computer and use it in GitHub Desktop.
Unity NPC Dialog with Branching Nodes and Unlimited Statements and Responses
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.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using UnityEngine.UI; | |
using TMPro; | |
namespace Daggasoft { | |
[System.Serializable] | |
public class Statement { | |
[TextArea(1, 10)] | |
public string name; | |
public AudioClip voiceOver; | |
public Action action; | |
public List<Response> responses; | |
} | |
[System.Serializable] | |
public class Response { | |
[TextArea(1, 10)] | |
public string name; | |
public List<Statement> statements; | |
} | |
[System.Serializable] | |
public class Action { | |
public GameObject prefab; | |
public Transform spawnTransform; | |
public Vector3 offset; | |
} | |
[System.Serializable] | |
public class NPCDialog : MonoBehaviour | |
{ | |
[Header("Dialog Branches")] | |
public List<Statement> statements; | |
Statement currentStatement; | |
Stack<Statement> statementsStack; | |
[Header("UI References")] | |
public TMP_Text statementText; | |
public Transform responsePanel; | |
public GameObject responseButtonPrefab; | |
AudioSource audioSource; | |
void Start() | |
{ | |
statementsStack = new Stack<Statement>(); | |
audioSource = GetComponent<AudioSource>(); | |
AddStatementsToStack(statements); | |
ProcessStack(); | |
} | |
void AddStatementsToStack(List<Statement> newStatements) { | |
newStatements.Reverse(); | |
foreach (Statement statement in newStatements) | |
{ | |
statementsStack.Push(statement); | |
} | |
} | |
public void ProcessStack() { | |
if (statementsStack.Count == 0) { | |
statementText.text = "Dialog Over"; | |
DeleteResponseButtons(); | |
return; | |
} | |
currentStatement = statementsStack.Pop(); | |
statementText.text = currentStatement.name; | |
if (currentStatement.voiceOver && audioSource) { | |
audioSource.PlayOneShot(currentStatement.voiceOver); | |
} | |
if (currentStatement.action.prefab) | |
{ | |
GameObject actionItem = Instantiate(currentStatement.action.prefab); | |
actionItem.transform.position = currentStatement.action.spawnTransform.position + currentStatement.action.offset; | |
} | |
BuildResponseButtons(); | |
} | |
void DeleteResponseButtons() | |
{ | |
foreach (Transform child in responsePanel) | |
{ | |
Destroy(child.gameObject); | |
} | |
} | |
void BuildResponseButtons() { | |
DeleteResponseButtons(); | |
if (currentStatement.responses.Count <= 0) | |
{ | |
BuildResponseButton("...").onClick.AddListener(Done); | |
return; | |
} | |
int index = 0; | |
foreach (Response response in currentStatement.responses) | |
{ | |
int indexCopy = index; | |
BuildResponseButton(response.name).onClick.AddListener(() => HandleResponse(indexCopy)); | |
index++; | |
} | |
} | |
Button BuildResponseButton(string text) { | |
GameObject buttonGameObject = (GameObject)Instantiate(responseButtonPrefab); | |
buttonGameObject.transform.SetParent(responsePanel); | |
buttonGameObject.transform.GetChild(0).GetComponent<TMP_Text>().text = text; | |
return buttonGameObject.GetComponent<Button>(); | |
} | |
void Done() { | |
Debug.Log("Done"); | |
ProcessStack(); | |
} | |
public void HandleResponse(int index) { | |
Response response = currentStatement.responses[index]; | |
if (response?.statements.Count > 0) { | |
AddStatementsToStack(response.statements); | |
} | |
ProcessStack(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment