feat: Add variable system, scene transition, and improve character layout

- **Introduce Variable System**
  - Added singleton class for variable management
  - Implemented script commands `[var]` and `[add]` for variable control
  - Supported variable substitution (`{variable}`) in dialogue and choices

- **Expand Script Features**
  - Added `[scene]` command for scene transition (load scene and link next script)

- **Improve Character Presentation**
  - Refactored character object hierarchy for better animation control
  - Applied mask-based soft transition effect (Soft Wipe) for expression changes
This commit is contained in:
2025-11-29 06:09:34 +09:00
parent 7a3069fa3c
commit 961f5ab8d8
7 changed files with 137 additions and 13 deletions

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
public class ScriptAction public class ScriptAction
{ {
public string Type { get; set; } public string Type { get; set; }
public Dictionary<string, object> Params { get; set; } = new Dictionary<string, object>(); public Dictionary<string, object> Params { get; set; } = new();
public List<Dictionary<string, string>> Choices { get; set; } public List<Dictionary<string, string>> Choices { get; set; }
public string GetParam(string key, string defaultValue = "") public string GetParam(string key, string defaultValue = "")

View File

@@ -3,6 +3,7 @@ using PrimeTween;
using TMPro; using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
using UnityEngine.UI; using UnityEngine.UI;
public class ScriptManager : MonoBehaviour public class ScriptManager : MonoBehaviour
@@ -30,6 +31,8 @@ public class ScriptManager : MonoBehaviour
private Tween dialogueTween; private Tween dialogueTween;
private Script _currentScript; private Script _currentScript;
public static string NextScriptPath = "";
void Start() void Start()
{ {
speakerText.SetText(" "); speakerText.SetText(" ");
@@ -37,7 +40,25 @@ public class ScriptManager : MonoBehaviour
dialogueText.SetText(" "); dialogueText.SetText(" ");
dialogueText.ForceMeshUpdate(true); dialogueText.ForceMeshUpdate(true);
if (!string.IsNullOrEmpty(NextScriptPath))
{
TextAsset loadedScript = Resources.Load<TextAsset>($"NovelScripts/{NextScriptPath}");
if (loadedScript != null)
{
_currentScript = ScriptParser.Parse(loadedScript.text);
NextScriptPath = "";
}
else
{
Debug.LogError($"ScriptManager :: Cannot find script: {NextScriptPath}");
_currentScript = ScriptParser.Parse(scriptFile.text); _currentScript = ScriptParser.Parse(scriptFile.text);
}
}
else
{
_currentScript = ScriptParser.Parse(scriptFile.text);
}
NextStep(); NextStep();
} }
@@ -144,6 +165,7 @@ public class ScriptManager : MonoBehaviour
if (speaker == "") if (speaker == "")
speakerSprite.SetActive(false); speakerSprite.SetActive(false);
speaker = VariableManager.Instance.ReplaceVariables(speaker);
speakerText.SetText(speaker); speakerText.SetText(speaker);
speakerText.ForceMeshUpdate(true); speakerText.ForceMeshUpdate(true);
NextStep(); NextStep();
@@ -152,6 +174,7 @@ public class ScriptManager : MonoBehaviour
if (action.Type == "msg") if (action.Type == "msg")
{ {
string dialogue = action.GetParam("content"); string dialogue = action.GetParam("content");
dialogue = VariableManager.Instance.ReplaceVariables(dialogue);
DisplayDialogue(dialogue); DisplayDialogue(dialogue);
return; return;
} }
@@ -174,7 +197,7 @@ public class ScriptManager : MonoBehaviour
foreach (var choice in action.Choices) foreach (var choice in action.Choices)
{ {
string text = choice["content"]; string text = VariableManager.Instance.ReplaceVariables(choice["content"]);
string target = choice["goto"]; string target = choice["goto"];
GameObject buttonObj = Instantiate(choiceButtonPrefab, choiceButtonContainer); GameObject buttonObj = Instantiate(choiceButtonPrefab, choiceButtonContainer);
buttonObj.GetComponentInChildren<TextMeshProUGUI>().text = text; buttonObj.GetComponentInChildren<TextMeshProUGUI>().text = text;
@@ -196,6 +219,34 @@ public class ScriptManager : MonoBehaviour
} }
return; return;
} }
if (action.Type == "var")
{
foreach (var entry in action.Params)
{
VariableManager.Instance.SetVariable(entry.Key, entry.Value.ToString());
}
NextStep();
return;
}
if (action.Type == "add")
{
foreach (var entry in action.Params)
{
VariableManager.Instance.AddVariable(entry.Key, entry.Value.ToString());
}
NextStep();
return;
}
if (action.Type == "scene")
{
string sceneName = action.GetParam("file");
string nextScript = action.GetParam("script");
Debug.Log($"ScriptManager :: Load Scene: {sceneName}, Next Script: {nextScript}");
NextScriptPath = nextScript;
SceneManager.LoadScene(sceneName);
return;
}
} }
public void DebugReload() public void DebugReload()

