feat : add SubtitleSequencePlayer and SubtitleTriggerZone for managing subtitle playback
This commit is contained in:
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a315c9c77ad8a49238033181ece48806
|
||||
@@ -1,13 +1,13 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
/// <summary>
|
||||
/// Auto-spawns in Level01 and displays an intro subtitle sequence.
|
||||
/// Reusable subtitle player that renders and plays subtitle lines loaded from JSON.
|
||||
/// </summary>
|
||||
public class Level01IntroSubtitles : MonoBehaviour
|
||||
public class SubtitleSequencePlayer : MonoBehaviour
|
||||
{
|
||||
[System.Serializable]
|
||||
[Serializable]
|
||||
private struct SubtitleLine
|
||||
{
|
||||
public string speaker;
|
||||
@@ -15,17 +15,15 @@ public class Level01IntroSubtitles : MonoBehaviour
|
||||
public float duration;
|
||||
}
|
||||
|
||||
[Header("Trigger")]
|
||||
[SerializeField] private string targetSceneName = "Level01";
|
||||
[SerializeField] private float initialDelay = 2.5f;
|
||||
|
||||
[Header("Subtitle Sequence")]
|
||||
[SerializeField] private SubtitleLine[] lines =
|
||||
[Serializable]
|
||||
private struct SubtitleFile
|
||||
{
|
||||
new SubtitleLine { speaker = "SYSTEME", text = "...Ici, quelque chose cloche.", duration = 2.5f },
|
||||
new SubtitleLine { speaker = "SYSTEME", text = "Reste calme. Observe la piece.", duration = 2.5f },
|
||||
new SubtitleLine { speaker = "SYSTEME", text = "Trouve une sortie.", duration = 2.2f },
|
||||
};
|
||||
public SubtitleLine[] lines;
|
||||
}
|
||||
|
||||
[Header("Optional Default Data")]
|
||||
[Tooltip("Used only if trigger zone calls PlayDefault().")]
|
||||
[SerializeField] private TextAsset defaultSubtitleJson;
|
||||
|
||||
[SerializeField] private float typewriterCharsPerSecond = 40f;
|
||||
[SerializeField] private float fadeDuration = 0.2f;
|
||||
@@ -40,58 +38,55 @@ public class Level01IntroSubtitles : MonoBehaviour
|
||||
[SerializeField] private Color speakerColor = new Color(1f, 0.85f, 0.35f, 1f);
|
||||
[SerializeField] private Color backgroundColor = new Color(0f, 0f, 0f, 0.62f);
|
||||
|
||||
private static bool s_bootstrapped;
|
||||
|
||||
private string m_currentSpeaker;
|
||||
private string m_currentText;
|
||||
private GUIStyle m_textStyle;
|
||||
private GUIStyle m_speakerStyle;
|
||||
private Texture2D m_background;
|
||||
private bool m_isShowing;
|
||||
private bool m_isPlaying;
|
||||
private float m_alpha;
|
||||
private SubtitleLine[] m_runtimeLines = Array.Empty<SubtitleLine>();
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
private static void ResetBootstrapFlag()
|
||||
public bool IsPlaying => m_isPlaying;
|
||||
|
||||
public bool TryPlay(TextAsset subtitleJson, float initialDelay = 0f)
|
||||
{
|
||||
s_bootstrapped = false;
|
||||
if (m_isPlaying)
|
||||
return false;
|
||||
|
||||
if (!TryReadLinesFromJson(subtitleJson, out SubtitleLine[] parsedLines))
|
||||
return false;
|
||||
|
||||
m_runtimeLines = parsedLines;
|
||||
StartCoroutine(PlaySequence(initialDelay));
|
||||
return true;
|
||||
}
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
||||
private static void Bootstrap()
|
||||
public bool PlayDefault(float initialDelay = 0f)
|
||||
{
|
||||
if (s_bootstrapped)
|
||||
return;
|
||||
|
||||
s_bootstrapped = true;
|
||||
|
||||
GameObject go = new GameObject(nameof(Level01IntroSubtitles));
|
||||
go.hideFlags = HideFlags.DontSave;
|
||||
go.AddComponent<Level01IntroSubtitles>();
|
||||
return TryPlay(defaultSubtitleJson, initialDelay);
|
||||
}
|
||||
|
||||
private void Start()
|
||||
private IEnumerator PlaySequence(float initialDelay)
|
||||
{
|
||||
Scene activeScene = SceneManager.GetActiveScene();
|
||||
if (activeScene.name != targetSceneName)
|
||||
m_isPlaying = true;
|
||||
|
||||
if (m_runtimeLines == null || m_runtimeLines.Length == 0)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
m_isPlaying = false;
|
||||
yield break;
|
||||
}
|
||||
|
||||
StartCoroutine(PlaySequence());
|
||||
}
|
||||
|
||||
private IEnumerator PlaySequence()
|
||||
{
|
||||
if (initialDelay > 0f)
|
||||
yield return new WaitForSeconds(initialDelay);
|
||||
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
for (int i = 0; i < m_runtimeLines.Length; i++)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(lines[i].text) || lines[i].duration <= 0f)
|
||||
if (string.IsNullOrWhiteSpace(m_runtimeLines[i].text) || m_runtimeLines[i].duration <= 0f)
|
||||
continue;
|
||||
|
||||
yield return StartCoroutine(ShowLine(lines[i]));
|
||||
yield return StartCoroutine(ShowLine(m_runtimeLines[i]));
|
||||
|
||||
if (gapBetweenLines > 0f)
|
||||
yield return new WaitForSeconds(gapBetweenLines);
|
||||
@@ -99,7 +94,31 @@ public class Level01IntroSubtitles : MonoBehaviour
|
||||
|
||||
m_currentSpeaker = string.Empty;
|
||||
m_currentText = string.Empty;
|
||||
Destroy(gameObject);
|
||||
m_isPlaying = false;
|
||||
}
|
||||
|
||||
private bool TryReadLinesFromJson(TextAsset subtitleJson, out SubtitleLine[] parsedLines)
|
||||
{
|
||||
parsedLines = Array.Empty<SubtitleLine>();
|
||||
|
||||
if (subtitleJson == null || string.IsNullOrWhiteSpace(subtitleJson.text))
|
||||
return false;
|
||||
|
||||
SubtitleFile file;
|
||||
try
|
||||
{
|
||||
file = JsonUtility.FromJson<SubtitleFile>(subtitleJson.text);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.lines == null || file.lines.Length == 0)
|
||||
return false;
|
||||
|
||||
parsedLines = file.lines;
|
||||
return true;
|
||||
}
|
||||
|
||||
private IEnumerator ShowLine(SubtitleLine line)
|
||||
@@ -233,7 +252,6 @@ public class Level01IntroSubtitles : MonoBehaviour
|
||||
clipping = TextClipping.Clip,
|
||||
};
|
||||
m_speakerStyle.normal.textColor = speakerColor;
|
||||
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
2
Assets/Code/Scripts/Level/SubtitleSequencePlayer.cs.meta
Normal file
2
Assets/Code/Scripts/Level/SubtitleSequencePlayer.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4947743d7bc9b4589b9932d429517d3a
|
||||
51
Assets/Code/Scripts/Level/SubtitleTriggerZone.cs
Normal file
51
Assets/Code/Scripts/Level/SubtitleTriggerZone.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Trigger zone that starts a subtitle JSON sequence on a linked SubtitleSequencePlayer.
|
||||
/// Put this on the zone collider object, and link the player on your empty object.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Collider))]
|
||||
public class SubtitleTriggerZone : MonoBehaviour
|
||||
{
|
||||
[Header("References")]
|
||||
[SerializeField] private SubtitleSequencePlayer subtitlePlayer;
|
||||
[SerializeField] private TextAsset subtitleJson;
|
||||
|
||||
[Header("Playback")]
|
||||
[SerializeField] private float initialDelay = 0f;
|
||||
[SerializeField] private bool oneShot = true;
|
||||
|
||||
private bool m_hasPlayed;
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
Collider col = GetComponent<Collider>();
|
||||
col.isTrigger = true;
|
||||
}
|
||||
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
if (!IsPlayer(other))
|
||||
return;
|
||||
|
||||
if (oneShot && m_hasPlayed)
|
||||
return;
|
||||
|
||||
if (subtitlePlayer == null)
|
||||
return;
|
||||
|
||||
if (subtitlePlayer.TryPlay(subtitleJson, initialDelay))
|
||||
m_hasPlayed = true;
|
||||
}
|
||||
|
||||
private bool IsPlayer(Collider other)
|
||||
{
|
||||
if (other.CompareTag("Player"))
|
||||
return true;
|
||||
|
||||
if (other.GetComponentInParent<PlayerMovement>() != null)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
2
Assets/Code/Scripts/Level/SubtitleTriggerZone.cs.meta
Normal file
2
Assets/Code/Scripts/Level/SubtitleTriggerZone.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33a33c65f75e2443383c2e29bd6bf5f1
|
||||
Reference in New Issue
Block a user