Files
Dino_Love_Simulation/Assets/02_Scripts/Communication/Dialog/CharacterExpressionCustom.cs
2026-07-27 14:38:25 +09:00

113 lines
4.3 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
// 표정에 따라 캐릭터의 "커스텀 연출"을 켜고 끄는 범용 컴포넌트. 캐릭터 루트에 붙인다.
// 눈 하이라이트, 볼 홍조, 땀방울, 눈동자 색 등 표정별로 달라지는 것들을 Key로 등록해 두고,
// ExpressionData.CustomKeys에 그 Key를 넣으면 그 표정이 재생되는 동안 활성화된다. (DialogPlayer가 호출)
//
// 각 Effect는 아래 중 필요한 것만 채운다 (여러 개 동시 가능):
// - 텍스처 스왑 : Texture Target + Texture Property + Active/Inactive Texture (MaterialPropertyBlock — 가볍고 원본 머티리얼 안 건드림)
// - 오브젝트 on/off : Toggle Object (+ Invert)
// - 그 외 무엇이든 : On Set (UnityEvent<bool>) — 활성 시 true, 비활성 시 false
public class CharacterExpressionCustom : MonoBehaviour
{
[Serializable] public class BoolEvent : UnityEvent<bool> { }
[Serializable]
public class Effect
{
[Tooltip("표정(ExpressionData.CustomKeys)에서 참조할 키. 예: eyeHighlightOff, blush, sweat")]
public string Key;
[Header("텍스처 스왑 (선택)")]
[Tooltip("텍스처를 바꿀 렌더러. 비우면 텍스처 스왑 안 함")]
public Renderer TextureTarget;
[Tooltip("바꿀 텍스처 프로퍼티. 이 프로젝트 BMAC_Toon 알베도=_Texture2D (URP Lit=_BaseMap)")]
public string TextureProperty = "_Texture2D";
[Tooltip("활성 시 텍스처. 비우면 원본 유지")]
public Texture ActiveTexture;
[Tooltip("비활성(기본) 시 텍스처. 비우면 시작 시점의 원본으로 복원")]
public Texture InactiveTexture;
[Header("오브젝트 on/off (선택)")]
public GameObject ToggleObject;
[Tooltip("켜면 반전 — 활성 시 오브젝트를 '끈다' (예: 하이라이트 오브젝트 숨기기)")]
public bool Invert;
[Header("그 외 (선택)")]
[Tooltip("활성 시 true, 비활성 시 false로 호출 — 인스펙터에서 무엇이든 연결")]
public BoolEvent OnSet;
// 런타임 내부 상태
[NonSerialized] public bool Active;
[NonSerialized] public bool Captured;
[NonSerialized] public int PropId;
[NonSerialized] public Texture CapturedDefault; // 시작 시점의 원본 텍스처
}
[SerializeField] private List<Effect> _effects = new();
private MaterialPropertyBlock _mpb;
private void Awake()
{
_mpb = new MaterialPropertyBlock();
foreach (var e in _effects)
Capture(e);
}
private static void Capture(Effect e)
{
if (e.Captured) return;
e.PropId = Shader.PropertyToID(e.TextureProperty);
if (e.TextureTarget != null && e.TextureTarget.sharedMaterial != null)
e.CapturedDefault = e.TextureTarget.sharedMaterial.GetTexture(e.PropId);
e.Captured = true;
}
// 주어진 Key 집합에 맞춰 모든 Effect를 켜고 끈다 (집합에 없는 Effect는 비활성/기본으로).
public void SetActiveKeys(ICollection<string> keys)
{
foreach (var e in _effects)
{
bool want = keys != null && keys.Count > 0
&& !string.IsNullOrEmpty(e.Key) && keys.Contains(e.Key);
Apply(e, want);
}
}
// 전부 기본(비활성)으로 되돌린다 — 대화 종료 시
public void ResetAll()
{
foreach (var e in _effects)
Apply(e, false);
}
private void Apply(Effect e, bool active)
{
Capture(e);
if (e.Active == active) return; // 상태 동일하면 스킵
e.Active = active;
// 텍스처 스왑
if (e.TextureTarget != null)
{
var tex = active
? (e.ActiveTexture != null ? e.ActiveTexture : e.CapturedDefault)
: (e.InactiveTexture != null ? e.InactiveTexture : e.CapturedDefault);
e.TextureTarget.GetPropertyBlock(_mpb); // 기존 MPB 값 보존
_mpb.SetTexture(e.PropId, tex);
e.TextureTarget.SetPropertyBlock(_mpb);
}
// 오브젝트 on/off
if (e.ToggleObject != null)
e.ToggleObject.SetActive(active ^ e.Invert);
// 그 외
e.OnSet?.Invoke(active);
}
}