View File

@@ -1,6 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using UnityEngine;
public class ScriptParser public class ScriptParser
{ {
@@ -32,7 +31,6 @@ public class ScriptParser
{ {
string tagName = tagMatch.Groups[1].Value; string tagName = tagMatch.Groups[1].Value;
string attrString = tagMatch.Groups[2].Value; string attrString = tagMatch.Groups[2].Value;
Debug.Log($"ScriptParser :: Tag: {tagName} {attrString}");
var scriptAction = new ScriptAction { Type = tagName }; var scriptAction = new ScriptAction { Type = tagName };

View File

@@ -0,0 +1,73 @@
using System.Collections.Generic;
using UnityEngine;
public class VariableManager
{
private static VariableManager _instance;
public static VariableManager Instance => _instance ??= new VariableManager();
private Dictionary<string, object> _variables = new();
public void SetVariable(string name, string value)
{
// Try to parse as int or float, otherwise string
if (int.TryParse(value, out int intVal))
_variables[name] = intVal;
else if (float.TryParse(value, out float floatVal))
_variables[name] = floatVal;
else
_variables[name] = value;
Debug.Log($"VariableManager :: Set {name} = {_variables[name]} ({_variables[name].GetType()})");
}
public object GetVariable(string name)
{
return _variables.ContainsKey(name) ? _variables[name] : null;
}
public void AddVariable(string name, string valueStr)
{
if (!_variables.ContainsKey(name))
{
SetVariable(name, valueStr);
return;
}
object current = _variables[name];
if (current is int currentInt && int.TryParse(valueStr, out int addInt))
{
_variables[name] = currentInt + addInt;
}
else if (current is float currentFloat && float.TryParse(valueStr, out float addFloat))
{
_variables[name] = currentFloat + addFloat;
}
else if (current is string currentStr)
{
_variables[name] = currentStr + valueStr;
}
else
{
Debug.LogWarning($"VariableManager :: Cannot add {valueStr} to {name} (Type: {current.GetType()})");
}
Debug.Log($"VariableManager :: Add {name} += {valueStr} -> {_variables[name]}");
}
public string ReplaceVariables(string text)
{
if (string.IsNullOrEmpty(text)) return text;
foreach (var kvp in _variables)
{
text = text.Replace($"{{{kvp.Key}}}", kvp.Value.ToString());
}
return text;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 309802f724fb94846a7b453b40357e1d

View File

@@ -1,5 +1,4 @@
using System.Collections; using System.Collections;
using System.Collections.Generic;
using PrimeTween; using PrimeTween;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
@@ -19,8 +18,6 @@ public class VisualNovelLayoutDirector : MonoBehaviour
public float defaultDuration = 0.5f; public float defaultDuration = 0.5f;
public float moveDistance = 800f; public float moveDistance = 800f;
// ========================= [1. 등장 (Entry)] ========================= // ========================= [1. 등장 (Entry)] =========================
public void AddCharacter(string fileName, EntranceType type) public void AddCharacter(string fileName, EntranceType type)
{ {

View File

@@ -8,6 +8,9 @@ EditorBuildSettings:
- enabled: 1 - enabled: 1
path: Assets/_MAIN/Scenes/VisualNovel.unity path: Assets/_MAIN/Scenes/VisualNovel.unity
guid: 199e3baed267a4c4fb4665bdaf82e49c guid: 199e3baed267a4c4fb4665bdaf82e49c
- enabled: 1
path: Assets/_MAIN/Scenes/Scene01.unity
guid: ab7661506d731e6428485450843b0c2c
m_configObjects: m_configObjects:
com.unity.input.settings.actions: {fileID: -944628639613478452, guid: 052faaac586de48259a63d0c4782560b, type: 3} com.unity.input.settings.actions: {fileID: -944628639613478452, guid: 052faaac586de48259a63d0c4782560b, type: 3}
m_UseUCBPForAssetBundles: 0 m_UseUCBPForAssetBundles: 0