타이틀 타이밍

This commit is contained in:
2026-07-13 11:25:44 +09:00
parent 33f2c13223
commit c30a74c9c3
19 changed files with 1557 additions and 4 deletions

View File

@@ -0,0 +1,133 @@
using System.Collections;
using UnityEngine;
[RequireComponent(typeof(AudioSource))]
public class IntroBGMController : MonoBehaviour
{
[Header("Audio Source")]
[SerializeField] private AudioSource audioSource;
[Header("Start Timing")]
[SerializeField] private bool playOnStart = true;
[SerializeField] private float startDelay = 0.5f;
[Header("Fade In")]
[SerializeField] private bool useFadeIn = true;
[SerializeField] private float fadeInDuration = 2.0f;
[Range(0f, 1f)]
[SerializeField] private float targetVolume = 0.55f;
[Header("Loop")]
[SerializeField] private bool loop = true;
private Coroutine bgmRoutine;
private void Awake()
{
if (audioSource == null)
{
audioSource = GetComponent<AudioSource>();
}
audioSource.playOnAwake = false;
audioSource.loop = loop;
audioSource.volume = 0f;
audioSource.spatialBlend = 0f;
}
private void Start()
{
if (playOnStart)
{
PlayBGM();
}
}
public void PlayBGM()
{
if (audioSource == null || audioSource.clip == null)
{
Debug.LogWarning("[IntroBGMController] AudioSource 또는 AudioClip이 없습니다.");
return;
}
if (bgmRoutine != null)
{
StopCoroutine(bgmRoutine);
}
bgmRoutine = StartCoroutine(PlayBGMRoutine());
}
private IEnumerator PlayBGMRoutine()
{
if (startDelay > 0f)
{
yield return new WaitForSeconds(startDelay);
}
audioSource.volume = useFadeIn ? 0f : targetVolume;
audioSource.loop = loop;
audioSource.Play();
if (useFadeIn)
{
float elapsed = 0f;
while (elapsed < fadeInDuration)
{
elapsed += Time.deltaTime;
float t = Mathf.Clamp01(elapsed / fadeInDuration);
float easedT = EaseOutCubic(t);
audioSource.volume = Mathf.Lerp(0f, targetVolume, easedT);
yield return null;
}
audioSource.volume = targetVolume;
}
bgmRoutine = null;
}
public void StopBGM(float fadeOutDuration = 1.0f)
{
if (audioSource == null)
return;
if (bgmRoutine != null)
{
StopCoroutine(bgmRoutine);
}
bgmRoutine = StartCoroutine(StopBGMRoutine(fadeOutDuration));
}
private IEnumerator StopBGMRoutine(float fadeOutDuration)
{
float startVolume = audioSource.volume;
float elapsed = 0f;
while (elapsed < fadeOutDuration)
{
elapsed += Time.deltaTime;
float t = Mathf.Clamp01(elapsed / fadeOutDuration);
float easedT = EaseOutCubic(t);
audioSource.volume = Mathf.Lerp(startVolume, 0f, easedT);
yield return null;
}
audioSource.volume = 0f;
audioSource.Stop();
bgmRoutine = null;
}
private float EaseOutCubic(float t)
{
return 1f - Mathf.Pow(1f - t, 3f);
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 653843badaadbba42a66bae2168ac836

View File

@@ -0,0 +1,222 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class IntroTitleSequence : MonoBehaviour
{
[System.Serializable]
public class IntroSequenceItem
{
[Header("Info")]
public string itemName;
[Header("Target")]
public GameObject target;
[Header("Timing")]
public float delay = 0f;
public float fadeDuration = 0.6f;
[Header("Appear Motion")]
[Range(0.1f, 1f)]
public float startScaleRatio = 0.92f;
public Vector3 startOffset = new Vector3(0f, -0.08f, 0f);
[Header("After Appear")]
public bool enableFloatingAfterAppear = true;
public bool enableParallaxAfterAppear = true;
public bool enableButtonPulseAfterAppear = false;
}
[Header("Sequence")]
public bool playOnStart = true;
public float globalDelayMultiplier = 1f;
public float globalFadeMultiplier = 1f;
public List<IntroSequenceItem> sequenceItems = new List<IntroSequenceItem>();
[Header("Optional Particle")]
public ParticleSystem petalParticle;
public float petalStartDelay = 0.2f;
private readonly Dictionary<GameObject, Vector3> originalPositions = new Dictionary<GameObject, Vector3>();
private readonly Dictionary<GameObject, Vector3> originalScales = new Dictionary<GameObject, Vector3>();
private void Start()
{
if (playOnStart)
{
PlaySequence();
}
}
public void PlaySequence()
{
StopAllCoroutines();
CacheOriginalTransforms();
PrepareAllItems();
StartCoroutine(PlaySequenceRoutine());
}
private void CacheOriginalTransforms()
{
originalPositions.Clear();
originalScales.Clear();
foreach (IntroSequenceItem item in sequenceItems)
{
if (item == null || item.target == null)
continue;
originalPositions[item.target] = item.target.transform.localPosition;
originalScales[item.target] = item.target.transform.localScale;
}
}
private void PrepareAllItems()
{
foreach (IntroSequenceItem item in sequenceItems)
{
if (item == null || item.target == null)
continue;
GameObject target = item.target;
target.SetActive(true);
Vector3 originalPosition = originalPositions[target];
Vector3 originalScale = originalScales[target];
target.transform.localPosition = originalPosition + item.startOffset;
target.transform.localScale = originalScale * item.startScaleRatio;
SetTargetAlpha(target, 0f);
SetOptionalMotionScripts(target, false, false, false);
}
}
private IEnumerator PlaySequenceRoutine()
{
if (petalParticle != null)
{
petalParticle.Stop();
if (petalStartDelay > 0f)
yield return new WaitForSeconds(petalStartDelay);
petalParticle.Play();
}
foreach (IntroSequenceItem item in sequenceItems)
{
if (item == null || item.target == null)
continue;
StartCoroutine(AppearRoutine(item));
}
}
private IEnumerator AppearRoutine(IntroSequenceItem item)
{
float delay = Mathf.Max(0f, item.delay * globalDelayMultiplier);
float duration = Mathf.Max(0.01f, item.fadeDuration * globalFadeMultiplier);
if (delay > 0f)
yield return new WaitForSeconds(delay);
GameObject target = item.target;
Vector3 originalPosition = originalPositions[target];
Vector3 originalScale = originalScales[target];
Vector3 startPosition = originalPosition + item.startOffset;
Vector3 startScale = originalScale * item.startScaleRatio;
target.transform.localPosition = startPosition;
target.transform.localScale = startScale;
SetTargetAlpha(target, 0f);
float elapsed = 0f;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
float t = Mathf.Clamp01(elapsed / duration);
float easedT = EaseOutCubic(t);
SetTargetAlpha(target, easedT);
target.transform.localPosition = Vector3.Lerp(startPosition, originalPosition, easedT);
target.transform.localScale = Vector3.Lerp(startScale, originalScale, easedT);
yield return null;
}
SetTargetAlpha(target, 1f);
target.transform.localPosition = originalPosition;
target.transform.localScale = originalScale;
SetOptionalMotionScripts(
target,
item.enableFloatingAfterAppear,
item.enableParallaxAfterAppear,
item.enableButtonPulseAfterAppear
);
}
private void SetTargetAlpha(GameObject target, float alpha)
{
SpriteRenderer[] spriteRenderers = target.GetComponentsInChildren<SpriteRenderer>(true);
foreach (SpriteRenderer sr in spriteRenderers)
{
Color color = sr.color;
color.a = alpha;
sr.color = color;
}
Graphic[] graphics = target.GetComponentsInChildren<Graphic>(true);
foreach (Graphic graphic in graphics)
{
Color color = graphic.color;
color.a = alpha;
graphic.color = color;
}
CanvasGroup[] canvasGroups = target.GetComponentsInChildren<CanvasGroup>(true);
foreach (CanvasGroup canvasGroup in canvasGroups)
{
canvasGroup.alpha = alpha;
}
}
private void SetOptionalMotionScripts(GameObject target, bool floating, bool parallax, bool buttonPulse)
{
IntroFloatingLayer[] floatingLayers = target.GetComponentsInChildren<IntroFloatingLayer>(true);
foreach (IntroFloatingLayer layer in floatingLayers)
{
layer.enabled = floating;
}
IntroParallaxLayer[] parallaxLayers = target.GetComponentsInChildren<IntroParallaxLayer>(true);
foreach (IntroParallaxLayer layer in parallaxLayers)
{
layer.enabled = parallax;
}
IntroButtonPulse[] buttonPulses = target.GetComponentsInChildren<IntroButtonPulse>(true);
foreach (IntroButtonPulse pulse in buttonPulses)
{
pulse.enabled = buttonPulse;
}
}
private float EaseOutCubic(float t)
{
return 1f - Mathf.Pow(1f - t, 3f);
}
}

View File

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