다이얼로그 작업중

This commit is contained in:
skrwns304@gmail.com
2026-06-09 15:51:29 +09:00
parent 54c6ddee0a
commit 404921f815
172 changed files with 9854 additions and 26 deletions

View File

@@ -0,0 +1,22 @@
using System.Threading.Tasks;
namespace Unity.GraphToolkit.Samples.VisualNovelDirector
{
/// <summary>
/// The interface for an executor of a visual novel node.
/// <br/><br/>
/// An executor defines the runtime behaviour for the data stored in a <see cref="VisualNovelRuntimeNode"/>. Each
/// <see cref="VisualNovelRuntimeNode"/> will have its own implementation of an executor.
/// </summary>
/// <remarks>
/// If we want to share runtime behaviour between nodes, a single class can implement multiple node executors and have
/// functions for shared behaviour. This is useful for nodes that have similar behaviour but different data.
/// We can see an example of this in the <see cref="SetDialogueExecutor"/> class, which implements
/// <see cref="IVisualNovelNodeExecutor{TNode}"/> for both <see cref="SetDialogueRuntimeNode"/> and <see cref="SetDialogueRuntimeNodeWithPreviousActor"/>
/// </remarks>
/// <typeparam name="TNode">The type of <see cref="VisualNovelRuntimeNode"/> to execute</typeparam>
public interface IVisualNovelNodeExecutor<in TNode> where TNode : VisualNovelRuntimeNode
{
Task ExecuteAsync(TNode node, VisualNovelDirector ctx);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bcf95c3fd8c7449eb6d6aa5368f3f575
timeCreated: 1743647517

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ab22459b58eb45bfa0373ce6130bfcfb
timeCreated: 1743647612

View File

@@ -0,0 +1,19 @@
using System.Threading.Tasks;
namespace Unity.GraphToolkit.Samples.VisualNovelDirector
{
/// <summary>
/// Executor for the <see cref="SetBackgroundRuntimeNode"/> node.
/// </summary>
public class SetBackgroundExecutor : IVisualNovelNodeExecutor<SetBackgroundRuntimeNode>
{
/// <summary>
/// Sets the background image of the visual novel director context to the specified sprite.
/// </summary>
public async Task ExecuteAsync(SetBackgroundRuntimeNode runtimeNode, VisualNovelDirector ctx)
{
ctx.BackgroundImage.sprite = runtimeNode.BackgroundSprite;
await Task.Yield();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ea05d640e7a44fb1a25b0543cc9f9e47
timeCreated: 1743647517

View File

@@ -0,0 +1,14 @@
using System;
using UnityEngine;
namespace Unity.GraphToolkit.Samples.VisualNovelDirector
{
/// <summary>
/// The serializable data representing a runtime node in the visual novel graph that sets the background image.
/// </summary>
[Serializable]
public class SetBackgroundRuntimeNode : VisualNovelRuntimeNode
{
public Sprite BackgroundSprite;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e5611765a9ea43fb8f1348a7e1dd8f55
timeCreated: 1743647517

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9b31022c513b476d8e79090f8556e1b6
timeCreated: 1743647633

View File

@@ -0,0 +1,114 @@
using System.Text;
using System.Threading.Tasks;
using TMPro;
using UnityEngine;
namespace Unity.GraphToolkit.Samples.VisualNovelDirector
{
/// <summary>
/// Executor for the <see cref="SetDialogueRuntimeNode"/> and <see cref="SetDialogueRuntimeNodeWithPreviousActor"/> nodes.
/// </summary>
public class SetDialogueExecutor :
IVisualNovelNodeExecutor<SetDialogueRuntimeNode>,
IVisualNovelNodeExecutor<SetDialogueRuntimeNodeWithPreviousActor>
{
/// <summary>
/// Executes the <see cref="SetDialogueRuntimeNode"/> node, setting the dialogue text and actor sprite settings.
/// </summary>
public async Task ExecuteAsync(SetDialogueRuntimeNode runtimeNode, VisualNovelDirector ctx)
{
if (string.IsNullOrEmpty(runtimeNode.DialogueText))
{
ctx.DialoguePanel.SetActive(false);
return;
}
ctx.DialoguePanel.SetActive(true);
ctx.ActorNameText.text = runtimeNode.ActorName;
foreach (var location in ctx.ActorLocationList)
location.enabled = false;
if (runtimeNode.ActorSprite != null)
{
var img = ctx.ActorLocationList[runtimeNode.LocationIndex];
img.enabled = true;
img.sprite = runtimeNode.ActorSprite;
}
await TypeTextWithSkipAsync(runtimeNode.DialogueText, ctx);
}
/// <summary>
/// Executes the <see cref="SetDialogueRuntimeNodeWithPreviousActor"/> node, and keeps all previous actor settings
/// while changing the dialogue text.
/// </summary>
public async Task ExecuteAsync(SetDialogueRuntimeNodeWithPreviousActor runtimeNode, VisualNovelDirector ctx)
{
if (string.IsNullOrEmpty(runtimeNode.DialogueText))
{
ctx.DialoguePanel.SetActive(false);
return;
}
ctx.DialoguePanel.SetActive(true);
await TypeTextWithSkipAsync(runtimeNode.DialogueText, ctx);
}
/// <summary>
/// Executes a typewriter effect on the given <see cref="TextMeshProUGUI"/> label.
/// </summary>
/// <param name="dialogueText">The text to set</param>
/// <param name="ctx">The <see cref="VisualNovelDirector"/> context to get settings and input from</param>
/// <remarks>
/// Input is used to skip the typewriter effect if it's in-progress.
/// </remarks>
static async Task TypeTextWithSkipAsync(string dialogueText, VisualNovelDirector ctx)
{
var label = ctx.DialogueText;
var delayPerCharSeconds = ctx.GlobalTextDelayPerCharacter;
var inputProvider = ctx.InputProvider;
label.text = "";
var builder = new StringBuilder();
var insideRichTag = false;
// Start listening for skip input
var skipInputDetected = inputProvider.InputDetected();
foreach (var c in dialogueText)
{
// Handle rich text tags (e.g., <b>, </i>)
if (c == '<')
insideRichTag = true;
builder.Append(c);
if (c == '>')
insideRichTag = false;
// Skip delay if rich text
if (insideRichTag || char.IsWhiteSpace(c)) continue;
label.text = builder.ToString();
var timer = 0f;
while (timer < delayPerCharSeconds)
{
if (skipInputDetected.IsCompleted)
{
label.text = dialogueText;
return;
}
timer += Time.deltaTime;
await Task.Yield();
}
}
label.text = dialogueText;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b5306b40600a4b1dba95ac4dbff24dc3
timeCreated: 1743647517

View File

@@ -0,0 +1,17 @@
using System;
using UnityEngine;
namespace Unity.GraphToolkit.Samples.VisualNovelDirector
{
/// <summary>
/// The serializable data representing a runtime node in the visual novel graph that sets the dialogue text and actor information.
/// </summary>
[Serializable]
public class SetDialogueRuntimeNode : VisualNovelRuntimeNode
{
public string ActorName;
public Sprite ActorSprite;
public int LocationIndex;
public string DialogueText;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3b73106ed058476487451106f4cd06cd
timeCreated: 1743647517

View File

@@ -0,0 +1,13 @@
using System;
namespace Unity.GraphToolkit.Samples.VisualNovelDirector
{
/// <summary>
/// The serializable data that represents a runtime node in the visual novel graph that sets the dialogue text only.
/// </summary>
[Serializable]
public class SetDialogueRuntimeNodeWithPreviousActor : VisualNovelRuntimeNode
{
public string DialogueText;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f76932495d484264a27c87163f5ee1a6
timeCreated: 1743647517

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e04bdd30e6acbb244ac32c9196b537d2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,37 @@
using System.Threading.Tasks;
using UnityEngine;
namespace Unity.GraphToolkit.Samples.VisualNovelDirector
{
/// <summary>
/// Executor for the <see cref="TwoOptionRuntimeNode"/> node.
/// </summary>
/// <remarks>
/// This executor displays a question and two dialogue options to the player,
/// waits for their selection, and records which option was chosen.
/// </remarks>
public class TwoOptionExecutor : IVisualNovelNodeExecutor<TwoOptionRuntimeNode>
{
/// <summary>
/// Executes the <see cref="TwoOptionRuntimeNode"/> node, displaying the dialogue options
/// and waiting for the player to select one.
/// </summary>
public async Task ExecuteAsync(TwoOptionRuntimeNode runtimeNode, VisualNovelDirector ctx)
{
// Display the options
ctx.OptionPanel.SetActive(true);
ctx.Option1Text.text = runtimeNode.Option1Text;
ctx.Option2Text.text = runtimeNode.Option2Text;
// Wait for player to select an option
var optionSelectionDetected = ctx.InputProvider.OptionSelectionDetected();
while (!optionSelectionDetected.IsCompleted)
{
await Task.Yield();
}
runtimeNode.SelectedOption = optionSelectionDetected.Result;
ctx.OptionPanel.SetActive(false);
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 67810d7679f665c4b869ceefa9f4736d

View File

@@ -0,0 +1,34 @@
using System;
namespace Unity.GraphToolkit.Samples.VisualNovelDirector
{
/// <summary>
/// The serializable data representing a runtime node in the visual novel graph
/// that presents two dialogue options to the player.
/// </summary>
[Serializable]
public class TwoOptionRuntimeNode : VisualNovelRuntimeNode
{
public string Option1Text;
public string Option2Text;
/// <summary>
/// The index of the node to execute if Option 1 is selected.
/// -1 indicates no continuation for this branch.
/// </summary>
public int Option1NextNodeIndex = -1;
/// <summary>
/// The index of the node to execute if Option 2 is selected.
/// -1 indicates no continuation for this branch.
/// </summary>
public int Option2NextNodeIndex = -1;
/// <summary>
/// Records which option the player selected (0 for option 1, 1 for option 2).
/// Set during runtime execution.
/// </summary>
public int SelectedOption;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: afce09435a9f2b049bc1fa2034268cca

View File

@@ -0,0 +1,18 @@
using System;
namespace Unity.GraphToolkit.Samples.VisualNovelDirector
{
/// <summary>
/// The base class for the runtime data for all visual novel nodes.
/// </summary>
[Serializable]
public abstract class VisualNovelRuntimeNode
{
/// <summary>
/// The index of the next node to execute in the runtime graph.
/// -1 indicates no next node (end of execution).
/// For branching nodes, this represents the default path.
/// </summary>
public int NextNodeIndex = -1;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b0bf19e6af304282be4060c37ab3aec7
timeCreated: 1743618864

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 162d7b6e660341b9b835e484ea6717c3
timeCreated: 1743647811

View File

@@ -0,0 +1,18 @@
using System.Threading.Tasks;
namespace Unity.GraphToolkit.Samples.VisualNovelDirector
{
/// <summary>
/// The executor for the <see cref="WaitForInputRuntimeNode"/> node.
/// </summary>
public class WaitForInputExecutor : IVisualNovelNodeExecutor<WaitForInputRuntimeNode>
{
/// <summary>
/// Asynchronously waits for user input to be detected before proceeding with the execution of the visual novel graph.
/// </summary>
public async Task ExecuteAsync(WaitForInputRuntimeNode _, VisualNovelDirector ctx)
{
await ctx.InputProvider.InputDetected();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4cfe275f272e4272aad2ae65d590f4fc
timeCreated: 1743647517

View File

@@ -0,0 +1,12 @@
using System;
namespace Unity.GraphToolkit.Samples.VisualNovelDirector
{
/// <summary>
/// The serializable data that represents a runtime node in the visual novel graph that waits for player input.
/// </summary>
[Serializable]
public class WaitForInputRuntimeNode : VisualNovelRuntimeNode
{
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 16cc3826ec21426989b58379033c6688
timeCreated: 1743647517