2026-07-10 대화 BGM,이펙트

This commit is contained in:
2026-07-10 17:27:11 +09:00
parent bda7752fb3
commit 932aad8c56
26 changed files with 1984 additions and 11 deletions

View File

@@ -25,6 +25,9 @@ public class DialogCondition
[Tooltip("밟으면 이 대화가 비활성화되는 이벤트존 Id들 (하나라도 밟았으면 비활성화)")]
public List<string> ExcludedZoneIds = new();
[Tooltip("켜져 있어야 하는 StoryTrigger Id들 (전부 필요). 예: 계약서 서명 트리거")]
public List<string> RequiredTriggerIds = new();
// affectionTarget: 호감도 조건을 검사할 캐릭터 (보통 대화를 거는 NPC 자신)
public bool IsMet(CharacterData affectionTarget)
{
@@ -52,6 +55,10 @@ public bool IsMet(CharacterData affectionTarget)
if (!string.IsNullOrEmpty(zoneId) && story.HasTriggeredZone(zoneId))
return false;
foreach (var triggerId in RequiredTriggerIds)
if (!string.IsNullOrEmpty(triggerId) && !StoryTrigger.IsActive(triggerId))
return false;
return true;
}
}

View File

@@ -0,0 +1,55 @@
using System.Collections.Generic;
using UnityEngine;
// 씬별 BGM 관리 (이벤트 구동). 씬마다 하나 배치한다.
// - 씬 시작 시 Default Bgm 재생
// - EventZone이 발동하거나 StoryTrigger가 켜지는 순간, 그 Id에 매핑된 곡이 있으면 교체 (크로스페이드)
// - 매핑에 없는 Id는 무시. 나중에 발동한 항목이 이긴다
// - 대화 전용 BGM(Override) 재생 중이면 곡은 보류됐다가 대화가 끝나면 반영된다
public class SceneBgm : MonoBehaviour
{
[System.Serializable]
public class BgmEntry
{
[Tooltip("EventZone의 Zone Id 또는 StoryTrigger의 Trigger Id")]
public string Id;
[Tooltip("해당 Id 발동 시 재생할 BGM. 비우면 무음")]
public AudioClip Clip;
}
[Tooltip("씬 시작 시 재생할 기본 BGM. 비우면 무음")]
[SerializeField] private AudioClip _defaultBgm;
[Tooltip("존/트리거 Id → BGM 매핑")]
[SerializeField] private List<BgmEntry> _entries = new();
private void Start()
{
if (SoundManager.Instance != null)
SoundManager.Instance.SetDefaultBGM(_defaultBgm);
}
private void OnEnable()
{
EventZone.OnZoneTriggered += HandleId;
StoryTrigger.OnActivated += HandleId;
}
private void OnDisable()
{
EventZone.OnZoneTriggered -= HandleId;
StoryTrigger.OnActivated -= HandleId;
}
private void HandleId(string id)
{
foreach (var entry in _entries)
{
if (entry.Id != id) continue;
if (SoundManager.Instance != null)
SoundManager.Instance.SetDefaultBGM(entry.Clip);
return;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 3a5362edc262af5439945354ddc95802

View File

@@ -40,7 +40,7 @@ private void Awake()
}
else
{
Destroy(gameObject); //이미 인스턴스가 있으면 자신을 파괴
Destroy(gameObject); //이미 인스턴스가 있으면 자신을 파괴 (씬마다 놓아도 첫 번째만 남음)
}
}
@@ -72,6 +72,7 @@ private void Start()
//기본 BGM 교체 (씬/분위기 전환용). 전용 BGM 재생 중이면 곡은 유지되고 복귀 시 반영됨.
public void SetDefaultBGM(AudioClip clip)
{
if (_defaultBgm == clip) return; //같은 곡이면 페이드 재시작 없이 유지
_defaultBgm = clip;
if (_overrideBgm == null)
StartBgmChange(clip);

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
@@ -29,6 +30,9 @@ public class EventZone : MonoBehaviour
public static bool IsAnyPlaying => _playingCount > 0;
private static int _playingCount;
// 존이 실제로 발동(기록)되는 순간 ZoneId와 함께 호출 (SceneBgm 등에서 구독)
public static event Action<string> OnZoneTriggered;
public string ZoneId => string.IsNullOrEmpty(_zoneId) ? name : _zoneId;
// 잠글 때 켜져 있던 프로바이더만 기록해서, 해제 시 원래 꺼져 있던 것까지 켜지 않도록 한다.
@@ -52,6 +56,7 @@ private void OnTriggerEnter(Collider other)
if (story != null)
story.RecordZoneTriggered(ZoneId);
_hasPlayed = true;
OnZoneTriggered?.Invoke(ZoneId);
if (_timeline == null)
{

View File

@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using UnityEngine;
// 씬에 배치하는 수동 트리거. Activate()가 호출되면 켜지고,
// DialogCondition의 RequiredTriggerIds에서 Id로 조회해 대화 활성화 조건으로 쓴다.
// 예: 계약서 서명(DrawablePaper.OnSigned) → 이 트리거 Activate → 서명 후 대화 활성화.
//
// 주의: 세이브에 기록되지 않는 런타임 상태다 (오브젝트가 비활성/파괴되면 꺼짐).
// 영구히 남아야 하는 진행은 기존처럼 대화 완료 기록/선택지 Code를 쓸 것.
public class StoryTrigger : MonoBehaviour
{
[Tooltip("조건에서 참조할 Id. 비워두면 오브젝트 이름 사용")]
[SerializeField] private string _triggerId;
[Tooltip("시작부터 켜진 상태로 둘지")]
[SerializeField] private bool _startActivated;
public string TriggerId => string.IsNullOrEmpty(_triggerId) ? name : _triggerId;
public bool IsActivated { get; private set; }
private static readonly Dictionary<string, StoryTrigger> _registry = new();
// 트리거가 꺼짐→켜짐으로 바뀌는 순간 TriggerId와 함께 호출 (SceneBgm 등에서 구독)
public static event Action<string> OnActivated;
private void Awake()
{
IsActivated = _startActivated;
}
private void OnEnable() => _registry[TriggerId] = this;
private void OnDisable() => _registry.Remove(TriggerId);
// UnityEvent에서 호출 (예: DrawablePaper.OnSigned)
public void Activate()
{
if (IsActivated) return;
IsActivated = true;
OnActivated?.Invoke(TriggerId);
}
public void Deactivate() => IsActivated = false;
// 해당 Id의 트리거가 씬에 있고 켜져 있는가 (DialogCondition에서 사용)
public static bool IsActive(string id)
=> _registry.TryGetValue(id, out var trigger) && trigger.IsActivated;
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 3dd49c6ec99ea3543addd745e4737dbc