Add Screen display mode with dedicated UI component

This commit is contained in:
2026-01-27 16:46:46 +01:00
parent 203b00578a
commit d1b7104676
37 changed files with 1306 additions and 254 deletions
@@ -10,6 +10,10 @@ namespace Unheimlich.Data.Dialogues
[Tooltip("Name of the speaker (common to all texts in this conversation)")]
public string SpeakerName;
[Header("Display Mode")]
[Tooltip("Choose how to display dialogue text: World (3D space) or Screen (Canvas UI)")]
public DialogueDisplayMode DisplayMode = DialogueDisplayMode.World;
[Header("Dialogue Flow")]
[Tooltip("Ordered list of dialogue nodes for this conversation")]
public List<DialogueNode> DialogueNodes = new List<DialogueNode>();
@@ -80,7 +80,7 @@ public class DialogueCreatorWindow : EditorWindow
public int TargetNodeIndex = 1;
}
[MenuItem("Tools/Dialogue/Dialogue Creator")]
[MenuItem("Unheimlich/Tools/Dialogue/Dialogue Creator")]
public static void ShowWindow()
{
var window = GetWindow<DialogueCreatorWindow>("Dialogue Creator");
@@ -0,0 +1,8 @@
namespace Unheimlich.Data.Dialogues
{
public enum DialogueDisplayMode
{
World,
Screen
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: b10588959f3745441b157488d7a8f6e9
@@ -8,12 +8,38 @@ public class DialogueConversationEditor : Editor
{
private SerializedProperty _speakerNameProp;
private SerializedProperty _speakerNameLocalizationKeyProp;
private SerializedProperty _displayModeProp;
private SerializedProperty _dialogueNodesProp;
[MenuItem("Unheimlich/Tools/Dialogue/Refresh All Conversations")]
public static void RefreshAllConversations()
{
string[] guids = AssetDatabase.FindAssets("t:DialogueConversation");
int count = 0;
foreach (string guid in guids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
DialogueConversation conv = AssetDatabase.LoadAssetAtPath<DialogueConversation>(path);
if (conv != null)
{
EditorUtility.SetDirty(conv);
count++;
}
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log($"<color=green>[Dialogue Refresher]</color> {count} conversations ont été rafraîchies !");
}
private void OnEnable()
{
_speakerNameProp = serializedObject.FindProperty("SpeakerName");
_speakerNameLocalizationKeyProp = serializedObject.FindProperty("SpeakerNameLocalizationKey");
_displayModeProp = serializedObject.FindProperty("DisplayMode");
_dialogueNodesProp = serializedObject.FindProperty("DialogueNodes");
}
@@ -27,6 +53,17 @@ public class DialogueConversationEditor : Editor
EditorGUILayout.PropertyField(_speakerNameProp);
EditorGUILayout.PropertyField(_speakerNameLocalizationKeyProp, new GUIContent("Localization Key (Optional)"));
EditorGUILayout.Space(5);
EditorGUILayout.LabelField("Display Settings", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(_displayModeProp, new GUIContent("Display Mode"));
EditorGUILayout.HelpBox(
"World: Dialogue text appears in 3D space\n" +
"Screen: Dialogue text appears on Canvas UI\n" +
"Choices always appear in World space.",
MessageType.Info);
EditorGUILayout.Space(10);
EditorGUILayout.LabelField("Dialogue Flow", EditorStyles.boldLabel);
@@ -1,12 +0,0 @@
namespace Unheimlich.Interactions.Dialogues
{
/// <summary>
/// Serializable data structure representing a single dialogue choice option.
/// Links choice text to the next dialogue node via ID.
/// </summary>
[System.Serializable]
public class DialogueChoice
{
public string ChoiceText; // Text on the choice button
}
}
@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: e7a6b90d8597f824a9451b58900227b9
@@ -165,8 +165,9 @@ namespace Unheimlich.Managers
string speakerName = _currentConversation.SpeakerName;
string dialogueText = _currentNode.DialogueText;
DialogueDisplayMode displayMode = _currentConversation.DisplayMode;
_activeDialogueUI.ShowDialogueBox(speakerName, dialogueText);
_activeDialogueUI.ShowDialogueBox(speakerName, dialogueText, displayMode);
_autoCloseTimer = 0f;
_isWaitingForAutoClose = false;
@@ -0,0 +1,91 @@
using UnityEngine;
using TMPro;
namespace Unheimlich.UI
{
public class DialogueScreenUI : MonoBehaviour
{
public static DialogueScreenUI Instance { get; private set; }
[Header("UI References")]
[SerializeField] private GameObject _dialoguePanel;
[SerializeField] private TextMeshProUGUI _speakerText;
[SerializeField] private TextMeshProUGUI _dialogueText;
[Header("Fade Settings")]
[SerializeField] private float _fadeOutDuration = 0.5f;
private CanvasGroup _canvasGroup;
private void Awake()
{
if (Instance != null && Instance != this)
{
Debug.LogWarning("[DialogueScreenUI] Multiple instances detected! Destroying duplicate.");
Destroy(gameObject);
return;
}
Instance = this;
if (_dialoguePanel != null)
{
_canvasGroup = _dialoguePanel.GetComponent<CanvasGroup>();
if (_canvasGroup == null)
_canvasGroup = _dialoguePanel.AddComponent<CanvasGroup>();
}
}
private void OnDestroy()
{
if (Instance == this)
Instance = null;
}
public void Show(string speaker, string dialogue)
{
if (_speakerText != null)
_speakerText.text = speaker;
if (_dialogueText != null)
_dialogueText.text = dialogue;
if (_dialoguePanel != null)
{
_dialoguePanel.SetActive(true);
if (_canvasGroup != null)
{
LeanTween.cancel(_dialoguePanel);
_canvasGroup.alpha = 1f;
}
}
}
public void Hide()
{
if (_dialoguePanel != null)
_dialoguePanel.SetActive(false);
}
public void FadeOutAndHide(System.Action onComplete = null)
{
if (_canvasGroup != null && _dialoguePanel != null)
{
LeanTween.cancel(_dialoguePanel);
LeanTween.alphaCanvas(_canvasGroup, 0f, _fadeOutDuration)
.setOnComplete(() =>
{
Hide();
onComplete?.Invoke();
});
}
else
{
Hide();
onComplete?.Invoke();
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: bc4c44e24ba19844b822608a46bde428
+65 -22
View File
@@ -1,11 +1,9 @@
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
using TMPro;
using Unheimlich.Data.Dialogues;
using Unheimlich.Gameplay.Player;
using Unheimlich.Interactions.FloatingWords;
using Unheimlich.Interactions.Dialogues;
namespace Unheimlich.UI
{
@@ -15,11 +13,11 @@ namespace Unheimlich.UI
/// </summary>
public class DialogueUI : MonoBehaviour
{
[Header("Panels")]
[Header("Panels - World Mode")]
[SerializeField] private GameObject _dialoguePanel;
public GameObject ChoiceContainer;
[Header("Text Fields")]
[Header("Text Fields - World Mode")]
[SerializeField] private TextMeshProUGUI _speakerText;
[SerializeField] private TextMeshProUGUI _contentText;
@@ -43,6 +41,8 @@ namespace Unheimlich.UI
private float _sortTimer = 0f;
private const float SORT_INTERVAL = 0.1f;
private DialogueDisplayMode _currentDisplayMode = DialogueDisplayMode.World;
private static readonly Vector3 ViewportCenter = new Vector3(0.5f, 0.5f, 0f);
private void Awake()
@@ -76,42 +76,85 @@ namespace Unheimlich.UI
SortChoicesByDistance();
}
public void ShowDialogueBox(string speaker, string content)
public void ShowDialogueBox(string speaker, string content, DialogueDisplayMode displayMode = DialogueDisplayMode.World)
{
_speakerText.text = speaker;
_contentText.text = content;
_dialoguePanel.SetActive(true);
_currentDisplayMode = displayMode;
if (_dialogueCanvasGroup != null)
if (displayMode == DialogueDisplayMode.World)
{
LeanTween.cancel(_dialoguePanel);
_dialogueCanvasGroup.alpha = 1f;
if (DialogueScreenUI.Instance != null)
DialogueScreenUI.Instance.Hide();
_speakerText.text = speaker;
_contentText.text = content;
_dialoguePanel.SetActive(true);
if (_dialogueCanvasGroup != null)
{
LeanTween.cancel(_dialoguePanel);
_dialogueCanvasGroup.alpha = 1f;
}
}
else
{
if (_dialoguePanel != null)
_dialoguePanel.SetActive(false);
if (DialogueScreenUI.Instance != null)
DialogueScreenUI.Instance.Show(speaker, content);
else
Debug.LogWarning("[DialogueUI] DialogueScreenUI.Instance is null! Make sure DialogueScreenUI component exists in the scene.");
}
}
public void HideDialogueBox()
{
_dialoguePanel.SetActive(false);
if (_dialoguePanel != null)
_dialoguePanel.SetActive(false);
if (DialogueScreenUI.Instance != null)
DialogueScreenUI.Instance.Hide();
ChoiceContainer.SetActive(false);
HideChoices();
}
public void FadeOutAndHide(System.Action onComplete = null)
{
if (_dialogueCanvasGroup != null)
if (_currentDisplayMode == DialogueDisplayMode.World)
{
LeanTween.cancel(_dialoguePanel);
LeanTween.alphaCanvas(_dialogueCanvasGroup, 0f, _fadeOutDuration)
.setOnComplete(() =>
{
HideDialogueBox();
onComplete?.Invoke();
});
if (_dialogueCanvasGroup != null && _dialoguePanel != null)
{
LeanTween.cancel(_dialoguePanel);
LeanTween.alphaCanvas(_dialogueCanvasGroup, 0f, _fadeOutDuration)
.setOnComplete(() =>
{
HideDialogueBox();
onComplete?.Invoke();
});
}
else
{
HideDialogueBox();
onComplete?.Invoke();
}
}
else
{
HideDialogueBox();
onComplete?.Invoke();
if (DialogueScreenUI.Instance != null)
{
DialogueScreenUI.Instance.FadeOutAndHide(() =>
{
HideChoices();
ChoiceContainer.SetActive(false);
onComplete?.Invoke();
});
}
else
{
HideDialogueBox();
onComplete?.Invoke();
}
}
}