From 4a15565ec8c1ef8dc99be27d139bf6096a72186d Mon Sep 17 00:00:00 2001 From: WooSH Date: Thu, 14 Aug 2025 23:35:38 +0900 Subject: [PATCH 01/13] =?UTF-8?q?docs=20:=20=ED=8C=8C=EC=9D=BC=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=20=EB=AC=B8=EC=84=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Docs/ProjectVG_Structure_Guide.md | 100 ++++++++++++++++++ Assets/Docs/ProjectVG_Structure_Guide.md.meta | 7 ++ 2 files changed, 107 insertions(+) create mode 100644 Assets/Docs/ProjectVG_Structure_Guide.md create mode 100644 Assets/Docs/ProjectVG_Structure_Guide.md.meta diff --git a/Assets/Docs/ProjectVG_Structure_Guide.md b/Assets/Docs/ProjectVG_Structure_Guide.md new file mode 100644 index 0000000..7f0b643 --- /dev/null +++ b/Assets/Docs/ProjectVG_Structure_Guide.md @@ -0,0 +1,100 @@ + +# ๐Ÿ“ ProjectVG Unity ํด๋ผ์ด์–ธํŠธ ๊ตฌ์กฐ ๊ฐ€์ด๋“œ + +์ด ๋ฌธ์„œ๋Š” `Assets/` ๋””๋ ‰ํ† ๋ฆฌ ๊ธฐ์ค€์˜ Unity ํด๋ผ์ด์–ธํŠธ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ ๋ฐ ๋””๋ ‰ํ† ๋ฆฌ ์šฉ๋„/๋ช…๋ช… ๊ทœ์น™์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. + +--- + +## ๐Ÿ—‚ ์ „์ฒด ๊ตฌ์กฐ + +``` +Assets/ +โ”œโ”€โ”€ App/ # ์ง„์ž…์ , ์ „์ฒด ์•ฑ ์„ค์ • +โ”‚ โ”œโ”€โ”€ Scenes/ # ๋ฉ”์ธ ์”ฌ, ๋กœ๋”ฉ ์”ฌ ๋“ฑ +โ”‚ โ””โ”€โ”€ App.cs # ์•ฑ ์ดˆ๊ธฐํ™”/์—”ํŠธ๋ฆฌ ์ง„์ž… + +โ”œโ”€โ”€ Core/ # ์ „์—ญ ๊ณตํ†ต ๊ธฐ๋Šฅ +โ”‚ โ”œโ”€โ”€ Input/ # ์ž…๋ ฅ ์ฒ˜๋ฆฌ (Touch, Mouse, Key ๋“ฑ) +โ”‚ โ”œโ”€โ”€ Audio/ # BGM, SFX, ์˜ค๋””์˜ค ๋งค๋‹ˆ์ € +โ”‚ โ”œโ”€โ”€ Localization/ # ๋‹ค๊ตญ์–ด ์ง€์› +โ”‚ โ”œโ”€โ”€ Time/ # ์‹œ๊ฐ„ ์œ ํ‹ธ (ํƒ€์ด๋จธ, ๋”œ๋ ˆ์ด ๋“ฑ) +โ”‚ โ”œโ”€โ”€ Extensions/ # Unity, LINQ, System ํ™•์žฅ ๋ฉ”์„œ๋“œ +โ”‚ โ””โ”€โ”€ Utils/ # ๋ฒ”์šฉ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค + +โ”œโ”€โ”€ Infrastructure/ # ์™ธ๋ถ€ ์„œ๋น„์Šค, ์ €์žฅ์†Œ, ๋„คํŠธ์›Œํฌ +โ”‚ โ”œโ”€โ”€ Data/ # ๋กœ์ปฌ/ํด๋ผ์šฐ๋“œ ์ €์žฅ์†Œ (SaveData, PlayerPrefs) +โ”‚ โ”œโ”€โ”€ Network/ # ์„œ๋ฒ„ API ํ˜ธ์ถœ, Response DTO +โ”‚ โ””โ”€โ”€ Bridge/ # Native, ์™ธ๋ถ€ SDK ์—ฐ๋™ (์˜ˆ: Android, iOS ๊ธฐ๋Šฅ) + +โ”œโ”€โ”€ Domain/ # ๋„๋ฉ”์ธ(๊ธฐ๋Šฅ)๋ณ„ ๋ฌถ์Œ +โ”‚ โ”œโ”€โ”€ Character/ +โ”‚ โ”‚ โ”œโ”€โ”€ Model/ +โ”‚ โ”‚ โ”œโ”€โ”€ View/ +โ”‚ โ”‚ โ”œโ”€โ”€ Script/ +โ”‚ โ”‚ โ””โ”€โ”€ Animation/ +โ”‚ โ”œโ”€โ”€ Chat/ +โ”‚ โ”‚ โ”œโ”€โ”€ Script/ +โ”‚ โ”‚ โ”œโ”€โ”€ View/ +โ”‚ โ”‚ โ””โ”€โ”€ Model/ +โ”‚ โ”œโ”€โ”€ Popup/ +โ”‚ โ”‚ โ””โ”€โ”€ System/ # ํŒ์—… ๋งค๋‹ˆ์ €, ํŒ์—… ํ +โ”‚ โ”‚ โ””โ”€โ”€ Instances/ # ์‹ค์ œ ํŒ์—… ํ”„๋ฆฌํŒน๋“ค +โ”‚ โ””โ”€โ”€ System/ # ๊ฒŒ์ž„ ๋กœ์ง ์ปจํŠธ๋กค๋Ÿฌ, FSM ๋“ฑ (์„ ํƒ) + +โ”œโ”€โ”€ UI/ # UI ๊ณตํ†ต ์š”์†Œ +โ”‚ โ”œโ”€โ”€ Prefabs/ # ๊ณต์šฉ UI ํ”„๋ฆฌํŒน (๋ฒ„ํŠผ, ์•„์ด์ฝ˜, ํˆดํŒ ๋“ฑ) +โ”‚ โ”œโ”€โ”€ Panels/ # HUD, ๋ฉ”์ธํŒจ๋„ ๋“ฑ +โ”‚ โ”œโ”€โ”€ Scripts/ # UI ์ƒํ˜ธ์ž‘์šฉ ์Šคํฌ๋ฆฝํŠธ +โ”‚ โ”œโ”€โ”€ Transitions/ # UI ์ „ํ™˜ ํšจ๊ณผ (ํŽ˜์ด๋“œ, ์ด๋™ ๋“ฑ) +โ”‚ โ””โ”€โ”€ Fonts/ # UI ํฐํŠธ + +โ”œโ”€โ”€ Resources/ # Resources.Load() ๋กœ๋“œ ๋Œ€์ƒ +โ”‚ โ”œโ”€โ”€ Configs/ # Json ๊ธฐ๋ฐ˜ ์„ค์ •ํŒŒ์ผ +โ”‚ โ””โ”€โ”€ AddressablesDummy/ # Addressable ์‚ฌ์šฉ ์•ˆ ํ•  ๋•Œ ๋Œ€์ฒด + +โ”œโ”€โ”€ Addressables/ # ์–ด๋“œ๋ ˆ์„œ๋ธ” ๊ด€๋ฆฌ์šฉ ๋ถ„๋ฆฌ ๋ฆฌ์†Œ์Šค (์„ ํƒ) +โ”‚ โ”œโ”€โ”€ UI/ +โ”‚ โ”œโ”€โ”€ Characters/ +โ”‚ โ””โ”€โ”€ Scenes/ + +โ”œโ”€โ”€ Plugins/ # ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ/Live2D SDK +โ”‚ โ””โ”€โ”€ Live2D/ +โ”‚ โ””โ”€โ”€ CubismSdkForUnity/ + +โ”œโ”€โ”€ Editor/ # ์ปค์Šคํ…€ ์—๋””ํ„ฐ ์ฝ”๋“œ (.cs๋งŒ ๊ฐ€๋Šฅ) +โ”‚ โ””โ”€โ”€ Inspectors/ +โ”‚ โ””โ”€โ”€ PropertyDrawers/ +โ”‚ โ””โ”€โ”€ MenuItems/ + +โ”œโ”€โ”€ Tests/ # ํ…Œ์ŠคํŠธ +โ”‚ โ”œโ”€โ”€ Editor/ # Editor Test +โ”‚ โ””โ”€โ”€ Runtime/ # PlayMode Test, ์œ ๋‹› ํ…Œ์ŠคํŠธ + +โ”œโ”€โ”€ Art/ # ๋””์ž์ธ ์›๋ณธ (PSD, AI ๋“ฑ) - ๋ฒ„์ „ ๊ด€๋ฆฌ ์ œ์™ธ ๊ฐ€๋Šฅ + +โ””โ”€โ”€ Docs/ # ๋ฌธ์„œ (์„ค๊ณ„, ํ๋ฆ„๋„, README ๋“ฑ) +``` + +--- + +## ๐Ÿ“Œ ๋„ค์ด๋ฐ ๊ฐ€์ด๋“œ ์š”์•ฝ + +- ๋””๋ ‰ํ† ๋ฆฌ ๋ฐ ํด๋ž˜์Šค๋ช…: **PascalCase** +- ๋ณ€์ˆ˜ ๋ฐ ๋ฉ”์„œ๋“œ: **camelCase** +- JSON ๋ฐ ์„ค์ •ํŒŒ์ผ: **snake_case** +- Addressable ํ‚ค: `/` ๊ตฌ๋ถ„, ์˜ˆ: `UI/PanelChat` +- UI ์˜ค๋ธŒ์ ํŠธ ์ ‘๋‘์–ด: `Panel`, `Btn`, `Txt`, `Img`, `Group` ๋“ฑ + +--- + +## โœ… ํ™œ์šฉ ์˜ˆ์‹œ + +- `Domain/Character/Model/` โ†’ Live2D `.moc3`, `.json`, ๋ชจ์…˜ ์„ค์ • +- `Core/Audio/AudioManager.cs` โ†’ SFX, BGM ์ „์—ญ ์ œ์–ด +- `Infrastructure/Network/ApiClient.cs` โ†’ REST API ํ†ต์‹  ์ฒ˜๋ฆฌ +- `UI/Panels/PanelMain.prefab` โ†’ ๋ฉ”์ธ ํ™”๋ฉด UI +- `Tests/Runtime/CharacterMotionTest.cs` โ†’ ์บ๋ฆญํ„ฐ ๋ชจ์…˜ ์œ ๋‹› ํ…Œ์ŠคํŠธ + +--- + +> ์ด ๋ฌธ์„œ๋Š” ์‹ ๊ทœ ํŒ€์›์ด ๊ตฌ์กฐ๋ฅผ ๋น ๋ฅด๊ฒŒ ์ดํ•ดํ•˜๊ณ  ๊ทœ์น™๋Œ€๋กœ ์ž‘์—…ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ž‘์„ฑ๋œ **๊ตฌ์กฐ ๋ฐ ๋ช…๋ช… ๊ฐ€์ด๋“œ๋ผ์ธ**์ž…๋‹ˆ๋‹ค. diff --git a/Assets/Docs/ProjectVG_Structure_Guide.md.meta b/Assets/Docs/ProjectVG_Structure_Guide.md.meta new file mode 100644 index 0000000..446b47b --- /dev/null +++ b/Assets/Docs/ProjectVG_Structure_Guide.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 04397c9c761edea4a83a995e11537cd9 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From 18249f1602cb7574d4f4b1d6ec85909315a2d564 Mon Sep 17 00:00:00 2001 From: WooSH Date: Thu, 14 Aug 2025 23:36:56 +0900 Subject: [PATCH 02/13] =?UTF-8?q?feat:=20livw2d=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=8A=A4=EC=BC=88=EB=A0=88=ED=86=A4=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Script/Controller/ActionController.cs | 198 ++++++++++++++-- .../Script/Controller/EmotionController.cs | 219 ++++++++++++++++-- .../Script/Controller/IActionController.cs | 16 +- .../Script/Controller/IEmotionController.cs | 18 +- .../Script/Controller/ILive2DModelApplier.cs | 9 +- .../Controller/ILive2DModelManagerFacade.cs | 15 ++ .../ILive2DModelManagerFacade.cs.meta | 2 + .../Script/Controller/Live2DModelApplier.cs | 78 ++++++- .../Controller/Live2DParameterController.cs | 91 ++++++-- .../Script/Manager/ILive2DCharacterManager.cs | 17 -- .../Manager/ILive2DCharacterManager.cs.meta | 2 - .../Script/Manager/Live2DCharacterManager.cs | 212 +++++++++++++++-- Assets/Domain/Chat/Service/ChatManager.cs | 72 +++++- 13 files changed, 834 insertions(+), 115 deletions(-) create mode 100644 Assets/Domain/Character/Script/Controller/ILive2DModelManagerFacade.cs create mode 100644 Assets/Domain/Character/Script/Controller/ILive2DModelManagerFacade.cs.meta delete mode 100644 Assets/Domain/Character/Script/Manager/ILive2DCharacterManager.cs delete mode 100644 Assets/Domain/Character/Script/Manager/ILive2DCharacterManager.cs.meta diff --git a/Assets/Domain/Character/Script/Controller/ActionController.cs b/Assets/Domain/Character/Script/Controller/ActionController.cs index f87b4c2..e5eaa87 100644 --- a/Assets/Domain/Character/Script/Controller/ActionController.cs +++ b/Assets/Domain/Character/Script/Controller/ActionController.cs @@ -1,20 +1,192 @@ using UnityEngine; +using System; +using System.Collections; +using Live2D.Cubism.Framework.Motion; namespace ProjectVG.Domain.Character.Service { - /// - /// ํ–‰๋™ โ†’ ๋ชจ์…˜/ํŒŒ๋ผ๋ฏธํ„ฐ ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ์˜ ์Šค์ผˆ๋ ˆํ†ค. - /// - public class ActionController : MonoBehaviour, IActionController - { - public void Initialize() - { - } - - public void TriggerAction(string action, object args = null) - { - } - } + /// + /// ํ–‰๋™ โ†’ ๋ชจ์…˜/ํŒŒ๋ผ๋ฏธํ„ฐ ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ + /// + public class ActionController : MonoBehaviour, IActionController + { + [Header("Components")] + [SerializeField] private CubismMotionController _motionController; + + [Header("Action Mapping")] + [SerializeField] private ActionMotionMapping[] _actionMappings; + + [Header("Settings")] + [SerializeField] private float _defaultMotionDuration = 2.0f; + + private string _currentAction; + private Coroutine _actionCoroutine; + private bool _isActionPlaying = false; + + #region IActionController Implementation + + public void Initialize() + { + if (_motionController == null) + { + _motionController = GetComponent(); + } + + if (_motionController == null) + { + Debug.LogError("[ActionController] CubismMotionController๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + return; + } + + Debug.Log("[ActionController] ์ดˆ๊ธฐํ™” ์™„๋ฃŒ"); + } + + public void TriggerAction(string action, object args = null) + { + if (string.IsNullOrEmpty(action)) + { + Debug.LogWarning("[ActionController] ์•ก์…˜์ด null์ž…๋‹ˆ๋‹ค."); + return; + } + + // ํ˜„์žฌ ์•ก์…˜์ด ์žฌ์ƒ ์ค‘์ด๋ผ๋ฉด ์ค‘๋‹จ + if (_isActionPlaying) + { + StopCurrentAction(); + } + + StartAction(action, args); + } + + #endregion + + #region Public Methods + + /// + /// ํ˜„์žฌ ์•ก์…˜์„ ์ค‘๋‹จํ•œ๋‹ค. + /// + public void StopCurrentAction() + { + if (_actionCoroutine != null) + { + StopCoroutine(_actionCoroutine); + _actionCoroutine = null; + } + + _isActionPlaying = false; + _currentAction = null; + + Debug.Log("[ActionController] ์•ก์…˜ ์ค‘๋‹จ"); + } + + /// + /// ํ˜„์žฌ ์•ก์…˜์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. + /// + public string GetCurrentAction() + { + return _currentAction; + } + + /// + /// ์•ก์…˜์ด ์žฌ์ƒ ์ค‘์ธ์ง€ ํ™•์ธํ•œ๋‹ค. + /// + public bool IsActionPlaying() + { + return _isActionPlaying; + } + + #endregion + + #region Private Methods + + private void StartAction(string action, object args) + { + var motionKey = GetMotionKey(action); + if (string.IsNullOrEmpty(motionKey)) + { + Debug.LogWarning($"[ActionController] ์•ก์…˜ '{action}'์— ๋Œ€ํ•œ ๋ชจ์…˜์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + return; + } + + _actionCoroutine = StartCoroutine(ActionCoroutine(action, motionKey, args)); + } + + private IEnumerator ActionCoroutine(string action, string motionKey, object args) + { + _currentAction = action; + _isActionPlaying = true; + + Debug.Log($"[ActionController] ์•ก์…˜ ์‹œ์ž‘: {action}"); + + // ๋ชจ์…˜ ์žฌ์ƒ (ํ˜„์žฌ๋Š” ๋”๋ฏธ ์ฒ˜๋ฆฌ) + PlayMotion(motionKey, args); + + // ๋ชจ์…˜ ์ง€์† ์‹œ๊ฐ„๋งŒํผ ๋Œ€๊ธฐ + var duration = GetActionDuration(action); + yield return new WaitForSeconds(duration); + + // ์•ก์…˜ ์™„๋ฃŒ + _isActionPlaying = false; + _currentAction = null; + + Debug.Log($"[ActionController] ์•ก์…˜ ์™„๋ฃŒ: {action}"); + } + + private void PlayMotion(string motionKey, object args) + { + if (_motionController == null) return; + + // TODO: ์‹ค์ œ ๋ชจ์…˜ ์žฌ์ƒ ๋กœ์ง ๊ตฌํ˜„ + // ํ˜„์žฌ๋Š” ๋”๋ฏธ ์ฒ˜๋ฆฌ๋กœ ๋กœ๊ทธ๋งŒ ์ถœ๋ ฅ + Debug.Log($"[ActionController] ๋ชจ์…˜ ์žฌ์ƒ: {motionKey}"); + + // ํ–ฅํ›„ ๊ตฌํ˜„ ์˜ˆ์ •: + // _motionController.PlayMotion(motionKey); + // ๋˜๋Š” + // _motionController.PlayMotionGroup(motionKey); + } + + private string GetMotionKey(string action) + { + foreach (var mapping in _actionMappings) + { + if (mapping.Action.Equals(action, StringComparison.OrdinalIgnoreCase)) + { + return mapping.MotionKey; + } + } + + // ๊ธฐ๋ณธ๊ฐ’ ๋ฐ˜ํ™˜ + return "idle"; + } + + private float GetActionDuration(string action) + { + foreach (var mapping in _actionMappings) + { + if (mapping.Action.Equals(action, StringComparison.OrdinalIgnoreCase)) + { + return mapping.Duration > 0 ? mapping.Duration : _defaultMotionDuration; + } + } + + return _defaultMotionDuration; + } + + #endregion + + #region Action Mapping + + [System.Serializable] + public class ActionMotionMapping + { + public string Action; + public string MotionKey; + public float Duration; + } + + #endregion + } } diff --git a/Assets/Domain/Character/Script/Controller/EmotionController.cs b/Assets/Domain/Character/Script/Controller/EmotionController.cs index 16ef56a..79d54fa 100644 --- a/Assets/Domain/Character/Script/Controller/EmotionController.cs +++ b/Assets/Domain/Character/Script/Controller/EmotionController.cs @@ -1,24 +1,209 @@ using UnityEngine; +using System; +using System.Collections; +using Live2D.Cubism.Framework.Expression; namespace ProjectVG.Domain.Character.Service { - /// - /// ๊ฐ์ • โ†’ Expression ๋งตํ•‘๊ณผ ๋ธ”๋ Œ๋”ฉ์„ ๊ตฌํ˜„ํ•˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ์˜ ์Šค์ผˆ๋ ˆํ†ค. - /// - public class EmotionController : MonoBehaviour, IEmotionController - { - public void Initialize() - { - } - - public void SetEmotion(string emotion, float intensity, int durationMs) - { - } - - public void ClearEmotion() - { - } - } + /// + /// ๊ฐ์ • โ†’ Expression ๋งตํ•‘๊ณผ ๋ธ”๋ Œ๋”ฉ์„ ๊ตฌํ˜„ํ•˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ + /// + public class EmotionController : MonoBehaviour, IEmotionController + { + [Header("Components")] + [SerializeField] private CubismExpressionController _expressionController; + + [Header("Emotion Mapping")] + [SerializeField] private EmotionExpressionMapping[] _emotionMappings; + + [Header("Blending Settings")] + [SerializeField] private float _defaultBlendTime = 0.5f; + [SerializeField] private float _voiceBlendTime = 1.0f; + + private string _currentEmotion = "neutral"; + private string _pendingEmotion; + private float _currentIntensity = 1.0f; + private Coroutine _emotionCoroutine; + private bool _isVoicePlaying = false; + + #region IEmotionController Implementation + + public void Initialize() + { + if (_expressionController == null) + { + _expressionController = GetComponent(); + } + + if (_expressionController == null) + { + Debug.LogError("[EmotionController] CubismExpressionController๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + return; + } + + // ๊ธฐ๋ณธ ๊ฐ์ • ์„ค์ • + SetEmotion("neutral", 1.0f, 0); + + Debug.Log("[EmotionController] ์ดˆ๊ธฐํ™” ์™„๋ฃŒ"); + } + + public void SetEmotion(string emotion, float intensity, int durationMs) + { + if (string.IsNullOrEmpty(emotion)) + { + Debug.LogWarning("[EmotionController] ๊ฐ์ •์ด null์ž…๋‹ˆ๋‹ค."); + return; + } + + // ์Œ์„ฑ ์žฌ์ƒ ์ค‘์ด๊ณ  ๊ณผ๊ฒฉํ•œ ๊ฐ์ • ์ „ํ™˜์ด๋ผ๋ฉด ๋Œ€๊ธฐ์—ด๋กœ ๋ณด๊ด€ + if (_isVoicePlaying && IsIntenseEmotionChange(_currentEmotion, emotion)) + { + _pendingEmotion = emotion; + Debug.Log($"[EmotionController] ์Œ์„ฑ ์žฌ์ƒ ์ค‘ ๊ฐ์ • ๋Œ€๊ธฐ: {emotion}"); + return; + } + + StartEmotionTransition(emotion, intensity, durationMs); + } + + public void ClearEmotion() + { + if (_emotionCoroutine != null) + { + StopCoroutine(_emotionCoroutine); + _emotionCoroutine = null; + } + + SetEmotion("neutral", 1.0f, 0); + } + + #endregion + + #region Public Methods + + /// + /// ์Œ์„ฑ ์žฌ์ƒ ์ƒํƒœ๋ฅผ ์„ค์ •ํ•œ๋‹ค. + /// + public void SetVoicePlaying(bool isPlaying) + { + _isVoicePlaying = isPlaying; + + // ์Œ์„ฑ ์žฌ์ƒ ์™„๋ฃŒ ์‹œ ๋Œ€๊ธฐ ์ค‘์ธ ๊ฐ์ • ์ ์šฉ + if (!isPlaying && !string.IsNullOrEmpty(_pendingEmotion)) + { + var pendingEmotion = _pendingEmotion; + _pendingEmotion = null; + SetEmotion(pendingEmotion, _currentIntensity, 2000); + } + } + + /// + /// ํ˜„์žฌ ๊ฐ์ •์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. + /// + public string GetCurrentEmotion() + { + return _currentEmotion; + } + + #endregion + + #region Private Methods + + private void StartEmotionTransition(string emotion, float intensity, int durationMs) + { + if (_emotionCoroutine != null) + { + StopCoroutine(_emotionCoroutine); + } + + _emotionCoroutine = StartCoroutine(EmotionTransitionCoroutine(emotion, intensity, durationMs)); + } + + private IEnumerator EmotionTransitionCoroutine(string emotion, float intensity, int durationMs) + { + var expressionKey = GetExpressionKey(emotion); + if (string.IsNullOrEmpty(expressionKey)) + { + Debug.LogWarning($"[EmotionController] ๊ฐ์ • '{emotion}'์— ๋Œ€ํ•œ Expression์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + yield break; + } + + // ๋ธ”๋ Œ๋“œ ์‹œ๊ฐ„ ๊ฒฐ์ • + var blendTime = _isVoicePlaying ? _voiceBlendTime : _defaultBlendTime; + + // Expression ๋ณ€๊ฒฝ + _expressionController.CurrentExpressionIndex = GetExpressionIndex(expressionKey); + _currentEmotion = emotion; + _currentIntensity = intensity; + + Debug.Log($"[EmotionController] ๊ฐ์ • ๋ณ€๊ฒฝ: {emotion} (๊ฐ•๋„: {intensity})"); + + // ์ง€์† ์‹œ๊ฐ„๋งŒํผ ๋Œ€๊ธฐ + if (durationMs > 0) + { + yield return new WaitForSeconds(durationMs / 1000f); + + // ์ง€์† ์‹œ๊ฐ„ ์ข…๋ฃŒ ์‹œ neutral๋กœ ๋ณต๊ท€ + if (_currentEmotion == emotion) // ๋‹ค๋ฅธ ๊ฐ์ •์œผ๋กœ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด + { + SetEmotion("neutral", 1.0f, 0); + } + } + } + + private string GetExpressionKey(string emotion) + { + foreach (var mapping in _emotionMappings) + { + if (mapping.Emotion.Equals(emotion, StringComparison.OrdinalIgnoreCase)) + { + return mapping.ExpressionKey; + } + } + + // ๊ธฐ๋ณธ๊ฐ’ ๋ฐ˜ํ™˜ + return "neutral"; + } + + private int GetExpressionIndex(string expressionKey) + { + if (_expressionController == null) return 0; + + // Expression ์ด๋ฆ„์œผ๋กœ ์ธ๋ฑ์Šค ์ฐพ๊ธฐ + for (int i = 0; i < _expressionController.ExpressionsList.CubismExpressionObjects.Length; i++) + { + if (_expressionController.ExpressionsList.CubismExpressionObjects[i].name.Equals(expressionKey, StringComparison.OrdinalIgnoreCase)) + { + return i; + } + } + + return 0; // ๊ธฐ๋ณธ๊ฐ’ + } + + private bool IsIntenseEmotionChange(string currentEmotion, string newEmotion) + { + // ๊ณผ๊ฒฉํ•œ ๊ฐ์ • ์ „ํ™˜ ํŒ๋‹จ ๋กœ์ง + var intenseEmotions = new[] { "angry", "surprised", "sad" }; + var isCurrentIntense = Array.Exists(intenseEmotions, e => e.Equals(currentEmotion, StringComparison.OrdinalIgnoreCase)); + var isNewIntense = Array.Exists(intenseEmotions, e => e.Equals(newEmotion, StringComparison.OrdinalIgnoreCase)); + + return isCurrentIntense && isNewIntense && currentEmotion != newEmotion; + } + + #endregion + + #region Emotion Mapping + + [System.Serializable] + public class EmotionExpressionMapping + { + public string Emotion; + public string ExpressionKey; + } + + #endregion + } } diff --git a/Assets/Domain/Character/Script/Controller/IActionController.cs b/Assets/Domain/Character/Script/Controller/IActionController.cs index df24f71..b807611 100644 --- a/Assets/Domain/Character/Script/Controller/IActionController.cs +++ b/Assets/Domain/Character/Script/Controller/IActionController.cs @@ -1,13 +1,13 @@ namespace ProjectVG.Domain.Character.Service { - /// - /// ํ–‰๋™ โ†’ ๋ชจ์…˜/ํŒŒ๋ผ๋ฏธํ„ฐ ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ๋‹ด๋‹นํ•œ๋‹ค. - /// - public interface IActionController - { - void Initialize(); - void TriggerAction(string action, object args = null); - } + /// + /// ํ–‰๋™ โ†’ ๋ชจ์…˜/ํŒŒ๋ผ๋ฏธํ„ฐ ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค + /// + public interface IActionController + { + void Initialize(); + void TriggerAction(string action, object args = null); + } } diff --git a/Assets/Domain/Character/Script/Controller/IEmotionController.cs b/Assets/Domain/Character/Script/Controller/IEmotionController.cs index 3232beb..9fcc894 100644 --- a/Assets/Domain/Character/Script/Controller/IEmotionController.cs +++ b/Assets/Domain/Character/Script/Controller/IEmotionController.cs @@ -1,14 +1,14 @@ namespace ProjectVG.Domain.Character.Service { - /// - /// ๊ฐ์ • โ†’ Expression ๋งตํ•‘๊ณผ ๋ธ”๋ Œ๋”ฉ์„ ๋‹ด๋‹นํ•œ๋‹ค. - /// - public interface IEmotionController - { - void Initialize(); - void SetEmotion(string emotion, float intensity, int durationMs); - void ClearEmotion(); - } + /// + /// ๊ฐ์ • โ†’ Expression ๋งตํ•‘๊ณผ ๋ธ”๋ Œ๋”ฉ์„ ๋‹ด๋‹นํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค + /// + public interface IEmotionController + { + void Initialize(); + void SetEmotion(string emotion, float intensity, int durationMs); + void ClearEmotion(); + } } diff --git a/Assets/Domain/Character/Script/Controller/ILive2DModelApplier.cs b/Assets/Domain/Character/Script/Controller/ILive2DModelApplier.cs index 6420840..5128c3e 100644 --- a/Assets/Domain/Character/Script/Controller/ILive2DModelApplier.cs +++ b/Assets/Domain/Character/Script/Controller/ILive2DModelApplier.cs @@ -1,12 +1,13 @@ using UnityEngine; +using ProjectVG.Domain.Character.Live2D.Model; namespace ProjectVG.Domain.Character.Service { + /// + /// Live2D ๋ชจ๋ธ์— ์„ค์ •์„ ์ ์šฉํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค + /// public interface ILive2DModelApplier { - /** ํ™œ์„ฑ ๋ชจ๋ธ๊ณผ ๊ตฌ์„ฑ์— ๋Œ€ํ•ด LookAt, LipSync, ์ธ๋„ค์ผ ๋“ฑ ์‹œ๊ฐ ์„ค์ •์„ ์ ์šฉํ•œ๋‹ค. */ - void Apply(GameObject activeModel, ProjectVG.Domain.Character.Live2D.Model.Live2DModelConfig characterConfig); + void Apply(GameObject activeModel, Live2DModelConfig characterConfig); } } - - diff --git a/Assets/Domain/Character/Script/Controller/ILive2DModelManagerFacade.cs b/Assets/Domain/Character/Script/Controller/ILive2DModelManagerFacade.cs new file mode 100644 index 0000000..0a6b9ab --- /dev/null +++ b/Assets/Domain/Character/Script/Controller/ILive2DModelManagerFacade.cs @@ -0,0 +1,15 @@ +using UnityEngine; + +namespace ProjectVG.Domain.Character.Service +{ + /// + /// Live2D ๋ชจ๋ธ ์ „๋ฐ˜์˜ ์ƒํƒœ๋ฅผ ๋‹จ์ผ ์ง„์ž…์ ์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค + /// + public interface ILive2DModelManagerFacade + { + void Initialize(); + void ApplyReaction(EmotionData emotionData, ActionData actionData); + void OnVoiceStarted(); + void OnVoiceFinished(); + } +} diff --git a/Assets/Domain/Character/Script/Controller/ILive2DModelManagerFacade.cs.meta b/Assets/Domain/Character/Script/Controller/ILive2DModelManagerFacade.cs.meta new file mode 100644 index 0000000..a72b053 --- /dev/null +++ b/Assets/Domain/Character/Script/Controller/ILive2DModelManagerFacade.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 8c750eed5093bf447aa877fed965a2e9 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Controller/Live2DModelApplier.cs b/Assets/Domain/Character/Script/Controller/Live2DModelApplier.cs index 43dd699..c5f7f68 100644 --- a/Assets/Domain/Character/Script/Controller/Live2DModelApplier.cs +++ b/Assets/Domain/Character/Script/Controller/Live2DModelApplier.cs @@ -1,11 +1,87 @@ using UnityEngine; +using ProjectVG.Domain.Character.Live2D.Model; namespace ProjectVG.Domain.Character.Service { + /// + /// Live2D ๋ชจ๋ธ์— ์„ค์ •์„ ์ ์šฉํ•˜๋Š” ๊ตฌํ˜„์ฒด + /// public class Live2DModelApplier : MonoBehaviour, ILive2DModelApplier { - public void Apply(GameObject activeModel, ProjectVG.Domain.Character.Live2D.Model.Live2DModelConfig characterConfig) + [Header("Components")] + [SerializeField] private EmotionController _emotionController; + [SerializeField] private ActionController _actionController; + + public void Apply(GameObject activeModel, Live2DModelConfig characterConfig) { + if (activeModel == null || characterConfig == null) + { + Debug.LogWarning("[Live2DModelApplier] ๋ชจ๋ธ ๋˜๋Š” ์„ค์ •์ด null์ž…๋‹ˆ๋‹ค."); + return; + } + + try + { + // ์ปดํฌ๋„ŒํŠธ๋“ค ์ฐพ๊ธฐ + FindComponents(activeModel); + + // ๊ธฐ๋ณธ ์„ค์ • ์ ์šฉ + ApplyBasicSettings(activeModel, characterConfig); + + // ์ปจํŠธ๋กค๋Ÿฌ๋“ค ์ดˆ๊ธฐํ™” + InitializeControllers(); + + Debug.Log($"[Live2DModelApplier] ๋ชจ๋ธ '{activeModel.name}'์— ์„ค์ • ์ ์šฉ ์™„๋ฃŒ"); + } + catch (System.Exception ex) + { + Debug.LogError($"[Live2DModelApplier] ์„ค์ • ์ ์šฉ ์‹คํŒจ: {ex.Message}"); + } + } + + private void FindComponents(GameObject activeModel) + { + // ๊ฐ์ • ์ปจํŠธ๋กค๋Ÿฌ ์ฐพ๊ธฐ + if (_emotionController == null) + { + _emotionController = activeModel.GetComponent(); + } + + // ์•ก์…˜ ์ปจํŠธ๋กค๋Ÿฌ ์ฐพ๊ธฐ + if (_actionController == null) + { + _actionController = activeModel.GetComponent(); + } + } + + private void ApplyBasicSettings(GameObject activeModel, Live2DModelConfig characterConfig) + { + // ๊ธฐ๋ณธ ๊ฐ์ • ์„ค์ • + if (_emotionController != null) + { + _emotionController.Initialize(); + _emotionController.SetEmotion("neutral", 1.0f, 0); + } + + // ๊ธฐ๋ณธ ์•ก์…˜ ์„ค์ • + if (_actionController != null) + { + _actionController.Initialize(); + } + } + + private void InitializeControllers() + { + // ์ปจํŠธ๋กค๋Ÿฌ๋“ค์ด ์ œ๋Œ€๋กœ ์ดˆ๊ธฐํ™”๋˜์—ˆ๋Š”์ง€ ํ™•์ธ + if (_emotionController == null) + { + Debug.LogWarning("[Live2DModelApplier] EmotionController๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + + if (_actionController == null) + { + Debug.LogWarning("[Live2DModelApplier] ActionController๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } } } } diff --git a/Assets/Domain/Character/Script/Controller/Live2DParameterController.cs b/Assets/Domain/Character/Script/Controller/Live2DParameterController.cs index a0877c1..04226c2 100644 --- a/Assets/Domain/Character/Script/Controller/Live2DParameterController.cs +++ b/Assets/Domain/Character/Script/Controller/Live2DParameterController.cs @@ -2,23 +2,80 @@ namespace ProjectVG.Domain.Character.Service { - /// - /// Live2D ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ง์ ‘ ์ œ์–ดํ•˜๊ฑฐ๋‚˜ ํ”„๋ฆฌ์…‹์„ ์ ์šฉํ•˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ์˜ ์Šค์ผˆ๋ ˆํ†ค. - /// - public class Live2DParameterController : MonoBehaviour - { - public void Initialize() - { - } - - public void ApplyPreset(string presetKey) - { - } - - public void SetParameter(string parameterId, float value) - { - } - } + /// + /// Live2D ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ง์ ‘ ์ œ์–ดํ•˜๊ฑฐ๋‚˜ ํ”„๋ฆฌ์…‹์„ ์ ์šฉํ•˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ (๋น„ํ™œ์„ฑํ™”๋จ) + /// + public class Live2DParameterController : MonoBehaviour + { + [Header("Status")] + [SerializeField] private bool _isEnabled = false; + + public void Initialize() + { + if (!_isEnabled) + { + Debug.Log("[Live2DParameterController] ๋น„ํ™œ์„ฑํ™”๋จ"); + return; + } + + Debug.Log("[Live2DParameterController] ์ดˆ๊ธฐํ™” ์™„๋ฃŒ"); + } + + /// + /// ํ”„๋ฆฌ์…‹์„ ์ ์šฉํ•œ๋‹ค. (๋น„ํ™œ์„ฑํ™”๋จ) + /// + public void ApplyPreset(string presetKey) + { + if (!_isEnabled) return; + Debug.Log($"[Live2DParameterController] ํ”„๋ฆฌ์…‹ ์ ์šฉ: {presetKey}"); + } + + /// + /// ๋‹จ์ผ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์„ค์ •ํ•œ๋‹ค. (๋น„ํ™œ์„ฑํ™”๋จ) + /// + public void SetParameter(string parameterId, float value) + { + if (!_isEnabled) return; + Debug.Log($"[Live2DParameterController] ํŒŒ๋ผ๋ฏธํ„ฐ ์„ค์ •: {parameterId} = {value}"); + } + + /// + /// ์—ฌ๋Ÿฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ•œ๋ฒˆ์— ์„ค์ •ํ•œ๋‹ค. (๋น„ํ™œ์„ฑํ™”๋จ) + /// + public void SetParameters(System.Collections.Generic.Dictionary parameters) + { + if (!_isEnabled) return; + Debug.Log($"[Live2DParameterController] ํŒŒ๋ผ๋ฏธํ„ฐ ์ผ๊ด„ ์„ค์ •: {parameters?.Count ?? 0}๊ฐœ"); + } + + /// + /// ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’์„ ๋ธ”๋ Œ๋”ฉํ•˜์—ฌ ์„ค์ •ํ•œ๋‹ค. (๋น„ํ™œ์„ฑํ™”๋จ) + /// + public void BlendParameter(string parameterId, float targetValue, float blendTime = -1f) + { + if (!_isEnabled) return; + Debug.Log($"[Live2DParameterController] ํŒŒ๋ผ๋ฏธํ„ฐ ๋ธ”๋ Œ๋”ฉ: {parameterId} -> {targetValue}"); + } + + /// + /// ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. (๋น„ํ™œ์„ฑํ™”๋จ) + /// + public float GetParameterValue(string parameterId) + { + if (!_isEnabled) return 0f; + Debug.Log($"[Live2DParameterController] ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’ ์กฐํšŒ: {parameterId}"); + return 0f; + } + + /// + /// ๋ชจ๋“  ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋ฆฌ์…‹ํ•œ๋‹ค. (๋น„ํ™œ์„ฑํ™”๋จ) + /// + public void ResetAllParameters() + { + if (!_isEnabled) return; + Debug.Log("[Live2DParameterController] ๋ชจ๋“  ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฆฌ์…‹"); + } + } } diff --git a/Assets/Domain/Character/Script/Manager/ILive2DCharacterManager.cs b/Assets/Domain/Character/Script/Manager/ILive2DCharacterManager.cs deleted file mode 100644 index 9e3f518..0000000 --- a/Assets/Domain/Character/Script/Manager/ILive2DCharacterManager.cs +++ /dev/null @@ -1,17 +0,0 @@ -using UnityEngine; - -namespace ProjectVG.Domain.Character.Service -{ - /// - /// ๋ชจ๋ธ ์ „๋ฐ˜์˜ ์ƒํƒœ๋ฅผ ๋‹จ์ผ ์ง„์ž…์ ์—์„œ ๊ด€๋ฆฌํ•œ๋‹ค. - /// - public interface ILive2DModelManagerFacade - { - void Initialize(); - void ApplyReaction(EmotionData emotionData, ActionData actionData); - void OnVoiceStarted(); - void OnVoiceFinished(); - } -} - - diff --git a/Assets/Domain/Character/Script/Manager/ILive2DCharacterManager.cs.meta b/Assets/Domain/Character/Script/Manager/ILive2DCharacterManager.cs.meta deleted file mode 100644 index 3fd378f..0000000 --- a/Assets/Domain/Character/Script/Manager/ILive2DCharacterManager.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: ea75b23f7c651a24c9e0055ae07e8234 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Manager/Live2DCharacterManager.cs b/Assets/Domain/Character/Script/Manager/Live2DCharacterManager.cs index f11edc4..0c40617 100644 --- a/Assets/Domain/Character/Script/Manager/Live2DCharacterManager.cs +++ b/Assets/Domain/Character/Script/Manager/Live2DCharacterManager.cs @@ -1,28 +1,198 @@ using UnityEngine; +using System; +using ProjectVG.Core.Audio; namespace ProjectVG.Domain.Character.Service { - /// - /// ์ƒ์œ„ ์กฐ์ •์ž. ๋ชจ๋ธ ๊ด€๋ฆฌ์ž/ํŒŒ๋ผ๋ฏธํ„ฐ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋ฌถ์–ด ๊ฐ์ •/ํ–‰๋™ ๋ฐ˜์‘์„ ์ผ๊ด€๋˜๊ฒŒ ์ ์šฉํ•œ๋‹ค. - /// - public class Live2DModelManagerFacade : MonoBehaviour, ILive2DModelManagerFacade - { - public void Initialize() - { - } - - public void ApplyReaction(EmotionData emotionData, ActionData actionData) - { - } - - public void OnVoiceStarted() - { - } - - public void OnVoiceFinished() - { - } - } + /// + /// Live2D ์บ๋ฆญํ„ฐ์˜ ์ƒ์œ„ ์กฐ์ •์ž. ๋ชจ๋ธ ๊ด€๋ฆฌ์ž/์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋ฌถ์–ด ๊ฐ์ •/ํ–‰๋™ ๋ฐ˜์‘์„ ์ผ๊ด€๋˜๊ฒŒ ์ ์šฉ + /// + public class Live2DCharacterManager : MonoBehaviour, ILive2DModelManagerFacade + { + [Header("Components")] + [SerializeField] private Live2DModelManager _modelManager; + [SerializeField] private EmotionController _emotionController; + [SerializeField] private ActionController _actionController; + + [Header("Settings")] + [SerializeField] private bool _enableEmotionControl = true; + [SerializeField] private bool _enableActionControl = true; + + private CharacterStateModel _stateModel; + private AudioManager _audioManager; + + private static Live2DCharacterManager _instance; + public static Live2DCharacterManager Instance + { + get + { + if (_instance == null) + { + _instance = FindAnyObjectByType(); + } + return _instance; + } + } + + #region Unity Lifecycle + + private void Awake() + { + if (_instance != null && _instance != this) + { + Destroy(gameObject); + return; + } + + _instance = this; + _stateModel = new CharacterStateModel(); + } + + private void Start() + { + Initialize(); + } + + #endregion + + #region ILive2DModelManagerFacade Implementation + + public void Initialize() + { + try + { + _audioManager = AudioManager.Instance; + + if (_emotionController != null) + { + _emotionController.Initialize(); + } + + if (_actionController != null) + { + _actionController.Initialize(); + } + + Debug.Log("[Live2DCharacterManager] ์ดˆ๊ธฐํ™” ์™„๋ฃŒ"); + } + catch (Exception ex) + { + Debug.LogError($"[Live2DCharacterManager] ์ดˆ๊ธฐํ™” ์‹คํŒจ: {ex.Message}"); + } + } + + public void ApplyReaction(EmotionData emotionData, ActionData actionData) + { + try + { + // Action > Voice Gating > Emotion ์šฐ์„ ์ˆœ์œ„ ์ ์šฉ + if (_enableActionControl && !string.IsNullOrEmpty(actionData.Action)) + { + _actionController?.TriggerAction(actionData.Action, actionData.Args); + _stateModel.currentAction = actionData.Action; + _stateModel.actionPlaying = true; + + Debug.Log($"[Live2DCharacterManager] ์•ก์…˜ ์ ์šฉ: {actionData.Action}"); + } + + if (_enableEmotionControl && !string.IsNullOrEmpty(emotionData.Emotion)) + { + _emotionController?.SetEmotion(emotionData.Emotion, emotionData.Intensity, emotionData.DurationMs); + _stateModel.currentEmotion = emotionData.Emotion; + _stateModel.emotionIntensity = emotionData.Intensity; + _stateModel.emotionExpireAt = DateTime.Now.AddMilliseconds(emotionData.DurationMs); + + Debug.Log($"[Live2DCharacterManager] ๊ฐ์ • ์ ์šฉ: {emotionData.Emotion} (๊ฐ•๋„: {emotionData.Intensity})"); + } + } + catch (Exception ex) + { + Debug.LogError($"[Live2DCharacterManager] ๋ฐ˜์‘ ์ ์šฉ ์‹คํŒจ: {ex.Message}"); + } + } + + public void OnVoiceStarted() + { + _stateModel.isVoicePlaying = true; + Debug.Log("[Live2DCharacterManager] ์Œ์„ฑ ์žฌ์ƒ ์‹œ์ž‘"); + } + + public void OnVoiceFinished() + { + _stateModel.isVoicePlaying = false; + + // ์Œ์„ฑ ์žฌ์ƒ ์™„๋ฃŒ ์‹œ ์ƒํƒœ ๋ณต์› + if (_stateModel.actionPlaying) + { + _stateModel.actionPlaying = false; + _stateModel.currentAction = null; + + // ์•ก์…˜ ์™„๋ฃŒ ํ›„ ์ด์ „ ๊ฐ์ • ๋ณต์› + if (!string.IsNullOrEmpty(_stateModel.currentEmotion)) + { + _emotionController?.SetEmotion(_stateModel.currentEmotion, _stateModel.emotionIntensity, 2000); + } + } + + Debug.Log("[Live2DCharacterManager] ์Œ์„ฑ ์žฌ์ƒ ์™„๋ฃŒ, ์ƒํƒœ ๋ณต์›"); + } + + #endregion + + #region Public Methods + + /// + /// ํ˜„์žฌ ์บ๋ฆญํ„ฐ ์ƒํƒœ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. + /// + public CharacterStateModel GetCurrentState() + { + return _stateModel; + } + + /// + /// ๊ฐ์ •์„ ์ฆ‰์‹œ ํ•ด์ œํ•œ๋‹ค. + /// + public void ClearEmotion() + { + _emotionController?.ClearEmotion(); + _stateModel.currentEmotion = null; + _stateModel.emotionIntensity = 0f; + _stateModel.emotionExpireAt = DateTime.MinValue; + } + + /// + /// ์•ก์…˜์„ ์ฆ‰์‹œ ์ค‘๋‹จํ•œ๋‹ค. + /// + public void StopAction() + { + _stateModel.actionPlaying = false; + _stateModel.currentAction = null; + } + + #endregion + + #region Character State Model + + /// + /// ์บ๋ฆญํ„ฐ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ชจ๋ธ + /// + [System.Serializable] + public class CharacterStateModel + { + public string currentEmotion; + public string pendingEmotion; + public float emotionIntensity; + public DateTime emotionExpireAt; + + public string currentAction; + public bool actionPlaying; + public DateTime actionExpireAt; + + public bool isVoicePlaying; + } + + #endregion + } } diff --git a/Assets/Domain/Chat/Service/ChatManager.cs b/Assets/Domain/Chat/Service/ChatManager.cs index a018824..e91f784 100644 --- a/Assets/Domain/Chat/Service/ChatManager.cs +++ b/Assets/Domain/Chat/Service/ChatManager.cs @@ -9,6 +9,7 @@ using ProjectVG.Infrastructure.Network.WebSocket; using ProjectVG.Infrastructure.Network.Services; using ProjectVG.Domain.Chat.View; +using ProjectVG.Domain.Character.Service; namespace ProjectVG.Domain.Chat.Service @@ -27,12 +28,16 @@ public class ChatManager : MonoBehaviour [SerializeField] private bool _enableMessageQueue = true; [SerializeField] private int _maxQueueSize = 100; + [Header("Live2D Integration")] + [SerializeField] private bool _enableLive2DIntegration = true; + private bool _isConnected = false; private bool _isInitialized = false; private bool _isProcessing = false; private WebSocketManager? _webSocketManager; private AudioManager? _audioManager; + private Live2DCharacterManager? _live2DCharacterManager; private readonly Queue _messageQueue = new Queue(); private readonly object _queueLock = new object(); @@ -119,6 +124,12 @@ public void Initialize() _webSocketManager = WebSocketManager.Instance; _audioManager = AudioManager.Instance; + // Live2D ๋ชจ๋“ˆ ์ดˆ๊ธฐํ™” + if (_enableLive2DIntegration) + { + _live2DCharacterManager = Live2DCharacterManager.Instance; + } + if (_webSocketManager != null) { _webSocketManager.OnChatMessageReceived += HandleChatMessageReceived; @@ -202,10 +213,6 @@ public void ClearMessageQueue() } } - - - - #endregion #region Private Methods @@ -261,6 +268,9 @@ private void ProcessMessageImmediately(ChatMessage chatMessage) _chatBubblePanel.CreateBubble(Actor.Character, chatMessage.Text); } + // Live2D ๋ฐ˜์‘ ์ ์šฉ + ApplyLive2DReaction(chatMessage); + if (chatMessage.VoiceData != null && _audioManager != null) { _audioManager.PlayVoice(chatMessage.VoiceData); @@ -284,6 +294,9 @@ private async UniTask ProcessMessageImmediatelyAsync(ChatMessage chatMessage) _chatBubblePanel.CreateBubble(Actor.Character, chatMessage.Text); } + // Live2D ๋ฐ˜์‘ ์ ์šฉ + ApplyLive2DReaction(chatMessage); + if (chatMessage.VoiceData != null && _audioManager != null) { await _audioManager.PlayVoiceAsync(chatMessage.VoiceData); @@ -296,6 +309,55 @@ private async UniTask ProcessMessageImmediatelyAsync(ChatMessage chatMessage) } } + private void ApplyLive2DReaction(ChatMessage chatMessage) + { + if (!_enableLive2DIntegration || _live2DCharacterManager == null) + return; + + try + { + // ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์—์„œ ๊ฐ์ •/ํ–‰๋™ ์ •๋ณด ์ถ”์ถœ + var emotionData = ExtractEmotionData(chatMessage); + var actionData = ExtractActionData(chatMessage); + + // Live2D ๋ฐ˜์‘ ์ ์šฉ + _live2DCharacterManager.ApplyReaction(emotionData, actionData); + + // ์Œ์„ฑ ์žฌ์ƒ ์‹œ์ž‘ ์•Œ๋ฆผ + if (chatMessage.VoiceData != null) + { + _live2DCharacterManager.OnVoiceStarted(); + } + } + catch (Exception ex) + { + Debug.LogError($"[ChatManager] Live2D ๋ฐ˜์‘ ์ ์šฉ ์‹คํŒจ: {ex.Message}"); + } + } + + private EmotionData ExtractEmotionData(ChatMessage chatMessage) + { + // TODO: ์‹ค์ œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์—์„œ ๊ฐ์ • ์ •๋ณด ์ถ”์ถœ + // ํ˜„์žฌ๋Š” ๊ธฐ๋ณธ๊ฐ’ ๋ฐ˜ํ™˜ + return new EmotionData + { + Emotion = "neutral", + Intensity = 0.5f, + DurationMs = 2000 + }; + } + + private ActionData ExtractActionData(ChatMessage chatMessage) + { + // TODO: ์‹ค์ œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์—์„œ ํ–‰๋™ ์ •๋ณด ์ถ”์ถœ + // ํ˜„์žฌ๋Š” ๊ธฐ๋ณธ๊ฐ’ ๋ฐ˜ํ™˜ + return new ActionData + { + Action = "", + Args = null + }; + } + private bool ValidateUserInput(string message) { if (string.IsNullOrWhiteSpace(message)) @@ -313,8 +375,6 @@ private bool ValidateUserInput(string message) return true; } - - private void HandleChatMessageReceived(ChatMessage chatMessage) { ProcessCharacterMessage(chatMessage); From ace4ee7fa59186a8dd1c74e3a23bc2d6531eb343 Mon Sep 17 00:00:00 2001 From: WooSH Date: Mon, 18 Aug 2025 23:57:48 +0900 Subject: [PATCH 03/13] =?UTF-8?q?feat:=20=EC=8A=A4=EC=BC=88=EB=A0=88?= =?UTF-8?q?=ED=86=A4=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Script/Controller/CharacterActionDTOs.cs | 233 +++++++++++++++ .../Controller/CharacterActionDTOs.cs.meta | 2 + Assets/Domain/Character/Script/Facade.meta | 8 + .../Script/Facade/CharacterFacade.cs | 149 ++++++++++ .../Script/Facade/CharacterFacade.cs.meta | 2 + .../Script/Facade/ICharacterFacade.cs | 83 ++++++ .../Script/Facade/ICharacterFacade.cs.meta | 2 + .../Script/Manager/CharacterModelManager.cs | 268 ++++++++++++++++++ .../Manager/CharacterModelManager.cs.meta | 2 + .../Script/Manager/ICharacterModelManager.cs | 61 ++++ .../Manager/ICharacterModelManager.cs.meta | 2 + Assets/Domain/Character/Script/Service.meta | 8 + .../Service/ICharacterActionResolver.cs | 15 + .../Service/ICharacterActionResolver.cs.meta | 2 + .../Script/Service/ICharacterActionService.cs | 45 +++ .../Service/ICharacterActionService.cs.meta | 2 + 16 files changed, 884 insertions(+) create mode 100644 Assets/Domain/Character/Script/Controller/CharacterActionDTOs.cs create mode 100644 Assets/Domain/Character/Script/Controller/CharacterActionDTOs.cs.meta create mode 100644 Assets/Domain/Character/Script/Facade.meta create mode 100644 Assets/Domain/Character/Script/Facade/CharacterFacade.cs create mode 100644 Assets/Domain/Character/Script/Facade/CharacterFacade.cs.meta create mode 100644 Assets/Domain/Character/Script/Facade/ICharacterFacade.cs create mode 100644 Assets/Domain/Character/Script/Facade/ICharacterFacade.cs.meta create mode 100644 Assets/Domain/Character/Script/Manager/CharacterModelManager.cs create mode 100644 Assets/Domain/Character/Script/Manager/CharacterModelManager.cs.meta create mode 100644 Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs create mode 100644 Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs.meta create mode 100644 Assets/Domain/Character/Script/Service.meta create mode 100644 Assets/Domain/Character/Script/Service/ICharacterActionResolver.cs create mode 100644 Assets/Domain/Character/Script/Service/ICharacterActionResolver.cs.meta create mode 100644 Assets/Domain/Character/Script/Service/ICharacterActionService.cs create mode 100644 Assets/Domain/Character/Script/Service/ICharacterActionService.cs.meta diff --git a/Assets/Domain/Character/Script/Controller/CharacterActionDTOs.cs b/Assets/Domain/Character/Script/Controller/CharacterActionDTOs.cs new file mode 100644 index 0000000..c690b85 --- /dev/null +++ b/Assets/Domain/Character/Script/Controller/CharacterActionDTOs.cs @@ -0,0 +1,233 @@ +using System; + +namespace ProjectVG.Domain.Character.Service +{ + /// + /// ์บ๋ฆญํ„ฐ ์•ก์…˜ ์š”์ฒญ์„ ๋‚˜ํƒ€๋‚ด๋Š” DTO + /// + [Serializable] + public struct CharacterActionRequest + { + /// + /// ๋Œ€์ƒ ์บ๋ฆญํ„ฐ ID + /// + public string CharacterId; + + /// + /// ์•ก์…˜ ํ‚ค (์˜ˆ: "happy", "wave", "nod") + /// + public string ActionKey; + + /// + /// ์•ก์…˜ ๊ฐ•๋„ (0.0 ~ 1.0) + /// + public float Intensity; + + /// + /// ์•ก์…˜ ์ง€์† ์‹œ๊ฐ„ (๋ฐ€๋ฆฌ์ดˆ, null์ด๋ฉด ๊ธฐ๋ณธ๊ฐ’ ์‚ฌ์šฉ) + /// + public int? DurationMs; + + /// + /// ์•ก์…˜ ์šฐ์„ ์ˆœ์œ„ + /// + public CharacterActionPriority Priority; + + /// + /// ์•ก์…˜ ์ทจ์†Œ ์ •์ฑ… + /// + public CharacterCancelPolicy CancelPolicy; + + public CharacterActionRequest(string characterId, string actionKey, float intensity = 1.0f, int? durationMs = null, CharacterActionPriority priority = CharacterActionPriority.Normal, CharacterCancelPolicy cancelPolicy = CharacterCancelPolicy.Replace) + { + CharacterId = characterId; + ActionKey = actionKey; + Intensity = intensity; + DurationMs = durationMs; + Priority = priority; + CancelPolicy = cancelPolicy; + } + } + + /// + /// ์บ๋ฆญํ„ฐ ์•ก์…˜ ํ•ธ๋“ค (์•ก์…˜ ์‹คํ–‰ ์ถ”์ ์šฉ) + /// + [Serializable] + public struct CharacterActionHandle + { + /// + /// ์•ก์…˜ ๊ณ ์œ  ID + /// + public string ActionId; + + /// + /// ์•ก์…˜ ์ƒํƒœ + /// + public CharacterActionStatus Status; + + public CharacterActionHandle(string actionId, CharacterActionStatus status = CharacterActionStatus.Pending) + { + ActionId = actionId; + Status = status; + } + } + + /// + /// ์บ๋ฆญํ„ฐ ์•ก์…˜ ์ƒํƒœ ์ •๋ณด + /// + [Serializable] + public struct CharacterActionState + { + /// + /// ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ ์•ก์…˜ ID + /// + public string CurrentActionId; + + /// + /// ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ ์•ก์…˜ ํ‚ค + /// + public string CurrentActionKey; + + /// + /// ์•ก์…˜์ด ์žฌ์ƒ ์ค‘์ธ์ง€ ์—ฌ๋ถ€ + /// + public bool IsPlaying; + + /// + /// ์Œ์„ฑ ๊ฒŒ์ดํŠธ๊ฐ€ ํ™œ์„ฑํ™”๋˜์–ด ์žˆ๋Š”์ง€ ์—ฌ๋ถ€ + /// + public bool IsVoiceGated; + + public CharacterActionState(string currentActionId = null, string currentActionKey = null, bool isPlaying = false, bool isVoiceGated = false) + { + CurrentActionId = currentActionId; + CurrentActionKey = currentActionKey; + IsPlaying = isPlaying; + IsVoiceGated = isVoiceGated; + } + } + + /// + /// ํ•ด์„๋œ ์บ๋ฆญํ„ฐ ์•ก์…˜ ์ •๋ณด + /// + [Serializable] + public struct CharacterResolvedAction + { + /// + /// ํ‘œํ˜„์‹ ํ‚ค + /// + public string ExpressionKey; + + /// + /// ๋ชจ์…˜ ํ‚ค + /// + public string MotionKey; + + /// + /// ์ง€์† ์‹œ๊ฐ„ (๋ฐ€๋ฆฌ์ดˆ) + /// + public int? DurationMs; + + /// + /// ๋ธ”๋ Œ๋“œ ์ธ ์‹œ๊ฐ„ (์ดˆ) + /// + public float BlendInSec; + + /// + /// ๋ธ”๋ Œ๋“œ ์•„์›ƒ ์‹œ๊ฐ„ (์ดˆ) + /// + public float BlendOutSec; + + public CharacterResolvedAction(string expressionKey = null, string motionKey = null, int? durationMs = null, float blendInSec = 0.3f, float blendOutSec = 0.3f) + { + ExpressionKey = expressionKey; + MotionKey = motionKey; + DurationMs = durationMs; + BlendInSec = blendInSec; + BlendOutSec = blendOutSec; + } + } + + /// + /// ์บ๋ฆญํ„ฐ ์•ก์…˜ ์šฐ์„ ์ˆœ์œ„ + /// + public enum CharacterActionPriority + { + /// + /// ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„ + /// + Low = 0, + + /// + /// ์ผ๋ฐ˜ ์šฐ์„ ์ˆœ์œ„ + /// + Normal = 1, + + /// + /// ๋†’์€ ์šฐ์„ ์ˆœ์œ„ + /// + High = 2, + + /// + /// ์ตœ๊ณ  ์šฐ์„ ์ˆœ์œ„ (์Œ์„ฑ ๋“ฑ) + /// + Critical = 3 + } + + /// + /// ์บ๋ฆญํ„ฐ ์•ก์…˜ ์ทจ์†Œ ์ •์ฑ… + /// + public enum CharacterCancelPolicy + { + /// + /// ๊ธฐ์กด ์•ก์…˜์„ ๋Œ€์ฒด + /// + Replace = 0, + + /// + /// ๊ธฐ์กด ์•ก์…˜์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ + /// + Wait = 1, + + /// + /// ๊ธฐ์กด ์•ก์…˜์„ ์ค‘๋‹จํ•˜๊ณ  ์ฆ‰์‹œ ์‹คํ–‰ + /// + Interrupt = 2, + + /// + /// ๊ธฐ์กด ์•ก์…˜์ด ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋‚ฎ์„ ๋•Œ๋งŒ ๋Œ€์ฒด + /// + ReplaceIfLowerPriority = 3 + } + + /// + /// ์บ๋ฆญํ„ฐ ์•ก์…˜ ์ƒํƒœ + /// + public enum CharacterActionStatus + { + /// + /// ๋Œ€๊ธฐ ์ค‘ + /// + Pending = 0, + + /// + /// ์‹คํ–‰ ์ค‘ + /// + Playing = 1, + + /// + /// ์™„๋ฃŒ๋จ + /// + Completed = 2, + + /// + /// ์ทจ์†Œ๋จ + /// + Cancelled = 3, + + /// + /// ์˜ค๋ฅ˜ ๋ฐœ์ƒ + /// + Error = 4 + } +} diff --git a/Assets/Domain/Character/Script/Controller/CharacterActionDTOs.cs.meta b/Assets/Domain/Character/Script/Controller/CharacterActionDTOs.cs.meta new file mode 100644 index 0000000..52f628e --- /dev/null +++ b/Assets/Domain/Character/Script/Controller/CharacterActionDTOs.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 94f7891b2539a7446908fcfdddadbe11 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Facade.meta b/Assets/Domain/Character/Script/Facade.meta new file mode 100644 index 0000000..f9a76d2 --- /dev/null +++ b/Assets/Domain/Character/Script/Facade.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1befe0315c2c0b644bf4c75afc6cd9bc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Domain/Character/Script/Facade/CharacterFacade.cs b/Assets/Domain/Character/Script/Facade/CharacterFacade.cs new file mode 100644 index 0000000..9ba1386 --- /dev/null +++ b/Assets/Domain/Character/Script/Facade/CharacterFacade.cs @@ -0,0 +1,149 @@ +using UnityEngine; +using ProjectVG.Domain.Character.Live2D.Model; + +namespace ProjectVG.Domain.Character.Service +{ + /// + /// ์บ๋ฆญํ„ฐ ์ œ์–ด๋ฅผ ๋‹จ์ผ ์ง„์ž…์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ํŒŒ์‚ฌ๋“œ ๊ตฌํ˜„์ฒด. + /// + public class CharacterFacade : MonoBehaviour, ICharacterFacade + { + private ICharacterModelManager _modelManager; + private ICharacterActionService _actionService; + private ICharacterActionResolver _actionResolver; + + /// + /// ํŒŒ์‚ฌ๋“œ๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค. + /// + public void Initialize() + { + // ์˜์กด์„ฑ ์ฃผ์ž…์„ ํ†ตํ•ด ์ดˆ๊ธฐํ™” + // TODO: ์‹ค์ œ ์˜์กด์„ฑ ์ฃผ์ž… ์ปจํ…Œ์ด๋„ˆ์—์„œ ๊ฐ€์ ธ์˜ค๋„๋ก ์ˆ˜์ • + Debug.LogWarning("[CharacterFacade] ์˜์กด์„ฑ ์ฃผ์ž…์ด ์•„์ง ๊ตฌํ˜„๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."); + } + + /// + /// ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•œ๋‹ค. + /// + /// ๋ชจ๋ธ ๋งค๋‹ˆ์ € + /// ์•ก์…˜ ์„œ๋น„์Šค + /// ์•ก์…˜ ํ•ด์„๊ธฐ + public void InjectDependencies(ICharacterModelManager modelManager, ICharacterActionService actionService, ICharacterActionResolver actionResolver) + { + _modelManager = modelManager; + _actionService = actionService; + _actionResolver = actionResolver; + } + + /// + /// ํŒŒ์‚ฌ๋“œ๋ฅผ ์ข…๋ฃŒํ•œ๋‹ค. + /// + public void Shutdown() + { + if (_modelManager != null) { + _modelManager.UnloadAll(); + } + } + + /// + /// ์บ๋ฆญํ„ฐ๋ฅผ ๋“ฑ๋กํ•œ๋‹ค. + /// + public void RegisterCharacter(string characterId, bool preload = false) + { + if (_modelManager != null) { + _modelManager.LoadModel(characterId, preload); + } + } + + /// + /// ์บ๋ฆญํ„ฐ ๋“ฑ๋ก์„ ํ•ด์ œํ•œ๋‹ค. + /// + public void UnregisterCharacter(string characterId) + { + if (_modelManager != null) { + _modelManager.UnloadModel(characterId); + } + } + + /// + /// ํ™œ์„ฑ ์บ๋ฆญํ„ฐ๋ฅผ ์„ค์ •ํ•œ๋‹ค. + /// + public void SetActiveCharacter(string characterId) + { + if (_modelManager != null) { + _modelManager.ActivateModel(characterId); + } + } + + /// + /// ํ™œ์„ฑ ์บ๋ฆญํ„ฐ๋ฅผ ํ•ด์ œํ•œ๋‹ค. + /// + public void ClearActiveCharacter() + { + if (_modelManager != null) { + _modelManager.DeactivateModel(); + } + } + + /// + /// ์•ก์…˜์„ ์ ์šฉํ•œ๋‹ค. + /// + public CharacterActionHandle ApplyAction(CharacterActionRequest request) + { + // TODO: ์•ก์…˜ ์„œ๋น„์Šค ๊ตฌํ˜„ ํ›„ ์—ฐ๊ฒฐ + Debug.LogWarning($"[CharacterFacade] ApplyAction ์•„์ง ๊ตฌํ˜„๋˜์ง€ ์•Š์Œ: {request.ActionKey}"); + return new CharacterActionHandle("not_implemented", CharacterActionStatus.Error); + } + + /// + /// ์•ก์…˜์„ ์ทจ์†Œํ•œ๋‹ค. + /// + public void CancelAction(string actionId) + { + // TODO: ์•ก์…˜ ์„œ๋น„์Šค ๊ตฌํ˜„ ํ›„ ์—ฐ๊ฒฐ + Debug.LogWarning($"[CharacterFacade] CancelAction ์•„์ง ๊ตฌํ˜„๋˜์ง€ ์•Š์Œ: {actionId}"); + } + + /// + /// ๋ชจ๋“  ์•ก์…˜์„ ์ทจ์†Œํ•œ๋‹ค. + /// + public void CancelAllActions() + { + // TODO: ์•ก์…˜ ์„œ๋น„์Šค ๊ตฌํ˜„ ํ›„ ์—ฐ๊ฒฐ + Debug.LogWarning("[CharacterFacade] CancelAllActions ์•„์ง ๊ตฌํ˜„๋˜์ง€ ์•Š์Œ"); + } + + /// + /// ์Œ์„ฑ ์žฌ์ƒ ์‹œ์ž‘์„ ์•Œ๋ฆฐ๋‹ค. + /// + public void OnVoiceStarted() + { + // TODO: ์Œ์„ฑ ๊ด€๋ จ ์ฒ˜๋ฆฌ ๊ตฌํ˜„ + } + + /// + /// ์Œ์„ฑ ์žฌ์ƒ ์ข…๋ฃŒ๋ฅผ ์•Œ๋ฆฐ๋‹ค. + /// + public void OnVoiceFinished() + { + // TODO: ์Œ์„ฑ ๊ด€๋ จ ์ฒ˜๋ฆฌ ๊ตฌํ˜„ + } + + /// + /// ํ™œ์„ฑ ์บ๋ฆญํ„ฐ ID๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. + /// + public string GetActiveCharacterId() + { + return _modelManager?.GetActiveModelId(); + } + + /// + /// ์บ๋ฆญํ„ฐ ๋“ฑ๋ก ์—ฌ๋ถ€๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. + /// + public bool IsRegistered(string characterId) + { + return _modelManager?.IsLoaded(characterId) ?? false; + } + } +} + diff --git a/Assets/Domain/Character/Script/Facade/CharacterFacade.cs.meta b/Assets/Domain/Character/Script/Facade/CharacterFacade.cs.meta new file mode 100644 index 0000000..ae723b5 --- /dev/null +++ b/Assets/Domain/Character/Script/Facade/CharacterFacade.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 6ae88dd8c9dcaab448ad7d8834e80f8e \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Facade/ICharacterFacade.cs b/Assets/Domain/Character/Script/Facade/ICharacterFacade.cs new file mode 100644 index 0000000..7819a2c --- /dev/null +++ b/Assets/Domain/Character/Script/Facade/ICharacterFacade.cs @@ -0,0 +1,83 @@ +namespace ProjectVG.Domain.Character.Service +{ + /// + /// ์บ๋ฆญํ„ฐ ์ œ์–ด๋ฅผ ์œ„ํ•œ ํŒŒ์‚ฌ๋“œ ์ธํ„ฐํŽ˜์ด์Šค + /// + public interface ICharacterFacade + { + /// + /// ํŒŒ์‚ฌ๋“œ๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค. + /// + void Initialize(); + + /// + /// ํŒŒ์‚ฌ๋“œ๋ฅผ ์ข…๋ฃŒํ•œ๋‹ค. + /// + void Shutdown(); + + /// + /// ์บ๋ฆญํ„ฐ๋ฅผ ๋“ฑ๋กํ•œ๋‹ค. + /// + /// ์บ๋ฆญํ„ฐ ID + /// ์‚ฌ์ „ ๋กœ๋“œ ์—ฌ๋ถ€ + void RegisterCharacter(string characterId, bool preload = false); + + /// + /// ์บ๋ฆญํ„ฐ ๋“ฑ๋ก์„ ํ•ด์ œํ•œ๋‹ค. + /// + /// ์บ๋ฆญํ„ฐ ID + void UnregisterCharacter(string characterId); + + /// + /// ํ™œ์„ฑ ์บ๋ฆญํ„ฐ๋ฅผ ์„ค์ •ํ•œ๋‹ค. + /// + /// ์บ๋ฆญํ„ฐ ID + void SetActiveCharacter(string characterId); + + /// + /// ํ™œ์„ฑ ์บ๋ฆญํ„ฐ๋ฅผ ํ•ด์ œํ•œ๋‹ค. + /// + void ClearActiveCharacter(); + + /// + /// ์•ก์…˜์„ ์ ์šฉํ•œ๋‹ค. + /// + /// ์•ก์…˜ ์š”์ฒญ + /// ์•ก์…˜ ํ•ธ๋“ค + CharacterActionHandle ApplyAction(CharacterActionRequest request); + + /// + /// ์•ก์…˜์„ ์ทจ์†Œํ•œ๋‹ค. + /// + /// ์•ก์…˜ ID + void CancelAction(string actionId); + + /// + /// ๋ชจ๋“  ์•ก์…˜์„ ์ทจ์†Œํ•œ๋‹ค. + /// + void CancelAllActions(); + + /// + /// ์Œ์„ฑ ์žฌ์ƒ ์‹œ์ž‘์„ ์•Œ๋ฆฐ๋‹ค. + /// + void OnVoiceStarted(); + + /// + /// ์Œ์„ฑ ์žฌ์ƒ ์ข…๋ฃŒ๋ฅผ ์•Œ๋ฆฐ๋‹ค. + /// + void OnVoiceFinished(); + + /// + /// ํ™œ์„ฑ ์บ๋ฆญํ„ฐ ID๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. + /// + /// ํ™œ์„ฑ ์บ๋ฆญํ„ฐ ID + string GetActiveCharacterId(); + + /// + /// ์บ๋ฆญํ„ฐ ๋“ฑ๋ก ์—ฌ๋ถ€๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. + /// + /// ์บ๋ฆญํ„ฐ ID + /// ๋“ฑ๋ก ์—ฌ๋ถ€ + bool IsRegistered(string characterId); + } +} diff --git a/Assets/Domain/Character/Script/Facade/ICharacterFacade.cs.meta b/Assets/Domain/Character/Script/Facade/ICharacterFacade.cs.meta new file mode 100644 index 0000000..ff16162 --- /dev/null +++ b/Assets/Domain/Character/Script/Facade/ICharacterFacade.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 5f24a632b53d90a458f448238a1de2d8 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Manager/CharacterModelManager.cs b/Assets/Domain/Character/Script/Manager/CharacterModelManager.cs new file mode 100644 index 0000000..e91f802 --- /dev/null +++ b/Assets/Domain/Character/Script/Manager/CharacterModelManager.cs @@ -0,0 +1,268 @@ +using UnityEngine; +using Cysharp.Threading.Tasks; +using System.Collections.Generic; +using ProjectVG.Domain.Character.Live2D.Model; + +namespace ProjectVG.Domain.Character.Service +{ + /// + /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์˜ ์ˆ˜๋ช…์ฃผ๊ธฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋งค๋‹ˆ์ € + /// + public class CharacterModelManager : MonoBehaviour, ICharacterModelManager + { + #region Private Fields + + private Transform _modelRoot; + private Live2DModelRegistry _modelRegistry; + private readonly Dictionary _characterIdToInstance = new Dictionary(); + private string _activeCharacterId; + + #endregion + + #region Unity Lifecycle + + #endregion + + #region Public Methods + + /// + /// ๋งค๋‹ˆ์ €๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค + /// + public void Initialize(Transform modelRoot, Live2DModelRegistry modelRegistry) + { + _modelRoot = modelRoot; + _modelRegistry = modelRegistry; + } + + /// + /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ๋กœ๋“œํ•œ๋‹ค + /// + public void LoadModel(string characterId, bool activateImmediately = false) + { + if (!ValidateLoadRequest(characterId, activateImmediately)) { + return; + } + + var config = GetCharacterConfig(characterId); + if (config == null) { + Debug.LogError($"[CharacterModelManager] ์บ๋ฆญํ„ฐ '{characterId}'์˜ ์„ค์ •์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + return; + } + + var instance = CreateModelInstance(config, characterId); + if (instance == null) { + return; + } + + RegisterModelInstance(characterId, instance, activateImmediately); + } + + /// + /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ๋น„๋™๊ธฐ๋กœ ๋กœ๋“œํ•œ๋‹ค + /// + public async UniTask LoadModelAsync(string characterId, bool activateImmediately = false) + { + if (!ValidateLoadRequest(characterId, activateImmediately)) { + return; + } + + var config = GetCharacterConfig(characterId); + if (config == null) { + Debug.LogError($"[CharacterModelManager] ์บ๋ฆญํ„ฐ '{characterId}'์˜ ์„ค์ •์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + return; + } + + var instance = await CreateModelInstanceAsync(config, characterId); + if (instance == null) { + return; + } + + RegisterModelInstance(characterId, instance, activateImmediately); + } + + /// + /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ์–ธ๋กœ๋“œํ•œ๋‹ค + /// + public void UnloadModel(string characterId) + { + if (!_characterIdToInstance.TryGetValue(characterId, out var instance)) { + return; + } + + if (instance != null) { + Destroy(instance); + } + + _characterIdToInstance.Remove(characterId); + + if (_activeCharacterId == characterId) { + _activeCharacterId = null; + } + } + + /// + /// ๋ชจ๋“  ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ์–ธ๋กœ๋“œํ•œ๋‹ค + /// + public void UnloadAll() + { + foreach (var kvp in _characterIdToInstance) { + if (kvp.Value != null) { + Destroy(kvp.Value); + } + } + + _characterIdToInstance.Clear(); + _activeCharacterId = null; + } + + /// + /// ์ง€์ •๋œ ์บ๋ฆญํ„ฐ๋ฅผ ํ™œ์„ฑํ™”ํ•œ๋‹ค + /// + public void ActivateModel(string characterId) + { + if (!_characterIdToInstance.TryGetValue(characterId, out var target)) { + Debug.LogWarning($"[CharacterModelManager] ์บ๋ฆญํ„ฐ '{characterId}'๊ฐ€ ๋กœ๋“œ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."); + return; + } + + DeactivateAllModels(); + + target.SetActive(true); + _activeCharacterId = characterId; + } + + /// + /// ํ™œ์„ฑ ๋ชจ๋ธ์„ ๋น„ํ™œ์„ฑํ™”ํ•œ๋‹ค + /// + public void DeactivateModel() + { + if (string.IsNullOrEmpty(_activeCharacterId)) { + return; + } + + if (_characterIdToInstance.TryGetValue(_activeCharacterId, out var current) && current != null) { + current.SetActive(false); + } + + _activeCharacterId = null; + } + + /// + /// ํ™œ์„ฑ ์บ๋ฆญํ„ฐ ID๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค + /// + public string GetActiveModelId() + { + return _activeCharacterId; + } + + /// + /// ์บ๋ฆญํ„ฐ ๋กœ๋“œ ์—ฌ๋ถ€๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค + /// + public bool IsLoaded(string characterId) + { + return !string.IsNullOrEmpty(characterId) && _characterIdToInstance.ContainsKey(characterId); + } + + #endregion + + #region Private Methods + + + + /// + /// ๋กœ๋“œ ์š”์ฒญ์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์ฆํ•œ๋‹ค + /// + private bool ValidateLoadRequest(string characterId, bool activateImmediately) + { + if (string.IsNullOrEmpty(characterId)) { + Debug.LogWarning("[CharacterModelManager] ์บ๋ฆญํ„ฐ ID๊ฐ€ null์ด๊ฑฐ๋‚˜ ๋น„์–ด์žˆ์Šต๋‹ˆ๋‹ค."); + return false; + } + + if (_characterIdToInstance.TryGetValue(characterId, out var existing)) { + if (activateImmediately) { + ActivateModel(characterId); + } + return false; + } + + return true; + } + + /// + /// ๋ชจ๋ธ ์ธ์Šคํ„ด์Šค๋ฅผ ๋“ฑ๋กํ•˜๊ณ  ํ•„์š”์‹œ ํ™œ์„ฑํ™”ํ•œ๋‹ค + /// + private void RegisterModelInstance(string characterId, GameObject instance, bool activateImmediately) + { + _characterIdToInstance[characterId] = instance; + + if (activateImmediately) { + ActivateModel(characterId); + } + } + + /// + /// ์บ๋ฆญํ„ฐ ์„ค์ •์„ ๊ฐ€์ ธ์˜จ๋‹ค + /// + private Live2DModelConfig GetCharacterConfig(string characterId) + { + if (_modelRegistry != null && _modelRegistry.TryGetConfig(characterId, out var config)) { + return config; + } + + return null; + } + + /// + /// ๋ชจ๋ธ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค + /// + private GameObject CreateModelInstance(Live2DModelConfig config, string characterId) + { + if (config == null || config.CharacterPrefab == null) { + Debug.LogError($"[CharacterModelManager] ์บ๋ฆญํ„ฐ '{characterId}'์˜ ํ”„๋ฆฌํŒน์ด null์ž…๋‹ˆ๋‹ค."); + return null; + } + + var parent = _modelRoot != null ? _modelRoot : transform; + + var instance = Instantiate(config.CharacterPrefab, parent); + instance.name = characterId; + instance.SetActive(false); + return instance; + } + + /// + /// ๋ชจ๋ธ ์ธ์Šคํ„ด์Šค๋ฅผ ๋น„๋™๊ธฐ๋กœ ์ƒ์„ฑํ•œ๋‹ค + /// + private async UniTask CreateModelInstanceAsync(Live2DModelConfig config, string characterId) + { + if (config == null || config.CharacterPrefab == null) { + Debug.LogError($"[CharacterModelManager] ์บ๋ฆญํ„ฐ '{characterId}'์˜ ํ”„๋ฆฌํŒน์ด null์ž…๋‹ˆ๋‹ค."); + return null; + } + + var parent = _modelRoot != null ? _modelRoot : transform; + + var instance = Instantiate(config.CharacterPrefab, parent); + instance.name = characterId; + instance.SetActive(false); + + await UniTask.Yield(); + return instance; + } + + /// + /// ๋ชจ๋“  ๋ชจ๋ธ์„ ๋น„ํ™œ์„ฑํ™”ํ•œ๋‹ค + /// + private void DeactivateAllModels() + { + foreach (var kvp in _characterIdToInstance) { + if (kvp.Value != null) { + kvp.Value.SetActive(false); + } + } + } + + #endregion + } +} diff --git a/Assets/Domain/Character/Script/Manager/CharacterModelManager.cs.meta b/Assets/Domain/Character/Script/Manager/CharacterModelManager.cs.meta new file mode 100644 index 0000000..cdc29a7 --- /dev/null +++ b/Assets/Domain/Character/Script/Manager/CharacterModelManager.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 3d3d2b2f6a9c04348b8fab2f4e3e6a7c \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs b/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs new file mode 100644 index 0000000..c22d1bb --- /dev/null +++ b/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs @@ -0,0 +1,61 @@ +using UnityEngine; +using Cysharp.Threading.Tasks; + +namespace ProjectVG.Domain.Character.Service +{ + /// + /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์˜ ์ˆ˜๋ช…์ฃผ๊ธฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค + /// + public interface ICharacterModelManager + { + + /// + /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ๋กœ๋“œํ•œ๋‹ค. + /// + /// ๋กœ๋“œํ•  ์บ๋ฆญํ„ฐ์˜ ๊ณ ์œ  ID + /// ์‚ฌ์ „ ๋กœ๋“œ ์—ฌ๋ถ€ (true๋ฉด ๋กœ๋“œ ํ›„ ์ฆ‰์‹œ ํ™œ์„ฑํ™”) + void LoadModel(string characterId, bool preload = false); + + /// + /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ๋น„๋™๊ธฐ๋กœ ๋กœ๋“œํ•œ๋‹ค. + /// + /// ๋กœ๋“œํ•  ์บ๋ฆญํ„ฐ์˜ ๊ณ ์œ  ID + /// ์‚ฌ์ „ ๋กœ๋“œ ์—ฌ๋ถ€ (true๋ฉด ๋กœ๋“œ ํ›„ ์ฆ‰์‹œ ํ™œ์„ฑํ™”) + UniTask LoadModelAsync(string characterId, bool preload = false); + + /// + /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ์–ธ๋กœ๋“œํ•œ๋‹ค. + /// + /// ์–ธ๋กœ๋“œํ•  ์บ๋ฆญํ„ฐ์˜ ๊ณ ์œ  ID + void UnloadModel(string characterId); + + /// + /// ๋ชจ๋“  ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ์–ธ๋กœ๋“œํ•œ๋‹ค. + /// + void UnloadAll(); + + /// + /// ์ง€์ •๋œ ์บ๋ฆญํ„ฐ๋ฅผ ํ™œ์„ฑํ™”ํ•œ๋‹ค. + /// + /// ํ™œ์„ฑํ™”ํ•  ์บ๋ฆญํ„ฐ์˜ ๊ณ ์œ  ID + void ActivateModel(string characterId); + + /// + /// ํ™œ์„ฑ ๋ชจ๋ธ์„ ๋น„ํ™œ์„ฑํ™”ํ•œ๋‹ค. + /// + void DeactivateModel(); + + /// + /// ์บ๋ฆญํ„ฐ ๋กœ๋“œ ์—ฌ๋ถ€๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. + /// + /// ํ™•์ธํ•  ์บ๋ฆญํ„ฐ์˜ ๊ณ ์œ  ID + /// ์บ๋ฆญํ„ฐ๊ฐ€ ๋กœ๋“œ๋˜์–ด ์žˆ์œผ๋ฉด true, ์•„๋‹ˆ๋ฉด false + bool IsLoaded(string characterId); + + /// + /// ํ™œ์„ฑ ์บ๋ฆญํ„ฐ ID๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. + /// + /// ํ™œ์„ฑ ์บ๋ฆญํ„ฐ์˜ ID, ํ™œ์„ฑ ์บ๋ฆญํ„ฐ๊ฐ€ ์—†์œผ๋ฉด null + string GetActiveModelId(); + } +} diff --git a/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs.meta b/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs.meta new file mode 100644 index 0000000..218a1ad --- /dev/null +++ b/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 7344ae5c9f3d5af44bbf485b38020449 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Service.meta b/Assets/Domain/Character/Script/Service.meta new file mode 100644 index 0000000..375e32d --- /dev/null +++ b/Assets/Domain/Character/Script/Service.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 39450afb9f613d84fab1d2b38836c892 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Domain/Character/Script/Service/ICharacterActionResolver.cs b/Assets/Domain/Character/Script/Service/ICharacterActionResolver.cs new file mode 100644 index 0000000..18a40e9 --- /dev/null +++ b/Assets/Domain/Character/Script/Service/ICharacterActionResolver.cs @@ -0,0 +1,15 @@ +namespace ProjectVG.Domain.Character.Service +{ + /// + /// ์บ๋ฆญํ„ฐ ์•ก์…˜ ์š”์ฒญ์„ ํ•ด์„ํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค + /// + public interface ICharacterActionResolver + { + /// + /// ์•ก์…˜ ์š”์ฒญ์„ ํ•ด์„ํ•˜์—ฌ ๊ตฌ์ฒด์ ์ธ ์•ก์…˜ ์ •๋ณด๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค. + /// + /// ์•ก์…˜ ์š”์ฒญ + /// ํ•ด์„๋œ ์•ก์…˜ ์ •๋ณด + CharacterResolvedAction Resolve(CharacterActionRequest request); + } +} diff --git a/Assets/Domain/Character/Script/Service/ICharacterActionResolver.cs.meta b/Assets/Domain/Character/Script/Service/ICharacterActionResolver.cs.meta new file mode 100644 index 0000000..6c5bb39 --- /dev/null +++ b/Assets/Domain/Character/Script/Service/ICharacterActionResolver.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: cf028008204016d468f696b5120e7109 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Service/ICharacterActionService.cs b/Assets/Domain/Character/Script/Service/ICharacterActionService.cs new file mode 100644 index 0000000..42fe9c5 --- /dev/null +++ b/Assets/Domain/Character/Script/Service/ICharacterActionService.cs @@ -0,0 +1,45 @@ +using Cysharp.Threading.Tasks; + +namespace ProjectVG.Domain.Character.Service +{ + /// + /// ์บ๋ฆญํ„ฐ ์•ก์…˜ ์‹คํ–‰์„ ๊ด€๋ฆฌํ•˜๋Š” ์„œ๋น„์Šค ์ธํ„ฐํŽ˜์ด์Šค + /// + public interface ICharacterActionService + { + /// + /// ์„œ๋น„์Šค๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค. + /// + void Initialize(); + + /// + /// ์•ก์…˜์„ ํ์— ์ถ”๊ฐ€ํ•œ๋‹ค. + /// + /// ์•ก์…˜ ์š”์ฒญ + /// ์•ก์…˜ ํ•ธ๋“ค + CharacterActionHandle Enqueue(CharacterActionRequest request); + + /// + /// ํŠน์ • ์•ก์…˜์„ ์ค‘์ง€ํ•œ๋‹ค. + /// + /// ์ค‘์ง€ํ•  ์•ก์…˜ ID + void Stop(string actionId); + + /// + /// ๋ชจ๋“  ์•ก์…˜์„ ์ค‘์ง€ํ•œ๋‹ค. + /// + void StopAll(); + + /// + /// ์Œ์„ฑ ๊ฒŒ์ดํŠธ๋ฅผ ์„ค์ •ํ•œ๋‹ค. + /// + /// ์Œ์„ฑ์ด ์žฌ์ƒ ์ค‘์ธ์ง€ ์—ฌ๋ถ€ + void SetVoiceGate(bool isVoicePlaying); + + /// + /// ํ˜„์žฌ ์•ก์…˜ ์ƒํƒœ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. + /// + /// ์•ก์…˜ ์ƒํƒœ + CharacterActionState GetState(); + } +} diff --git a/Assets/Domain/Character/Script/Service/ICharacterActionService.cs.meta b/Assets/Domain/Character/Script/Service/ICharacterActionService.cs.meta new file mode 100644 index 0000000..0989487 --- /dev/null +++ b/Assets/Domain/Character/Script/Service/ICharacterActionService.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: db3c66c11864369439cd34ad5e19c9ce \ No newline at end of file From 731ebebc7fe73d6e3c9a76668f663bbafed63c32 Mon Sep 17 00:00:00 2001 From: WooSH Date: Tue, 19 Aug 2025 00:46:27 +0900 Subject: [PATCH 04/13] =?UTF-8?q?feat:=20nullable=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Core/Audio/AudioRecorder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Core/Audio/AudioRecorder.cs b/Assets/Core/Audio/AudioRecorder.cs index bb40579..0613383 100644 --- a/Assets/Core/Audio/AudioRecorder.cs +++ b/Assets/Core/Audio/AudioRecorder.cs @@ -136,7 +136,7 @@ public bool StartRecording() if (_recordingClip != null) { - AudioClip processedClip = ProcessRecordingClip(actualRecordingDuration); + AudioClip? processedClip = ProcessRecordingClip(actualRecordingDuration); if (processedClip != null) { Debug.Log($"[AudioRecorder] ์Œ์„ฑ ๋…น์Œ ์™„๋ฃŒ๋จ ({actualRecordingDuration:F1}์ดˆ, {processedClip.samples} ์ƒ˜ํ”Œ)"); From 281826c595c8ac1360e6628c351aa739e3a4090f Mon Sep 17 00:00:00 2001 From: WooSH Date: Tue, 19 Aug 2025 13:39:05 +0900 Subject: [PATCH 05/13] =?UTF-8?q?feat:=20=EC=94=AC=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/App/Scenes/MainSence.unity | 63 ++++++++++++++----- .../Character/Live2DModelRegistry.asset | 2 + 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/Assets/App/Scenes/MainSence.unity b/Assets/App/Scenes/MainSence.unity index 3f2933f..32a6f7d 100644 --- a/Assets/App/Scenes/MainSence.unity +++ b/Assets/App/Scenes/MainSence.unity @@ -378,12 +378,6 @@ MonoBehaviour: m_EditorClassIdentifier: _btnVoice: {fileID: 1411584568} _btnVoiceStop: {fileID: 948060135} - _txtVoiceStatus: {fileID: 1273342020} - _progressBar: {fileID: 0} - _maxRecordingTime: 30 - _voiceStatusRecording: "\uB179\uC74C \uC911..." - _voiceStatusProcessing: "\uC74C\uC131\uC744 \uD14D\uC2A4\uD2B8\uB85C \uBCC0\uD658 - \uC911..." --- !u!114 &332900995 MonoBehaviour: m_ObjectHideFlags: 0 @@ -718,6 +712,7 @@ MonoBehaviour: _userId: bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb _enableMessageQueue: 1 _maxQueueSize: 100 + _enableLive2DIntegration: 1 --- !u!4 &829067253 Transform: m_ObjectHideFlags: 0 @@ -733,6 +728,50 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &873552997 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 873552999} + - component: {fileID: 873552998} + m_Layer: 0 + m_Name: CharacterManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &873552998 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 873552997} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6ae88dd8c9dcaab448ad7d8834e80f8e, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!4 &873552999 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 873552997} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &920517454 GameObject: m_ObjectHideFlags: 0 @@ -1160,17 +1199,6 @@ MonoBehaviour: _audioSource: {fileID: 0} _audioMixerGroup: {fileID: 0} _poolSize: 10 ---- !u!114 &1273342020 stripped -MonoBehaviour: - m_CorrespondingSourceObject: {fileID: 8225492411684342768, guid: 4e9594e3adccfc04793a3b9f79181ebd, type: 3} - m_PrefabInstance: {fileID: 2142640635} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} - m_Name: - m_EditorClassIdentifier: --- !u!224 &1274474669 stripped RectTransform: m_CorrespondingSourceObject: {fileID: 8165641762294667086, guid: cd9756d05ff7ef847b3abde3e768a611, type: 3} @@ -1553,3 +1581,4 @@ SceneRoots: - {fileID: 133449156} - {fileID: 332900996} - {fileID: 622824879} + - {fileID: 873552999} diff --git a/Assets/Resources/Character/Live2DModelRegistry.asset b/Assets/Resources/Character/Live2DModelRegistry.asset index 72bdee3..59a664b 100644 --- a/Assets/Resources/Character/Live2DModelRegistry.asset +++ b/Assets/Resources/Character/Live2DModelRegistry.asset @@ -15,3 +15,5 @@ MonoBehaviour: _entries: - characterId: zero characterConfig: {fileID: 11400000, guid: 4c6d1f5cb9556f24c843f3e9fe14d49e, type: 2} + - characterId: zero + characterConfig: {fileID: 11400000, guid: 4c6d1f5cb9556f24c843f3e9fe14d49e, type: 2} From dda40f78b5f1908cf6ab34860c1680d147b26af6 Mon Sep 17 00:00:00 2001 From: WooSH Date: Tue, 19 Aug 2025 13:52:50 +0900 Subject: [PATCH 06/13] =?UTF-8?q?feat:=20=EC=83=88=EB=A1=9C=EC=9A=B4=20?= =?UTF-8?q?=EB=AA=A8=EB=8D=B8=20=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ์น˜์™€์งฑ ๋ชจ๋ธ์€ ์œ ๋ฃŒ ๋ชจ๋ธ์ด๋ฏ€๋กœ ์œ ์ถœ ๋ฐฉ์ง€, ์ œ์ž‘์ž์—๊ฒŒ ์†ํ•ด๋ฅผ ์ž…ํžˆ์ง€ ์•Š๋„๋ก ์œ ์˜ํ•œ๋‹ค. - ๊ฐœ๋ฐœ์ž์ธ ๊ฒฝ์šฐ ์น˜์™€์งฑ ๋ชจ๋ธ์„ ์š”์ฒญํ•˜์„ธ์š” --- .gitignore | 1 + Assets/Domain/Character/Model/Chikuwa.meta | 8 ++++ .../Character/Model/Chikuwa/ziraitikuwa.meta | 8 ++++ Assets/Domain/Character/Model/Sample.meta | 8 ++++ .../Model/{ => Sample}/natoriConfig.asset | 0 .../{ => Sample}/natoriConfig.asset.meta | 0 .../{ => Sample}/natori_pro_Sample.prefab | 0 .../natori_pro_Sample.prefab.meta | 0 .../Model/{ => Sample}/natori_pro_en.meta | 0 .../{ => Sample}/natori_pro_en/ReadMe.txt | 0 .../natori_pro_en/ReadMe.txt.meta | 0 .../natori_pro_en/natori_pro_exp_t03.can3 | Bin .../natori_pro_exp_t03.can3.meta | 0 .../natori_pro_en/natori_pro_motions_t03.can3 | Bin .../natori_pro_motions_t03.can3.meta | 0 .../natori_pro_en/natori_pro_t06.cmo3 | Bin .../natori_pro_en/natori_pro_t06.cmo3.meta | 0 .../{ => Sample}/natori_pro_en/runtime.meta | 0 .../natori_pro_en/runtime/exp.meta | 0 .../runtime/exp/Angry.exp3.asset | 0 .../runtime/exp/Angry.exp3.asset.meta | 0 .../natori_pro_en/runtime/exp/Angry.exp3.json | 0 .../runtime/exp/Angry.exp3.json.meta | 0 .../runtime/exp/Blushing.exp3.asset | 0 .../runtime/exp/Blushing.exp3.asset.meta | 0 .../runtime/exp/Blushing.exp3.json | 0 .../runtime/exp/Blushing.exp3.json.meta | 0 .../runtime/exp/Normal.exp3.asset | 0 .../runtime/exp/Normal.exp3.asset.meta | 0 .../runtime/exp/Normal.exp3.json | 0 .../runtime/exp/Normal.exp3.json.meta | 0 .../natori_pro_en/runtime/exp/Sad.exp3.asset | 0 .../runtime/exp/Sad.exp3.asset.meta | 0 .../natori_pro_en/runtime/exp/Sad.exp3.json | 0 .../runtime/exp/Sad.exp3.json.meta | 0 .../runtime/exp/Smile.exp3.asset | 0 .../runtime/exp/Smile.exp3.asset.meta | 0 .../natori_pro_en/runtime/exp/Smile.exp3.json | 0 .../runtime/exp/Smile.exp3.json.meta | 0 .../runtime/exp/Surprised.exp3.asset | 0 .../runtime/exp/Surprised.exp3.asset.meta | 0 .../runtime/exp/Surprised.exp3.json | 0 .../runtime/exp/Surprised.exp3.json.meta | 0 .../runtime/exp/exp_01.exp3.asset | 0 .../runtime/exp/exp_01.exp3.asset.meta | 0 .../runtime/exp/exp_01.exp3.json | 0 .../runtime/exp/exp_01.exp3.json.meta | 0 .../runtime/exp/exp_02.exp3.asset | 0 .../runtime/exp/exp_02.exp3.asset.meta | 0 .../runtime/exp/exp_02.exp3.json | 0 .../runtime/exp/exp_02.exp3.json.meta | 0 .../runtime/exp/exp_03.exp3.asset | 0 .../runtime/exp/exp_03.exp3.asset.meta | 0 .../runtime/exp/exp_03.exp3.json | 0 .../runtime/exp/exp_03.exp3.json.meta | 0 .../runtime/exp/exp_04.exp3.asset | 0 .../runtime/exp/exp_04.exp3.asset.meta | 0 .../runtime/exp/exp_04.exp3.json | 0 .../runtime/exp/exp_04.exp3.json.meta | 0 .../runtime/exp/exp_05.exp3.asset | 0 .../runtime/exp/exp_05.exp3.asset.meta | 0 .../runtime/exp/exp_05.exp3.json | 0 .../runtime/exp/exp_05.exp3.json.meta | 0 .../natori_pro_en/runtime/motions.meta | 0 .../natori_pro_en/runtime/motions/mtn_00.anim | 0 .../runtime/motions/mtn_00.anim.meta | 0 .../runtime/motions/mtn_00.fade.asset | 0 .../runtime/motions/mtn_00.fade.asset.meta | 0 .../runtime/motions/mtn_00.motion3.json | 0 .../runtime/motions/mtn_00.motion3.json.meta | 0 .../natori_pro_en/runtime/motions/mtn_01.anim | 0 .../runtime/motions/mtn_01.anim.meta | 0 .../runtime/motions/mtn_01.fade.asset | 0 .../runtime/motions/mtn_01.fade.asset.meta | 0 .../runtime/motions/mtn_01.motion3.json | 0 .../runtime/motions/mtn_01.motion3.json.meta | 0 .../natori_pro_en/runtime/motions/mtn_02.anim | 0 .../runtime/motions/mtn_02.anim.meta | 0 .../runtime/motions/mtn_02.fade.asset | 0 .../runtime/motions/mtn_02.fade.asset.meta | 0 .../runtime/motions/mtn_02.motion3.json | 0 .../runtime/motions/mtn_02.motion3.json.meta | 0 .../natori_pro_en/runtime/motions/mtn_03.anim | 0 .../runtime/motions/mtn_03.anim.meta | 0 .../runtime/motions/mtn_03.fade.asset | 0 .../runtime/motions/mtn_03.fade.asset.meta | 0 .../runtime/motions/mtn_03.motion3.json | 0 .../runtime/motions/mtn_03.motion3.json.meta | 0 .../natori_pro_en/runtime/motions/mtn_04.anim | 0 .../runtime/motions/mtn_04.anim.meta | 0 .../runtime/motions/mtn_04.fade.asset | 0 .../runtime/motions/mtn_04.fade.asset.meta | 0 .../runtime/motions/mtn_04.motion3.json | 0 .../runtime/motions/mtn_04.motion3.json.meta | 0 .../natori_pro_en/runtime/motions/mtn_05.anim | 0 .../runtime/motions/mtn_05.anim.meta | 0 .../runtime/motions/mtn_05.fade.asset | 0 .../runtime/motions/mtn_05.fade.asset.meta | 0 .../runtime/motions/mtn_05.motion3.json | 0 .../runtime/motions/mtn_05.motion3.json.meta | 0 .../natori_pro_en/runtime/motions/mtn_06.anim | 0 .../runtime/motions/mtn_06.anim.meta | 0 .../runtime/motions/mtn_06.fade.asset | 0 .../runtime/motions/mtn_06.fade.asset.meta | 0 .../runtime/motions/mtn_06.motion3.json | 0 .../runtime/motions/mtn_06.motion3.json.meta | 0 .../natori_pro_en/runtime/motions/mtn_07.anim | 0 .../runtime/motions/mtn_07.anim.meta | 0 .../runtime/motions/mtn_07.fade.asset | 0 .../runtime/motions/mtn_07.fade.asset.meta | 0 .../runtime/motions/mtn_07.motion3.json | 0 .../runtime/motions/mtn_07.motion3.json.meta | 0 .../natori_pro_en/runtime/natori.pose3.json | 0 .../runtime/natori.pose3.json.meta | 0 .../runtime/natori_pro_t06.4096.meta | 0 .../natori_pro_t06.4096/texture_00.png | Bin .../natori_pro_t06.4096/texture_00.png.meta | 0 .../runtime/natori_pro_t06.asset | 0 .../runtime/natori_pro_t06.asset.meta | 0 .../runtime/natori_pro_t06.cdi3.json | 0 .../runtime/natori_pro_t06.cdi3.json.meta | 0 .../runtime/natori_pro_t06.controller | 0 .../runtime/natori_pro_t06.controller.meta | 0 .../natori_pro_en/runtime/natori_pro_t06.moc3 | Bin .../runtime/natori_pro_t06.moc3.meta | 0 .../runtime/natori_pro_t06.model3.json | 0 .../runtime/natori_pro_t06.model3.json.meta | 0 .../runtime/natori_pro_t06.physics3.json | 0 .../runtime/natori_pro_t06.physics3.json.meta | 0 .../runtime/natori_pro_t06.prefab | 0 .../runtime/natori_pro_t06.prefab.meta | 0 .../runtime/natori_pro_t06MaskTexture.asset | 0 .../natori_pro_t06MaskTexture.asset.meta | 0 .../runtime/runtime.expressionList.asset | 0 .../runtime/runtime.expressionList.asset.meta | 0 .../runtime/runtime.fadeMotionList.asset | 0 .../runtime/runtime.fadeMotionList.asset.meta | 0 .../Script/Facade/CharacterFacade.cs | 45 ++++++++---------- .../Script/Manager/ICharacterModelManager.cs | 18 +++++-- .../Character/Model/CharacterZero.asset | 33 ------------- .../Character/Model/Live2DModelConfig.asset | 26 ++++++++++ ...sset.meta => Live2DModelConfig.asset.meta} | 2 +- 142 files changed, 85 insertions(+), 64 deletions(-) create mode 100644 Assets/Domain/Character/Model/Chikuwa.meta create mode 100644 Assets/Domain/Character/Model/Chikuwa/ziraitikuwa.meta create mode 100644 Assets/Domain/Character/Model/Sample.meta rename Assets/Domain/Character/Model/{ => Sample}/natoriConfig.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natoriConfig.asset.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_Sample.prefab (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_Sample.prefab.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/ReadMe.txt (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/ReadMe.txt.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/natori_pro_exp_t03.can3 (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/natori_pro_exp_t03.can3.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/natori_pro_motions_t03.can3 (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/natori_pro_motions_t03.can3.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/natori_pro_t06.cmo3 (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/natori_pro_t06.cmo3.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Angry.exp3.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Angry.exp3.asset.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Angry.exp3.json (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Angry.exp3.json.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Blushing.exp3.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Blushing.exp3.asset.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Blushing.exp3.json (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Blushing.exp3.json.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Normal.exp3.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Normal.exp3.asset.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Normal.exp3.json (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Normal.exp3.json.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Sad.exp3.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Sad.exp3.asset.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Sad.exp3.json (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Sad.exp3.json.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Smile.exp3.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Smile.exp3.asset.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Smile.exp3.json (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Smile.exp3.json.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Surprised.exp3.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Surprised.exp3.asset.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Surprised.exp3.json (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/Surprised.exp3.json.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/exp_01.exp3.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/exp_01.exp3.asset.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/exp_01.exp3.json (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/exp_01.exp3.json.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/exp_02.exp3.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/exp_02.exp3.asset.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/exp_02.exp3.json (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/exp_02.exp3.json.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/exp_03.exp3.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/exp_03.exp3.asset.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/exp_03.exp3.json (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/exp_03.exp3.json.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/exp_04.exp3.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/exp_04.exp3.asset.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/exp_04.exp3.json (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/exp_04.exp3.json.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/exp_05.exp3.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/exp_05.exp3.asset.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/exp_05.exp3.json (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/exp/exp_05.exp3.json.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_00.anim (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_00.anim.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_00.fade.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_00.fade.asset.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_00.motion3.json (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_00.motion3.json.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_01.anim (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_01.anim.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_01.fade.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_01.fade.asset.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_01.motion3.json (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_01.motion3.json.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_02.anim (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_02.anim.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_02.fade.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_02.fade.asset.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_02.motion3.json (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_02.motion3.json.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_03.anim (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_03.anim.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_03.fade.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_03.fade.asset.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_03.motion3.json (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_03.motion3.json.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_04.anim (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_04.anim.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_04.fade.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_04.fade.asset.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_04.motion3.json (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_04.motion3.json.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_05.anim (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_05.anim.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_05.fade.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_05.fade.asset.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_05.motion3.json (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_05.motion3.json.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_06.anim (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_06.anim.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_06.fade.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_06.fade.asset.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_06.motion3.json (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_06.motion3.json.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_07.anim (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_07.anim.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_07.fade.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_07.fade.asset.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_07.motion3.json (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/motions/mtn_07.motion3.json.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/natori.pose3.json (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/natori.pose3.json.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/natori_pro_t06.4096.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/natori_pro_t06.4096/texture_00.png (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/natori_pro_t06.4096/texture_00.png.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/natori_pro_t06.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/natori_pro_t06.asset.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/natori_pro_t06.cdi3.json (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/natori_pro_t06.cdi3.json.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/natori_pro_t06.controller (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/natori_pro_t06.controller.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/natori_pro_t06.moc3 (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/natori_pro_t06.moc3.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/natori_pro_t06.model3.json (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/natori_pro_t06.model3.json.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/natori_pro_t06.physics3.json (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/natori_pro_t06.physics3.json.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/natori_pro_t06.prefab (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/natori_pro_t06.prefab.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/natori_pro_t06MaskTexture.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/natori_pro_t06MaskTexture.asset.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/runtime.expressionList.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/runtime.expressionList.asset.meta (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/runtime.fadeMotionList.asset (100%) rename Assets/Domain/Character/Model/{ => Sample}/natori_pro_en/runtime/runtime.fadeMotionList.asset.meta (100%) delete mode 100644 Assets/Resources/Character/Model/CharacterZero.asset create mode 100644 Assets/Resources/Character/Model/Live2DModelConfig.asset rename Assets/Resources/Character/Model/{CharacterZero.asset.meta => Live2DModelConfig.asset.meta} (79%) diff --git a/.gitignore b/.gitignore index 485374c..4b284fc 100644 --- a/.gitignore +++ b/.gitignore @@ -119,3 +119,4 @@ InitTestScene*.unity* # ํŠน์ • ํ”Œ๋Ÿฌ๊ทธ์ธ /Assets/Plugins/FiveMinuteChat /Assets/Plugins/WebGLTemplates +/Assets/Domain/Character/Model/Chikuwa/ziraitikuwa diff --git a/Assets/Domain/Character/Model/Chikuwa.meta b/Assets/Domain/Character/Model/Chikuwa.meta new file mode 100644 index 0000000..9b5cda9 --- /dev/null +++ b/Assets/Domain/Character/Model/Chikuwa.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c4265468216d110429e94c574b954da1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Domain/Character/Model/Chikuwa/ziraitikuwa.meta b/Assets/Domain/Character/Model/Chikuwa/ziraitikuwa.meta new file mode 100644 index 0000000..3b8b80e --- /dev/null +++ b/Assets/Domain/Character/Model/Chikuwa/ziraitikuwa.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f5ef28f9c36ba994ba303f866721f03e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Domain/Character/Model/Sample.meta b/Assets/Domain/Character/Model/Sample.meta new file mode 100644 index 0000000..6c9c9f0 --- /dev/null +++ b/Assets/Domain/Character/Model/Sample.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 58240e09a72cdad44921af9fe40f68bc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Domain/Character/Model/natoriConfig.asset b/Assets/Domain/Character/Model/Sample/natoriConfig.asset similarity index 100% rename from Assets/Domain/Character/Model/natoriConfig.asset rename to Assets/Domain/Character/Model/Sample/natoriConfig.asset diff --git a/Assets/Domain/Character/Model/natoriConfig.asset.meta b/Assets/Domain/Character/Model/Sample/natoriConfig.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natoriConfig.asset.meta rename to Assets/Domain/Character/Model/Sample/natoriConfig.asset.meta diff --git a/Assets/Domain/Character/Model/natori_pro_Sample.prefab b/Assets/Domain/Character/Model/Sample/natori_pro_Sample.prefab similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_Sample.prefab rename to Assets/Domain/Character/Model/Sample/natori_pro_Sample.prefab diff --git a/Assets/Domain/Character/Model/natori_pro_Sample.prefab.meta b/Assets/Domain/Character/Model/Sample/natori_pro_Sample.prefab.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_Sample.prefab.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_Sample.prefab.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/ReadMe.txt b/Assets/Domain/Character/Model/Sample/natori_pro_en/ReadMe.txt similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/ReadMe.txt rename to Assets/Domain/Character/Model/Sample/natori_pro_en/ReadMe.txt diff --git a/Assets/Domain/Character/Model/natori_pro_en/ReadMe.txt.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/ReadMe.txt.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/ReadMe.txt.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/ReadMe.txt.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/natori_pro_exp_t03.can3 b/Assets/Domain/Character/Model/Sample/natori_pro_en/natori_pro_exp_t03.can3 similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/natori_pro_exp_t03.can3 rename to Assets/Domain/Character/Model/Sample/natori_pro_en/natori_pro_exp_t03.can3 diff --git a/Assets/Domain/Character/Model/natori_pro_en/natori_pro_exp_t03.can3.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/natori_pro_exp_t03.can3.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/natori_pro_exp_t03.can3.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/natori_pro_exp_t03.can3.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/natori_pro_motions_t03.can3 b/Assets/Domain/Character/Model/Sample/natori_pro_en/natori_pro_motions_t03.can3 similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/natori_pro_motions_t03.can3 rename to Assets/Domain/Character/Model/Sample/natori_pro_en/natori_pro_motions_t03.can3 diff --git a/Assets/Domain/Character/Model/natori_pro_en/natori_pro_motions_t03.can3.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/natori_pro_motions_t03.can3.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/natori_pro_motions_t03.can3.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/natori_pro_motions_t03.can3.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/natori_pro_t06.cmo3 b/Assets/Domain/Character/Model/Sample/natori_pro_en/natori_pro_t06.cmo3 similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/natori_pro_t06.cmo3 rename to Assets/Domain/Character/Model/Sample/natori_pro_en/natori_pro_t06.cmo3 diff --git a/Assets/Domain/Character/Model/natori_pro_en/natori_pro_t06.cmo3.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/natori_pro_t06.cmo3.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/natori_pro_t06.cmo3.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/natori_pro_t06.cmo3.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Angry.exp3.asset b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Angry.exp3.asset similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Angry.exp3.asset rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Angry.exp3.asset diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Angry.exp3.asset.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Angry.exp3.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Angry.exp3.asset.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Angry.exp3.asset.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Angry.exp3.json b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Angry.exp3.json similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Angry.exp3.json rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Angry.exp3.json diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Angry.exp3.json.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Angry.exp3.json.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Angry.exp3.json.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Angry.exp3.json.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Blushing.exp3.asset b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Blushing.exp3.asset similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Blushing.exp3.asset rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Blushing.exp3.asset diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Blushing.exp3.asset.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Blushing.exp3.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Blushing.exp3.asset.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Blushing.exp3.asset.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Blushing.exp3.json b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Blushing.exp3.json similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Blushing.exp3.json rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Blushing.exp3.json diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Blushing.exp3.json.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Blushing.exp3.json.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Blushing.exp3.json.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Blushing.exp3.json.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Normal.exp3.asset b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Normal.exp3.asset similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Normal.exp3.asset rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Normal.exp3.asset diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Normal.exp3.asset.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Normal.exp3.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Normal.exp3.asset.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Normal.exp3.asset.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Normal.exp3.json b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Normal.exp3.json similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Normal.exp3.json rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Normal.exp3.json diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Normal.exp3.json.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Normal.exp3.json.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Normal.exp3.json.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Normal.exp3.json.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Sad.exp3.asset b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Sad.exp3.asset similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Sad.exp3.asset rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Sad.exp3.asset diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Sad.exp3.asset.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Sad.exp3.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Sad.exp3.asset.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Sad.exp3.asset.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Sad.exp3.json b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Sad.exp3.json similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Sad.exp3.json rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Sad.exp3.json diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Sad.exp3.json.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Sad.exp3.json.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Sad.exp3.json.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Sad.exp3.json.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Smile.exp3.asset b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Smile.exp3.asset similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Smile.exp3.asset rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Smile.exp3.asset diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Smile.exp3.asset.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Smile.exp3.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Smile.exp3.asset.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Smile.exp3.asset.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Smile.exp3.json b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Smile.exp3.json similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Smile.exp3.json rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Smile.exp3.json diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Smile.exp3.json.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Smile.exp3.json.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Smile.exp3.json.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Smile.exp3.json.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Surprised.exp3.asset b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Surprised.exp3.asset similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Surprised.exp3.asset rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Surprised.exp3.asset diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Surprised.exp3.asset.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Surprised.exp3.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Surprised.exp3.asset.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Surprised.exp3.asset.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Surprised.exp3.json b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Surprised.exp3.json similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Surprised.exp3.json rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Surprised.exp3.json diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Surprised.exp3.json.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Surprised.exp3.json.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/Surprised.exp3.json.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Surprised.exp3.json.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_01.exp3.asset b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_01.exp3.asset similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_01.exp3.asset rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_01.exp3.asset diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_01.exp3.asset.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_01.exp3.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_01.exp3.asset.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_01.exp3.asset.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_01.exp3.json b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_01.exp3.json similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_01.exp3.json rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_01.exp3.json diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_01.exp3.json.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_01.exp3.json.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_01.exp3.json.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_01.exp3.json.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_02.exp3.asset b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_02.exp3.asset similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_02.exp3.asset rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_02.exp3.asset diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_02.exp3.asset.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_02.exp3.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_02.exp3.asset.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_02.exp3.asset.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_02.exp3.json b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_02.exp3.json similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_02.exp3.json rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_02.exp3.json diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_02.exp3.json.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_02.exp3.json.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_02.exp3.json.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_02.exp3.json.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_03.exp3.asset b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_03.exp3.asset similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_03.exp3.asset rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_03.exp3.asset diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_03.exp3.asset.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_03.exp3.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_03.exp3.asset.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_03.exp3.asset.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_03.exp3.json b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_03.exp3.json similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_03.exp3.json rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_03.exp3.json diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_03.exp3.json.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_03.exp3.json.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_03.exp3.json.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_03.exp3.json.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_04.exp3.asset b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_04.exp3.asset similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_04.exp3.asset rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_04.exp3.asset diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_04.exp3.asset.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_04.exp3.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_04.exp3.asset.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_04.exp3.asset.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_04.exp3.json b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_04.exp3.json similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_04.exp3.json rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_04.exp3.json diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_04.exp3.json.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_04.exp3.json.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_04.exp3.json.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_04.exp3.json.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_05.exp3.asset b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_05.exp3.asset similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_05.exp3.asset rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_05.exp3.asset diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_05.exp3.asset.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_05.exp3.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_05.exp3.asset.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_05.exp3.asset.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_05.exp3.json b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_05.exp3.json similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_05.exp3.json rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_05.exp3.json diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_05.exp3.json.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_05.exp3.json.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/exp/exp_05.exp3.json.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_05.exp3.json.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_00.anim b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_00.anim similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_00.anim rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_00.anim diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_00.anim.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_00.anim.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_00.anim.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_00.anim.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_00.fade.asset b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_00.fade.asset similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_00.fade.asset rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_00.fade.asset diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_00.fade.asset.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_00.fade.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_00.fade.asset.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_00.fade.asset.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_00.motion3.json b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_00.motion3.json similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_00.motion3.json rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_00.motion3.json diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_00.motion3.json.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_00.motion3.json.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_00.motion3.json.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_00.motion3.json.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_01.anim b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_01.anim similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_01.anim rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_01.anim diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_01.anim.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_01.anim.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_01.anim.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_01.anim.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_01.fade.asset b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_01.fade.asset similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_01.fade.asset rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_01.fade.asset diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_01.fade.asset.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_01.fade.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_01.fade.asset.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_01.fade.asset.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_01.motion3.json b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_01.motion3.json similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_01.motion3.json rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_01.motion3.json diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_01.motion3.json.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_01.motion3.json.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_01.motion3.json.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_01.motion3.json.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_02.anim b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_02.anim similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_02.anim rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_02.anim diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_02.anim.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_02.anim.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_02.anim.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_02.anim.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_02.fade.asset b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_02.fade.asset similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_02.fade.asset rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_02.fade.asset diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_02.fade.asset.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_02.fade.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_02.fade.asset.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_02.fade.asset.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_02.motion3.json b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_02.motion3.json similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_02.motion3.json rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_02.motion3.json diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_02.motion3.json.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_02.motion3.json.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_02.motion3.json.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_02.motion3.json.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_03.anim b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_03.anim similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_03.anim rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_03.anim diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_03.anim.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_03.anim.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_03.anim.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_03.anim.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_03.fade.asset b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_03.fade.asset similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_03.fade.asset rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_03.fade.asset diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_03.fade.asset.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_03.fade.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_03.fade.asset.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_03.fade.asset.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_03.motion3.json b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_03.motion3.json similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_03.motion3.json rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_03.motion3.json diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_03.motion3.json.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_03.motion3.json.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_03.motion3.json.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_03.motion3.json.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_04.anim b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_04.anim similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_04.anim rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_04.anim diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_04.anim.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_04.anim.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_04.anim.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_04.anim.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_04.fade.asset b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_04.fade.asset similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_04.fade.asset rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_04.fade.asset diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_04.fade.asset.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_04.fade.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_04.fade.asset.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_04.fade.asset.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_04.motion3.json b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_04.motion3.json similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_04.motion3.json rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_04.motion3.json diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_04.motion3.json.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_04.motion3.json.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_04.motion3.json.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_04.motion3.json.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_05.anim b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_05.anim similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_05.anim rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_05.anim diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_05.anim.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_05.anim.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_05.anim.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_05.anim.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_05.fade.asset b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_05.fade.asset similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_05.fade.asset rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_05.fade.asset diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_05.fade.asset.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_05.fade.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_05.fade.asset.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_05.fade.asset.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_05.motion3.json b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_05.motion3.json similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_05.motion3.json rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_05.motion3.json diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_05.motion3.json.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_05.motion3.json.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_05.motion3.json.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_05.motion3.json.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_06.anim b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_06.anim similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_06.anim rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_06.anim diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_06.anim.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_06.anim.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_06.anim.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_06.anim.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_06.fade.asset b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_06.fade.asset similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_06.fade.asset rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_06.fade.asset diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_06.fade.asset.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_06.fade.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_06.fade.asset.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_06.fade.asset.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_06.motion3.json b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_06.motion3.json similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_06.motion3.json rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_06.motion3.json diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_06.motion3.json.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_06.motion3.json.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_06.motion3.json.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_06.motion3.json.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_07.anim b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_07.anim similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_07.anim rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_07.anim diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_07.anim.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_07.anim.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_07.anim.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_07.anim.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_07.fade.asset b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_07.fade.asset similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_07.fade.asset rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_07.fade.asset diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_07.fade.asset.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_07.fade.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_07.fade.asset.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_07.fade.asset.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_07.motion3.json b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_07.motion3.json similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_07.motion3.json rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_07.motion3.json diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_07.motion3.json.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_07.motion3.json.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/motions/mtn_07.motion3.json.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_07.motion3.json.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/natori.pose3.json b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori.pose3.json similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/natori.pose3.json rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori.pose3.json diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/natori.pose3.json.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori.pose3.json.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/natori.pose3.json.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori.pose3.json.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.4096.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.4096.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.4096.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.4096.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.4096/texture_00.png b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.4096/texture_00.png similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.4096/texture_00.png rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.4096/texture_00.png diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.4096/texture_00.png.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.4096/texture_00.png.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.4096/texture_00.png.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.4096/texture_00.png.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.asset b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.asset similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.asset rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.asset diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.asset.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.asset.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.asset.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.cdi3.json b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.cdi3.json similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.cdi3.json rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.cdi3.json diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.cdi3.json.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.cdi3.json.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.cdi3.json.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.cdi3.json.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.controller b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.controller similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.controller rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.controller diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.controller.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.controller.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.controller.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.controller.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.moc3 b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.moc3 similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.moc3 rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.moc3 diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.moc3.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.moc3.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.moc3.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.moc3.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.model3.json b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.model3.json similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.model3.json rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.model3.json diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.model3.json.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.model3.json.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.model3.json.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.model3.json.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.physics3.json b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.physics3.json similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.physics3.json rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.physics3.json diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.physics3.json.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.physics3.json.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.physics3.json.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.physics3.json.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.prefab b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.prefab similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.prefab rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.prefab diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.prefab.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.prefab.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06.prefab.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.prefab.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06MaskTexture.asset b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06MaskTexture.asset similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06MaskTexture.asset rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06MaskTexture.asset diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06MaskTexture.asset.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06MaskTexture.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/natori_pro_t06MaskTexture.asset.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06MaskTexture.asset.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/runtime.expressionList.asset b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/runtime.expressionList.asset similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/runtime.expressionList.asset rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/runtime.expressionList.asset diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/runtime.expressionList.asset.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/runtime.expressionList.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/runtime.expressionList.asset.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/runtime.expressionList.asset.meta diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/runtime.fadeMotionList.asset b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/runtime.fadeMotionList.asset similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/runtime.fadeMotionList.asset rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/runtime.fadeMotionList.asset diff --git a/Assets/Domain/Character/Model/natori_pro_en/runtime/runtime.fadeMotionList.asset.meta b/Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/runtime.fadeMotionList.asset.meta similarity index 100% rename from Assets/Domain/Character/Model/natori_pro_en/runtime/runtime.fadeMotionList.asset.meta rename to Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/runtime.fadeMotionList.asset.meta diff --git a/Assets/Domain/Character/Script/Facade/CharacterFacade.cs b/Assets/Domain/Character/Script/Facade/CharacterFacade.cs index 9ba1386..a94d6ab 100644 --- a/Assets/Domain/Character/Script/Facade/CharacterFacade.cs +++ b/Assets/Domain/Character/Script/Facade/CharacterFacade.cs @@ -8,6 +8,10 @@ namespace ProjectVG.Domain.Character.Service /// public class CharacterFacade : MonoBehaviour, ICharacterFacade { + [SerializeField] private Transform _modelTransform; + [SerializeField] private Live2DModelRegistry _modelRegistry; + + private ICharacterModelManager _modelManager; private ICharacterActionService _actionService; private ICharacterActionResolver _actionResolver; @@ -17,34 +21,15 @@ public class CharacterFacade : MonoBehaviour, ICharacterFacade /// public void Initialize() { - // ์˜์กด์„ฑ ์ฃผ์ž…์„ ํ†ตํ•ด ์ดˆ๊ธฐํ™” - // TODO: ์‹ค์ œ ์˜์กด์„ฑ ์ฃผ์ž… ์ปจํ…Œ์ด๋„ˆ์—์„œ ๊ฐ€์ ธ์˜ค๋„๋ก ์ˆ˜์ • - Debug.LogWarning("[CharacterFacade] ์˜์กด์„ฑ ์ฃผ์ž…์ด ์•„์ง ๊ตฌํ˜„๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."); - } + _modelManager = GetComponent(); + _modelManager.Initialize(_modelTransform, _modelRegistry); + - /// - /// ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•œ๋‹ค. - /// - /// ๋ชจ๋ธ ๋งค๋‹ˆ์ € - /// ์•ก์…˜ ์„œ๋น„์Šค - /// ์•ก์…˜ ํ•ด์„๊ธฐ - public void InjectDependencies(ICharacterModelManager modelManager, ICharacterActionService actionService, ICharacterActionResolver actionResolver) - { - _modelManager = modelManager; - _actionService = actionService; - _actionResolver = actionResolver; - } - /// - /// ํŒŒ์‚ฌ๋“œ๋ฅผ ์ข…๋ฃŒํ•œ๋‹ค. - /// - public void Shutdown() - { - if (_modelManager != null) { - _modelManager.UnloadAll(); - } } + + /// /// ์บ๋ฆญํ„ฐ๋ฅผ ๋“ฑ๋กํ•œ๋‹ค. /// @@ -144,6 +129,16 @@ public bool IsRegistered(string characterId) { return _modelManager?.IsLoaded(characterId) ?? false; } - } + + /// + /// ํŒŒ์‚ฌ๋“œ๋ฅผ ์ข…๋ฃŒํ•œ๋‹ค. + /// + public void Shutdown() + { + if (_modelManager != null) { + _modelManager.UnloadAll(); + } + } + } } diff --git a/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs b/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs index c22d1bb..d39b4e1 100644 --- a/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs +++ b/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs @@ -1,5 +1,6 @@ -using UnityEngine; using Cysharp.Threading.Tasks; +using ProjectVG.Domain.Character.Live2D.Model; +using UnityEngine; namespace ProjectVG.Domain.Character.Service { @@ -10,11 +11,18 @@ public interface ICharacterModelManager { /// - /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ๋กœ๋“œํ•œ๋‹ค. + /// ๋งค๋‹ˆ์ €๋ฅผ ์ดˆ๊ธฐํ™” ํ•œ๋‹ค. /// - /// ๋กœ๋“œํ•  ์บ๋ฆญํ„ฐ์˜ ๊ณ ์œ  ID - /// ์‚ฌ์ „ ๋กœ๋“œ ์—ฌ๋ถ€ (true๋ฉด ๋กœ๋“œ ํ›„ ์ฆ‰์‹œ ํ™œ์„ฑํ™”) - void LoadModel(string characterId, bool preload = false); + /// ๋ชจ๋ธ ์œ„์น˜ + /// ๋ชจ๋ธ ๋“ฑ๋ก์ž + public void Initialize(Transform modelRoot, Live2DModelRegistry modelRegistry); + + /// + /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ๋กœ๋“œํ•œ๋‹ค. + /// + /// ๋กœ๋“œํ•  ์บ๋ฆญํ„ฐ์˜ ๊ณ ์œ  ID + /// ์‚ฌ์ „ ๋กœ๋“œ ์—ฌ๋ถ€ (true๋ฉด ๋กœ๋“œ ํ›„ ์ฆ‰์‹œ ํ™œ์„ฑํ™”) + void LoadModel(string characterId, bool preload = false); /// /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ๋น„๋™๊ธฐ๋กœ ๋กœ๋“œํ•œ๋‹ค. diff --git a/Assets/Resources/Character/Model/CharacterZero.asset b/Assets/Resources/Character/Model/CharacterZero.asset deleted file mode 100644 index 09aed52..0000000 --- a/Assets/Resources/Character/Model/CharacterZero.asset +++ /dev/null @@ -1,33 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!114 &11400000 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 2a91fff055b553542acd1857fd9bbf0f, type: 3} - m_Name: CharacterZero - m_EditorClassIdentifier: - characterId: zero - characterName: "\uC81C\uB85C" - characterPrefab: {fileID: 6181935751025943507, guid: 43e77a085e1072e4dbc6393f20643f3b, type: 3} - thumbnail: {fileID: 2800000, guid: 7ee051ec4bca36046934826113d64c32, type: 3} - characterDescription: "\uCE90\uB9AD\uD130 \uC81C\uB85C" - emotionMappings: - - emotionKey: - expressionName: - defaultIntensity: 0 - defaultDurationMs: 0 - actionMappings: - - actionKey: - motionGroup: - motionName: - isLockAtActive: 1 - lookSensitivity: 1 - lockAtDamping: 0 - gain: 1 - smoothing: 1 diff --git a/Assets/Resources/Character/Model/Live2DModelConfig.asset b/Assets/Resources/Character/Model/Live2DModelConfig.asset new file mode 100644 index 0000000..e4e7493 --- /dev/null +++ b/Assets/Resources/Character/Model/Live2DModelConfig.asset @@ -0,0 +1,26 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f22d39f6e7c71524d911cb4b658d7fd9, type: 3} + m_Name: Live2DModelConfig + m_EditorClassIdentifier: + characterId: + characterName: + characterPrefab: {fileID: 0} + thumbnail: {fileID: 0} + characterDescription: + emotionMappings: [] + actionMappings: [] + isLockAtActive: 1 + lookSensitivity: 1 + lockAtDamping: 0 + gain: 1 + smoothing: 1 diff --git a/Assets/Resources/Character/Model/CharacterZero.asset.meta b/Assets/Resources/Character/Model/Live2DModelConfig.asset.meta similarity index 79% rename from Assets/Resources/Character/Model/CharacterZero.asset.meta rename to Assets/Resources/Character/Model/Live2DModelConfig.asset.meta index 9d69fe5..14845cd 100644 --- a/Assets/Resources/Character/Model/CharacterZero.asset.meta +++ b/Assets/Resources/Character/Model/Live2DModelConfig.asset.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 4c6d1f5cb9556f24c843f3e9fe14d49e +guid: 58c40b616ae7d944c91fbc00193d5d3b NativeFormatImporter: externalObjects: {} mainObjectFileID: 11400000 From f7c91c9debf94d6ad9dce5da8912fc5ea10bc533 Mon Sep 17 00:00:00 2001 From: WooSH Date: Tue, 19 Aug 2025 14:29:14 +0900 Subject: [PATCH 07/13] =?UTF-8?q?feat:=20=EB=AA=A8=EB=8D=B8=20=EA=B8=B0?= =?UTF-8?q?=EC=A4=80=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=EC=8A=A4=ED=81=AC?= =?UTF-8?q?=EB=A6=B0=20=ED=95=B4=EC=83=81=EB=8F=84=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Guides/Live2D_Resolution_Scaling_Guide.md | 225 ++++++++++++ .../Live2D_Resolution_Scaling_Guide.md.meta} | 5 +- .../Component/CameraResolutionScaler.cs | 214 ++++++++++++ .../Component/CameraResolutionScaler.cs.meta | 2 + .../Script/Component/Live2DModelScaler.cs | 238 +++++++++++++ .../Component/Live2DModelScaler.cs.meta | 2 + .../Config/ResolutionModelScaleConfig.cs | 326 ++++++++++++++++++ .../Config/ResolutionModelScaleConfig.cs.meta | 2 + .../Script/Facade/CharacterFacade.cs | 40 ++- .../Script/Manager/ResolutionManager.cs | 233 +++++++++++++ .../Script/Manager/ResolutionManager.cs.meta | 2 + .../Script/Test/ResolutionDebugger.cs | 160 +++++++++ .../Script/Test/ResolutionDebugger.cs.meta | 2 + .../Resources/Character/Character-Zero.asset | 33 ++ ...g.asset.meta => Character-Zero.asset.meta} | 0 .../Character/Model/Live2DModelConfig.asset | 26 -- .../ResolutionModelScaleConfig.asset | 84 +++++ .../ResolutionModelScaleConfig.asset.meta | 8 + 18 files changed, 1564 insertions(+), 38 deletions(-) create mode 100644 Assets/Docs/Guides/Live2D_Resolution_Scaling_Guide.md rename Assets/{Resources/Character/Model.meta => Docs/Guides/Live2D_Resolution_Scaling_Guide.md.meta} (57%) create mode 100644 Assets/Domain/Character/Script/Component/CameraResolutionScaler.cs create mode 100644 Assets/Domain/Character/Script/Component/CameraResolutionScaler.cs.meta create mode 100644 Assets/Domain/Character/Script/Component/Live2DModelScaler.cs create mode 100644 Assets/Domain/Character/Script/Component/Live2DModelScaler.cs.meta create mode 100644 Assets/Domain/Character/Script/Config/ResolutionModelScaleConfig.cs create mode 100644 Assets/Domain/Character/Script/Config/ResolutionModelScaleConfig.cs.meta create mode 100644 Assets/Domain/Character/Script/Manager/ResolutionManager.cs create mode 100644 Assets/Domain/Character/Script/Manager/ResolutionManager.cs.meta create mode 100644 Assets/Domain/Character/Script/Test/ResolutionDebugger.cs create mode 100644 Assets/Domain/Character/Script/Test/ResolutionDebugger.cs.meta create mode 100644 Assets/Resources/Character/Character-Zero.asset rename Assets/Resources/Character/{Model/Live2DModelConfig.asset.meta => Character-Zero.asset.meta} (100%) delete mode 100644 Assets/Resources/Character/Model/Live2DModelConfig.asset create mode 100644 Assets/Resources/ResolutionModelScaleConfig.asset create mode 100644 Assets/Resources/ResolutionModelScaleConfig.asset.meta diff --git a/Assets/Docs/Guides/Live2D_Resolution_Scaling_Guide.md b/Assets/Docs/Guides/Live2D_Resolution_Scaling_Guide.md new file mode 100644 index 0000000..f00c150 --- /dev/null +++ b/Assets/Docs/Guides/Live2D_Resolution_Scaling_Guide.md @@ -0,0 +1,225 @@ +# Live2D ๋ชจ๋ธ ํ•ด์ƒ๋„๋ณ„ ํฌ๊ธฐ ์กฐ์ • ์‹œ์Šคํ…œ ๊ฐ€์ด๋“œ + +## ๊ฐœ์š” + +์ด ์‹œ์Šคํ…œ์€ ๋‹ค์–‘ํ•œ ํ•ด์ƒ๋„์™€ ๋””๋ฐ”์ด์Šค์—์„œ Live2D ๋ชจ๋ธ์˜ ํฌ๊ธฐ๋ฅผ ์ž๋™์œผ๋กœ ์กฐ์ •ํ•˜์—ฌ ์ผ๊ด€๋œ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. + +## ์ฃผ์š” ๊ตฌ์„ฑ ์š”์†Œ + +### 1. ResolutionModelScaleConfig +- ํ•ด์ƒ๋„๋ณ„ ์Šค์ผ€์ผ ๊ทœ์น™์„ ์ •์˜ํ•˜๋Š” ScriptableObject +- ์ž๋™ ์Šค์ผ€์ผ ๊ณ„์‚ฐ ๋กœ์ง ํฌํ•จ +- PC/๋ชจ๋ฐ”์ผ ๊ตฌ๋ถ„ ์ง€์› + +### 2. Live2DModelScaler +- ๊ฐœ๋ณ„ Live2D ๋ชจ๋ธ์— ์Šค์ผ€์ผ์„ ์ ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ +- ํ•ด์ƒ๋„ ๋ณ€๊ฒฝ ์‹œ ์ž๋™ ์Šค์ผ€์ผ ์กฐ์ • +- ์›๋ณธ ํฌ๊ธฐ ๋ณด์กด ๋ฐ ๋ณต์› ๊ธฐ๋Šฅ + +### 3. ResolutionManager +- ์ „์ฒด ์‹œ์Šคํ…œ์„ ๊ด€๋ฆฌํ•˜๋Š” ์‹ฑ๊ธ€ํ†ค ๋งค๋‹ˆ์ € +- ๋ชจ๋“  ๋ชจ๋ธ ์Šค์ผ€์ผ๋Ÿฌ๋ฅผ ์ค‘์•™์—์„œ ๊ด€๋ฆฌ +- ํ•ด์ƒ๋„ ๋ณ€๊ฒฝ ๊ฐ์ง€ ๋ฐ ์ž๋™ ์ ์šฉ + +## ์‚ฌ์šฉ ๋ฐฉ๋ฒ• + +### 1. ๊ธฐ๋ณธ ์„ค์ • + +1. **ResolutionModelScaleConfig ์ƒ์„ฑ** + ``` + Assets > Create > ProjectVG > Character > Resolution Model Scale Config + ``` + +2. **๊ธฐ๋ณธ ์„ค์ • ๊ตฌ์„ฑ** + - Reference Resolution: ๊ธฐ์ค€ ํ•ด์ƒ๋„ (์˜ˆ: 1920x1080) + - Reference Scale: ๊ธฐ์ค€ ์Šค์ผ€์ผ (์˜ˆ: 1.0) + - Scale Mode: ์Šค์ผ€์ผ ๊ณ„์‚ฐ ๋ฐฉ์‹ ์„ ํƒ + +### 2. ํ•ด์ƒ๋„๋ณ„ ๊ทœ์น™ ์„ค์ • + +```csharp +// ๊ธฐ๋ณธ ๊ทœ์น™๋“ค (๋ชจ๋ฐ”์ผ ๊ธฐ์ค€ ์ตœ์ ํ™”) +- ๋ชจ๋ฐ”์ผ ๊ธฐ๋ณธ: 0.85x ์Šค์ผ€์ผ +- ๋ชจ๋ฐ”์ผ ์„ธ๋กœ (์ž‘์€ ํ™”๋ฉด): 0.7x ์Šค์ผ€์ผ +- ๋ชจ๋ฐ”์ผ ๊ฐ€๋กœ/ํƒœ๋ธ”๋ฆฟ ์„ธ๋กœ: 0.8x ์Šค์ผ€์ผ +- ํƒœ๋ธ”๋ฆฟ ๊ฐ€๋กœ: 0.9x ์Šค์ผ€์ผ +- PC FHD (1920x1080): 1.1x ์Šค์ผ€์ผ +- PC QHD ์ด์ƒ: 1.3x ์Šค์ผ€์ผ +``` + +### 3. ์ž๋™ ์ ์šฉ + +1. **CharacterFacade ์‚ฌ์šฉ ์‹œ** + - ๋ชจ๋ธ ์Šค์ผ€์ผ๋Ÿฌ๊ฐ€ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋จ + - ๋ณ„๋„ ์„ค์ • ๋ถˆํ•„์š” + +2. **์ˆ˜๋™ ์ ์šฉ** + ```csharp + // Live2DModelScaler ์ปดํฌ๋„ŒํŠธ ์ถ”๊ฐ€ + var scaler = modelObject.AddComponent(); + scaler.ApplyScale(); + ``` + +## ํ•ด์ƒ๋„๋ณ„ ์ƒ์„ธ ์„ค์ • + +### ๋ชจ๋ฐ”์ผ ํ™˜๊ฒฝ +- **๊ธฐ์ค€ ํ•ด์ƒ๋„**: 1080x1920 (์„ธ๋กœ ๋ชจ๋“œ) +- **๋ชจ๋ฐ”์ผ ๊ธฐ๋ณธ**: 0.85x ์Šค์ผ€์ผ (๋Œ€๋ถ€๋ถ„์˜ ๋ชจ๋ฐ”์ผ ๊ธฐ๊ธฐ) +- **์ž‘์€ ํ™”๋ฉด**: 0.7x ์Šค์ผ€์ผ (480px ์ดํ•˜ ๋„ˆ๋น„) +- **๊ฐ€๋กœ ๋ชจ๋“œ**: 0.8x ์Šค์ผ€์ผ (481-768px ๋„ˆ๋น„) +- **๊ณ ํ•ด์ƒ๋„**: 0.9x ์Šค์ผ€์ผ (1440px ์ด์ƒ ๋„ˆ๋น„, 2960px ์ด์ƒ ๋†’์ด) + +### ํƒœ๋ธ”๋ฆฟ ํ™˜๊ฒฝ +- **ํƒœ๋ธ”๋ฆฟ ์„ธ๋กœ**: 0.8x ์Šค์ผ€์ผ (481-768px ๋„ˆ๋น„) +- **ํƒœ๋ธ”๋ฆฟ ๊ฐ€๋กœ**: 0.9x ์Šค์ผ€์ผ (769-1024px ๋„ˆ๋น„) + +### PC ํ™˜๊ฒฝ +- **PC FHD**: 1.1x ์Šค์ผ€์ผ (1025-1920px ๋„ˆ๋น„, 1080px ์ดํ•˜ ๋†’์ด) +- **PC QHD+**: 1.3x ์Šค์ผ€์ผ (1921px ์ด์ƒ ๋„ˆ๋น„, 1081px ์ด์ƒ ๋†’์ด) + +## ์Šค์ผ€์ผ ๊ณ„์‚ฐ ๋ฐฉ์‹ + +### 1. HeightBased +- ํ™”๋ฉด ๋†’์ด๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์Šค์ผ€์ผ ๊ณ„์‚ฐ +- ์„ธ๋กœ ๋ชจ๋“œ์— ์ ํ•ฉ + +### 2. WidthBased +- ํ™”๋ฉด ๋„ˆ๋น„๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์Šค์ผ€์ผ ๊ณ„์‚ฐ +- ๊ฐ€๋กœ ๋ชจ๋“œ์— ์ ํ•ฉ + +### 3. AspectRatioBased (๊ถŒ์žฅ) +- ์ข…ํšก๋น„๋ฅผ ๊ณ ๋ คํ•œ ์Šค์ผ€์ผ ๊ณ„์‚ฐ +- ๋‹ค์–‘ํ•œ ํ™”๋ฉด ๋น„์œจ์— ๋Œ€์‘ +- ๋ชจ๋ฐ”์ผ๊ณผ PC ํ™˜๊ฒฝ ๋ชจ๋‘์— ์ตœ์ ํ™” + +## ์„ค์ • ์˜ˆ์‹œ + +### ๋ชจ๋ฐ”์ผ ํ™˜๊ฒฝ (๊ธฐ์ค€) +```yaml +Reference Resolution: 1080x1920 +Reference Scale: 1.0 +Scale Mode: AspectRatioBased +Min Scale: 0.6 +Max Scale: 1.5 +``` + +### PC ํ™˜๊ฒฝ +```yaml +Reference Resolution: 1920x1080 +Reference Scale: 1.1 +Scale Mode: AspectRatioBased +Min Scale: 0.6 +Max Scale: 1.5 +``` + +## ๋””๋ฒ„๊น… + +### 1. ๋””๋ฒ„๊ทธ ์ •๋ณด ํ™œ์„ฑํ™” +```csharp +// ResolutionManager์—์„œ +showDebugInfo = true; + +// Live2DModelScaler์—์„œ +showDebugInfo = true; +``` + +### 2. ํ˜„์žฌ ์ƒํƒœ ํ™•์ธ +```csharp +var info = ResolutionManager.Instance.GetCurrentResolutionInfo(); +Debug.Log($"ํ•ด์ƒ๋„: {info.Resolution}, ์Šค์ผ€์ผ: {info.Scale}"); +``` + +## ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ + +### 1. ์ˆ˜๋™ ์Šค์ผ€์ผ ์„ค์ • +```csharp +var scaler = GetComponent(); +scaler.SetManualScale(1.5f); +``` + +### 2. ์›๋ณธ ํฌ๊ธฐ ๋ณต์› +```csharp +scaler.ResetToOriginalScale(); +``` + +### 3. ๋™์  ๊ทœ์น™ ์ถ”๊ฐ€ +```csharp +// ๋Ÿฐํƒ€์ž„์— ๊ทœ์น™ ์ถ”๊ฐ€ ๊ฐ€๋Šฅ +var config = Resources.Load("ResolutionModelScaleConfig"); +// ๊ทœ์น™ ์ˆ˜์ • ํ›„ ์ ์šฉ +``` + +## ์ฃผ์˜์‚ฌํ•ญ + +1. **Resources ํด๋”** + - ResolutionModelScaleConfig๋Š” ๋ฐ˜๋“œ์‹œ Resources ํด๋”์— ์œ„์น˜ํ•ด์•ผ ํ•จ + - ๊ธฐ๋ณธ ๊ฒฝ๋กœ: `Assets/Resources/ResolutionModelScaleConfig.asset` + +2. **์„ฑ๋Šฅ ๊ณ ๋ ค** + - ํ•ด์ƒ๋„ ๋ณ€๊ฒฝ ๊ฐ์ง€๋Š” Update์—์„œ ์ˆ˜ํ–‰ + - ๋ถˆํ•„์š”ํ•œ ๊ฒฝ์šฐ `applyOnResolutionChange = false` ์„ค์ • + +3. **์Šค์ผ€์ผ ๋ฒ”์œ„** + - Min/Max Scale ๋ฒ”์œ„๋ฅผ ์ ์ ˆํžˆ ์„ค์ • + - ๋„ˆ๋ฌด ๊ทน๋‹จ์ ์ธ ๊ฐ’์€ ํ”ผํ•˜๊ธฐ + +## ์‹ค์ œ ์‚ฌ์šฉ ์˜ˆ์‹œ + +### ์ผ๋ฐ˜์ ์ธ ํ•ด์ƒ๋„๋ณ„ ๊ฒฐ๊ณผ +```csharp +// iPhone SE (375x667) - ๋ชจ๋ฐ”์ผ ์„ธ๋กœ (์ž‘์€ ํ™”๋ฉด) +// ์Šค์ผ€์ผ: 0.7x + +// iPhone 12 (390x844) - ๋ชจ๋ฐ”์ผ ๊ธฐ๋ณธ +// ์Šค์ผ€์ผ: 0.85x + +// ๋ชจ๋ฐ”์ผ ๊ณ ํ•ด์ƒ๋„ (1440x2960) - ๋ชจ๋ฐ”์ผ ๊ณ ํ•ด์ƒ๋„ +// ์Šค์ผ€์ผ: 0.9x + +// iPad (768x1024) - ํƒœ๋ธ”๋ฆฟ ์„ธ๋กœ +// ์Šค์ผ€์ผ: 0.8x + +// iPad ๊ฐ€๋กœ (1024x768) - ํƒœ๋ธ”๋ฆฟ ๊ฐ€๋กœ +// ์Šค์ผ€์ผ: 0.9x + +// PC FHD (1920x1080) - PC FHD +// ์Šค์ผ€์ผ: 1.1x + +// PC QHD (2560x1440) - PC QHD+ +// ์Šค์ผ€์ผ: 1.3x +``` + +### ๋””๋ฒ„๊ทธ ์ •๋ณด ํ™•์ธ +```csharp +// ํ˜„์žฌ ํ•ด์ƒ๋„ ์ •๋ณด ์ถœ๋ ฅ +var info = ResolutionManager.Instance.GetCurrentResolutionInfo(); +Debug.Log($"ํ˜„์žฌ ํ•ด์ƒ๋„: {info.Resolution}"); +Debug.Log($"์ ์šฉ๋œ ์Šค์ผ€์ผ: {info.Scale:F2}"); +Debug.Log($"์ข…ํšก๋น„: {info.AspectRatio:F2}"); +Debug.Log($"๋ชจ๋ฐ”์ผ ์—ฌ๋ถ€: {info.IsMobile}"); +``` + +## ๋ฌธ์ œ ํ•ด๊ฒฐ + +### 1. ์Šค์ผ€์ผ์ด ์ ์šฉ๋˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ +- ResolutionModelScaleConfig๊ฐ€ Resources ํด๋”์— ์žˆ๋Š”์ง€ ํ™•์ธ +- Live2DModelScaler ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ๋Š”์ง€ ํ™•์ธ +- ๋””๋ฒ„๊ทธ ์ •๋ณด๋ฅผ ํ™œ์„ฑํ™”ํ•˜์—ฌ ๋กœ๊ทธ ํ™•์ธ + +### 2. ์Šค์ผ€์ผ์ด ์˜ˆ์ƒ๊ณผ ๋‹ค๋ฅธ ๊ฒฝ์šฐ +- Reference Resolution๊ณผ Reference Scale ์„ค์ • ํ™•์ธ +- Scale Mode ์„ค์ • ํ™•์ธ +- ํ•ด์ƒ๋„๋ณ„ ๊ทœ์น™ ์šฐ์„ ์ˆœ์œ„ ํ™•์ธ + +### 3. ์„ฑ๋Šฅ ๋ฌธ์ œ +- `autoDetectScalers = false` ์„ค์ • +- ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋งŒ ์ˆ˜๋™์œผ๋กœ ์Šค์ผ€์ผ๋Ÿฌ ๋“ฑ๋ก + +### 4. ํŠน์ • ํ•ด์ƒ๋„์—์„œ ๋ฌธ์ œ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ +```csharp +// ํŠน์ • ํ•ด์ƒ๋„์— ๋Œ€ํ•œ ์Šค์ผ€์ผ ํ™•์ธ +var config = Resources.Load("ResolutionModelScaleConfig"); +var testResolution = new Vector2(1920, 1080); +var scale = config.CalculateScaleForResolution(testResolution); +Debug.Log($"1920x1080์—์„œ์˜ ์Šค์ผ€์ผ: {scale:F2}"); +``` diff --git a/Assets/Resources/Character/Model.meta b/Assets/Docs/Guides/Live2D_Resolution_Scaling_Guide.md.meta similarity index 57% rename from Assets/Resources/Character/Model.meta rename to Assets/Docs/Guides/Live2D_Resolution_Scaling_Guide.md.meta index cea515c..fd60952 100644 --- a/Assets/Resources/Character/Model.meta +++ b/Assets/Docs/Guides/Live2D_Resolution_Scaling_Guide.md.meta @@ -1,7 +1,6 @@ fileFormatVersion: 2 -guid: c4f92c17fdb8d804492c23f2446382db -folderAsset: yes -DefaultImporter: +guid: 0c70642f3f9f98841a9d5145ecf5a822 +TextScriptImporter: externalObjects: {} userData: assetBundleName: diff --git a/Assets/Domain/Character/Script/Component/CameraResolutionScaler.cs b/Assets/Domain/Character/Script/Component/CameraResolutionScaler.cs new file mode 100644 index 0000000..78918b3 --- /dev/null +++ b/Assets/Domain/Character/Script/Component/CameraResolutionScaler.cs @@ -0,0 +1,214 @@ +using UnityEngine; +using ProjectVG.Domain.Character.Config; + +namespace ProjectVG.Domain.Character.Component +{ + /// + /// ํ•ด์ƒ๋„์— ๋”ฐ๋ผ ์นด๋ฉ”๋ผ์˜ Orthographic Size๋ฅผ ์ž๋™ ์กฐ์ •ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ + /// + public class CameraResolutionScaler : MonoBehaviour + { + [Header("์„ค์ •")] + [SerializeField] private ResolutionModelScaleConfig scaleConfig; + [SerializeField] private bool applyOnStart = true; + [SerializeField] private bool applyOnResolutionChange = true; + [SerializeField] private float baseOrthographicSize = 5f; + [SerializeField] private Vector2 baseResolution = new Vector2(1080, 1920); + + [Header("๋””๋ฒ„๊ทธ")] + [SerializeField] private bool showDebugInfo = true; + + private Camera targetCamera; + private Vector2 lastResolution; + private float originalOrthographicSize; + + #region Unity Lifecycle + + private void Start() + { + Initialize(); + } + + private void Update() + { + if (applyOnResolutionChange && HasResolutionChanged()) + { + ApplyCameraScale(); + } + } + + #endregion + + #region Public Methods + + /// + /// ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค. + /// + public void Initialize() + { + targetCamera = GetComponent(); + if (targetCamera == null) + { + Debug.LogError("[CameraResolutionScaler] Camera ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + return; + } + + if (scaleConfig == null) + { + scaleConfig = Resources.Load("ResolutionModelScaleConfig"); + if (scaleConfig == null) + { + Debug.LogWarning("[CameraResolutionScaler] ResolutionModelScaleConfig๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + } + + originalOrthographicSize = targetCamera.orthographicSize; + lastResolution = new Vector2(Screen.width, Screen.height); + + if (applyOnStart) + { + ApplyCameraScale(); + } + + if (showDebugInfo) + { + Debug.Log($"[CameraResolutionScaler] ์ดˆ๊ธฐํ™” ์™„๋ฃŒ. ์นด๋ฉ”๋ผ: {targetCamera.name}"); + } + } + + /// + /// ์นด๋ฉ”๋ผ ์Šค์ผ€์ผ์„ ์ ์šฉํ•œ๋‹ค. + /// + public void ApplyCameraScale() + { + if (targetCamera == null) + { + Debug.LogWarning("[CameraResolutionScaler] ์นด๋ฉ”๋ผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค."); + return; + } + + var currentResolution = new Vector2(Screen.width, Screen.height); + var scale = CalculateCameraScale(currentResolution); + + targetCamera.orthographicSize = baseOrthographicSize * scale; + lastResolution = currentResolution; + + if (showDebugInfo) + { + var info = scaleConfig?.GetCurrentResolutionInfo(); + Debug.Log($"[CameraResolutionScaler] ์นด๋ฉ”๋ผ ์Šค์ผ€์ผ ์ ์šฉ ์™„๋ฃŒ: {targetCamera.name}\n" + + $"=== ํ•ด์ƒ๋„ ์ •๋ณด ===\n" + + $"ํ˜„์žฌ ํ•ด์ƒ๋„: {currentResolution.x} x {currentResolution.y}\n" + + $"๊ธฐ์ค€ ํ•ด์ƒ๋„: {baseResolution.x} x {baseResolution.y}\n" + + $"ํ”Œ๋žซํผ: {(info?.IsMobile == true ? "๋ชจ๋ฐ”์ผ" : "PC")}\n" + + $"=== ์นด๋ฉ”๋ผ ์„ค์ • ===\n" + + $"์›๋ณธ Orthographic Size: {originalOrthographicSize:F2}\n" + + $"๊ณ„์‚ฐ๋œ ์Šค์ผ€์ผ: {scale:F3}๋ฐฐ\n" + + $"์ ์šฉ๋œ Orthographic Size: {targetCamera.orthographicSize:F2}"); + } + } + + /// + /// ์›๋ณธ ํฌ๊ธฐ๋กœ ๋˜๋Œ๋ฆฐ๋‹ค. + /// + public void ResetToOriginalSize() + { + if (targetCamera != null) + { + targetCamera.orthographicSize = originalOrthographicSize; + + if (showDebugInfo) + { + Debug.Log($"[CameraResolutionScaler] ์›๋ณธ ํฌ๊ธฐ๋กœ ๋ณต์›: {targetCamera.name}"); + } + } + } + + /// + /// ํ˜„์žฌ ์นด๋ฉ”๋ผ ์Šค์ผ€์ผ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. + /// + public float GetCurrentCameraScale() + { + if (targetCamera == null) return 1f; + return targetCamera.orthographicSize / baseOrthographicSize; + } + + #endregion + + #region Private Methods + + /// + /// ํ•ด์ƒ๋„๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. + /// + private bool HasResolutionChanged() + { + var currentResolution = new Vector2(Screen.width, Screen.height); + return currentResolution != lastResolution; + } + + /// + /// ์นด๋ฉ”๋ผ ์Šค์ผ€์ผ์„ ๊ณ„์‚ฐํ•œ๋‹ค. + /// + private float CalculateCameraScale(Vector2 resolution) + { + // ResolutionModelScaleConfig์—์„œ ์Šค์ผ€์ผ ๊ฐ€์ ธ์˜ค๊ธฐ + if (scaleConfig != null) + { + var configScale = scaleConfig.CalculateScale(); + + // ์นด๋ฉ”๋ผ ์Šค์ผ€์ผ์€ ํ•ด์ƒ๋„ ๋น„์œจ๊ณผ ์„ค์ • ์Šค์ผ€์ผ์„ ์กฐํ•ฉ + var resolutionRatio = CalculateResolutionRatio(resolution); + var finalScale = resolutionRatio * configScale; + + if (showDebugInfo) + { + Debug.Log($"[CameraResolutionScaler] ์Šค์ผ€์ผ ๊ณ„์‚ฐ:\n" + + $"ํ•ด์ƒ๋„ ๋น„์œจ: {resolutionRatio:F3}\n" + + $"์„ค์ • ์Šค์ผ€์ผ: {configScale:F3}\n" + + $"์ตœ์ข… ์Šค์ผ€์ผ: {finalScale:F3}"); + } + + return finalScale; + } + + // ์„ค์ •์ด ์—†์œผ๋ฉด ํ•ด์ƒ๋„ ๋น„์œจ๋งŒ ์‚ฌ์šฉ + return CalculateResolutionRatio(resolution); + } + + /// + /// ํ•ด์ƒ๋„ ๋น„์œจ์„ ๊ณ„์‚ฐํ•œ๋‹ค. + /// + private float CalculateResolutionRatio(Vector2 resolution) + { + // ์„ธ๋กœ ๊ธฐ์ค€์œผ๋กœ ๋น„์œจ ๊ณ„์‚ฐ (๋ชจ๋ฐ”์ผ ์„ธ๋กœ ๋ชจ๋“œ ๊ณ ๋ ค) + var currentAspect = resolution.x / resolution.y; + var baseAspect = baseResolution.x / baseResolution.y; + + // ์„ธ๋กœ ๋ชจ๋“œ์ธ ๊ฒฝ์šฐ ๋†’์ด ๊ธฐ์ค€, ๊ฐ€๋กœ ๋ชจ๋“œ์ธ ๊ฒฝ์šฐ ๋„ˆ๋น„ ๊ธฐ์ค€ + if (currentAspect < 1.0f) // ์„ธ๋กœ ๋ชจ๋“œ + { + return resolution.y / baseResolution.y; + } + else // ๊ฐ€๋กœ ๋ชจ๋“œ + { + return resolution.x / baseResolution.x; + } + } + + #endregion + + #region Editor Methods + + #if UNITY_EDITOR + private void OnValidate() + { + if (scaleConfig == null) + { + scaleConfig = Resources.Load("ResolutionModelScaleConfig"); + } + } + #endif + + #endregion + } +} diff --git a/Assets/Domain/Character/Script/Component/CameraResolutionScaler.cs.meta b/Assets/Domain/Character/Script/Component/CameraResolutionScaler.cs.meta new file mode 100644 index 0000000..a033eff --- /dev/null +++ b/Assets/Domain/Character/Script/Component/CameraResolutionScaler.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: a172350175672e545ba7e4abb18f0b59 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Component/Live2DModelScaler.cs b/Assets/Domain/Character/Script/Component/Live2DModelScaler.cs new file mode 100644 index 0000000..50a71b5 --- /dev/null +++ b/Assets/Domain/Character/Script/Component/Live2DModelScaler.cs @@ -0,0 +1,238 @@ +using UnityEngine; +using ProjectVG.Domain.Character.Config; + +namespace ProjectVG.Domain.Character.Component +{ + /// + /// Live2D ๋ชจ๋ธ ํฌ๊ธฐ๋ฅผ ํ•ด์ƒ๋„์— ๋งž๊ฒŒ ์ž๋™ ์กฐ์ •ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ + /// + public class Live2DModelScaler : MonoBehaviour + { + [Header("์„ค์ •")] + [SerializeField] private ResolutionModelScaleConfig scaleConfig; + [SerializeField] private bool applyOnStart = true; + [SerializeField] private bool applyOnResolutionChange = true; + + [Header("๋””๋ฒ„๊ทธ")] + [SerializeField] private bool showDebugInfo = true; + + private Vector2 lastResolution; + private float currentScale; + private Vector3 originalScale; + + #region Unity Lifecycle + + private void Start() + { + if (applyOnStart) + { + ApplyScale(); + } + + // ResolutionManager์— ์ž๋™ ๋“ฑ๋ก + RegisterToResolutionManager(); + } + + private void OnDestroy() + { + // ResolutionManager์—์„œ ์ œ๊ฑฐ + UnregisterFromResolutionManager(); + } + + private void Update() + { + if (applyOnResolutionChange && HasResolutionChanged()) + { + ApplyScale(); + } + } + + #endregion + + #region Public Methods + + /// + /// ํ˜„์žฌ ํ•ด์ƒ๋„์— ๋งž๋Š” ์Šค์ผ€์ผ์„ ์ ์šฉํ•œ๋‹ค. + /// + public void ApplyScale() + { + if (scaleConfig == null) + { + Debug.LogWarning("[Live2DModelScaler] ์Šค์ผ€์ผ ์„ค์ •์ด ์—†์Šต๋‹ˆ๋‹ค."); + return; + } + + // ํ˜„์žฌ ํ•ด์ƒ๋„์— ๋งž๋Š” ์Šค์ผ€์ผ ๊ณ„์‚ฐ + var scale = scaleConfig.CalculateScale(); + ApplyScaleWithPreCalculatedScale(scale); + } + + /// + /// ๋ฏธ๋ฆฌ ๊ณ„์‚ฐ๋œ ์Šค์ผ€์ผ์„ ์ ์šฉํ•œ๋‹ค. + /// + public void ApplyScaleWithPreCalculatedScale(float scale) + { + if (scaleConfig == null) + { + Debug.LogWarning("[Live2DModelScaler] ์Šค์ผ€์ผ ์„ค์ •์ด ์—†์Šต๋‹ˆ๋‹ค."); + return; + } + + // ์›๋ณธ ์Šค์ผ€์ผ ์ €์žฅ (์ตœ์ดˆ 1ํšŒ๋งŒ) + if (originalScale == Vector3.zero) + { + originalScale = transform.localScale; + } + + // ์Šค์ผ€์ผ ์ ์šฉ + currentScale = scale; + transform.localScale = originalScale * currentScale; + + // ํ•ด์ƒ๋„ ์ •๋ณด ์—…๋ฐ์ดํŠธ + lastResolution = new Vector2(Screen.width, Screen.height); + + if (showDebugInfo) + { + var info = scaleConfig.GetCurrentResolutionInfo(); + var originalSize = originalScale; + var newSize = transform.localScale; + var scaleMultiplier = currentScale; + + Debug.Log($"[Live2DModelScaler] ์Šค์ผ€์ผ ์ ์šฉ ์™„๋ฃŒ: {gameObject.name}\n" + + $"=== ํ•ด์ƒ๋„ ์ •๋ณด ===\n" + + $"ํ˜„์žฌ ํ•ด์ƒ๋„: {info.Resolution.x} x {info.Resolution.y}\n" + + $"์ข…ํšก๋น„: {info.AspectRatio:F3}\n" + + $"ํ”Œ๋žซํผ: {(info.IsMobile ? "๋ชจ๋ฐ”์ผ" : "PC")}\n" + + $"=== ์Šค์ผ€์ผ ์ •๋ณด ===\n" + + $"์ ์šฉ๋œ ์Šค์ผ€์ผ: {scaleMultiplier:F3}๋ฐฐ\n" + + $"์›๋ณธ ํฌ๊ธฐ: {originalSize}\n" + + $"๋ณ€๊ฒฝ๋œ ํฌ๊ธฐ: {newSize}\n" + + $"ํฌ๊ธฐ ๋ณ€ํ™”: {scaleMultiplier:F1}๋ฐฐ"); + } + } + + /// + /// ์›๋ณธ ํฌ๊ธฐ๋กœ ๋˜๋Œ๋ฆฐ๋‹ค. + /// + public void ResetToOriginalScale() + { + if (originalScale != Vector3.zero) + { + transform.localScale = originalScale; + currentScale = 1.0f; + + if (showDebugInfo) + { + Debug.Log($"[Live2DModelScaler] ์›๋ณธ ํฌ๊ธฐ๋กœ ๋ณต์›: {gameObject.name}"); + } + } + } + + /// + /// ์ˆ˜๋™์œผ๋กœ ์Šค์ผ€์ผ์„ ์„ค์ •ํ•œ๋‹ค. + /// + public void SetManualScale(float scale) + { + if (originalScale == Vector3.zero) + { + originalScale = transform.localScale; + } + + currentScale = scale; + transform.localScale = originalScale * scale; + + if (showDebugInfo) + { + Debug.Log($"[Live2DModelScaler] ์ˆ˜๋™ ์Šค์ผ€์ผ ์„ค์ •: {gameObject.name} -> {scale:F2}"); + } + } + + /// + /// ํ˜„์žฌ ์Šค์ผ€์ผ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. + /// + public float GetCurrentScale() + { + return currentScale; + } + + /// + /// ์›๋ณธ ์Šค์ผ€์ผ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. + /// + public Vector3 GetOriginalScale() + { + return originalScale; + } + + #endregion + + #region Private Methods + + /// + /// ํ•ด์ƒ๋„๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. + /// + private bool HasResolutionChanged() + { + var currentResolution = new Vector2(Screen.width, Screen.height); + return currentResolution != lastResolution; + } + + /// + /// ResolutionManager์— ๋“ฑ๋กํ•œ๋‹ค. + /// + private void RegisterToResolutionManager() + { + try + { + var manager = ProjectVG.Domain.Character.Manager.ResolutionManager.Instance; + if (manager != null) + { + manager.RegisterModelScaler(this); + } + } + catch (System.Exception ex) + { + Debug.LogWarning($"[Live2DModelScaler] ResolutionManager ๋“ฑ๋ก ์‹คํŒจ: {ex.Message}"); + } + } + + /// + /// ResolutionManager์—์„œ ์ œ๊ฑฐํ•œ๋‹ค. + /// + private void UnregisterFromResolutionManager() + { + try + { + var manager = ProjectVG.Domain.Character.Manager.ResolutionManager.Instance; + if (manager != null) + { + manager.UnregisterModelScaler(this); + } + } + catch (System.Exception ex) + { + Debug.LogWarning($"[Live2DModelScaler] ResolutionManager ์ œ๊ฑฐ ์‹คํŒจ: {ex.Message}"); + } + } + + #endregion + + #region Editor Methods + + #if UNITY_EDITOR + private void OnValidate() + { + if (scaleConfig == null) + { + // ๊ธฐ๋ณธ ์„ค์ • ์ž๋™ ์ƒ์„ฑ + scaleConfig = Resources.Load("ResolutionModelScaleConfig"); + if (scaleConfig == null) + { + Debug.LogWarning("[Live2DModelScaler] Resources ํด๋”์— ResolutionModelScaleConfig๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค."); + } + } + } + #endif + + #endregion + } +} diff --git a/Assets/Domain/Character/Script/Component/Live2DModelScaler.cs.meta b/Assets/Domain/Character/Script/Component/Live2DModelScaler.cs.meta new file mode 100644 index 0000000..ff87c26 --- /dev/null +++ b/Assets/Domain/Character/Script/Component/Live2DModelScaler.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 5ba83c8a090667149ad21fedc63b1fa9 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Config/ResolutionModelScaleConfig.cs b/Assets/Domain/Character/Script/Config/ResolutionModelScaleConfig.cs new file mode 100644 index 0000000..28f09a8 --- /dev/null +++ b/Assets/Domain/Character/Script/Config/ResolutionModelScaleConfig.cs @@ -0,0 +1,326 @@ +using UnityEngine; +using System; +using System.Collections.Generic; + +namespace ProjectVG.Domain.Character.Config +{ + /// + /// ํ•ด์ƒ๋„๋ณ„ Live2D ๋ชจ๋ธ ํฌ๊ธฐ ์กฐ์ • ์„ค์ • + /// + [CreateAssetMenu(fileName = "ResolutionModelScaleConfig", menuName = "ProjectVG/Character/Resolution Model Scale Config")] + public class ResolutionModelScaleConfig : ScriptableObject + { + [Header("๊ธฐ๋ณธ ์„ค์ •")] + [SerializeField] private Vector2 referenceResolution = new Vector2(1920, 1080); + [SerializeField] private float referenceScale = 1.0f; + + public Vector2 ReferenceResolution => referenceResolution; + public float ReferenceScale => referenceScale; + + [Header("ํ•ด์ƒ๋„๋ณ„ ์Šค์ผ€์ผ ์„ค์ •")] + [SerializeField] private List scaleRules = new List(); + + [Header("์ž๋™ ์กฐ์ • ์„ค์ •")] + [SerializeField] private bool enableAutoScale = true; + [SerializeField] private float minScale = 0.5f; + [SerializeField] private float maxScale = 2.0f; + [SerializeField] private ScaleMode scaleMode = ScaleMode.HeightBased; + + /// + /// ํ˜„์žฌ ํ•ด์ƒ๋„์— ๋งž๋Š” ์Šค์ผ€์ผ์„ ๊ณ„์‚ฐํ•œ๋‹ค. + /// + public float CalculateScale() + { + if (!enableAutoScale) + { + return referenceScale; + } + + var currentResolution = new Vector2(Screen.width, Screen.height); + var scale = CalculateScaleForResolution(currentResolution); + var clampedScale = Mathf.Clamp(scale, minScale, maxScale); + + Debug.Log($"[ResolutionModelScaleConfig] ์Šค์ผ€์ผ ๊ณ„์‚ฐ ๊ณผ์ •:\n" + + $"๊ณ„์‚ฐ๋œ ์Šค์ผ€์ผ: {scale:F3}๋ฐฐ\n" + + $"์ œํ•œ ๋ฒ”์œ„: {minScale:F3} ~ {maxScale:F3}\n" + + $"์ตœ์ข… ์Šค์ผ€์ผ: {clampedScale:F3}๋ฐฐ"); + + return clampedScale; + } + + /// + /// ํŠน์ • ํ•ด์ƒ๋„์— ๋Œ€ํ•œ ์Šค์ผ€์ผ์„ ๊ณ„์‚ฐํ•œ๋‹ค. + /// + public float CalculateScaleForResolution(Vector2 resolution) + { + // ์ตœ์ ์˜ ๊ทœ์น™์„ ์ฐพ๊ธฐ + var bestRule = FindBestMatchingRule(resolution); + + if (bestRule != null) + { + Debug.Log($"[ResolutionModelScaleConfig] ์ตœ์  ๊ทœ์น™ ๋งค์นญ ์„ฑ๊ณต!\n" + + $"ํ•ด์ƒ๋„: {resolution.x} x {resolution.y}\n" + + $"์ ์šฉ ๊ทœ์น™: {bestRule.Description}\n" + + $"๊ทœ์น™ ๋ฒ”์œ„: {bestRule.MinWidth}x{bestRule.MinHeight} ~ {bestRule.MaxWidth}x{bestRule.MaxHeight}\n" + + $"์Šค์ผ€์ผ: {bestRule.Scale:F3}๋ฐฐ"); + return bestRule.Scale; + } + + Debug.Log($"[ResolutionModelScaleConfig] ๊ทœ์น™ ๋งค์นญ ์—†์Œ\n" + + $"ํ•ด์ƒ๋„: {resolution.x} x {resolution.y}\n" + + $"์ž๋™ ๊ณ„์‚ฐ ๋ชจ๋“œ: {scaleMode}\n" + + $"๊ธฐ์ค€ ํ•ด์ƒ๋„: {referenceResolution.x} x {referenceResolution.y}"); + + // ์ž๋™ ๊ณ„์‚ฐ + switch (scaleMode) + { + case ScaleMode.HeightBased: + return CalculateHeightBasedScale(resolution); + case ScaleMode.WidthBased: + return CalculateWidthBasedScale(resolution); + case ScaleMode.AspectRatioBased: + return CalculateAspectRatioBasedScale(resolution); + default: + return referenceScale; + } + } + + /// + /// ์ฃผ์–ด์ง„ ํ•ด์ƒ๋„์— ๊ฐ€์žฅ ์ ํ•ฉํ•œ ๊ทœ์น™์„ ์ฐพ๋Š”๋‹ค. + /// + private ResolutionScaleRule FindBestMatchingRule(Vector2 resolution) + { + var matchingRules = new List(); + + // ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ๋ชจ๋“  ๊ทœ์น™์„ ์ฐพ๊ธฐ + foreach (var rule in scaleRules) + { + if (rule.Matches(resolution)) + { + matchingRules.Add(rule); + } + } + + if (matchingRules.Count == 0) + { + return null; + } + + if (matchingRules.Count == 1) + { + return matchingRules[0]; + } + + // ์—ฌ๋Ÿฌ ๊ทœ์น™์ด ๋งค์นญ๋˜๋Š” ๊ฒฝ์šฐ ์ตœ์ ์˜ ๊ทœ์น™ ์„ ํƒ + return FindOptimalRule(resolution, matchingRules); + } + + /// + /// ์—ฌ๋Ÿฌ ๋งค์นญ ๊ทœ์น™ ์ค‘์—์„œ ์ตœ์ ์˜ ๊ทœ์น™์„ ์„ ํƒํ•œ๋‹ค. + /// + private ResolutionScaleRule FindOptimalRule(Vector2 resolution, List matchingRules) + { + ResolutionScaleRule bestRule = null; + float bestScore = float.MaxValue; + + foreach (var rule in matchingRules) + { + var score = CalculateRuleFitnessScore(resolution, rule); + + if (score < bestScore) + { + bestScore = score; + bestRule = rule; + } + } + + Debug.Log($"[ResolutionModelScaleConfig] ์ตœ์  ๊ทœ์น™ ์„ ํƒ ๊ณผ์ •:\n" + + $"ํ•ด์ƒ๋„: {resolution.x} x {resolution.y}\n" + + $"๋งค์นญ๋œ ๊ทœ์น™ ์ˆ˜: {matchingRules.Count}\n" + + $"์„ ํƒ๋œ ๊ทœ์น™: {bestRule?.Description}\n" + + $"์ ํ•ฉ๋„ ์ ์ˆ˜: {bestScore:F3}"); + + return bestRule; + } + + /// + /// ๊ทœ์น™์˜ ์ ํ•ฉ๋„๋ฅผ ๊ณ„์‚ฐํ•œ๋‹ค. ์ ์ˆ˜๊ฐ€ ๋‚ฎ์„์ˆ˜๋ก ๋” ์ ํ•ฉํ•จ. + /// + private float CalculateRuleFitnessScore(Vector2 resolution, ResolutionScaleRule rule) + { + // ๊ทœ์น™์˜ ์ค‘์•™์  ๊ณ„์‚ฐ + var ruleCenterWidth = (rule.MinWidth + rule.MaxWidth) / 2f; + var ruleCenterHeight = (rule.MinHeight + rule.MaxHeight) / 2f; + + // ํ˜„์žฌ ํ•ด์ƒ๋„์™€ ๊ทœ์น™ ์ค‘์•™์  ๊ฐ„์˜ ๊ฑฐ๋ฆฌ ๊ณ„์‚ฐ + var widthDistance = Mathf.Abs(resolution.x - ruleCenterWidth); + var heightDistance = Mathf.Abs(resolution.y - ruleCenterHeight); + + // ์œ ํด๋ฆฌ๋“œ ๊ฑฐ๋ฆฌ ๊ณ„์‚ฐ + var distance = Mathf.Sqrt(widthDistance * widthDistance + heightDistance * heightDistance); + + // ๊ทœ์น™์˜ ํฌ๊ธฐ ๋ฒ”์œ„ ๊ณ ๋ ค (๋ฒ”์œ„๊ฐ€ ์ž‘์„์ˆ˜๋ก ๋” ๊ตฌ์ฒด์ ์ด๋ฏ€๋กœ ๊ฐ€์ค‘์น˜ ๋ถ€์—ฌ) + var ruleWidthRange = rule.MaxWidth - rule.MinWidth; + var ruleHeightRange = rule.MaxHeight - rule.MinHeight; + var ruleArea = ruleWidthRange * ruleHeightRange; + + // ๋ฒ”์œ„๊ฐ€ ์ž‘์„์ˆ˜๋ก ๋” ๊ตฌ์ฒด์ ์ด๋ฏ€๋กœ ๋ณด๋„ˆ์Šค ์ ์ˆ˜ + var specificityBonus = Mathf.Max(0, 1000f - ruleArea) / 1000f; + + // ์ตœ์ข… ์ ์ˆ˜ = ๊ฑฐ๋ฆฌ - ๊ตฌ์ฒด์„ฑ ๋ณด๋„ˆ์Šค (์ ์ˆ˜๊ฐ€ ๋‚ฎ์„์ˆ˜๋ก ๋” ์ ํ•ฉ) + var finalScore = distance - specificityBonus; + + Debug.Log($"[ResolutionModelScaleConfig] ๊ทœ์น™ ์ ํ•ฉ๋„ ๊ณ„์‚ฐ:\n" + + $"๊ทœ์น™: {rule.Description}\n" + + $"๊ทœ์น™ ๋ฒ”์œ„: {rule.MinWidth}x{rule.MinHeight} ~ {rule.MaxWidth}x{rule.MaxHeight}\n" + + $"๊ทœ์น™ ์ค‘์•™: {ruleCenterWidth:F0}x{ruleCenterHeight:F0}\n" + + $"ํ˜„์žฌ ํ•ด์ƒ๋„: {resolution.x:F0}x{resolution.y:F0}\n" + + $"๊ฑฐ๋ฆฌ: {distance:F2}\n" + + $"๊ตฌ์ฒด์„ฑ ๋ณด๋„ˆ์Šค: {specificityBonus:F3}\n" + + $"์ตœ์ข… ์ ์ˆ˜: {finalScore:F3}"); + + return finalScore; + } + + /// + /// ๋†’์ด ๊ธฐ๋ฐ˜ ์Šค์ผ€์ผ ๊ณ„์‚ฐ + /// + private float CalculateHeightBasedScale(Vector2 resolution) + { + float heightRatio = resolution.y / referenceResolution.y; + return referenceScale * heightRatio; + } + + /// + /// ๋„ˆ๋น„ ๊ธฐ๋ฐ˜ ์Šค์ผ€์ผ ๊ณ„์‚ฐ + /// + private float CalculateWidthBasedScale(Vector2 resolution) + { + float widthRatio = resolution.x / referenceResolution.x; + return referenceScale * widthRatio; + } + + /// + /// ์ข…ํšก๋น„ ๊ธฐ๋ฐ˜ ์Šค์ผ€์ผ ๊ณ„์‚ฐ + /// + private float CalculateAspectRatioBasedScale(Vector2 resolution) + { + float currentAspect = resolution.x / resolution.y; + float referenceAspect = referenceResolution.x / referenceResolution.y; + float aspectRatio = currentAspect / referenceAspect; + + // ์ข…ํšก๋น„๊ฐ€ ํฌ๋ฉด ๋„ˆ๋น„ ๊ธฐ์ค€, ์ž‘์œผ๋ฉด ๋†’์ด ๊ธฐ์ค€ + if (aspectRatio > 1.0f) + { + return CalculateWidthBasedScale(resolution); + } + else + { + return CalculateHeightBasedScale(resolution); + } + } + + /// + /// ํ˜„์žฌ ํ•ด์ƒ๋„ ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. + /// + public ResolutionInfo GetCurrentResolutionInfo() + { + var resolution = new Vector2(Screen.width, Screen.height); + var scale = CalculateScale(); + + return new ResolutionInfo + { + Resolution = resolution, + Scale = scale, + AspectRatio = resolution.x / resolution.y, + IsMobile = IsMobilePlatform(resolution) + }; + } + + /// + /// ํ•ด์ƒ๋„๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ชจ๋ฐ”์ผ ํ”Œ๋žซํผ์ธ์ง€ ํŒ๋‹จํ•œ๋‹ค. + /// + private bool IsMobilePlatform(Vector2 resolution) + { + // Unity ์—๋””ํ„ฐ์—์„œ๋„ ๋ชจ๋ฐ”์ผ ํ•ด์ƒ๋„๋กœ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ƒ๋„ ๊ธฐ๋ฐ˜ ํŒ๋‹จ + var aspectRatio = resolution.x / resolution.y; + + // ์„ธ๋กœ ๋ชจ๋“œ (์ข…ํšก๋น„ < 1)์ด๊ณ  ๋†’์ด๊ฐ€ 1000px ์ด์ƒ์ด๋ฉด ๋ชจ๋ฐ”์ผ๋กœ ํŒ๋‹จ + if (aspectRatio < 1.0f && resolution.y >= 1000f) + { + return true; + } + + // Unity์˜ ๊ธฐ๋ณธ ํ”Œ๋žซํผ ๊ฐ์ง€๋„ ํ•จ๊ป˜ ์‚ฌ์šฉ + return Application.isMobilePlatform; + } + + /// + /// ์Šค์ผ€์ผ ๋ชจ๋“œ + /// + public enum ScaleMode + { + HeightBased, // ๋†’์ด ๊ธฐ์ค€ + WidthBased, // ๋„ˆ๋น„ ๊ธฐ์ค€ + AspectRatioBased // ์ข…ํšก๋น„ ๊ธฐ์ค€ + } + + /// + /// ํ•ด์ƒ๋„๋ณ„ ์Šค์ผ€์ผ ๊ทœ์น™ + /// + [Serializable] + public class ResolutionScaleRule + { + [Header("ํ•ด์ƒ๋„ ์กฐ๊ฑด")] + [SerializeField] private int minWidth = 0; + [SerializeField] private int maxWidth = 9999; + [SerializeField] private int minHeight = 0; + [SerializeField] private int maxHeight = 9999; + [SerializeField] private bool isMobileOnly = false; + + [Header("์Šค์ผ€์ผ ์„ค์ •")] + [SerializeField] private float scale = 1.0f; + [SerializeField] private string description = ""; + + public float Scale => scale; + public string Description => description; + public int MinWidth => minWidth; + public int MaxWidth => maxWidth; + public int MinHeight => minHeight; + public int MaxHeight => maxHeight; + + /// + /// ํ•ด์ƒ๋„๊ฐ€ ์ด ๊ทœ์น™์— ๋งž๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. + /// + public bool Matches(Vector2 resolution) + { + // ๋ชจ๋ฐ”์ผ ์ „์šฉ ๊ทœ์น™์ธ ๊ฒฝ์šฐ ํ•ด์ƒ๋„ ๊ธฐ๋ฐ˜์œผ๋กœ๋„ ํŒ๋‹จ + if (isMobileOnly) + { + var aspectRatio = resolution.x / resolution.y; + var isMobileByResolution = aspectRatio < 1.0f && resolution.y >= 1000f; + + if (!Application.isMobilePlatform && !isMobileByResolution) + { + return false; + } + } + + return resolution.x >= minWidth && resolution.x <= maxWidth && + resolution.y >= minHeight && resolution.y <= maxHeight; + } + } + + /// + /// ํ•ด์ƒ๋„ ์ •๋ณด + /// + [Serializable] + public class ResolutionInfo + { + public Vector2 Resolution; + public float Scale; + public float AspectRatio; + public bool IsMobile; + } + } +} diff --git a/Assets/Domain/Character/Script/Config/ResolutionModelScaleConfig.cs.meta b/Assets/Domain/Character/Script/Config/ResolutionModelScaleConfig.cs.meta new file mode 100644 index 0000000..5aa6c94 --- /dev/null +++ b/Assets/Domain/Character/Script/Config/ResolutionModelScaleConfig.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 2f1d81d7191952e419c1237b0fe91ebb \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Facade/CharacterFacade.cs b/Assets/Domain/Character/Script/Facade/CharacterFacade.cs index a94d6ab..3d11c87 100644 --- a/Assets/Domain/Character/Script/Facade/CharacterFacade.cs +++ b/Assets/Domain/Character/Script/Facade/CharacterFacade.cs @@ -1,5 +1,7 @@ -using UnityEngine; using ProjectVG.Domain.Character.Live2D.Model; +using UnityEngine; +using UnityEngine.TextCore.Text; +using ProjectVG.Domain.Character.Component; namespace ProjectVG.Domain.Character.Service { @@ -16,24 +18,44 @@ public class CharacterFacade : MonoBehaviour, ICharacterFacade private ICharacterActionService _actionService; private ICharacterActionResolver _actionResolver; - /// + #region Unity Lifecycle + + void Start() + { + Initialize(); + } + + #endregion + + + /// /// ํŒŒ์‚ฌ๋“œ๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค. /// public void Initialize() { _modelManager = GetComponent(); - _modelManager.Initialize(_modelTransform, _modelRegistry); - - - - } + if (_modelManager == null) + { + _modelManager = gameObject.AddComponent(); + Debug.Log($"[CharacterFacade] CharacterModelManager๊ฐ€ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค: {gameObject.name}"); + } + _modelManager.Initialize(_modelTransform, _modelRegistry); + _modelManager.LoadModel("zero", true); // ์ž„์‹œ ํ™œ์„ฑํ™” + + if (_modelTransform != null) { + var scaler = _modelTransform.GetComponent(); + if (scaler == null) { + scaler = _modelTransform.gameObject.AddComponent(); + Debug.Log($"[CharacterFacade] Live2DModelScaler๊ฐ€ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค: {_modelTransform.name}"); + } + } + } - /// /// ์บ๋ฆญํ„ฐ๋ฅผ ๋“ฑ๋กํ•œ๋‹ค. /// - public void RegisterCharacter(string characterId, bool preload = false) + public void RegisterCharacter(string characterId, bool preload = true) { if (_modelManager != null) { _modelManager.LoadModel(characterId, preload); diff --git a/Assets/Domain/Character/Script/Manager/ResolutionManager.cs b/Assets/Domain/Character/Script/Manager/ResolutionManager.cs new file mode 100644 index 0000000..3c29307 --- /dev/null +++ b/Assets/Domain/Character/Script/Manager/ResolutionManager.cs @@ -0,0 +1,233 @@ +using UnityEngine; +using System.Collections.Generic; +using System.Linq; +using ProjectVG.Domain.Character.Component; +using ProjectVG.Domain.Character.Config; + +namespace ProjectVG.Domain.Character.Manager +{ + /// + /// ํ•ด์ƒ๋„ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•˜๊ณ  Live2D ๋ชจ๋ธ ํฌ๊ธฐ๋ฅผ ์ž๋™ ์กฐ์ •ํ•˜๋Š” ๋งค๋‹ˆ์ € + /// + public class ResolutionManager : MonoBehaviour + { + [Header("์„ค์ •")] + [SerializeField] private ResolutionModelScaleConfig scaleConfig; + [SerializeField] private bool autoDetectScalers = true; + [SerializeField] private bool applyOnResolutionChange = true; + + [Header("๋””๋ฒ„๊ทธ")] + [SerializeField] private bool showDebugInfo = true; + + private Vector2 lastResolution; + private List modelScalers = new List(); + + #region Unity Lifecycle + + private void Start() + { + Initialize(); + } + + private void Update() + { + if (applyOnResolutionChange && HasResolutionChanged()) + { + ApplyScaleToAllModels(); + } + } + + #endregion + + #region Public Methods + + /// + /// ๋งค๋‹ˆ์ €๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค. + /// + public void Initialize() + { + if (scaleConfig == null) + { + scaleConfig = Resources.Load("ResolutionModelScaleConfig"); + if (scaleConfig == null) + { + Debug.LogError("[ResolutionManager] ResolutionModelScaleConfig๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + return; + } + } + + lastResolution = new Vector2(Screen.width, Screen.height); + + if (autoDetectScalers) + { + FindAllModelScalers(); + } + + // ์ดˆ๊ธฐ ์Šค์ผ€์ผ ์ ์šฉ + ApplyScaleToAllModels(); + + if (showDebugInfo) + { + Debug.Log($"[ResolutionManager] ์ดˆ๊ธฐํ™” ์™„๋ฃŒ. ๋ฐœ๊ฒฌ๋œ ์Šค์ผ€์ผ๋Ÿฌ: {modelScalers.Count}๊ฐœ"); + } + } + + /// + /// ๋ชจ๋“  ๋ชจ๋ธ ์Šค์ผ€์ผ๋Ÿฌ๋ฅผ ์ฐพ๋Š”๋‹ค. + /// + public void FindAllModelScalers() + { + modelScalers.Clear(); + var scalers = FindObjectsOfType(); + modelScalers.AddRange(scalers); + + if (showDebugInfo) + { + Debug.Log($"[ResolutionManager] {modelScalers.Count}๊ฐœ์˜ ๋ชจ๋ธ ์Šค์ผ€์ผ๋Ÿฌ๋ฅผ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค."); + } + } + + /// + /// ๋ชจ๋“  ๋ชจ๋ธ์— ์Šค์ผ€์ผ์„ ์ ์šฉํ•œ๋‹ค. + /// + public void ApplyScaleToAllModels() + { + if (scaleConfig == null) + { + Debug.LogWarning("[ResolutionManager] ์Šค์ผ€์ผ ์„ค์ •์ด ์—†์Šต๋‹ˆ๋‹ค."); + return; + } + + // ์ž๋™ ๊ฐ์ง€๊ฐ€ ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์œผ๋ฉด ์Šค์ผ€์ผ๋Ÿฌ ๋‹ค์‹œ ์ฐพ๊ธฐ + if (autoDetectScalers) + { + FindAllModelScalers(); + } + + var currentResolution = new Vector2(Screen.width, Screen.height); + + // ์Šค์ผ€์ผ ๊ณ„์‚ฐ์€ ํ•œ ๋ฒˆ๋งŒ ์ˆ˜ํ–‰ + var scale = scaleConfig.CalculateScale(); + + foreach (var scaler in modelScalers) + { + if (scaler != null) + { + // ์Šค์ผ€์ผ๋Ÿฌ์— ๋ฏธ๋ฆฌ ๊ณ„์‚ฐ๋œ ์Šค์ผ€์ผ์„ ์ „๋‹ฌ + scaler.ApplyScaleWithPreCalculatedScale(scale); + } + } + + lastResolution = currentResolution; + + if (showDebugInfo) + { + var info = scaleConfig.GetCurrentResolutionInfo(); + Debug.Log($"[ResolutionManager] ๋ชจ๋“  ๋ชจ๋ธ์— ์Šค์ผ€์ผ ์ ์šฉ ์™„๋ฃŒ\n" + + $"=== ํ•ด์ƒ๋„ ์„ค์ • ===\n" + + $"ํ˜„์žฌ ํ•ด์ƒ๋„: {info.Resolution.x} x {info.Resolution.y}\n" + + $"์ข…ํšก๋น„: {info.AspectRatio:F3}\n" + + $"ํ”Œ๋žซํผ: {(info.IsMobile ? "๋ชจ๋ฐ”์ผ" : "PC")}\n" + + $"=== ์Šค์ผ€์ผ ์„ค์ • ===\n" + + $"์ ์šฉ๋œ ์Šค์ผ€์ผ: {scale:F3}๋ฐฐ\n" + + $"์ ์šฉ๋œ ๋ชจ๋ธ ์ˆ˜: {modelScalers.Count}๊ฐœ\n" + + $"=== ๋ชจ๋ธ ๋ชฉ๋ก ===\n" + + string.Join("\n", modelScalers.Select(s => $"- {s.name}: {s.GetCurrentScale():F3}๋ฐฐ"))); + } + } + + /// + /// ํŠน์ • ๋ชจ๋ธ ์Šค์ผ€์ผ๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•œ๋‹ค. + /// + public void RegisterModelScaler(Live2DModelScaler scaler) + { + if (scaler != null && !modelScalers.Contains(scaler)) + { + modelScalers.Add(scaler); + + if (showDebugInfo) + { + Debug.Log($"[ResolutionManager] ๋ชจ๋ธ ์Šค์ผ€์ผ๋Ÿฌ ๋“ฑ๋ก: {scaler.name}"); + } + } + } + + /// + /// ํŠน์ • ๋ชจ๋ธ ์Šค์ผ€์ผ๋Ÿฌ๋ฅผ ์ œ๊ฑฐํ•œ๋‹ค. + /// + public void UnregisterModelScaler(Live2DModelScaler scaler) + { + if (modelScalers.Remove(scaler) && showDebugInfo) + { + Debug.Log($"[ResolutionManager] ๋ชจ๋ธ ์Šค์ผ€์ผ๋Ÿฌ ์ œ๊ฑฐ: {scaler.name}"); + } + } + + /// + /// ํ˜„์žฌ ํ•ด์ƒ๋„ ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. + /// + public ResolutionModelScaleConfig.ResolutionInfo GetCurrentResolutionInfo() + { + return scaleConfig?.GetCurrentResolutionInfo(); + } + + /// + /// ๋“ฑ๋ก๋œ ๋ชจ๋ธ ์Šค์ผ€์ผ๋Ÿฌ ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. + /// + public int GetRegisteredScalerCount() + { + return modelScalers.Count; + } + + #endregion + + #region Private Methods + + /// + /// ํ•ด์ƒ๋„๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. + /// + private bool HasResolutionChanged() + { + var currentResolution = new Vector2(Screen.width, Screen.height); + return currentResolution != lastResolution; + } + + #endregion + + #region Singleton Pattern + + private static ResolutionManager _instance; + public static ResolutionManager Instance + { + get + { + if (_instance == null) + { + _instance = FindObjectOfType(); + if (_instance == null) + { + var go = new GameObject("ResolutionManager"); + _instance = go.AddComponent(); + DontDestroyOnLoad(go); + } + } + return _instance; + } + } + + private void Awake() + { + if (_instance == null) + { + _instance = this; + DontDestroyOnLoad(gameObject); + } + else if (_instance != this) + { + Destroy(gameObject); + } + } + + #endregion + } +} diff --git a/Assets/Domain/Character/Script/Manager/ResolutionManager.cs.meta b/Assets/Domain/Character/Script/Manager/ResolutionManager.cs.meta new file mode 100644 index 0000000..56bfa4a --- /dev/null +++ b/Assets/Domain/Character/Script/Manager/ResolutionManager.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: a1d6440929102a34b926d1c2477daec1 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Test/ResolutionDebugger.cs b/Assets/Domain/Character/Script/Test/ResolutionDebugger.cs new file mode 100644 index 0000000..65358d6 --- /dev/null +++ b/Assets/Domain/Character/Script/Test/ResolutionDebugger.cs @@ -0,0 +1,160 @@ +using UnityEngine; +using ProjectVG.Domain.Character.Manager; +using ProjectVG.Domain.Character.Config; + +namespace ProjectVG.Domain.Character.Test +{ + /// + /// ํ•ด์ƒ๋„ ๋””๋ฒ„๊น…์„ ์œ„ํ•œ ํ…Œ์ŠคํŠธ ์Šคํฌ๋ฆฝํŠธ + /// + public class ResolutionDebugger : MonoBehaviour + { + [Header("ํ…Œ์ŠคํŠธ ์„ค์ •")] + [SerializeField] private bool testOnStart = true; + [SerializeField] private Vector2 testResolution = new Vector2(1440, 2960); + + [Header("๋””๋ฒ„๊ทธ ์ •๋ณด")] + [SerializeField] private bool showCurrentResolution = true; + [SerializeField] private bool showScaleCalculation = true; + + private void Start() + { + if (testOnStart) + { + TestResolution(); + } + } + + [ContextMenu("ํ•ด์ƒ๋„ ํ…Œ์ŠคํŠธ")] + public void TestResolution() + { + Debug.Log("=== ํ•ด์ƒ๋„ ๋””๋ฒ„๊น… ์‹œ์ž‘ ==="); + + // ํ˜„์žฌ ํ•ด์ƒ๋„ ์ •๋ณด + if (showCurrentResolution) + { + Debug.Log($"=== ํ˜„์žฌ ์‹œ์Šคํ…œ ์ •๋ณด ===\n" + + $"ํ•ด์ƒ๋„: {Screen.width} x {Screen.height}\n" + + $"์ข…ํšก๋น„: {(float)Screen.width / Screen.height:F3}\n" + + $"ํ”Œ๋žซํผ: {(Application.isMobilePlatform ? "๋ชจ๋ฐ”์ผ" : "PC")}\n" + + $"DPI: {Screen.dpi:F1}"); + } + + // ResolutionManager ํ…Œ์ŠคํŠธ + var manager = ResolutionManager.Instance; + if (manager != null) + { + var info = manager.GetCurrentResolutionInfo(); + Debug.Log($"=== ResolutionManager ์ƒํƒœ ===\n" + + $"ํ•ด์ƒ๋„: {info.Resolution.x} x {info.Resolution.y}\n" + + $"์ ์šฉ ์Šค์ผ€์ผ: {info.Scale:F3}๋ฐฐ\n" + + $"๋“ฑ๋ก๋œ ์Šค์ผ€์ผ๋Ÿฌ: {manager.GetRegisteredScalerCount()}๊ฐœ"); + } + else + { + Debug.LogWarning("ResolutionManager๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + + // ์„ค์ • ํŒŒ์ผ ์ง์ ‘ ํ…Œ์ŠคํŠธ + var config = Resources.Load("ResolutionModelScaleConfig"); + if (config != null) + { + Debug.Log($"=== ์„ค์ • ํŒŒ์ผ ์ •๋ณด ===\n" + + $"ํŒŒ์ผ๋ช…: {config.name}\n" + + $"๊ธฐ์ค€ ํ•ด์ƒ๋„: {config.ReferenceResolution.x} x {config.ReferenceResolution.y}\n" + + $"๊ธฐ์ค€ ์Šค์ผ€์ผ: {config.ReferenceScale:F3}๋ฐฐ"); + + // ํ˜„์žฌ ํ•ด์ƒ๋„๋กœ ์Šค์ผ€์ผ ๊ณ„์‚ฐ + var currentScale = config.CalculateScale(); + Debug.Log($"=== ํ˜„์žฌ ํ•ด์ƒ๋„ ์Šค์ผ€์ผ ===\n" + + $"ํ•ด์ƒ๋„: {Screen.width} x {Screen.height}\n" + + $"์ ์šฉ ์Šค์ผ€์ผ: {currentScale:F3}๋ฐฐ"); + + // ํ…Œ์ŠคํŠธ ํ•ด์ƒ๋„๋กœ ์Šค์ผ€์ผ ๊ณ„์‚ฐ + var testScale = config.CalculateScaleForResolution(testResolution); + Debug.Log($"=== ํ…Œ์ŠคํŠธ ํ•ด์ƒ๋„ ์Šค์ผ€์ผ ===\n" + + $"ํ•ด์ƒ๋„: {testResolution.x} x {testResolution.y}\n" + + $"์ ์šฉ ์Šค์ผ€์ผ: {testScale:F3}๋ฐฐ"); + + if (showScaleCalculation) + { + TestScaleCalculation(config); + } + } + else + { + Debug.LogError("ResolutionModelScaleConfig๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + } + + Debug.Log("=== ํ•ด์ƒ๋„ ๋””๋ฒ„๊น… ์™„๋ฃŒ ==="); + } + + private void TestScaleCalculation(ResolutionModelScaleConfig config) + { + Debug.Log("--- ์Šค์ผ€์ผ ๊ณ„์‚ฐ ์ƒ์„ธ ๋ถ„์„ ---"); + + var currentResolution = new Vector2(Screen.width, Screen.height); + var testResolution = new Vector2(1440, 2960); + + // ๊ฐ ํ•ด์ƒ๋„๋ณ„ ์Šค์ผ€์ผ ๊ณ„์‚ฐ + var resolutions = new Vector2[] + { + currentResolution, + testResolution, + new Vector2(1920, 1080), // PC FHD + new Vector2(1080, 1920), // ๋ชจ๋ฐ”์ผ ์„ธ๋กœ + new Vector2(390, 844), // iPhone 12 + new Vector2(375, 667) // iPhone SE + }; + + foreach (var resolution in resolutions) + { + var scale = config.CalculateScaleForResolution(resolution); + var aspectRatio = resolution.x / resolution.y; + var platform = Application.isMobilePlatform ? "๋ชจ๋ฐ”์ผ" : "PC"; + Debug.Log($"=== {resolution.x} x {resolution.y} ===\n" + + $"์ข…ํšก๋น„: {aspectRatio:F3}\n" + + $"ํ”Œ๋žซํผ: {platform}\n" + + $"์ ์šฉ ์Šค์ผ€์ผ: {scale:F3}๋ฐฐ"); + } + } + + [ContextMenu("๊ฐ•์ œ ์Šค์ผ€์ผ ์ ์šฉ")] + public void ForceApplyScale() + { + var manager = ResolutionManager.Instance; + if (manager != null) + { + manager.ApplyScaleToAllModels(); + Debug.Log("๋ชจ๋“  ๋ชจ๋ธ์— ์Šค์ผ€์ผ ๊ฐ•์ œ ์ ์šฉ ์™„๋ฃŒ"); + } + } + + [ContextMenu("์Šค์ผ€์ผ๋Ÿฌ ์ฐพ๊ธฐ")] + public void FindScalers() + { + var scalers = FindObjectsOfType(); + Debug.Log($"=== ๋ฐœ๊ฒฌ๋œ Live2DModelScaler: {scalers.Length}๊ฐœ ==="); + + if (scalers.Length == 0) + { + Debug.LogWarning("Live2DModelScaler๊ฐ€ ๋ฐœ๊ฒฌ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."); + return; + } + + foreach (var scaler in scalers) + { + var originalScale = scaler.GetOriginalScale(); + var currentScale = scaler.GetCurrentScale(); + var transform = scaler.transform; + + Debug.Log($"=== {scaler.name} ===\n" + + $"์œ„์น˜: {transform.position}\n" + + $"์›๋ณธ ํฌ๊ธฐ: {originalScale}\n" + + $"ํ˜„์žฌ ํฌ๊ธฐ: {transform.localScale}\n" + + $"์ ์šฉ ์Šค์ผ€์ผ: {currentScale:F3}๋ฐฐ\n" + + $"๋ถ€๋ชจ: {(transform.parent != null ? transform.parent.name : "์—†์Œ")}"); + } + } + } +} diff --git a/Assets/Domain/Character/Script/Test/ResolutionDebugger.cs.meta b/Assets/Domain/Character/Script/Test/ResolutionDebugger.cs.meta new file mode 100644 index 0000000..867d5c4 --- /dev/null +++ b/Assets/Domain/Character/Script/Test/ResolutionDebugger.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: fc5d277ba2de30b42bcaf2f8519e48a1 \ No newline at end of file diff --git a/Assets/Resources/Character/Character-Zero.asset b/Assets/Resources/Character/Character-Zero.asset new file mode 100644 index 0000000..d592cf4 --- /dev/null +++ b/Assets/Resources/Character/Character-Zero.asset @@ -0,0 +1,33 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f22d39f6e7c71524d911cb4b658d7fd9, type: 3} + m_Name: Character-Zero + m_EditorClassIdentifier: + characterId: zero + characterName: "\uC81C\uB85C" + characterPrefab: {fileID: 403089766806550510, guid: 611b9f4305cdce9458ff7eaead5c0a53, type: 3} + thumbnail: {fileID: 2800000, guid: 17a1333e87a81c34693569503c7ba213, type: 3} + characterDescription: "\uCE58\uC640\uC9F1 \uBAA8\uB378 \uAE30\uBC18" + emotionMappings: + - emotionKey: + expressionName: + defaultIntensity: 0 + defaultDurationMs: 0 + actionMappings: + - actionKey: + motionGroup: + motionName: + isLockAtActive: 0 + lookSensitivity: 1 + lockAtDamping: 0 + gain: 1 + smoothing: 1 diff --git a/Assets/Resources/Character/Model/Live2DModelConfig.asset.meta b/Assets/Resources/Character/Character-Zero.asset.meta similarity index 100% rename from Assets/Resources/Character/Model/Live2DModelConfig.asset.meta rename to Assets/Resources/Character/Character-Zero.asset.meta diff --git a/Assets/Resources/Character/Model/Live2DModelConfig.asset b/Assets/Resources/Character/Model/Live2DModelConfig.asset deleted file mode 100644 index e4e7493..0000000 --- a/Assets/Resources/Character/Model/Live2DModelConfig.asset +++ /dev/null @@ -1,26 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!114 &11400000 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: f22d39f6e7c71524d911cb4b658d7fd9, type: 3} - m_Name: Live2DModelConfig - m_EditorClassIdentifier: - characterId: - characterName: - characterPrefab: {fileID: 0} - thumbnail: {fileID: 0} - characterDescription: - emotionMappings: [] - actionMappings: [] - isLockAtActive: 1 - lookSensitivity: 1 - lockAtDamping: 0 - gain: 1 - smoothing: 1 diff --git a/Assets/Resources/ResolutionModelScaleConfig.asset b/Assets/Resources/ResolutionModelScaleConfig.asset new file mode 100644 index 0000000..360aaad --- /dev/null +++ b/Assets/Resources/ResolutionModelScaleConfig.asset @@ -0,0 +1,84 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2f1d81d7191952e419c1237b0fe91ebb, type: 3} + m_Name: ResolutionModelScaleConfig + m_EditorClassIdentifier: + referenceResolution: {x: 1080, y: 1920} + referenceScale: 1 + scaleRules: + - minWidth: 1440 + maxWidth: 1440 + minHeight: 2960 + maxHeight: 2960 + isMobileOnly: true + scale: 0.9 + description: ๋ชจ๋ฐ”์ผ ๊ณ ํ•ด์ƒ๋„ (1440x2960) + - minWidth: 2560 + maxWidth: 9999 + minHeight: 1440 + maxHeight: 9999 + isMobileOnly: false + scale: 1.4 + description: PC QHD+ (2560x1440 ์ด์ƒ) + - minWidth: 1921 + maxWidth: 9999 + minHeight: 1081 + maxHeight: 9999 + isMobileOnly: false + scale: 1.3 + description: PC QHD ์ด์ƒ + - minWidth: 1025 + maxWidth: 1920 + minHeight: 0 + maxHeight: 1080 + isMobileOnly: false + scale: 1.1 + description: PC FHD + - minWidth: 769 + maxWidth: 1024 + minHeight: 0 + maxHeight: 9999 + isMobileOnly: false + scale: 0.9 + description: ํƒœ๋ธ”๋ฆฟ ๊ฐ€๋กœ + - minWidth: 481 + maxWidth: 768 + minHeight: 0 + maxHeight: 9999 + isMobileOnly: false + scale: 0.8 + description: ๋ชจ๋ฐ”์ผ ๊ฐ€๋กœ/ํƒœ๋ธ”๋ฆฟ ์„ธ๋กœ + - minWidth: 0 + maxWidth: 480 + minHeight: 0 + maxHeight: 9999 + isMobileOnly: false + scale: 0.7 + description: ๋ชจ๋ฐ”์ผ ์„ธ๋กœ (์ž‘์€ ํ™”๋ฉด) + - minWidth: 0 + maxWidth: 9999 + minHeight: 0 + maxHeight: 9999 + isMobileOnly: true + scale: 0.85 + description: ๋ชจ๋ฐ”์ผ ๊ธฐ๋ณธ + - minWidth: 0 + maxWidth: 9999 + minHeight: 0 + maxHeight: 9999 + isMobileOnly: false + scale: 1 + description: ๊ธฐ๋ณธ ๊ทœ์น™ + enableAutoScale: 1 + minScale: 0.1 + maxScale: 10 + scaleMode: 1 diff --git a/Assets/Resources/ResolutionModelScaleConfig.asset.meta b/Assets/Resources/ResolutionModelScaleConfig.asset.meta new file mode 100644 index 0000000..1386f0a --- /dev/null +++ b/Assets/Resources/ResolutionModelScaleConfig.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0f80d8435f4ccee4184aa458f6b86634 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: From 082446def2dc9151b67d5e89ea45fb4af89c0277 Mon Sep 17 00:00:00 2001 From: WooSH Date: Wed, 20 Aug 2025 21:18:40 +0900 Subject: [PATCH 08/13] =?UTF-8?q?feat:=20=EB=AA=A8=EB=8D=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + Assets/App/Scenes/MainSence.unity | 6 +- Assets/Core/Audio/AudioManager.cs | 7 + .../Model/Chikuwa}/Character-Zero.asset | 14 +- .../Model/Chikuwa}/Character-Zero.asset.meta | 0 .../Chikuwa/{ziraitikuwa.meta => model.meta} | 2 +- .../Model/Model.fadeMotionList.asset | 17 ++ .../Model/Model.fadeMotionList.asset.meta} | 6 +- .../Script/Config/Live2DModelConfig.cs | 47 +++++ .../Script/Facade/CharacterFacade.cs | 36 +++- .../Script/Manager/CharacterModelManager.cs | 160 +++++++++++++++++- .../Script/Manager/ICharacterModelManager.cs | 3 +- .../{Character => }/Live2DModelRegistry.asset | 4 +- .../Live2DModelRegistry.asset.meta | 0 .../ResolutionModelScaleConfig.asset | 36 ++-- 15 files changed, 298 insertions(+), 41 deletions(-) rename Assets/{Resources/Character => Domain/Character/Model/Chikuwa}/Character-Zero.asset (66%) rename Assets/{Resources/Character => Domain/Character/Model/Chikuwa}/Character-Zero.asset.meta (100%) rename Assets/Domain/Character/Model/Chikuwa/{ziraitikuwa.meta => model.meta} (77%) create mode 100644 Assets/Domain/Character/Model/Model.fadeMotionList.asset rename Assets/{Resources/Character.meta => Domain/Character/Model/Model.fadeMotionList.asset.meta} (52%) rename Assets/Resources/{Character => }/Live2DModelRegistry.asset (68%) rename Assets/Resources/{Character => }/Live2DModelRegistry.asset.meta (100%) diff --git a/.gitignore b/.gitignore index 4b284fc..fab0a65 100644 --- a/.gitignore +++ b/.gitignore @@ -120,3 +120,4 @@ InitTestScene*.unity* /Assets/Plugins/FiveMinuteChat /Assets/Plugins/WebGLTemplates /Assets/Domain/Character/Model/Chikuwa/ziraitikuwa +/Assets/Domain/Character/Model/Chikuwa/model diff --git a/Assets/App/Scenes/MainSence.unity b/Assets/App/Scenes/MainSence.unity index 32a6f7d..a132881 100644 --- a/Assets/App/Scenes/MainSence.unity +++ b/Assets/App/Scenes/MainSence.unity @@ -271,7 +271,7 @@ Camera: far clip plane: 1000 field of view: 60 orthographic: 1 - orthographic size: 5 + orthographic size: 1 m_Depth: -1 m_CullingMask: serializedVersion: 2 @@ -525,7 +525,7 @@ AudioSource: m_GameObject: {fileID: 622824876} m_Enabled: 1 serializedVersion: 4 - OutputAudioMixerGroup: {fileID: 0} + OutputAudioMixerGroup: {fileID: 24300002, guid: 3bf46f7daa3260847aa1b964ad2b01a7, type: 2} m_audioClip: {fileID: 0} m_Resource: {fileID: 0} m_PlayOnAwake: 1 @@ -757,6 +757,8 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 6ae88dd8c9dcaab448ad7d8834e80f8e, type: 3} m_Name: m_EditorClassIdentifier: + _modelTransform: {fileID: 873552999} + _modelRegistry: {fileID: 11400000, guid: cf68d9632ab0d2c42a02de12beecadff, type: 2} --- !u!4 &873552999 Transform: m_ObjectHideFlags: 0 diff --git a/Assets/Core/Audio/AudioManager.cs b/Assets/Core/Audio/AudioManager.cs index f617ed3..b3446c3 100644 --- a/Assets/Core/Audio/AudioManager.cs +++ b/Assets/Core/Audio/AudioManager.cs @@ -44,6 +44,7 @@ public class AudioManager : Singleton public event Action? OnSfxVolumeChanged; public event Action? OnUiVolumeChanged; public event Action? OnVoiceVolumeChanged; + public event Action? OnInitialized; #region Unity Lifecycle @@ -76,6 +77,7 @@ public void Initialize() LoadVolumeSettings(); _isInitialized = true; + OnInitialized?.Invoke(); Debug.Log("[AudioManager] ์ดˆ๊ธฐํ™” ์™„๋ฃŒ"); } catch (Exception ex) @@ -249,6 +251,11 @@ public int GetActiveUICount() return 0; } + public VoiceController? GetVoiceController() + { + return _voiceController; + } + #endregion #region Private Methods diff --git a/Assets/Resources/Character/Character-Zero.asset b/Assets/Domain/Character/Model/Chikuwa/Character-Zero.asset similarity index 66% rename from Assets/Resources/Character/Character-Zero.asset rename to Assets/Domain/Character/Model/Chikuwa/Character-Zero.asset index d592cf4..d5b8bda 100644 --- a/Assets/Resources/Character/Character-Zero.asset +++ b/Assets/Domain/Character/Model/Chikuwa/Character-Zero.asset @@ -14,8 +14,8 @@ MonoBehaviour: m_EditorClassIdentifier: characterId: zero characterName: "\uC81C\uB85C" - characterPrefab: {fileID: 403089766806550510, guid: 611b9f4305cdce9458ff7eaead5c0a53, type: 3} - thumbnail: {fileID: 2800000, guid: 17a1333e87a81c34693569503c7ba213, type: 3} + characterPrefab: {fileID: 8051974178353762967, guid: 9be43d9f8e24d634186c522ebce74618, type: 3} + thumbnail: {fileID: 2800000, guid: e029dae0a0b46124ba7b0b6b2ac00a76, type: 3} characterDescription: "\uCE58\uC640\uC9F1 \uBAA8\uB378 \uAE30\uBC18" emotionMappings: - emotionKey: @@ -29,5 +29,13 @@ MonoBehaviour: isLockAtActive: 0 lookSensitivity: 1 lockAtDamping: 0 - gain: 1 + useLipSync: 1 + gain: 10 smoothing: 1 + useAutoEyeBlink: 1 + eyeBlinkMean: 2.5 + eyeBlinkMaximumDeviation: 2 + eyeBlinkTimescale: 10 + eyeBlinkClosingSeconds: 1 + eyeBlinkClosedSeconds: 0.5 + eyeBlinkOpeningSeconds: 1.5 diff --git a/Assets/Resources/Character/Character-Zero.asset.meta b/Assets/Domain/Character/Model/Chikuwa/Character-Zero.asset.meta similarity index 100% rename from Assets/Resources/Character/Character-Zero.asset.meta rename to Assets/Domain/Character/Model/Chikuwa/Character-Zero.asset.meta diff --git a/Assets/Domain/Character/Model/Chikuwa/ziraitikuwa.meta b/Assets/Domain/Character/Model/Chikuwa/model.meta similarity index 77% rename from Assets/Domain/Character/Model/Chikuwa/ziraitikuwa.meta rename to Assets/Domain/Character/Model/Chikuwa/model.meta index 3b8b80e..a05dce7 100644 --- a/Assets/Domain/Character/Model/Chikuwa/ziraitikuwa.meta +++ b/Assets/Domain/Character/Model/Chikuwa/model.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: f5ef28f9c36ba994ba303f866721f03e +guid: 2fc0b961763d08c47b49035b4dfb295d folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Assets/Domain/Character/Model/Model.fadeMotionList.asset b/Assets/Domain/Character/Model/Model.fadeMotionList.asset new file mode 100644 index 0000000..798e9bd --- /dev/null +++ b/Assets/Domain/Character/Model/Model.fadeMotionList.asset @@ -0,0 +1,17 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 403ae2dd693bb1d4b924f6b8d206b053, type: 3} + m_Name: Model.fadeMotionList + m_EditorClassIdentifier: + MotionInstanceIds: + CubismFadeMotionObjects: + - {fileID: 0} diff --git a/Assets/Resources/Character.meta b/Assets/Domain/Character/Model/Model.fadeMotionList.asset.meta similarity index 52% rename from Assets/Resources/Character.meta rename to Assets/Domain/Character/Model/Model.fadeMotionList.asset.meta index 892336f..9b3ec5d 100644 --- a/Assets/Resources/Character.meta +++ b/Assets/Domain/Character/Model/Model.fadeMotionList.asset.meta @@ -1,8 +1,8 @@ fileFormatVersion: 2 -guid: 33c3ee053842bbb4a8f0c00ad6fbf92a -folderAsset: yes -DefaultImporter: +guid: b1083908ab87d904a95806ad80fc75eb +NativeFormatImporter: externalObjects: {} + mainObjectFileID: 11400000 userData: assetBundleName: assetBundleVariant: diff --git a/Assets/Domain/Character/Script/Config/Live2DModelConfig.cs b/Assets/Domain/Character/Script/Config/Live2DModelConfig.cs index 4c1c54b..369a05f 100644 --- a/Assets/Domain/Character/Script/Config/Live2DModelConfig.cs +++ b/Assets/Domain/Character/Script/Config/Live2DModelConfig.cs @@ -90,6 +90,9 @@ public class ActionMapping [Header("[ ๋ฆฝ์‹ฑํฌ ์„ค์ • ]")] [Space(2)] + [Tooltip("๋ฆฝ์‹ฑํฌ ์‚ฌ์šฉ ์—ฌ๋ถ€")] + [SerializeField] private bool useLipSync = true; + [Tooltip("์Œ๋Ÿ‰ ๋ฐฐ์ˆ˜ (1 = ๊ธฐ๋ณธ)")] [Range(1f, 10f)] [SerializeField] private float gain = 1f; @@ -98,6 +101,40 @@ public class ActionMapping [Range(0f, 1f)] [SerializeField] private float smoothing = 1f; + [Space(5)] + [Header("โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€")] + [Header("[ ์ž๋™ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์„ค์ • ]")] + [Space(2)] + + [Tooltip("์ž๋™ ๋ˆˆ ๊นœ๋นก์ž„ ์‚ฌ์šฉ ์—ฌ๋ถ€")] + [SerializeField] private bool useAutoEyeBlink = true; + + [Header("๋ˆˆ ๊นœ๋นก์ž„ ํƒ€์ด๋ฐ ์„ค์ •")] + [Tooltip("๋ˆˆ ๊นœ๋นก์ž„ ๊ฐ„๊ฒฉ์˜ ํ‰๊ท  ์‹œ๊ฐ„ (์ดˆ)")] + [Range(1f, 10f)] + [SerializeField] private float eyeBlinkMean = 2.5f; + + [Tooltip("ํ‰๊ท ์—์„œ์˜ ์ตœ๋Œ€ ํŽธ์ฐจ (์ดˆ)")] + [Range(0.5f, 5f)] + [SerializeField] private float eyeBlinkMaximumDeviation = 2f; + + [Tooltip("๋ˆˆ ๊นœ๋นก์ž„ ์‹œ๊ฐ„ ์Šค์ผ€์ผ")] + [Range(1f, 20f)] + [SerializeField] private float eyeBlinkTimescale = 10f; + + [Header("๋ˆˆ ๊นœ๋นก์ž„ ๋™์ž‘ ์„ธ๋ถ€ ์„ค์ •")] + [Tooltip("๋ˆˆ์„ ๊ฐ๋Š” ๋™์ž‘ ์‹œ๊ฐ„ (์ดˆ)")] + [Range(0.1f, 3f)] + [SerializeField] private float eyeBlinkClosingSeconds = 1.0f; + + [Tooltip("๋ˆˆ์ด ๊ฐ๊ธด ์ƒํƒœ ์ง€์† ์‹œ๊ฐ„ (์ดˆ)")] + [Range(0.1f, 2f)] + [SerializeField] private float eyeBlinkClosedSeconds = 0.5f; + + [Tooltip("๋ˆˆ์„ ์—ฌ๋Š” ๋™์ž‘ ์‹œ๊ฐ„ (์ดˆ)")] + [Range(0.1f, 3f)] + [SerializeField] private float eyeBlinkOpeningSeconds = 1.5f; + // ์บ๋ฆญํ„ฐ ๊ธฐ๋ณธ์ •๋ณด public string CharacterId => characterId; public string CharacterName => characterName; @@ -120,6 +157,16 @@ public class ActionMapping public float LockAtDamping => lockAtDamping; public float Gain => gain; public float Smoothing => smoothing; + public bool UseLipSync => useLipSync; + public bool UseAutoEyeBlink => useAutoEyeBlink; + + // ๋ˆˆ ๊นœ๋นก์ž„ ์„ค์ • + public float EyeBlinkMean => eyeBlinkMean; + public float EyeBlinkMaximumDeviation => eyeBlinkMaximumDeviation; + public float EyeBlinkTimescale => eyeBlinkTimescale; + public float EyeBlinkClosingSeconds => eyeBlinkClosingSeconds; + public float EyeBlinkClosedSeconds => eyeBlinkClosedSeconds; + public float EyeBlinkOpeningSeconds => eyeBlinkOpeningSeconds; } } diff --git a/Assets/Domain/Character/Script/Facade/CharacterFacade.cs b/Assets/Domain/Character/Script/Facade/CharacterFacade.cs index 3d11c87..96d113d 100644 --- a/Assets/Domain/Character/Script/Facade/CharacterFacade.cs +++ b/Assets/Domain/Character/Script/Facade/CharacterFacade.cs @@ -2,6 +2,7 @@ using UnityEngine; using UnityEngine.TextCore.Text; using ProjectVG.Domain.Character.Component; +using ProjectVG.Core.Audio; namespace ProjectVG.Domain.Character.Service { @@ -13,7 +14,6 @@ public class CharacterFacade : MonoBehaviour, ICharacterFacade [SerializeField] private Transform _modelTransform; [SerializeField] private Live2DModelRegistry _modelRegistry; - private ICharacterModelManager _modelManager; private ICharacterActionService _actionService; private ICharacterActionResolver _actionResolver; @@ -39,7 +39,12 @@ public void Initialize() _modelManager = gameObject.AddComponent(); Debug.Log($"[CharacterFacade] CharacterModelManager๊ฐ€ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค: {gameObject.name}"); } - _modelManager.Initialize(_modelTransform, _modelRegistry); + + // AudioManager์—์„œ Voice AudioSource ๊ฐ€์ ธ์˜ค๊ธฐ + var voiceAudioSource = GetVoiceAudioSource(); + + _modelManager.Initialize(_modelTransform, _modelRegistry, voiceAudioSource); + _modelManager.LoadModel("zero", true); // ์ž„์‹œ ํ™œ์„ฑํ™” if (_modelTransform != null) { @@ -161,6 +166,33 @@ public void Shutdown() _modelManager.UnloadAll(); } } + + + + /// + /// AudioManager์—์„œ Voice AudioSource๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค. + /// + private AudioSource? GetVoiceAudioSource() + { + var audioManager = AudioManager.Instance; + if (audioManager != null && audioManager.IsInitialized) + { + var voiceController = audioManager.GetVoiceController(); + if (voiceController != null) + { + var audioSource = voiceController.GetAudioSource(); + if (audioSource != null) + { + Debug.Log("[CharacterFacade] Voice AudioSource ๊ฐ€์ ธ์˜ค๊ธฐ ์™„๋ฃŒ"); + return audioSource; + } + } + } + + Debug.LogWarning("[CharacterFacade] AudioManager๊ฐ€ ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์•˜๊ฑฐ๋‚˜ Voice AudioSource๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + return null; + } } } + diff --git a/Assets/Domain/Character/Script/Manager/CharacterModelManager.cs b/Assets/Domain/Character/Script/Manager/CharacterModelManager.cs index e91f802..be7c19a 100644 --- a/Assets/Domain/Character/Script/Manager/CharacterModelManager.cs +++ b/Assets/Domain/Character/Script/Manager/CharacterModelManager.cs @@ -1,7 +1,11 @@ -using UnityEngine; using Cysharp.Threading.Tasks; -using System.Collections.Generic; +using Live2D.Cubism.Framework; +using Live2D.Cubism.Framework.MouthMovement; +using Live2D.Cubism.Core; +using ProjectVG.Core.Audio; using ProjectVG.Domain.Character.Live2D.Model; +using System.Collections.Generic; +using UnityEngine; namespace ProjectVG.Domain.Character.Service { @@ -16,11 +20,20 @@ public class CharacterModelManager : MonoBehaviour, ICharacterModelManager private Live2DModelRegistry _modelRegistry; private readonly Dictionary _characterIdToInstance = new Dictionary(); private string _activeCharacterId; + private AudioSource? _voiceAudioSource; #endregion #region Unity Lifecycle + private void Update() + { + // ๋ฆฝ์‹ฑํฌ ๋””๋ฒ„๊น…์„ ์œ„ํ•œ AudioSource ์ƒํƒœ ๋ชจ๋‹ˆํ„ฐ๋ง + if (_voiceAudioSource != null && _voiceAudioSource.isPlaying) { + Debug.Log($"[CharacterModelManager] AudioSource ์žฌ์ƒ์ค‘: {_voiceAudioSource.name}, ๋ณผ๋ฅจ: {_voiceAudioSource.volume}, ์‹œ๊ฐ„: {_voiceAudioSource.time}"); + } + } + #endregion #region Public Methods @@ -28,10 +41,11 @@ public class CharacterModelManager : MonoBehaviour, ICharacterModelManager /// /// ๋งค๋‹ˆ์ €๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค /// - public void Initialize(Transform modelRoot, Live2DModelRegistry modelRegistry) + public void Initialize(Transform modelRoot, Live2DModelRegistry modelRegistry, AudioSource? voiceAudioSource = null) { _modelRoot = modelRoot; _modelRegistry = modelRegistry; + _voiceAudioSource = voiceAudioSource; } /// @@ -105,13 +119,19 @@ public void UnloadModel(string characterId) /// public void UnloadAll() { + var keysToRemove = new List(); + foreach (var kvp in _characterIdToInstance) { if (kvp.Value != null) { Destroy(kvp.Value); } + keysToRemove.Add(kvp.Key); } - _characterIdToInstance.Clear(); + foreach (var key in keysToRemove) { + _characterIdToInstance.Remove(key); + } + _activeCharacterId = null; } @@ -125,6 +145,12 @@ public void ActivateModel(string characterId) return; } + if (target == null) { + _characterIdToInstance.Remove(characterId); + Debug.LogWarning($"[CharacterModelManager] ์บ๋ฆญํ„ฐ '{characterId}'์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ํŒŒ๊ดด๋˜์—ˆ์Šต๋‹ˆ๋‹ค."); + return; + } + DeactivateAllModels(); target.SetActive(true); @@ -140,8 +166,12 @@ public void DeactivateModel() return; } - if (_characterIdToInstance.TryGetValue(_activeCharacterId, out var current) && current != null) { - current.SetActive(false); + if (_characterIdToInstance.TryGetValue(_activeCharacterId, out var current)) { + if (current != null) { + current.SetActive(false); + } else { + _characterIdToInstance.Remove(_activeCharacterId); + } } _activeCharacterId = null; @@ -163,6 +193,7 @@ public bool IsLoaded(string characterId) return !string.IsNullOrEmpty(characterId) && _characterIdToInstance.ContainsKey(characterId); } + #endregion #region Private Methods @@ -228,6 +259,8 @@ private GameObject CreateModelInstance(Live2DModelConfig config, string characte var instance = Instantiate(config.CharacterPrefab, parent); instance.name = characterId; instance.SetActive(false); + + SetupModelComponents(instance, config); return instance; } @@ -247,20 +280,131 @@ private async UniTask CreateModelInstanceAsync(Live2DModelConfig con instance.name = characterId; instance.SetActive(false); + SetupModelComponents(instance, config); await UniTask.Yield(); return instance; } + /// + /// ๋ชจ๋ธ์— ํ•„์š”ํ•œ ์ปดํฌ๋„ŒํŠธ๋“ค์„ ์„ค์ •ํ•œ๋‹ค + /// + private void SetupModelComponents(GameObject modelInstance, Live2DModelConfig config) + { + SetupLipSync(modelInstance, config); + SetupAutoEyeBlink(modelInstance, config); + } + + /// + /// ๋ฆฝ์‹ฑํฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์„ค์ •ํ•œ๋‹ค + /// + private void SetupLipSync(GameObject modelInstance, Live2DModelConfig config) + { + if (!config.UseLipSync) { + Debug.Log($"[CharacterModelManager] ๋ฆฝ์‹ฑํฌ๊ฐ€ ๋น„ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค: {modelInstance.name}"); + return; + } + + var mouthController = modelInstance.GetComponent(); + if (mouthController == null) { + mouthController = modelInstance.AddComponent(); + Debug.Log($"[CharacterModelManager] CubismAudioMouthInput ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค: {modelInstance.name}"); + } + + if (_voiceAudioSource == null) { + Debug.LogWarning($"[CharacterModelManager] Voice AudioSource๊ฐ€ null์ž…๋‹ˆ๋‹ค. ๋ฆฝ์‹ฑํฌ๊ฐ€ ๋™์ž‘ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: {modelInstance.name}"); + } else { + Debug.Log($"[CharacterModelManager] Voice AudioSource ์„ค์ • ์™„๋ฃŒ: {modelInstance.name}, AudioSource: {_voiceAudioSource.name}"); + } + + mouthController.AudioInput = _voiceAudioSource; + mouthController.Gain = config.Gain; + mouthController.Smoothing = config.Smoothing; + + // Live2D ๋ชจ๋ธ์— Mouth ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ + var model = modelInstance.GetComponent(); + if (model != null) { + var mouthParameter = model.Parameters.FindById("ParamMouthOpenY"); + if (mouthParameter != null) { + Debug.Log($"[CharacterModelManager] Mouth ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐœ๊ฒฌ: {mouthParameter.Id}, ํ˜„์žฌ๊ฐ’: {mouthParameter.Value}"); + } else { + Debug.LogWarning($"[CharacterModelManager] Mouth ํŒŒ๋ผ๋ฏธํ„ฐ(ParamMouthOpenY)๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: {modelInstance.name}"); + } + } + + Debug.Log($"[CharacterModelManager] ๋ฆฝ์‹ฑํฌ ์„ค์ • ์™„๋ฃŒ: {modelInstance.name}, Gain: {config.Gain}, Smoothing: {config.Smoothing}"); + } + + /// + /// ์ž๋™ ๋ˆˆ ๊นœ๋นก์ž„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์„ค์ •ํ•œ๋‹ค + /// + private void SetupAutoEyeBlink(GameObject modelInstance, Live2DModelConfig config) + { + if (!config.UseAutoEyeBlink) { + return; + } + + var eyeBlinkController = modelInstance.GetComponent(); + if (eyeBlinkController == null) { + eyeBlinkController = modelInstance.AddComponent(); + } + + eyeBlinkController.Mean = config.EyeBlinkMean; + eyeBlinkController.MaximumDeviation = config.EyeBlinkMaximumDeviation; + eyeBlinkController.Timescale = config.EyeBlinkTimescale; + eyeBlinkController.SetBlinkingSettings( + config.EyeBlinkClosingSeconds, + config.EyeBlinkClosedSeconds, + config.EyeBlinkOpeningSeconds + ); + } + /// /// ๋ชจ๋“  ๋ชจ๋ธ์„ ๋น„ํ™œ์„ฑํ™”ํ•œ๋‹ค /// private void DeactivateAllModels() { + var keysToRemove = new List(); + foreach (var kvp in _characterIdToInstance) { - if (kvp.Value != null) { - kvp.Value.SetActive(false); + if (kvp.Value == null) { + keysToRemove.Add(kvp.Key); + continue; + } + + kvp.Value.SetActive(false); + } + + foreach (var key in keysToRemove) { + _characterIdToInstance.Remove(key); + } + } + + /// + /// ๋ชจ๋“  ๋กœ๋“œ๋œ ๋ชจ๋ธ์˜ ๋ฆฝ์‹ฑํฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์—…๋ฐ์ดํŠธํ•œ๋‹ค + /// + private void UpdateAllLipSyncComponents() + { + var keysToRemove = new List(); + + foreach (var kvp in _characterIdToInstance) { + if (kvp.Value == null) { + keysToRemove.Add(kvp.Key); + continue; + } + + var mouthController = kvp.Value.GetComponent(); + if (mouthController != null) { + mouthController.AudioInput = _voiceAudioSource; + + if (_voiceAudioSource != null) { + Debug.Log($"[CharacterModelManager] ๋ฆฝ์‹ฑํฌ ์—…๋ฐ์ดํŠธ: {kvp.Key}, AudioSource ์žฌ์ƒ์ค‘: {_voiceAudioSource.isPlaying}, ๋ณผ๋ฅจ: {_voiceAudioSource.volume}"); + } } } + + foreach (var key in keysToRemove) { + _characterIdToInstance.Remove(key); + } } #endregion diff --git a/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs b/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs index d39b4e1..aef83ff 100644 --- a/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs +++ b/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs @@ -15,7 +15,8 @@ public interface ICharacterModelManager /// /// ๋ชจ๋ธ ์œ„์น˜ /// ๋ชจ๋ธ ๋“ฑ๋ก์ž - public void Initialize(Transform modelRoot, Live2DModelRegistry modelRegistry); + /// ์Œ์„ฑ AudioSource (์„ ํƒ์‚ฌํ•ญ) + public void Initialize(Transform modelRoot, Live2DModelRegistry modelRegistry, AudioSource? voiceAudioSource = null); /// /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ๋กœ๋“œํ•œ๋‹ค. diff --git a/Assets/Resources/Character/Live2DModelRegistry.asset b/Assets/Resources/Live2DModelRegistry.asset similarity index 68% rename from Assets/Resources/Character/Live2DModelRegistry.asset rename to Assets/Resources/Live2DModelRegistry.asset index 59a664b..65a8162 100644 --- a/Assets/Resources/Character/Live2DModelRegistry.asset +++ b/Assets/Resources/Live2DModelRegistry.asset @@ -14,6 +14,4 @@ MonoBehaviour: m_EditorClassIdentifier: _entries: - characterId: zero - characterConfig: {fileID: 11400000, guid: 4c6d1f5cb9556f24c843f3e9fe14d49e, type: 2} - - characterId: zero - characterConfig: {fileID: 11400000, guid: 4c6d1f5cb9556f24c843f3e9fe14d49e, type: 2} + characterConfig: {fileID: 11400000, guid: 58c40b616ae7d944c91fbc00193d5d3b, type: 2} diff --git a/Assets/Resources/Character/Live2DModelRegistry.asset.meta b/Assets/Resources/Live2DModelRegistry.asset.meta similarity index 100% rename from Assets/Resources/Character/Live2DModelRegistry.asset.meta rename to Assets/Resources/Live2DModelRegistry.asset.meta diff --git a/Assets/Resources/ResolutionModelScaleConfig.asset b/Assets/Resources/ResolutionModelScaleConfig.asset index 360aaad..d471886 100644 --- a/Assets/Resources/ResolutionModelScaleConfig.asset +++ b/Assets/Resources/ResolutionModelScaleConfig.asset @@ -19,65 +19,65 @@ MonoBehaviour: maxWidth: 1440 minHeight: 2960 maxHeight: 2960 - isMobileOnly: true - scale: 0.9 - description: ๋ชจ๋ฐ”์ผ ๊ณ ํ•ด์ƒ๋„ (1440x2960) + isMobileOnly: 0 + scale: 1.5 + description: "\uBAA8\uBC14\uC77C \uACE0\uD574\uC0C1\uB3C4 (1440x2960)" - minWidth: 2560 maxWidth: 9999 minHeight: 1440 maxHeight: 9999 - isMobileOnly: false + isMobileOnly: 0 scale: 1.4 - description: PC QHD+ (2560x1440 ์ด์ƒ) + description: "PC QHD+ (2560x1440 \uC774\uC0C1)" - minWidth: 1921 maxWidth: 9999 minHeight: 1081 maxHeight: 9999 - isMobileOnly: false + isMobileOnly: 0 scale: 1.3 - description: PC QHD ์ด์ƒ + description: "PC QHD \uC774\uC0C1" - minWidth: 1025 maxWidth: 1920 minHeight: 0 maxHeight: 1080 - isMobileOnly: false + isMobileOnly: 0 scale: 1.1 description: PC FHD - minWidth: 769 maxWidth: 1024 minHeight: 0 maxHeight: 9999 - isMobileOnly: false + isMobileOnly: 0 scale: 0.9 - description: ํƒœ๋ธ”๋ฆฟ ๊ฐ€๋กœ + description: "\uD0DC\uBE14\uB9BF \uAC00\uB85C" - minWidth: 481 maxWidth: 768 minHeight: 0 maxHeight: 9999 - isMobileOnly: false + isMobileOnly: 0 scale: 0.8 - description: ๋ชจ๋ฐ”์ผ ๊ฐ€๋กœ/ํƒœ๋ธ”๋ฆฟ ์„ธ๋กœ + description: "\uBAA8\uBC14\uC77C \uAC00\uB85C/\uD0DC\uBE14\uB9BF \uC138\uB85C" - minWidth: 0 maxWidth: 480 minHeight: 0 maxHeight: 9999 - isMobileOnly: false + isMobileOnly: 0 scale: 0.7 - description: ๋ชจ๋ฐ”์ผ ์„ธ๋กœ (์ž‘์€ ํ™”๋ฉด) + description: "\uBAA8\uBC14\uC77C \uC138\uB85C (\uC791\uC740 \uD654\uBA74)" - minWidth: 0 maxWidth: 9999 minHeight: 0 maxHeight: 9999 - isMobileOnly: true + isMobileOnly: 0 scale: 0.85 - description: ๋ชจ๋ฐ”์ผ ๊ธฐ๋ณธ + description: "\uBAA8\uBC14\uC77C \uAE30\uBCF8" - minWidth: 0 maxWidth: 9999 minHeight: 0 maxHeight: 9999 - isMobileOnly: false + isMobileOnly: 0 scale: 1 - description: ๊ธฐ๋ณธ ๊ทœ์น™ + description: "\uAE30\uBCF8 \uADDC\uCE59" enableAutoScale: 1 minScale: 0.1 maxScale: 10 From f722557051bb8d7e7a91f848a9d6820062277a56 Mon Sep 17 00:00:00 2001 From: WooSH Date: Thu, 21 Aug 2025 16:27:55 +0900 Subject: [PATCH 09/13] =?UTF-8?q?reafactory:=20=EB=AF=B8=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=20CharManger,=20Charactor=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=8F=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Core/Audio/NewAudioMixer.mixer | 8 +- Assets/Core/Managers/SystemManager.cs | 2 +- .../Script/Component/CubismHitHandler.cs | 76 ---- .../Script/Component/CubismHitHandler.cs.meta | 2 - .../Script/Component/CubismLookTarget.cs | 25 -- .../Script/Component/CubismLookTarget.cs.meta | 2 - .../Config/ResolutionModelScaleConfig.cs | 36 -- .../Script/Controller/ActionController.cs | 192 --------- .../Controller/ActionController.cs.meta | 2 - .../Script/Controller/EmotionController.cs | 209 ---------- .../Controller/EmotionController.cs.meta | 2 - .../Script/Controller/IActionController.cs | 13 - .../Controller/IActionController.cs.meta | 2 - .../Script/Controller/IEmotionController.cs | 14 - .../Controller/IEmotionController.cs.meta | 2 - .../Script/Controller/ILive2DModelApplier.cs | 13 - .../Controller/ILive2DModelApplier.cs.meta | 2 - .../Controller/ILive2DModelManagerFacade.cs | 15 - .../ILive2DModelManagerFacade.cs.meta | 2 - .../Script/Controller/Live2DModelApplier.cs | 89 ---- .../Controller/Live2DModelApplier.cs.meta | 2 - .../Controller/Live2DParameterController.cs | 81 ---- .../Live2DParameterController.cs.meta | 2 - ...CharacterFacade.cs => CharacterManager.cs} | 18 +- ...acade.cs.meta => CharacterManager.cs.meta} | 0 ...haracterFacade.cs => ICharacterManager.cs} | 2 +- ...cade.cs.meta => ICharacterManager.cs.meta} | 0 .../Script/Manager/CharacterModelManager.cs | 12 +- .../Script/Manager/ICharacterModelManager.cs | 2 +- .../Script/Manager/Live2DCharacterManager.cs | 198 --------- .../Manager/Live2DCharacterManager.cs.meta | 2 - .../Script/Manager/Live2DModelManager.cs | 377 ----------------- .../Script/Manager/Live2DModelManager.cs.meta | 2 - Assets/Domain/Character/Script/Test.meta | 8 - .../Character/Script/Test/Live2DModelTest.cs | 180 -------- .../Script/Test/Live2DModelTest.cs.meta | 2 - .../Script/Test/ResolutionDebugger.cs | 160 -------- .../Script/Test/ResolutionDebugger.cs.meta | 2 - .../Domain/Character/Script/Test/TestVoice.cs | 14 - .../Character/Script/Test/TestVoice.cs.meta | 2 - Assets/Domain/Chat/Service/ChatManager.cs | 385 ------------------ .../Domain/Chat/Service/ChatMessageQueue.cs | 141 +++++++ .../Chat/Service/ChatMessageQueue.cs.meta | 2 + .../Domain/Chat/Service/ChatSystemManager.cs | 209 ++++++++++ ...ager.cs.meta => ChatSystemManager.cs.meta} | 0 Assets/Domain/Chat/View/TextInputView.cs | 6 +- Assets/Domain/Chat/View/VoiceInputView.cs | 6 +- Assets/Samples.meta | 8 - Assets/Samples/Core.meta | 8 - Assets/Samples/Core/Managers.meta | 8 - .../Core/Managers/SampleSystemManager.cs | 71 ---- .../Core/Managers/SampleSystemManager.cs.meta | 2 - Assets/Voice.mixer | 69 ---- Assets/Voice.mixer.meta | 8 - 54 files changed, 376 insertions(+), 2321 deletions(-) delete mode 100644 Assets/Domain/Character/Script/Component/CubismHitHandler.cs delete mode 100644 Assets/Domain/Character/Script/Component/CubismHitHandler.cs.meta delete mode 100644 Assets/Domain/Character/Script/Component/CubismLookTarget.cs delete mode 100644 Assets/Domain/Character/Script/Component/CubismLookTarget.cs.meta delete mode 100644 Assets/Domain/Character/Script/Controller/ActionController.cs delete mode 100644 Assets/Domain/Character/Script/Controller/ActionController.cs.meta delete mode 100644 Assets/Domain/Character/Script/Controller/EmotionController.cs delete mode 100644 Assets/Domain/Character/Script/Controller/EmotionController.cs.meta delete mode 100644 Assets/Domain/Character/Script/Controller/IActionController.cs delete mode 100644 Assets/Domain/Character/Script/Controller/IActionController.cs.meta delete mode 100644 Assets/Domain/Character/Script/Controller/IEmotionController.cs delete mode 100644 Assets/Domain/Character/Script/Controller/IEmotionController.cs.meta delete mode 100644 Assets/Domain/Character/Script/Controller/ILive2DModelApplier.cs delete mode 100644 Assets/Domain/Character/Script/Controller/ILive2DModelApplier.cs.meta delete mode 100644 Assets/Domain/Character/Script/Controller/ILive2DModelManagerFacade.cs delete mode 100644 Assets/Domain/Character/Script/Controller/ILive2DModelManagerFacade.cs.meta delete mode 100644 Assets/Domain/Character/Script/Controller/Live2DModelApplier.cs delete mode 100644 Assets/Domain/Character/Script/Controller/Live2DModelApplier.cs.meta delete mode 100644 Assets/Domain/Character/Script/Controller/Live2DParameterController.cs delete mode 100644 Assets/Domain/Character/Script/Controller/Live2DParameterController.cs.meta rename Assets/Domain/Character/Script/Facade/{CharacterFacade.cs => CharacterManager.cs} (83%) rename Assets/Domain/Character/Script/Facade/{CharacterFacade.cs.meta => CharacterManager.cs.meta} (100%) rename Assets/Domain/Character/Script/Facade/{ICharacterFacade.cs => ICharacterManager.cs} (98%) rename Assets/Domain/Character/Script/Facade/{ICharacterFacade.cs.meta => ICharacterManager.cs.meta} (100%) delete mode 100644 Assets/Domain/Character/Script/Manager/Live2DCharacterManager.cs delete mode 100644 Assets/Domain/Character/Script/Manager/Live2DCharacterManager.cs.meta delete mode 100644 Assets/Domain/Character/Script/Manager/Live2DModelManager.cs delete mode 100644 Assets/Domain/Character/Script/Manager/Live2DModelManager.cs.meta delete mode 100644 Assets/Domain/Character/Script/Test.meta delete mode 100644 Assets/Domain/Character/Script/Test/Live2DModelTest.cs delete mode 100644 Assets/Domain/Character/Script/Test/Live2DModelTest.cs.meta delete mode 100644 Assets/Domain/Character/Script/Test/ResolutionDebugger.cs delete mode 100644 Assets/Domain/Character/Script/Test/ResolutionDebugger.cs.meta delete mode 100644 Assets/Domain/Character/Script/Test/TestVoice.cs delete mode 100644 Assets/Domain/Character/Script/Test/TestVoice.cs.meta delete mode 100644 Assets/Domain/Chat/Service/ChatManager.cs create mode 100644 Assets/Domain/Chat/Service/ChatMessageQueue.cs create mode 100644 Assets/Domain/Chat/Service/ChatMessageQueue.cs.meta create mode 100644 Assets/Domain/Chat/Service/ChatSystemManager.cs rename Assets/Domain/Chat/Service/{ChatManager.cs.meta => ChatSystemManager.cs.meta} (100%) delete mode 100644 Assets/Samples.meta delete mode 100644 Assets/Samples/Core.meta delete mode 100644 Assets/Samples/Core/Managers.meta delete mode 100644 Assets/Samples/Core/Managers/SampleSystemManager.cs delete mode 100644 Assets/Samples/Core/Managers/SampleSystemManager.cs.meta delete mode 100644 Assets/Voice.mixer delete mode 100644 Assets/Voice.mixer.meta diff --git a/Assets/Core/Audio/NewAudioMixer.mixer b/Assets/Core/Audio/NewAudioMixer.mixer index d1bfeb0..c99b10a 100644 --- a/Assets/Core/Audio/NewAudioMixer.mixer +++ b/Assets/Core/Audio/NewAudioMixer.mixer @@ -6,7 +6,7 @@ AudioMixerGroupController: m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_Name: SFX + m_Name: SFXVolume m_AudioMixer: {fileID: 24100000} m_GroupID: 603178d5b1e127d429609f70b6dfaf79 m_Children: [] @@ -128,7 +128,7 @@ AudioMixerGroupController: m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_Name: BGM + m_Name: BGMVolume m_AudioMixer: {fileID: 24100000} m_GroupID: 1bf7479df396fed488843a11e4f16671 m_Children: [] @@ -161,7 +161,7 @@ AudioMixerGroupController: m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_Name: UI + m_Name: UIVolume m_AudioMixer: {fileID: 24100000} m_GroupID: 323361331de8fbf46b26000797bc1794 m_Children: [] @@ -180,7 +180,7 @@ AudioMixerGroupController: m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_Name: Voice + m_Name: VoiceVolume m_AudioMixer: {fileID: 24100000} m_GroupID: 1c2c7c658d2c3e647adee42f03d62f41 m_Children: [] diff --git a/Assets/Core/Managers/SystemManager.cs b/Assets/Core/Managers/SystemManager.cs index 2d76b8c..ddc05c0 100644 --- a/Assets/Core/Managers/SystemManager.cs +++ b/Assets/Core/Managers/SystemManager.cs @@ -20,7 +20,7 @@ public class SystemManager : Singleton [SerializeField] private HttpApiClient? _httpApiClient; [SerializeField] private AudioManager? _audioManager; [SerializeField] private LoadingManager? _loadingManager; - [SerializeField] private ChatManager? _chatManager; + [SerializeField] private ChatSystemManager? _chatManager; [Header("Settings")] diff --git a/Assets/Domain/Character/Script/Component/CubismHitHandler.cs b/Assets/Domain/Character/Script/Component/CubismHitHandler.cs deleted file mode 100644 index 8b066ba..0000000 --- a/Assets/Domain/Character/Script/Component/CubismHitHandler.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Collections; -using Live2D.Cubism.Framework.Expression; -using Live2D.Cubism.Framework.Raycasting; -using UnityEngine; - -public class CubismHitHandler : MonoBehaviour -{ - private CubismRaycaster _raycaster = null; - private CubismExpressionController _expressionController = null; - - public void Initialize() - { - _raycaster = GetComponent(); - _expressionController = GetComponent(); - - if (_raycaster != null && ScreenTapManager.Instance != null) - { - ScreenTapManager.Instance.SetRaycaster(_raycaster); - } - else - { - Debug.LogWarning("[CubismHitHandler] Raycaster ๋˜๋Š” ScreenTapManager๊ฐ€ null์ž…๋‹ˆ๋‹ค."); - } - } - - private void Update() - { - if (ScreenTapManager.Instance == null) - { - return; - } - - if (ScreenTapManager.Instance.TryGetTapUpPosition(out var hits)) - { - foreach (var hit in hits) - { - if(hit.Drawable is null) continue; - HandleHit(hit.Drawable.name); - } - } - } - - private void HandleHit(string drawableName) - { - // ์—ฌ๊ธฐ์„œ ํ„ฐ์น˜๋œ ํŒŒ์ธ ๋ณ„๋กœ ๋ฐ˜์‘ - switch (drawableName) - { - case "HitAreaHead": - Debug.Log("๋จธ๋ฆฌ ํ„ฐ์น˜ โ†’ ํ‘œ์ • ๋ณ€๊ฒฝ or ๋ชจ์…˜ ์žฌ์ƒ"); - ExpressionChange(); - break; - case "HitAreaBody": - Debug.Log("๋ชธํ†ต ํ„ฐ์น˜ โ†’ ๋‹ค๋ฅธ ๋ฐ˜์‘"); - break; - } - } - - // TODO : ์ถ”ํ›„ ํ‘œ์ • ๊ด€๋ฆฌ ํด๋ž˜์Šค๋กœ ๋ถ„๋ฆฌ - private void ExpressionChange() - { - _expressionController.CurrentExpressionIndex = - GetNextExpressionIndex(_expressionController.CurrentExpressionIndex, 0, - _expressionController.ExpressionsList.CubismExpressionObjects.Length); - } - - private int GetNextExpressionIndex(int current, int min, int max) - { - return ((current - min + 1) % (max - min + 1)) + min; - } - - public void ExpressionChange_Btn() - { - ExpressionChange(); - } -} diff --git a/Assets/Domain/Character/Script/Component/CubismHitHandler.cs.meta b/Assets/Domain/Character/Script/Component/CubismHitHandler.cs.meta deleted file mode 100644 index 284028f..0000000 --- a/Assets/Domain/Character/Script/Component/CubismHitHandler.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 49951858c8af46f4c85cecf4a03db08b \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Component/CubismLookTarget.cs b/Assets/Domain/Character/Script/Component/CubismLookTarget.cs deleted file mode 100644 index 5e515c8..0000000 --- a/Assets/Domain/Character/Script/Component/CubismLookTarget.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Live2D.Cubism.Framework.LookAt; -using UnityEngine; - -public class CubismLookTarget : MonoBehaviour, ICubismLookTarget -{ - private ModelConfig _modelConfig; - - public void Initialize(ModelConfig modelConfig) - { - _modelConfig = modelConfig; - } - - public Vector3 GetPosition() - { - if (!ScreenTapManager.Instance.TryGetLookDirection(out var lookDir)) - return Vector3.zero; - - return lookDir * _modelConfig.LookSensitivity; - } - - public bool IsActive() - { - return _modelConfig.IsLockAtActive; - } -} diff --git a/Assets/Domain/Character/Script/Component/CubismLookTarget.cs.meta b/Assets/Domain/Character/Script/Component/CubismLookTarget.cs.meta deleted file mode 100644 index 4230601..0000000 --- a/Assets/Domain/Character/Script/Component/CubismLookTarget.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 8c57f28f2e390324eb9e52c4fd0c3dde \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Config/ResolutionModelScaleConfig.cs b/Assets/Domain/Character/Script/Config/ResolutionModelScaleConfig.cs index 28f09a8..1b85f7e 100644 --- a/Assets/Domain/Character/Script/Config/ResolutionModelScaleConfig.cs +++ b/Assets/Domain/Character/Script/Config/ResolutionModelScaleConfig.cs @@ -40,11 +40,6 @@ public float CalculateScale() var scale = CalculateScaleForResolution(currentResolution); var clampedScale = Mathf.Clamp(scale, minScale, maxScale); - Debug.Log($"[ResolutionModelScaleConfig] ์Šค์ผ€์ผ ๊ณ„์‚ฐ ๊ณผ์ •:\n" + - $"๊ณ„์‚ฐ๋œ ์Šค์ผ€์ผ: {scale:F3}๋ฐฐ\n" + - $"์ œํ•œ ๋ฒ”์œ„: {minScale:F3} ~ {maxScale:F3}\n" + - $"์ตœ์ข… ์Šค์ผ€์ผ: {clampedScale:F3}๋ฐฐ"); - return clampedScale; } @@ -58,19 +53,9 @@ public float CalculateScaleForResolution(Vector2 resolution) if (bestRule != null) { - Debug.Log($"[ResolutionModelScaleConfig] ์ตœ์  ๊ทœ์น™ ๋งค์นญ ์„ฑ๊ณต!\n" + - $"ํ•ด์ƒ๋„: {resolution.x} x {resolution.y}\n" + - $"์ ์šฉ ๊ทœ์น™: {bestRule.Description}\n" + - $"๊ทœ์น™ ๋ฒ”์œ„: {bestRule.MinWidth}x{bestRule.MinHeight} ~ {bestRule.MaxWidth}x{bestRule.MaxHeight}\n" + - $"์Šค์ผ€์ผ: {bestRule.Scale:F3}๋ฐฐ"); return bestRule.Scale; } - Debug.Log($"[ResolutionModelScaleConfig] ๊ทœ์น™ ๋งค์นญ ์—†์Œ\n" + - $"ํ•ด์ƒ๋„: {resolution.x} x {resolution.y}\n" + - $"์ž๋™ ๊ณ„์‚ฐ ๋ชจ๋“œ: {scaleMode}\n" + - $"๊ธฐ์ค€ ํ•ด์ƒ๋„: {referenceResolution.x} x {referenceResolution.y}"); - // ์ž๋™ ๊ณ„์‚ฐ switch (scaleMode) { @@ -134,12 +119,6 @@ private ResolutionScaleRule FindOptimalRule(Vector2 resolution, List private float CalculateRuleFitnessScore(Vector2 resolution, ResolutionScaleRule rule) { - // ๊ทœ์น™์˜ ์ค‘์•™์  ๊ณ„์‚ฐ var ruleCenterWidth = (rule.MinWidth + rule.MaxWidth) / 2f; var ruleCenterHeight = (rule.MinHeight + rule.MaxHeight) / 2f; - // ํ˜„์žฌ ํ•ด์ƒ๋„์™€ ๊ทœ์น™ ์ค‘์•™์  ๊ฐ„์˜ ๊ฑฐ๋ฆฌ ๊ณ„์‚ฐ var widthDistance = Mathf.Abs(resolution.x - ruleCenterWidth); var heightDistance = Mathf.Abs(resolution.y - ruleCenterHeight); - // ์œ ํด๋ฆฌ๋“œ ๊ฑฐ๋ฆฌ ๊ณ„์‚ฐ var distance = Mathf.Sqrt(widthDistance * widthDistance + heightDistance * heightDistance); - // ๊ทœ์น™์˜ ํฌ๊ธฐ ๋ฒ”์œ„ ๊ณ ๋ ค (๋ฒ”์œ„๊ฐ€ ์ž‘์„์ˆ˜๋ก ๋” ๊ตฌ์ฒด์ ์ด๋ฏ€๋กœ ๊ฐ€์ค‘์น˜ ๋ถ€์—ฌ) var ruleWidthRange = rule.MaxWidth - rule.MinWidth; var ruleHeightRange = rule.MaxHeight - rule.MinHeight; var ruleArea = ruleWidthRange * ruleHeightRange; - // ๋ฒ”์œ„๊ฐ€ ์ž‘์„์ˆ˜๋ก ๋” ๊ตฌ์ฒด์ ์ด๋ฏ€๋กœ ๋ณด๋„ˆ์Šค ์ ์ˆ˜ var specificityBonus = Mathf.Max(0, 1000f - ruleArea) / 1000f; - // ์ตœ์ข… ์ ์ˆ˜ = ๊ฑฐ๋ฆฌ - ๊ตฌ์ฒด์„ฑ ๋ณด๋„ˆ์Šค (์ ์ˆ˜๊ฐ€ ๋‚ฎ์„์ˆ˜๋ก ๋” ์ ํ•ฉ) var finalScore = distance - specificityBonus; - Debug.Log($"[ResolutionModelScaleConfig] ๊ทœ์น™ ์ ํ•ฉ๋„ ๊ณ„์‚ฐ:\n" + - $"๊ทœ์น™: {rule.Description}\n" + - $"๊ทœ์น™ ๋ฒ”์œ„: {rule.MinWidth}x{rule.MinHeight} ~ {rule.MaxWidth}x{rule.MaxHeight}\n" + - $"๊ทœ์น™ ์ค‘์•™: {ruleCenterWidth:F0}x{ruleCenterHeight:F0}\n" + - $"ํ˜„์žฌ ํ•ด์ƒ๋„: {resolution.x:F0}x{resolution.y:F0}\n" + - $"๊ฑฐ๋ฆฌ: {distance:F2}\n" + - $"๊ตฌ์ฒด์„ฑ ๋ณด๋„ˆ์Šค: {specificityBonus:F3}\n" + - $"์ตœ์ข… ์ ์ˆ˜: {finalScore:F3}"); - return finalScore; } diff --git a/Assets/Domain/Character/Script/Controller/ActionController.cs b/Assets/Domain/Character/Script/Controller/ActionController.cs deleted file mode 100644 index e5eaa87..0000000 --- a/Assets/Domain/Character/Script/Controller/ActionController.cs +++ /dev/null @@ -1,192 +0,0 @@ -using UnityEngine; -using System; -using System.Collections; -using Live2D.Cubism.Framework.Motion; - -namespace ProjectVG.Domain.Character.Service -{ - /// - /// ํ–‰๋™ โ†’ ๋ชจ์…˜/ํŒŒ๋ผ๋ฏธํ„ฐ ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ - /// - public class ActionController : MonoBehaviour, IActionController - { - [Header("Components")] - [SerializeField] private CubismMotionController _motionController; - - [Header("Action Mapping")] - [SerializeField] private ActionMotionMapping[] _actionMappings; - - [Header("Settings")] - [SerializeField] private float _defaultMotionDuration = 2.0f; - - private string _currentAction; - private Coroutine _actionCoroutine; - private bool _isActionPlaying = false; - - #region IActionController Implementation - - public void Initialize() - { - if (_motionController == null) - { - _motionController = GetComponent(); - } - - if (_motionController == null) - { - Debug.LogError("[ActionController] CubismMotionController๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); - return; - } - - Debug.Log("[ActionController] ์ดˆ๊ธฐํ™” ์™„๋ฃŒ"); - } - - public void TriggerAction(string action, object args = null) - { - if (string.IsNullOrEmpty(action)) - { - Debug.LogWarning("[ActionController] ์•ก์…˜์ด null์ž…๋‹ˆ๋‹ค."); - return; - } - - // ํ˜„์žฌ ์•ก์…˜์ด ์žฌ์ƒ ์ค‘์ด๋ผ๋ฉด ์ค‘๋‹จ - if (_isActionPlaying) - { - StopCurrentAction(); - } - - StartAction(action, args); - } - - #endregion - - #region Public Methods - - /// - /// ํ˜„์žฌ ์•ก์…˜์„ ์ค‘๋‹จํ•œ๋‹ค. - /// - public void StopCurrentAction() - { - if (_actionCoroutine != null) - { - StopCoroutine(_actionCoroutine); - _actionCoroutine = null; - } - - _isActionPlaying = false; - _currentAction = null; - - Debug.Log("[ActionController] ์•ก์…˜ ์ค‘๋‹จ"); - } - - /// - /// ํ˜„์žฌ ์•ก์…˜์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. - /// - public string GetCurrentAction() - { - return _currentAction; - } - - /// - /// ์•ก์…˜์ด ์žฌ์ƒ ์ค‘์ธ์ง€ ํ™•์ธํ•œ๋‹ค. - /// - public bool IsActionPlaying() - { - return _isActionPlaying; - } - - #endregion - - #region Private Methods - - private void StartAction(string action, object args) - { - var motionKey = GetMotionKey(action); - if (string.IsNullOrEmpty(motionKey)) - { - Debug.LogWarning($"[ActionController] ์•ก์…˜ '{action}'์— ๋Œ€ํ•œ ๋ชจ์…˜์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); - return; - } - - _actionCoroutine = StartCoroutine(ActionCoroutine(action, motionKey, args)); - } - - private IEnumerator ActionCoroutine(string action, string motionKey, object args) - { - _currentAction = action; - _isActionPlaying = true; - - Debug.Log($"[ActionController] ์•ก์…˜ ์‹œ์ž‘: {action}"); - - // ๋ชจ์…˜ ์žฌ์ƒ (ํ˜„์žฌ๋Š” ๋”๋ฏธ ์ฒ˜๋ฆฌ) - PlayMotion(motionKey, args); - - // ๋ชจ์…˜ ์ง€์† ์‹œ๊ฐ„๋งŒํผ ๋Œ€๊ธฐ - var duration = GetActionDuration(action); - yield return new WaitForSeconds(duration); - - // ์•ก์…˜ ์™„๋ฃŒ - _isActionPlaying = false; - _currentAction = null; - - Debug.Log($"[ActionController] ์•ก์…˜ ์™„๋ฃŒ: {action}"); - } - - private void PlayMotion(string motionKey, object args) - { - if (_motionController == null) return; - - // TODO: ์‹ค์ œ ๋ชจ์…˜ ์žฌ์ƒ ๋กœ์ง ๊ตฌํ˜„ - // ํ˜„์žฌ๋Š” ๋”๋ฏธ ์ฒ˜๋ฆฌ๋กœ ๋กœ๊ทธ๋งŒ ์ถœ๋ ฅ - Debug.Log($"[ActionController] ๋ชจ์…˜ ์žฌ์ƒ: {motionKey}"); - - // ํ–ฅํ›„ ๊ตฌํ˜„ ์˜ˆ์ •: - // _motionController.PlayMotion(motionKey); - // ๋˜๋Š” - // _motionController.PlayMotionGroup(motionKey); - } - - private string GetMotionKey(string action) - { - foreach (var mapping in _actionMappings) - { - if (mapping.Action.Equals(action, StringComparison.OrdinalIgnoreCase)) - { - return mapping.MotionKey; - } - } - - // ๊ธฐ๋ณธ๊ฐ’ ๋ฐ˜ํ™˜ - return "idle"; - } - - private float GetActionDuration(string action) - { - foreach (var mapping in _actionMappings) - { - if (mapping.Action.Equals(action, StringComparison.OrdinalIgnoreCase)) - { - return mapping.Duration > 0 ? mapping.Duration : _defaultMotionDuration; - } - } - - return _defaultMotionDuration; - } - - #endregion - - #region Action Mapping - - [System.Serializable] - public class ActionMotionMapping - { - public string Action; - public string MotionKey; - public float Duration; - } - - #endregion - } -} - - diff --git a/Assets/Domain/Character/Script/Controller/ActionController.cs.meta b/Assets/Domain/Character/Script/Controller/ActionController.cs.meta deleted file mode 100644 index 1cc9337..0000000 --- a/Assets/Domain/Character/Script/Controller/ActionController.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 7ea94bb5086c84841b65fdc5cf3c652b \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Controller/EmotionController.cs b/Assets/Domain/Character/Script/Controller/EmotionController.cs deleted file mode 100644 index 79d54fa..0000000 --- a/Assets/Domain/Character/Script/Controller/EmotionController.cs +++ /dev/null @@ -1,209 +0,0 @@ -using UnityEngine; -using System; -using System.Collections; -using Live2D.Cubism.Framework.Expression; - -namespace ProjectVG.Domain.Character.Service -{ - /// - /// ๊ฐ์ • โ†’ Expression ๋งตํ•‘๊ณผ ๋ธ”๋ Œ๋”ฉ์„ ๊ตฌํ˜„ํ•˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ - /// - public class EmotionController : MonoBehaviour, IEmotionController - { - [Header("Components")] - [SerializeField] private CubismExpressionController _expressionController; - - [Header("Emotion Mapping")] - [SerializeField] private EmotionExpressionMapping[] _emotionMappings; - - [Header("Blending Settings")] - [SerializeField] private float _defaultBlendTime = 0.5f; - [SerializeField] private float _voiceBlendTime = 1.0f; - - private string _currentEmotion = "neutral"; - private string _pendingEmotion; - private float _currentIntensity = 1.0f; - private Coroutine _emotionCoroutine; - private bool _isVoicePlaying = false; - - #region IEmotionController Implementation - - public void Initialize() - { - if (_expressionController == null) - { - _expressionController = GetComponent(); - } - - if (_expressionController == null) - { - Debug.LogError("[EmotionController] CubismExpressionController๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); - return; - } - - // ๊ธฐ๋ณธ ๊ฐ์ • ์„ค์ • - SetEmotion("neutral", 1.0f, 0); - - Debug.Log("[EmotionController] ์ดˆ๊ธฐํ™” ์™„๋ฃŒ"); - } - - public void SetEmotion(string emotion, float intensity, int durationMs) - { - if (string.IsNullOrEmpty(emotion)) - { - Debug.LogWarning("[EmotionController] ๊ฐ์ •์ด null์ž…๋‹ˆ๋‹ค."); - return; - } - - // ์Œ์„ฑ ์žฌ์ƒ ์ค‘์ด๊ณ  ๊ณผ๊ฒฉํ•œ ๊ฐ์ • ์ „ํ™˜์ด๋ผ๋ฉด ๋Œ€๊ธฐ์—ด๋กœ ๋ณด๊ด€ - if (_isVoicePlaying && IsIntenseEmotionChange(_currentEmotion, emotion)) - { - _pendingEmotion = emotion; - Debug.Log($"[EmotionController] ์Œ์„ฑ ์žฌ์ƒ ์ค‘ ๊ฐ์ • ๋Œ€๊ธฐ: {emotion}"); - return; - } - - StartEmotionTransition(emotion, intensity, durationMs); - } - - public void ClearEmotion() - { - if (_emotionCoroutine != null) - { - StopCoroutine(_emotionCoroutine); - _emotionCoroutine = null; - } - - SetEmotion("neutral", 1.0f, 0); - } - - #endregion - - #region Public Methods - - /// - /// ์Œ์„ฑ ์žฌ์ƒ ์ƒํƒœ๋ฅผ ์„ค์ •ํ•œ๋‹ค. - /// - public void SetVoicePlaying(bool isPlaying) - { - _isVoicePlaying = isPlaying; - - // ์Œ์„ฑ ์žฌ์ƒ ์™„๋ฃŒ ์‹œ ๋Œ€๊ธฐ ์ค‘์ธ ๊ฐ์ • ์ ์šฉ - if (!isPlaying && !string.IsNullOrEmpty(_pendingEmotion)) - { - var pendingEmotion = _pendingEmotion; - _pendingEmotion = null; - SetEmotion(pendingEmotion, _currentIntensity, 2000); - } - } - - /// - /// ํ˜„์žฌ ๊ฐ์ •์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. - /// - public string GetCurrentEmotion() - { - return _currentEmotion; - } - - #endregion - - #region Private Methods - - private void StartEmotionTransition(string emotion, float intensity, int durationMs) - { - if (_emotionCoroutine != null) - { - StopCoroutine(_emotionCoroutine); - } - - _emotionCoroutine = StartCoroutine(EmotionTransitionCoroutine(emotion, intensity, durationMs)); - } - - private IEnumerator EmotionTransitionCoroutine(string emotion, float intensity, int durationMs) - { - var expressionKey = GetExpressionKey(emotion); - if (string.IsNullOrEmpty(expressionKey)) - { - Debug.LogWarning($"[EmotionController] ๊ฐ์ • '{emotion}'์— ๋Œ€ํ•œ Expression์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); - yield break; - } - - // ๋ธ”๋ Œ๋“œ ์‹œ๊ฐ„ ๊ฒฐ์ • - var blendTime = _isVoicePlaying ? _voiceBlendTime : _defaultBlendTime; - - // Expression ๋ณ€๊ฒฝ - _expressionController.CurrentExpressionIndex = GetExpressionIndex(expressionKey); - _currentEmotion = emotion; - _currentIntensity = intensity; - - Debug.Log($"[EmotionController] ๊ฐ์ • ๋ณ€๊ฒฝ: {emotion} (๊ฐ•๋„: {intensity})"); - - // ์ง€์† ์‹œ๊ฐ„๋งŒํผ ๋Œ€๊ธฐ - if (durationMs > 0) - { - yield return new WaitForSeconds(durationMs / 1000f); - - // ์ง€์† ์‹œ๊ฐ„ ์ข…๋ฃŒ ์‹œ neutral๋กœ ๋ณต๊ท€ - if (_currentEmotion == emotion) // ๋‹ค๋ฅธ ๊ฐ์ •์œผ๋กœ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด - { - SetEmotion("neutral", 1.0f, 0); - } - } - } - - private string GetExpressionKey(string emotion) - { - foreach (var mapping in _emotionMappings) - { - if (mapping.Emotion.Equals(emotion, StringComparison.OrdinalIgnoreCase)) - { - return mapping.ExpressionKey; - } - } - - // ๊ธฐ๋ณธ๊ฐ’ ๋ฐ˜ํ™˜ - return "neutral"; - } - - private int GetExpressionIndex(string expressionKey) - { - if (_expressionController == null) return 0; - - // Expression ์ด๋ฆ„์œผ๋กœ ์ธ๋ฑ์Šค ์ฐพ๊ธฐ - for (int i = 0; i < _expressionController.ExpressionsList.CubismExpressionObjects.Length; i++) - { - if (_expressionController.ExpressionsList.CubismExpressionObjects[i].name.Equals(expressionKey, StringComparison.OrdinalIgnoreCase)) - { - return i; - } - } - - return 0; // ๊ธฐ๋ณธ๊ฐ’ - } - - private bool IsIntenseEmotionChange(string currentEmotion, string newEmotion) - { - // ๊ณผ๊ฒฉํ•œ ๊ฐ์ • ์ „ํ™˜ ํŒ๋‹จ ๋กœ์ง - var intenseEmotions = new[] { "angry", "surprised", "sad" }; - var isCurrentIntense = Array.Exists(intenseEmotions, e => e.Equals(currentEmotion, StringComparison.OrdinalIgnoreCase)); - var isNewIntense = Array.Exists(intenseEmotions, e => e.Equals(newEmotion, StringComparison.OrdinalIgnoreCase)); - - return isCurrentIntense && isNewIntense && currentEmotion != newEmotion; - } - - #endregion - - #region Emotion Mapping - - [System.Serializable] - public class EmotionExpressionMapping - { - public string Emotion; - public string ExpressionKey; - } - - #endregion - } -} - - diff --git a/Assets/Domain/Character/Script/Controller/EmotionController.cs.meta b/Assets/Domain/Character/Script/Controller/EmotionController.cs.meta deleted file mode 100644 index af39ad2..0000000 --- a/Assets/Domain/Character/Script/Controller/EmotionController.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: d1977c0cf1b32534daaeb00c3f777fe8 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Controller/IActionController.cs b/Assets/Domain/Character/Script/Controller/IActionController.cs deleted file mode 100644 index b807611..0000000 --- a/Assets/Domain/Character/Script/Controller/IActionController.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace ProjectVG.Domain.Character.Service -{ - /// - /// ํ–‰๋™ โ†’ ๋ชจ์…˜/ํŒŒ๋ผ๋ฏธํ„ฐ ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค - /// - public interface IActionController - { - void Initialize(); - void TriggerAction(string action, object args = null); - } -} - - diff --git a/Assets/Domain/Character/Script/Controller/IActionController.cs.meta b/Assets/Domain/Character/Script/Controller/IActionController.cs.meta deleted file mode 100644 index a8816b8..0000000 --- a/Assets/Domain/Character/Script/Controller/IActionController.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 2d7f3153340a3c444996d299a6f29f3a \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Controller/IEmotionController.cs b/Assets/Domain/Character/Script/Controller/IEmotionController.cs deleted file mode 100644 index 9fcc894..0000000 --- a/Assets/Domain/Character/Script/Controller/IEmotionController.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace ProjectVG.Domain.Character.Service -{ - /// - /// ๊ฐ์ • โ†’ Expression ๋งตํ•‘๊ณผ ๋ธ”๋ Œ๋”ฉ์„ ๋‹ด๋‹นํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค - /// - public interface IEmotionController - { - void Initialize(); - void SetEmotion(string emotion, float intensity, int durationMs); - void ClearEmotion(); - } -} - - diff --git a/Assets/Domain/Character/Script/Controller/IEmotionController.cs.meta b/Assets/Domain/Character/Script/Controller/IEmotionController.cs.meta deleted file mode 100644 index b4e8673..0000000 --- a/Assets/Domain/Character/Script/Controller/IEmotionController.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 7ef1b6519703d4244af92ace857a98ce \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Controller/ILive2DModelApplier.cs b/Assets/Domain/Character/Script/Controller/ILive2DModelApplier.cs deleted file mode 100644 index 5128c3e..0000000 --- a/Assets/Domain/Character/Script/Controller/ILive2DModelApplier.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UnityEngine; -using ProjectVG.Domain.Character.Live2D.Model; - -namespace ProjectVG.Domain.Character.Service -{ - /// - /// Live2D ๋ชจ๋ธ์— ์„ค์ •์„ ์ ์šฉํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค - /// - public interface ILive2DModelApplier - { - void Apply(GameObject activeModel, Live2DModelConfig characterConfig); - } -} diff --git a/Assets/Domain/Character/Script/Controller/ILive2DModelApplier.cs.meta b/Assets/Domain/Character/Script/Controller/ILive2DModelApplier.cs.meta deleted file mode 100644 index 29399d9..0000000 --- a/Assets/Domain/Character/Script/Controller/ILive2DModelApplier.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 2234415ca6f22a14e9e1db4b6ca01091 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Controller/ILive2DModelManagerFacade.cs b/Assets/Domain/Character/Script/Controller/ILive2DModelManagerFacade.cs deleted file mode 100644 index 0a6b9ab..0000000 --- a/Assets/Domain/Character/Script/Controller/ILive2DModelManagerFacade.cs +++ /dev/null @@ -1,15 +0,0 @@ -using UnityEngine; - -namespace ProjectVG.Domain.Character.Service -{ - /// - /// Live2D ๋ชจ๋ธ ์ „๋ฐ˜์˜ ์ƒํƒœ๋ฅผ ๋‹จ์ผ ์ง„์ž…์ ์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค - /// - public interface ILive2DModelManagerFacade - { - void Initialize(); - void ApplyReaction(EmotionData emotionData, ActionData actionData); - void OnVoiceStarted(); - void OnVoiceFinished(); - } -} diff --git a/Assets/Domain/Character/Script/Controller/ILive2DModelManagerFacade.cs.meta b/Assets/Domain/Character/Script/Controller/ILive2DModelManagerFacade.cs.meta deleted file mode 100644 index a72b053..0000000 --- a/Assets/Domain/Character/Script/Controller/ILive2DModelManagerFacade.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 8c750eed5093bf447aa877fed965a2e9 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Controller/Live2DModelApplier.cs b/Assets/Domain/Character/Script/Controller/Live2DModelApplier.cs deleted file mode 100644 index c5f7f68..0000000 --- a/Assets/Domain/Character/Script/Controller/Live2DModelApplier.cs +++ /dev/null @@ -1,89 +0,0 @@ -using UnityEngine; -using ProjectVG.Domain.Character.Live2D.Model; - -namespace ProjectVG.Domain.Character.Service -{ - /// - /// Live2D ๋ชจ๋ธ์— ์„ค์ •์„ ์ ์šฉํ•˜๋Š” ๊ตฌํ˜„์ฒด - /// - public class Live2DModelApplier : MonoBehaviour, ILive2DModelApplier - { - [Header("Components")] - [SerializeField] private EmotionController _emotionController; - [SerializeField] private ActionController _actionController; - - public void Apply(GameObject activeModel, Live2DModelConfig characterConfig) - { - if (activeModel == null || characterConfig == null) - { - Debug.LogWarning("[Live2DModelApplier] ๋ชจ๋ธ ๋˜๋Š” ์„ค์ •์ด null์ž…๋‹ˆ๋‹ค."); - return; - } - - try - { - // ์ปดํฌ๋„ŒํŠธ๋“ค ์ฐพ๊ธฐ - FindComponents(activeModel); - - // ๊ธฐ๋ณธ ์„ค์ • ์ ์šฉ - ApplyBasicSettings(activeModel, characterConfig); - - // ์ปจํŠธ๋กค๋Ÿฌ๋“ค ์ดˆ๊ธฐํ™” - InitializeControllers(); - - Debug.Log($"[Live2DModelApplier] ๋ชจ๋ธ '{activeModel.name}'์— ์„ค์ • ์ ์šฉ ์™„๋ฃŒ"); - } - catch (System.Exception ex) - { - Debug.LogError($"[Live2DModelApplier] ์„ค์ • ์ ์šฉ ์‹คํŒจ: {ex.Message}"); - } - } - - private void FindComponents(GameObject activeModel) - { - // ๊ฐ์ • ์ปจํŠธ๋กค๋Ÿฌ ์ฐพ๊ธฐ - if (_emotionController == null) - { - _emotionController = activeModel.GetComponent(); - } - - // ์•ก์…˜ ์ปจํŠธ๋กค๋Ÿฌ ์ฐพ๊ธฐ - if (_actionController == null) - { - _actionController = activeModel.GetComponent(); - } - } - - private void ApplyBasicSettings(GameObject activeModel, Live2DModelConfig characterConfig) - { - // ๊ธฐ๋ณธ ๊ฐ์ • ์„ค์ • - if (_emotionController != null) - { - _emotionController.Initialize(); - _emotionController.SetEmotion("neutral", 1.0f, 0); - } - - // ๊ธฐ๋ณธ ์•ก์…˜ ์„ค์ • - if (_actionController != null) - { - _actionController.Initialize(); - } - } - - private void InitializeControllers() - { - // ์ปจํŠธ๋กค๋Ÿฌ๋“ค์ด ์ œ๋Œ€๋กœ ์ดˆ๊ธฐํ™”๋˜์—ˆ๋Š”์ง€ ํ™•์ธ - if (_emotionController == null) - { - Debug.LogWarning("[Live2DModelApplier] EmotionController๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); - } - - if (_actionController == null) - { - Debug.LogWarning("[Live2DModelApplier] ActionController๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); - } - } - } -} - - diff --git a/Assets/Domain/Character/Script/Controller/Live2DModelApplier.cs.meta b/Assets/Domain/Character/Script/Controller/Live2DModelApplier.cs.meta deleted file mode 100644 index 132d5ba..0000000 --- a/Assets/Domain/Character/Script/Controller/Live2DModelApplier.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 599c8846d73d1244a80788addacb4285 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Controller/Live2DParameterController.cs b/Assets/Domain/Character/Script/Controller/Live2DParameterController.cs deleted file mode 100644 index 04226c2..0000000 --- a/Assets/Domain/Character/Script/Controller/Live2DParameterController.cs +++ /dev/null @@ -1,81 +0,0 @@ -using UnityEngine; - -namespace ProjectVG.Domain.Character.Service -{ - /// - /// Live2D ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ง์ ‘ ์ œ์–ดํ•˜๊ฑฐ๋‚˜ ํ”„๋ฆฌ์…‹์„ ์ ์šฉํ•˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ (๋น„ํ™œ์„ฑํ™”๋จ) - /// - public class Live2DParameterController : MonoBehaviour - { - [Header("Status")] - [SerializeField] private bool _isEnabled = false; - - public void Initialize() - { - if (!_isEnabled) - { - Debug.Log("[Live2DParameterController] ๋น„ํ™œ์„ฑํ™”๋จ"); - return; - } - - Debug.Log("[Live2DParameterController] ์ดˆ๊ธฐํ™” ์™„๋ฃŒ"); - } - - /// - /// ํ”„๋ฆฌ์…‹์„ ์ ์šฉํ•œ๋‹ค. (๋น„ํ™œ์„ฑํ™”๋จ) - /// - public void ApplyPreset(string presetKey) - { - if (!_isEnabled) return; - Debug.Log($"[Live2DParameterController] ํ”„๋ฆฌ์…‹ ์ ์šฉ: {presetKey}"); - } - - /// - /// ๋‹จ์ผ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์„ค์ •ํ•œ๋‹ค. (๋น„ํ™œ์„ฑํ™”๋จ) - /// - public void SetParameter(string parameterId, float value) - { - if (!_isEnabled) return; - Debug.Log($"[Live2DParameterController] ํŒŒ๋ผ๋ฏธํ„ฐ ์„ค์ •: {parameterId} = {value}"); - } - - /// - /// ์—ฌ๋Ÿฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ•œ๋ฒˆ์— ์„ค์ •ํ•œ๋‹ค. (๋น„ํ™œ์„ฑํ™”๋จ) - /// - public void SetParameters(System.Collections.Generic.Dictionary parameters) - { - if (!_isEnabled) return; - Debug.Log($"[Live2DParameterController] ํŒŒ๋ผ๋ฏธํ„ฐ ์ผ๊ด„ ์„ค์ •: {parameters?.Count ?? 0}๊ฐœ"); - } - - /// - /// ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’์„ ๋ธ”๋ Œ๋”ฉํ•˜์—ฌ ์„ค์ •ํ•œ๋‹ค. (๋น„ํ™œ์„ฑํ™”๋จ) - /// - public void BlendParameter(string parameterId, float targetValue, float blendTime = -1f) - { - if (!_isEnabled) return; - Debug.Log($"[Live2DParameterController] ํŒŒ๋ผ๋ฏธํ„ฐ ๋ธ”๋ Œ๋”ฉ: {parameterId} -> {targetValue}"); - } - - /// - /// ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. (๋น„ํ™œ์„ฑํ™”๋จ) - /// - public float GetParameterValue(string parameterId) - { - if (!_isEnabled) return 0f; - Debug.Log($"[Live2DParameterController] ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’ ์กฐํšŒ: {parameterId}"); - return 0f; - } - - /// - /// ๋ชจ๋“  ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋ฆฌ์…‹ํ•œ๋‹ค. (๋น„ํ™œ์„ฑํ™”๋จ) - /// - public void ResetAllParameters() - { - if (!_isEnabled) return; - Debug.Log("[Live2DParameterController] ๋ชจ๋“  ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฆฌ์…‹"); - } - } -} - - diff --git a/Assets/Domain/Character/Script/Controller/Live2DParameterController.cs.meta b/Assets/Domain/Character/Script/Controller/Live2DParameterController.cs.meta deleted file mode 100644 index 6e1f953..0000000 --- a/Assets/Domain/Character/Script/Controller/Live2DParameterController.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 2c84ef8b52117614e806503794e80e87 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Facade/CharacterFacade.cs b/Assets/Domain/Character/Script/Facade/CharacterManager.cs similarity index 83% rename from Assets/Domain/Character/Script/Facade/CharacterFacade.cs rename to Assets/Domain/Character/Script/Facade/CharacterManager.cs index 96d113d..621f420 100644 --- a/Assets/Domain/Character/Script/Facade/CharacterFacade.cs +++ b/Assets/Domain/Character/Script/Facade/CharacterManager.cs @@ -9,7 +9,7 @@ namespace ProjectVG.Domain.Character.Service /// /// ์บ๋ฆญํ„ฐ ์ œ์–ด๋ฅผ ๋‹จ์ผ ์ง„์ž…์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ํŒŒ์‚ฌ๋“œ ๊ตฌํ˜„์ฒด. /// - public class CharacterFacade : MonoBehaviour, ICharacterFacade + public class CharacterManager : MonoBehaviour, ICharacterManager { [SerializeField] private Transform _modelTransform; [SerializeField] private Live2DModelRegistry _modelRegistry; @@ -37,7 +37,7 @@ public void Initialize() if (_modelManager == null) { _modelManager = gameObject.AddComponent(); - Debug.Log($"[CharacterFacade] CharacterModelManager๊ฐ€ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค: {gameObject.name}"); + Debug.Log($"[CharacterManager] CharacterModelManager๊ฐ€ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค: {gameObject.name}"); } // AudioManager์—์„œ Voice AudioSource ๊ฐ€์ ธ์˜ค๊ธฐ @@ -51,7 +51,7 @@ public void Initialize() var scaler = _modelTransform.GetComponent(); if (scaler == null) { scaler = _modelTransform.gameObject.AddComponent(); - Debug.Log($"[CharacterFacade] Live2DModelScaler๊ฐ€ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค: {_modelTransform.name}"); + Debug.Log($"[CharacterManager] Live2DModelScaler๊ฐ€ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค: {_modelTransform.name}"); } } } @@ -103,7 +103,7 @@ public void ClearActiveCharacter() public CharacterActionHandle ApplyAction(CharacterActionRequest request) { // TODO: ์•ก์…˜ ์„œ๋น„์Šค ๊ตฌํ˜„ ํ›„ ์—ฐ๊ฒฐ - Debug.LogWarning($"[CharacterFacade] ApplyAction ์•„์ง ๊ตฌํ˜„๋˜์ง€ ์•Š์Œ: {request.ActionKey}"); + Debug.LogWarning($"[CharacterManager] ApplyAction ์•„์ง ๊ตฌํ˜„๋˜์ง€ ์•Š์Œ: {request.ActionKey}"); return new CharacterActionHandle("not_implemented", CharacterActionStatus.Error); } @@ -113,7 +113,7 @@ public CharacterActionHandle ApplyAction(CharacterActionRequest request) public void CancelAction(string actionId) { // TODO: ์•ก์…˜ ์„œ๋น„์Šค ๊ตฌํ˜„ ํ›„ ์—ฐ๊ฒฐ - Debug.LogWarning($"[CharacterFacade] CancelAction ์•„์ง ๊ตฌํ˜„๋˜์ง€ ์•Š์Œ: {actionId}"); + Debug.LogWarning($"[CharacterManager] CancelAction ์•„์ง ๊ตฌํ˜„๋˜์ง€ ์•Š์Œ: {actionId}"); } /// @@ -122,7 +122,7 @@ public void CancelAction(string actionId) public void CancelAllActions() { // TODO: ์•ก์…˜ ์„œ๋น„์Šค ๊ตฌํ˜„ ํ›„ ์—ฐ๊ฒฐ - Debug.LogWarning("[CharacterFacade] CancelAllActions ์•„์ง ๊ตฌํ˜„๋˜์ง€ ์•Š์Œ"); + Debug.LogWarning("[CharacterManager] CancelAllActions ์•„์ง ๊ตฌํ˜„๋˜์ง€ ์•Š์Œ"); } /// @@ -172,7 +172,7 @@ public void Shutdown() /// /// AudioManager์—์„œ Voice AudioSource๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค. /// - private AudioSource? GetVoiceAudioSource() + private AudioSource GetVoiceAudioSource() { var audioManager = AudioManager.Instance; if (audioManager != null && audioManager.IsInitialized) @@ -183,13 +183,13 @@ public void Shutdown() var audioSource = voiceController.GetAudioSource(); if (audioSource != null) { - Debug.Log("[CharacterFacade] Voice AudioSource ๊ฐ€์ ธ์˜ค๊ธฐ ์™„๋ฃŒ"); + Debug.Log("[CharacterManager] Voice AudioSource ๊ฐ€์ ธ์˜ค๊ธฐ ์™„๋ฃŒ"); return audioSource; } } } - Debug.LogWarning("[CharacterFacade] AudioManager๊ฐ€ ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์•˜๊ฑฐ๋‚˜ Voice AudioSource๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + Debug.LogWarning("[CharacterManager] AudioManager๊ฐ€ ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์•˜๊ฑฐ๋‚˜ Voice AudioSource๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); return null; } } diff --git a/Assets/Domain/Character/Script/Facade/CharacterFacade.cs.meta b/Assets/Domain/Character/Script/Facade/CharacterManager.cs.meta similarity index 100% rename from Assets/Domain/Character/Script/Facade/CharacterFacade.cs.meta rename to Assets/Domain/Character/Script/Facade/CharacterManager.cs.meta diff --git a/Assets/Domain/Character/Script/Facade/ICharacterFacade.cs b/Assets/Domain/Character/Script/Facade/ICharacterManager.cs similarity index 98% rename from Assets/Domain/Character/Script/Facade/ICharacterFacade.cs rename to Assets/Domain/Character/Script/Facade/ICharacterManager.cs index 7819a2c..1606058 100644 --- a/Assets/Domain/Character/Script/Facade/ICharacterFacade.cs +++ b/Assets/Domain/Character/Script/Facade/ICharacterManager.cs @@ -3,7 +3,7 @@ namespace ProjectVG.Domain.Character.Service /// /// ์บ๋ฆญํ„ฐ ์ œ์–ด๋ฅผ ์œ„ํ•œ ํŒŒ์‚ฌ๋“œ ์ธํ„ฐํŽ˜์ด์Šค /// - public interface ICharacterFacade + public interface ICharacterManager { /// /// ํŒŒ์‚ฌ๋“œ๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค. diff --git a/Assets/Domain/Character/Script/Facade/ICharacterFacade.cs.meta b/Assets/Domain/Character/Script/Facade/ICharacterManager.cs.meta similarity index 100% rename from Assets/Domain/Character/Script/Facade/ICharacterFacade.cs.meta rename to Assets/Domain/Character/Script/Facade/ICharacterManager.cs.meta diff --git a/Assets/Domain/Character/Script/Manager/CharacterModelManager.cs b/Assets/Domain/Character/Script/Manager/CharacterModelManager.cs index be7c19a..6ae9fc9 100644 --- a/Assets/Domain/Character/Script/Manager/CharacterModelManager.cs +++ b/Assets/Domain/Character/Script/Manager/CharacterModelManager.cs @@ -20,20 +20,12 @@ public class CharacterModelManager : MonoBehaviour, ICharacterModelManager private Live2DModelRegistry _modelRegistry; private readonly Dictionary _characterIdToInstance = new Dictionary(); private string _activeCharacterId; - private AudioSource? _voiceAudioSource; + private AudioSource _voiceAudioSource; #endregion #region Unity Lifecycle - private void Update() - { - // ๋ฆฝ์‹ฑํฌ ๋””๋ฒ„๊น…์„ ์œ„ํ•œ AudioSource ์ƒํƒœ ๋ชจ๋‹ˆํ„ฐ๋ง - if (_voiceAudioSource != null && _voiceAudioSource.isPlaying) { - Debug.Log($"[CharacterModelManager] AudioSource ์žฌ์ƒ์ค‘: {_voiceAudioSource.name}, ๋ณผ๋ฅจ: {_voiceAudioSource.volume}, ์‹œ๊ฐ„: {_voiceAudioSource.time}"); - } - } - #endregion #region Public Methods @@ -41,7 +33,7 @@ private void Update() /// /// ๋งค๋‹ˆ์ €๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค /// - public void Initialize(Transform modelRoot, Live2DModelRegistry modelRegistry, AudioSource? voiceAudioSource = null) + public void Initialize(Transform modelRoot, Live2DModelRegistry modelRegistry, AudioSource voiceAudioSource) { _modelRoot = modelRoot; _modelRegistry = modelRegistry; diff --git a/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs b/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs index aef83ff..d9fe74c 100644 --- a/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs +++ b/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs @@ -16,7 +16,7 @@ public interface ICharacterModelManager /// ๋ชจ๋ธ ์œ„์น˜ /// ๋ชจ๋ธ ๋“ฑ๋ก์ž /// ์Œ์„ฑ AudioSource (์„ ํƒ์‚ฌํ•ญ) - public void Initialize(Transform modelRoot, Live2DModelRegistry modelRegistry, AudioSource? voiceAudioSource = null); + public void Initialize(Transform modelRoot, Live2DModelRegistry modelRegistry, AudioSource voiceAudioSource); /// /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ๋กœ๋“œํ•œ๋‹ค. diff --git a/Assets/Domain/Character/Script/Manager/Live2DCharacterManager.cs b/Assets/Domain/Character/Script/Manager/Live2DCharacterManager.cs deleted file mode 100644 index 0c40617..0000000 --- a/Assets/Domain/Character/Script/Manager/Live2DCharacterManager.cs +++ /dev/null @@ -1,198 +0,0 @@ -using UnityEngine; -using System; -using ProjectVG.Core.Audio; - -namespace ProjectVG.Domain.Character.Service -{ - /// - /// Live2D ์บ๋ฆญํ„ฐ์˜ ์ƒ์œ„ ์กฐ์ •์ž. ๋ชจ๋ธ ๊ด€๋ฆฌ์ž/์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋ฌถ์–ด ๊ฐ์ •/ํ–‰๋™ ๋ฐ˜์‘์„ ์ผ๊ด€๋˜๊ฒŒ ์ ์šฉ - /// - public class Live2DCharacterManager : MonoBehaviour, ILive2DModelManagerFacade - { - [Header("Components")] - [SerializeField] private Live2DModelManager _modelManager; - [SerializeField] private EmotionController _emotionController; - [SerializeField] private ActionController _actionController; - - [Header("Settings")] - [SerializeField] private bool _enableEmotionControl = true; - [SerializeField] private bool _enableActionControl = true; - - private CharacterStateModel _stateModel; - private AudioManager _audioManager; - - private static Live2DCharacterManager _instance; - public static Live2DCharacterManager Instance - { - get - { - if (_instance == null) - { - _instance = FindAnyObjectByType(); - } - return _instance; - } - } - - #region Unity Lifecycle - - private void Awake() - { - if (_instance != null && _instance != this) - { - Destroy(gameObject); - return; - } - - _instance = this; - _stateModel = new CharacterStateModel(); - } - - private void Start() - { - Initialize(); - } - - #endregion - - #region ILive2DModelManagerFacade Implementation - - public void Initialize() - { - try - { - _audioManager = AudioManager.Instance; - - if (_emotionController != null) - { - _emotionController.Initialize(); - } - - if (_actionController != null) - { - _actionController.Initialize(); - } - - Debug.Log("[Live2DCharacterManager] ์ดˆ๊ธฐํ™” ์™„๋ฃŒ"); - } - catch (Exception ex) - { - Debug.LogError($"[Live2DCharacterManager] ์ดˆ๊ธฐํ™” ์‹คํŒจ: {ex.Message}"); - } - } - - public void ApplyReaction(EmotionData emotionData, ActionData actionData) - { - try - { - // Action > Voice Gating > Emotion ์šฐ์„ ์ˆœ์œ„ ์ ์šฉ - if (_enableActionControl && !string.IsNullOrEmpty(actionData.Action)) - { - _actionController?.TriggerAction(actionData.Action, actionData.Args); - _stateModel.currentAction = actionData.Action; - _stateModel.actionPlaying = true; - - Debug.Log($"[Live2DCharacterManager] ์•ก์…˜ ์ ์šฉ: {actionData.Action}"); - } - - if (_enableEmotionControl && !string.IsNullOrEmpty(emotionData.Emotion)) - { - _emotionController?.SetEmotion(emotionData.Emotion, emotionData.Intensity, emotionData.DurationMs); - _stateModel.currentEmotion = emotionData.Emotion; - _stateModel.emotionIntensity = emotionData.Intensity; - _stateModel.emotionExpireAt = DateTime.Now.AddMilliseconds(emotionData.DurationMs); - - Debug.Log($"[Live2DCharacterManager] ๊ฐ์ • ์ ์šฉ: {emotionData.Emotion} (๊ฐ•๋„: {emotionData.Intensity})"); - } - } - catch (Exception ex) - { - Debug.LogError($"[Live2DCharacterManager] ๋ฐ˜์‘ ์ ์šฉ ์‹คํŒจ: {ex.Message}"); - } - } - - public void OnVoiceStarted() - { - _stateModel.isVoicePlaying = true; - Debug.Log("[Live2DCharacterManager] ์Œ์„ฑ ์žฌ์ƒ ์‹œ์ž‘"); - } - - public void OnVoiceFinished() - { - _stateModel.isVoicePlaying = false; - - // ์Œ์„ฑ ์žฌ์ƒ ์™„๋ฃŒ ์‹œ ์ƒํƒœ ๋ณต์› - if (_stateModel.actionPlaying) - { - _stateModel.actionPlaying = false; - _stateModel.currentAction = null; - - // ์•ก์…˜ ์™„๋ฃŒ ํ›„ ์ด์ „ ๊ฐ์ • ๋ณต์› - if (!string.IsNullOrEmpty(_stateModel.currentEmotion)) - { - _emotionController?.SetEmotion(_stateModel.currentEmotion, _stateModel.emotionIntensity, 2000); - } - } - - Debug.Log("[Live2DCharacterManager] ์Œ์„ฑ ์žฌ์ƒ ์™„๋ฃŒ, ์ƒํƒœ ๋ณต์›"); - } - - #endregion - - #region Public Methods - - /// - /// ํ˜„์žฌ ์บ๋ฆญํ„ฐ ์ƒํƒœ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. - /// - public CharacterStateModel GetCurrentState() - { - return _stateModel; - } - - /// - /// ๊ฐ์ •์„ ์ฆ‰์‹œ ํ•ด์ œํ•œ๋‹ค. - /// - public void ClearEmotion() - { - _emotionController?.ClearEmotion(); - _stateModel.currentEmotion = null; - _stateModel.emotionIntensity = 0f; - _stateModel.emotionExpireAt = DateTime.MinValue; - } - - /// - /// ์•ก์…˜์„ ์ฆ‰์‹œ ์ค‘๋‹จํ•œ๋‹ค. - /// - public void StopAction() - { - _stateModel.actionPlaying = false; - _stateModel.currentAction = null; - } - - #endregion - - #region Character State Model - - /// - /// ์บ๋ฆญํ„ฐ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ชจ๋ธ - /// - [System.Serializable] - public class CharacterStateModel - { - public string currentEmotion; - public string pendingEmotion; - public float emotionIntensity; - public DateTime emotionExpireAt; - - public string currentAction; - public bool actionPlaying; - public DateTime actionExpireAt; - - public bool isVoicePlaying; - } - - #endregion - } -} - - diff --git a/Assets/Domain/Character/Script/Manager/Live2DCharacterManager.cs.meta b/Assets/Domain/Character/Script/Manager/Live2DCharacterManager.cs.meta deleted file mode 100644 index c06ba09..0000000 --- a/Assets/Domain/Character/Script/Manager/Live2DCharacterManager.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: aeee24d07701ad747a92d82afe08caa6 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Manager/Live2DModelManager.cs b/Assets/Domain/Character/Script/Manager/Live2DModelManager.cs deleted file mode 100644 index 8e390b0..0000000 --- a/Assets/Domain/Character/Script/Manager/Live2DModelManager.cs +++ /dev/null @@ -1,377 +0,0 @@ -using System.Collections.Generic; -using UnityEngine; -using Cysharp.Threading.Tasks; -using ProjectVG.Domain.Character.Live2D.Model; -using Live2D.Cubism.Framework.LookAt; -using Live2D.Cubism.Framework.MouthMovement; - -namespace ProjectVG.Domain.Character.Service -{ - public class Live2DModelManager : Singleton - { - [Header("์„ค์ •")] - [SerializeField] private Transform _modelRoot; - [SerializeField] private Live2DModelRegistry _modelRegistry; - - private readonly Dictionary _characterIdToInstance = new Dictionary(); - private string _activeCharacterId; - - protected override void Awake() - { - base.Awake(); - } - - #region Public Methods - - /// - /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ๋กœ๋“œํ•˜๊ณ  ์„ค์ •์„ ์ ์šฉํ•œ๋‹ค. - /// - public GameObject LoadCharacter(string characterId, bool activateImmediately = false) - { - if (string.IsNullOrEmpty(characterId)) - { - Debug.LogWarning("[Live2DModelManager] ์บ๋ฆญํ„ฐ ID๊ฐ€ null์ž…๋‹ˆ๋‹ค."); - return null; - } - - // ์ด๋ฏธ ๋กœ๋“œ๋œ ๋ชจ๋ธ์ด ์žˆ๋Š”์ง€ ํ™•์ธ - if (_characterIdToInstance.TryGetValue(characterId, out var existing)) - { - Debug.Log($"[Live2DModelManager] ์บ๋ฆญํ„ฐ '{characterId}'๊ฐ€ ์ด๋ฏธ ๋กœ๋“œ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค."); - if (activateImmediately) - { - ActivateCharacter(characterId); - } - return existing; - } - - // ์„ค์ • ๊ฐ€์ ธ์˜ค๊ธฐ - var config = GetCharacterConfig(characterId); - if (config == null) - { - Debug.LogError($"[Live2DModelManager] ์บ๋ฆญํ„ฐ '{characterId}'์˜ ์„ค์ •์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); - return null; - } - - // ๋ชจ๋ธ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ - var instance = CreateModelInstance(config, characterId); - if (instance == null) - { - return null; - } - - // ์„ค์ • ์ ์šฉ - ApplyCharacterConfig(instance, config); - - // ๋”•์…”๋„ˆ๋ฆฌ์— ์ €์žฅ - _characterIdToInstance[characterId] = instance; - - // ์ฆ‰์‹œ ํ™œ์„ฑํ™” ์˜ต์…˜ - if (activateImmediately) - { - ActivateCharacter(characterId); - } - - Debug.Log($"[Live2DModelManager] ์บ๋ฆญํ„ฐ '{characterId}' ๋กœ๋“œ ์™„๋ฃŒ"); - return instance; - } - - /// - /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ๋น„๋™๊ธฐ๋กœ ๋กœ๋“œํ•˜๊ณ  ์„ค์ •์„ ์ ์šฉํ•œ๋‹ค. - /// - public async UniTask LoadCharacterAsync(string characterId, bool activateImmediately = false) - { - if (string.IsNullOrEmpty(characterId)) - { - Debug.LogWarning("[Live2DModelManager] ์บ๋ฆญํ„ฐ ID๊ฐ€ null์ž…๋‹ˆ๋‹ค."); - return null; - } - - // ์ด๋ฏธ ๋กœ๋“œ๋œ ๋ชจ๋ธ์ด ์žˆ๋Š”์ง€ ํ™•์ธ - if (_characterIdToInstance.TryGetValue(characterId, out var existing)) - { - Debug.Log($"[Live2DModelManager] ์บ๋ฆญํ„ฐ '{characterId}'๊ฐ€ ์ด๋ฏธ ๋กœ๋“œ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค."); - if (activateImmediately) - { - ActivateCharacter(characterId); - } - return existing; - } - - // ์„ค์ • ๊ฐ€์ ธ์˜ค๊ธฐ - var config = GetCharacterConfig(characterId); - if (config == null) - { - Debug.LogError($"[Live2DModelManager] ์บ๋ฆญํ„ฐ '{characterId}'์˜ ์„ค์ •์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); - return null; - } - - // ๋ชจ๋ธ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ (๋น„๋™๊ธฐ) - var instance = await CreateModelInstanceAsync(config, characterId); - if (instance == null) - { - return null; - } - - // ์„ค์ • ์ ์šฉ - ApplyCharacterConfig(instance, config); - - // ๋”•์…”๋„ˆ๋ฆฌ์— ์ €์žฅ - _characterIdToInstance[characterId] = instance; - - // ์ฆ‰์‹œ ํ™œ์„ฑํ™” ์˜ต์…˜ - if (activateImmediately) - { - ActivateCharacter(characterId); - } - - Debug.Log($"[Live2DModelManager] ์บ๋ฆญํ„ฐ '{characterId}' ๋น„๋™๊ธฐ ๋กœ๋“œ ์™„๋ฃŒ"); - return instance; - } - - /// - /// ์บ๋ฆญํ„ฐ๋ฅผ ํ™œ์„ฑํ™”ํ•œ๋‹ค. - /// - public void ActivateCharacter(string characterId) - { - if (!_characterIdToInstance.TryGetValue(characterId, out var target)) - { - Debug.LogWarning($"[Live2DModelManager] ์บ๋ฆญํ„ฐ '{characterId}'๊ฐ€ ๋กœ๋“œ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."); - return; - } - - // ๋ชจ๋“  ๋ชจ๋ธ ๋น„ํ™œ์„ฑํ™” - DeactivateAllModels(); - - // ๋Œ€์ƒ ๋ชจ๋ธ ํ™œ์„ฑํ™” - target.SetActive(true); - _activeCharacterId = characterId; - - Debug.Log($"[Live2DModelManager] ์บ๋ฆญํ„ฐ '{characterId}' ํ™œ์„ฑํ™”"); - } - - /// - /// ํ˜„์žฌ ํ™œ์„ฑ ์บ๋ฆญํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. - /// - public GameObject GetActiveCharacter() - { - if (string.IsNullOrEmpty(_activeCharacterId)) - { - return null; - } - - _characterIdToInstance.TryGetValue(_activeCharacterId, out var character); - return character; - } - - /// - /// ์บ๋ฆญํ„ฐ๊ฐ€ ๋กœ๋“œ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. - /// - public bool HasCharacter(string characterId) - { - return !string.IsNullOrEmpty(characterId) && _characterIdToInstance.ContainsKey(characterId); - } - - /// - /// ์บ๋ฆญํ„ฐ๋ฅผ ์–ธ๋กœ๋“œํ•œ๋‹ค. - /// - public void UnloadCharacter(string characterId) - { - if (!_characterIdToInstance.TryGetValue(characterId, out var instance)) - { - return; - } - - if (instance != null) - { - Destroy(instance); - } - - _characterIdToInstance.Remove(characterId); - - // ํ˜„์žฌ ํ™œ์„ฑ ์บ๋ฆญํ„ฐ์˜€๋‹ค๋ฉด ํ™œ์„ฑ ID ์ดˆ๊ธฐํ™” - if (_activeCharacterId == characterId) - { - _activeCharacterId = null; - } - - Debug.Log($"[Live2DModelManager] ์บ๋ฆญํ„ฐ '{characterId}' ์–ธ๋กœ๋“œ ์™„๋ฃŒ"); - } - - /// - /// ๋ชจ๋“  ์บ๋ฆญํ„ฐ๋ฅผ ์–ธ๋กœ๋“œํ•œ๋‹ค. - /// - public void UnloadAllCharacters() - { - foreach (var kvp in _characterIdToInstance) - { - if (kvp.Value != null) - { - Destroy(kvp.Value); - } - } - - _characterIdToInstance.Clear(); - _activeCharacterId = null; - - Debug.Log("[Live2DModelManager] ๋ชจ๋“  ์บ๋ฆญํ„ฐ ์–ธ๋กœ๋“œ ์™„๋ฃŒ"); - } - - /// - /// ํ™œ์„ฑ ์บ๋ฆญํ„ฐ์˜ ๊ฐ€์‹œ์„ฑ์„ ์„ค์ •ํ•œ๋‹ค. - /// - public void SetCharacterVisibility(bool isVisible) - { - var active = GetActiveCharacter(); - if (active == null) - { - Debug.LogWarning("[Live2DModelManager] ํ™œ์„ฑ ์บ๋ฆญํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค."); - return; - } - - active.SetActive(isVisible); - } - - #endregion - - #region Private Methods - - /// - /// ์บ๋ฆญํ„ฐ ์„ค์ •์„ ๊ฐ€์ ธ์˜จ๋‹ค. - /// - private Live2DModelConfig GetCharacterConfig(string characterId) - { - // ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์—์„œ ๋จผ์ € ์ฐพ๊ธฐ - if (_modelRegistry != null && _modelRegistry.TryGetConfig(characterId, out var config)) - { - return config; - } - - // ์„ค์ •์„ ์ฐพ์„ ์ˆ˜ ์—†์Œ - Debug.LogError($"[Live2DModelManager] ์บ๋ฆญํ„ฐ '{characterId}'์˜ ์„ค์ •์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”."); - return null; - } - - /// - /// ๋ชจ๋ธ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. - /// - private GameObject CreateModelInstance(Live2DModelConfig config, string characterId) - { - if (config.CharacterPrefab == null) - { - Debug.LogError($"[Live2DModelManager] ์บ๋ฆญํ„ฐ '{characterId}'์˜ ํ”„๋ฆฌํŒน์ด null์ž…๋‹ˆ๋‹ค."); - return null; - } - - var parent = _modelRoot != null ? _modelRoot : transform; - var instance = Instantiate(config.CharacterPrefab, parent); - instance.name = characterId; - instance.SetActive(false); - - return instance; - } - - /// - /// ๋ชจ๋ธ ์ธ์Šคํ„ด์Šค๋ฅผ ๋น„๋™๊ธฐ๋กœ ์ƒ์„ฑํ•œ๋‹ค. - /// - private async UniTask CreateModelInstanceAsync(Live2DModelConfig config, string characterId) - { - if (config.CharacterPrefab == null) - { - Debug.LogError($"[Live2DModelManager] ์บ๋ฆญํ„ฐ '{characterId}'์˜ ํ”„๋ฆฌํŒน์ด null์ž…๋‹ˆ๋‹ค."); - return null; - } - - var parent = _modelRoot != null ? _modelRoot : transform; - var instance = Instantiate(config.CharacterPrefab, parent); - instance.name = characterId; - instance.SetActive(false); - - // ๋น„๋™๊ธฐ ์ž‘์—…์„ ์œ„ํ•œ ์ง€์—ฐ (ํ•„์š”์‹œ) - await UniTask.Yield(); - - return instance; - } - - /// - /// ์บ๋ฆญํ„ฐ ์„ค์ •์„ ์ ์šฉํ•œ๋‹ค. - /// - private void ApplyCharacterConfig(GameObject character, Live2DModelConfig config) - { - if (character == null || config == null) - { - return; - } - - ApplyLookAtSettings(character, config); - ApplyLipSyncSettings(character, config); - InitializeHitHandler(character); - } - - /// - /// ์‹œ์„  ์ถ”์  ์„ค์ •์„ ์ ์šฉํ•œ๋‹ค. - /// - private void ApplyLookAtSettings(GameObject character, Live2DModelConfig config) - { - var lookController = character.GetComponent(); - if (lookController == null) - { - Debug.LogWarning($"[Live2DModelManager] CubismLookController๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: {character.name}"); - return; - } - - lookController.Target = null; // TODO: ์‹œ์„  ํƒ€๊ฒŸ ์„ค์ • ํ•„์š” - lookController.Damping = config.LockAtDamping; - } - - /// - /// ๋ฆฝ์‹ฑํฌ ์„ค์ •์„ ์ ์šฉํ•œ๋‹ค. - /// - private void ApplyLipSyncSettings(GameObject character, Live2DModelConfig config) - { - var mouthInput = character.GetComponent(); - if (mouthInput == null) - { - Debug.LogWarning($"[Live2DModelManager] CubismAudioMouthInput์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: {character.name}"); - return; - } - - mouthInput.AudioInput = null; // TODO: ์˜ค๋””์˜ค ์†Œ์Šค ์„ค์ • ํ•„์š” - mouthInput.Gain = config.Gain; - mouthInput.Smoothing = config.Smoothing; - } - - /// - /// ํ„ฐ์น˜ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค. - /// - private void InitializeHitHandler(GameObject character) - { - var hitHandler = character.GetComponent(); - if (hitHandler == null) - { - Debug.LogWarning($"[Live2DModelManager] CubismHitHandler๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: {character.name}"); - return; - } - - hitHandler.Initialize(); - } - - /// - /// ๋ชจ๋“  ๋ชจ๋ธ์„ ๋น„ํ™œ์„ฑํ™”ํ•œ๋‹ค. - /// - private void DeactivateAllModels() - { - foreach (var kvp in _characterIdToInstance) - { - if (kvp.Value != null) - { - kvp.Value.SetActive(false); - } - } - } - - #endregion - } -} - - diff --git a/Assets/Domain/Character/Script/Manager/Live2DModelManager.cs.meta b/Assets/Domain/Character/Script/Manager/Live2DModelManager.cs.meta deleted file mode 100644 index 58c5d74..0000000 --- a/Assets/Domain/Character/Script/Manager/Live2DModelManager.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: bbd7bfb36017db743b3b7cab74183970 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Test.meta b/Assets/Domain/Character/Script/Test.meta deleted file mode 100644 index 793641e..0000000 --- a/Assets/Domain/Character/Script/Test.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: c762a71a10e06714a96c237780ce3100 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Domain/Character/Script/Test/Live2DModelTest.cs b/Assets/Domain/Character/Script/Test/Live2DModelTest.cs deleted file mode 100644 index 94be95e..0000000 --- a/Assets/Domain/Character/Script/Test/Live2DModelTest.cs +++ /dev/null @@ -1,180 +0,0 @@ -using UnityEngine; -using UnityEngine.UI; -using ProjectVG.Domain.Character.Service; -using ProjectVG.Domain.Character.Live2D.Model; - -namespace ProjectVG.Domain.Character.Test -{ - /// - /// Live2DModelManager ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ์ปดํฌ๋„ŒํŠธ - /// - public class Live2DModelTest : MonoBehaviour - { - [Header("ํ…Œ์ŠคํŠธ ์„ค์ •")] - [SerializeField] private string _testCharacterId = "test_character"; - - [Header("UI ์š”์†Œ")] - [SerializeField] private Button _loadCharacterBtn; - [SerializeField] private Button _activateCharacterBtn; - [SerializeField] private Button _unloadCharacterBtn; - [SerializeField] private Button _unloadAllBtn; - [SerializeField] private Button _changeVisibilityBtn; - [SerializeField] private InputField _characterIdInput; - - [Header("ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ")] - [SerializeField] private Text _statusText; - - private bool _isCharacterVisible = true; - - private void Start() - { - SetupUI(); - - // Live2DModelManager ์ดˆ๊ธฐํ™” ํ™•์ธ - if (Live2DModelManager.Instance != null) - { - UpdateStatus("Live2DModelManager ์ค€๋น„ ์™„๋ฃŒ"); - } - else - { - UpdateStatus("Live2DModelManager ์ดˆ๊ธฐํ™” ์‹คํŒจ"); - } - } - - /// - /// UI ์š”์†Œ๋“ค์„ ์„ค์ •ํ•œ๋‹ค. - /// - private void SetupUI() - { - if (_loadCharacterBtn != null) - { - _loadCharacterBtn.onClick.RemoveAllListeners(); - _loadCharacterBtn.onClick.AddListener(OnLoadCharacterClicked); - } - - if (_activateCharacterBtn != null) - { - _activateCharacterBtn.onClick.RemoveAllListeners(); - _activateCharacterBtn.onClick.AddListener(OnActivateCharacterClicked); - } - - if (_unloadCharacterBtn != null) - { - _unloadCharacterBtn.onClick.RemoveAllListeners(); - _unloadCharacterBtn.onClick.AddListener(OnUnloadCharacterClicked); - } - - if (_unloadAllBtn != null) - { - _unloadAllBtn.onClick.RemoveAllListeners(); - _unloadAllBtn.onClick.AddListener(OnUnloadAllClicked); - } - - if (_changeVisibilityBtn != null) - { - _changeVisibilityBtn.onClick.RemoveAllListeners(); - _changeVisibilityBtn.onClick.AddListener(OnChangeVisibilityClicked); - } - - if (_characterIdInput != null) - { - _characterIdInput.text = _testCharacterId; - _characterIdInput.onValueChanged.AddListener(OnCharacterIdChanged); - } - - UpdateStatus("ํ…Œ์ŠคํŠธ ์ค€๋น„ ์™„๋ฃŒ"); - } - - /// - /// ์บ๋ฆญํ„ฐ ๋กœ๋“œ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ํ˜ธ์ถœ๋œ๋‹ค. - /// - private async void OnLoadCharacterClicked() - { - UpdateStatus($"์บ๋ฆญํ„ฐ ๋กœ๋“œ ์‹œ์ž‘: {_testCharacterId}"); - - var character = await Live2DModelManager.Instance.LoadCharacterAsync(_testCharacterId, activateImmediately: true); - if (character != null) - { - UpdateStatus($"์บ๋ฆญํ„ฐ ๋กœ๋“œ ๋ฐ ํ™œ์„ฑํ™” ์™„๋ฃŒ: {_testCharacterId}"); - } - else - { - UpdateStatus($"์บ๋ฆญํ„ฐ ๋กœ๋“œ ์‹คํŒจ: {_testCharacterId}"); - } - } - - /// - /// ์บ๋ฆญํ„ฐ ํ™œ์„ฑํ™” ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ํ˜ธ์ถœ๋œ๋‹ค. - /// - private void OnActivateCharacterClicked() - { - Live2DModelManager.Instance.ActivateCharacter(_testCharacterId); - UpdateStatus($"์บ๋ฆญํ„ฐ ํ™œ์„ฑํ™”: {_testCharacterId}"); - } - - /// - /// ์บ๋ฆญํ„ฐ ์–ธ๋กœ๋“œ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ํ˜ธ์ถœ๋œ๋‹ค. - /// - private void OnUnloadCharacterClicked() - { - Live2DModelManager.Instance.UnloadCharacter(_testCharacterId); - UpdateStatus($"์บ๋ฆญํ„ฐ ์–ธ๋กœ๋“œ ์™„๋ฃŒ: {_testCharacterId}"); - } - - /// - /// ๋ชจ๋“  ์บ๋ฆญํ„ฐ ์–ธ๋กœ๋“œ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ํ˜ธ์ถœ๋œ๋‹ค. - /// - private void OnUnloadAllClicked() - { - Live2DModelManager.Instance.UnloadAllCharacters(); - UpdateStatus("๋ชจ๋“  ์บ๋ฆญํ„ฐ ์–ธ๋กœ๋“œ ์™„๋ฃŒ"); - } - - /// - /// ๊ฐ€์‹œ์„ฑ ๋ณ€๊ฒฝ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ํ˜ธ์ถœ๋œ๋‹ค. - /// - private void OnChangeVisibilityClicked() - { - _isCharacterVisible = !_isCharacterVisible; - Live2DModelManager.Instance.SetCharacterVisibility(_isCharacterVisible); - UpdateStatus($"์บ๋ฆญํ„ฐ ๊ฐ€์‹œ์„ฑ ๋ณ€๊ฒฝ: {(_isCharacterVisible ? "๋ณด์ž„" : "์ˆจ๊น€")}"); - } - - /// - /// ์บ๋ฆญํ„ฐ ID ์ž…๋ ฅ ๋ณ€๊ฒฝ ์‹œ ํ˜ธ์ถœ๋œ๋‹ค. - /// - private void OnCharacterIdChanged(string newId) - { - _testCharacterId = newId; - } - - /// - /// ์ƒํƒœ ํ…์ŠคํŠธ๋ฅผ ์—…๋ฐ์ดํŠธํ•œ๋‹ค. - /// - private void UpdateStatus(string message) - { - Debug.Log($"[Live2DModelTest] {message}"); - - if (_statusText != null) - { - _statusText.text = message; - } - } - - /// - /// ํ˜„์žฌ ์ƒํƒœ ์ •๋ณด๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค. - /// - [ContextMenu("ํ˜„์žฌ ์ƒํƒœ ์ถœ๋ ฅ")] - private void PrintCurrentStatus() - { - var activeCharacter = Live2DModelManager.Instance.GetActiveCharacter(); - var hasCharacter = Live2DModelManager.Instance.HasCharacter(_testCharacterId); - - Debug.Log($"[Live2DModelTest] ํ˜„์žฌ ์ƒํƒœ:"); - Debug.Log($" - ํ…Œ์ŠคํŠธ ์บ๋ฆญํ„ฐ ID: {_testCharacterId}"); - Debug.Log($" - ์บ๋ฆญํ„ฐ ๋กœ๋“œ ์—ฌ๋ถ€: {hasCharacter}"); - Debug.Log($" - ํ™œ์„ฑ ์บ๋ฆญํ„ฐ: {(activeCharacter != null ? activeCharacter.name : "์—†์Œ")}"); - } - } -} - diff --git a/Assets/Domain/Character/Script/Test/Live2DModelTest.cs.meta b/Assets/Domain/Character/Script/Test/Live2DModelTest.cs.meta deleted file mode 100644 index d21d899..0000000 --- a/Assets/Domain/Character/Script/Test/Live2DModelTest.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 7fb1442983837c140abae42a85733fe6 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Test/ResolutionDebugger.cs b/Assets/Domain/Character/Script/Test/ResolutionDebugger.cs deleted file mode 100644 index 65358d6..0000000 --- a/Assets/Domain/Character/Script/Test/ResolutionDebugger.cs +++ /dev/null @@ -1,160 +0,0 @@ -using UnityEngine; -using ProjectVG.Domain.Character.Manager; -using ProjectVG.Domain.Character.Config; - -namespace ProjectVG.Domain.Character.Test -{ - /// - /// ํ•ด์ƒ๋„ ๋””๋ฒ„๊น…์„ ์œ„ํ•œ ํ…Œ์ŠคํŠธ ์Šคํฌ๋ฆฝํŠธ - /// - public class ResolutionDebugger : MonoBehaviour - { - [Header("ํ…Œ์ŠคํŠธ ์„ค์ •")] - [SerializeField] private bool testOnStart = true; - [SerializeField] private Vector2 testResolution = new Vector2(1440, 2960); - - [Header("๋””๋ฒ„๊ทธ ์ •๋ณด")] - [SerializeField] private bool showCurrentResolution = true; - [SerializeField] private bool showScaleCalculation = true; - - private void Start() - { - if (testOnStart) - { - TestResolution(); - } - } - - [ContextMenu("ํ•ด์ƒ๋„ ํ…Œ์ŠคํŠธ")] - public void TestResolution() - { - Debug.Log("=== ํ•ด์ƒ๋„ ๋””๋ฒ„๊น… ์‹œ์ž‘ ==="); - - // ํ˜„์žฌ ํ•ด์ƒ๋„ ์ •๋ณด - if (showCurrentResolution) - { - Debug.Log($"=== ํ˜„์žฌ ์‹œ์Šคํ…œ ์ •๋ณด ===\n" + - $"ํ•ด์ƒ๋„: {Screen.width} x {Screen.height}\n" + - $"์ข…ํšก๋น„: {(float)Screen.width / Screen.height:F3}\n" + - $"ํ”Œ๋žซํผ: {(Application.isMobilePlatform ? "๋ชจ๋ฐ”์ผ" : "PC")}\n" + - $"DPI: {Screen.dpi:F1}"); - } - - // ResolutionManager ํ…Œ์ŠคํŠธ - var manager = ResolutionManager.Instance; - if (manager != null) - { - var info = manager.GetCurrentResolutionInfo(); - Debug.Log($"=== ResolutionManager ์ƒํƒœ ===\n" + - $"ํ•ด์ƒ๋„: {info.Resolution.x} x {info.Resolution.y}\n" + - $"์ ์šฉ ์Šค์ผ€์ผ: {info.Scale:F3}๋ฐฐ\n" + - $"๋“ฑ๋ก๋œ ์Šค์ผ€์ผ๋Ÿฌ: {manager.GetRegisteredScalerCount()}๊ฐœ"); - } - else - { - Debug.LogWarning("ResolutionManager๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); - } - - // ์„ค์ • ํŒŒ์ผ ์ง์ ‘ ํ…Œ์ŠคํŠธ - var config = Resources.Load("ResolutionModelScaleConfig"); - if (config != null) - { - Debug.Log($"=== ์„ค์ • ํŒŒ์ผ ์ •๋ณด ===\n" + - $"ํŒŒ์ผ๋ช…: {config.name}\n" + - $"๊ธฐ์ค€ ํ•ด์ƒ๋„: {config.ReferenceResolution.x} x {config.ReferenceResolution.y}\n" + - $"๊ธฐ์ค€ ์Šค์ผ€์ผ: {config.ReferenceScale:F3}๋ฐฐ"); - - // ํ˜„์žฌ ํ•ด์ƒ๋„๋กœ ์Šค์ผ€์ผ ๊ณ„์‚ฐ - var currentScale = config.CalculateScale(); - Debug.Log($"=== ํ˜„์žฌ ํ•ด์ƒ๋„ ์Šค์ผ€์ผ ===\n" + - $"ํ•ด์ƒ๋„: {Screen.width} x {Screen.height}\n" + - $"์ ์šฉ ์Šค์ผ€์ผ: {currentScale:F3}๋ฐฐ"); - - // ํ…Œ์ŠคํŠธ ํ•ด์ƒ๋„๋กœ ์Šค์ผ€์ผ ๊ณ„์‚ฐ - var testScale = config.CalculateScaleForResolution(testResolution); - Debug.Log($"=== ํ…Œ์ŠคํŠธ ํ•ด์ƒ๋„ ์Šค์ผ€์ผ ===\n" + - $"ํ•ด์ƒ๋„: {testResolution.x} x {testResolution.y}\n" + - $"์ ์šฉ ์Šค์ผ€์ผ: {testScale:F3}๋ฐฐ"); - - if (showScaleCalculation) - { - TestScaleCalculation(config); - } - } - else - { - Debug.LogError("ResolutionModelScaleConfig๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); - } - - Debug.Log("=== ํ•ด์ƒ๋„ ๋””๋ฒ„๊น… ์™„๋ฃŒ ==="); - } - - private void TestScaleCalculation(ResolutionModelScaleConfig config) - { - Debug.Log("--- ์Šค์ผ€์ผ ๊ณ„์‚ฐ ์ƒ์„ธ ๋ถ„์„ ---"); - - var currentResolution = new Vector2(Screen.width, Screen.height); - var testResolution = new Vector2(1440, 2960); - - // ๊ฐ ํ•ด์ƒ๋„๋ณ„ ์Šค์ผ€์ผ ๊ณ„์‚ฐ - var resolutions = new Vector2[] - { - currentResolution, - testResolution, - new Vector2(1920, 1080), // PC FHD - new Vector2(1080, 1920), // ๋ชจ๋ฐ”์ผ ์„ธ๋กœ - new Vector2(390, 844), // iPhone 12 - new Vector2(375, 667) // iPhone SE - }; - - foreach (var resolution in resolutions) - { - var scale = config.CalculateScaleForResolution(resolution); - var aspectRatio = resolution.x / resolution.y; - var platform = Application.isMobilePlatform ? "๋ชจ๋ฐ”์ผ" : "PC"; - Debug.Log($"=== {resolution.x} x {resolution.y} ===\n" + - $"์ข…ํšก๋น„: {aspectRatio:F3}\n" + - $"ํ”Œ๋žซํผ: {platform}\n" + - $"์ ์šฉ ์Šค์ผ€์ผ: {scale:F3}๋ฐฐ"); - } - } - - [ContextMenu("๊ฐ•์ œ ์Šค์ผ€์ผ ์ ์šฉ")] - public void ForceApplyScale() - { - var manager = ResolutionManager.Instance; - if (manager != null) - { - manager.ApplyScaleToAllModels(); - Debug.Log("๋ชจ๋“  ๋ชจ๋ธ์— ์Šค์ผ€์ผ ๊ฐ•์ œ ์ ์šฉ ์™„๋ฃŒ"); - } - } - - [ContextMenu("์Šค์ผ€์ผ๋Ÿฌ ์ฐพ๊ธฐ")] - public void FindScalers() - { - var scalers = FindObjectsOfType(); - Debug.Log($"=== ๋ฐœ๊ฒฌ๋œ Live2DModelScaler: {scalers.Length}๊ฐœ ==="); - - if (scalers.Length == 0) - { - Debug.LogWarning("Live2DModelScaler๊ฐ€ ๋ฐœ๊ฒฌ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."); - return; - } - - foreach (var scaler in scalers) - { - var originalScale = scaler.GetOriginalScale(); - var currentScale = scaler.GetCurrentScale(); - var transform = scaler.transform; - - Debug.Log($"=== {scaler.name} ===\n" + - $"์œ„์น˜: {transform.position}\n" + - $"์›๋ณธ ํฌ๊ธฐ: {originalScale}\n" + - $"ํ˜„์žฌ ํฌ๊ธฐ: {transform.localScale}\n" + - $"์ ์šฉ ์Šค์ผ€์ผ: {currentScale:F3}๋ฐฐ\n" + - $"๋ถ€๋ชจ: {(transform.parent != null ? transform.parent.name : "์—†์Œ")}"); - } - } - } -} diff --git a/Assets/Domain/Character/Script/Test/ResolutionDebugger.cs.meta b/Assets/Domain/Character/Script/Test/ResolutionDebugger.cs.meta deleted file mode 100644 index 867d5c4..0000000 --- a/Assets/Domain/Character/Script/Test/ResolutionDebugger.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: fc5d277ba2de30b42bcaf2f8519e48a1 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Test/TestVoice.cs b/Assets/Domain/Character/Script/Test/TestVoice.cs deleted file mode 100644 index 4cefb39..0000000 --- a/Assets/Domain/Character/Script/Test/TestVoice.cs +++ /dev/null @@ -1,14 +0,0 @@ -using UnityEngine; - -[RequireComponent(typeof(AudioSource))] -public class TestVoice : MonoBehaviour -{ - [SerializeField] private AudioClip audioClip; - private AudioSource _audioSource; - - void Awake() - { - _audioSource = GetComponent(); - _audioSource.clip = audioClip; - } -} diff --git a/Assets/Domain/Character/Script/Test/TestVoice.cs.meta b/Assets/Domain/Character/Script/Test/TestVoice.cs.meta deleted file mode 100644 index 2abfbe6..0000000 --- a/Assets/Domain/Character/Script/Test/TestVoice.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: ff42adec7c6fd2f4f972f5cffb445dc2 \ No newline at end of file diff --git a/Assets/Domain/Chat/Service/ChatManager.cs b/Assets/Domain/Chat/Service/ChatManager.cs deleted file mode 100644 index e91f784..0000000 --- a/Assets/Domain/Chat/Service/ChatManager.cs +++ /dev/null @@ -1,385 +0,0 @@ -#nullable enable -using System; -using System.Collections; -using System.Collections.Generic; -using UnityEngine; -using Cysharp.Threading.Tasks; -using ProjectVG.Core.Audio; -using ProjectVG.Domain.Chat.Model; -using ProjectVG.Infrastructure.Network.WebSocket; -using ProjectVG.Infrastructure.Network.Services; -using ProjectVG.Domain.Chat.View; -using ProjectVG.Domain.Character.Service; - - -namespace ProjectVG.Domain.Chat.Service -{ - public class ChatManager : MonoBehaviour - { - [Header("Components")] - [SerializeField] private ChatBubblePanel? _chatBubblePanel; - - - [Header("Chat Settings")] - [SerializeField] private string _characterId = "44444444-4444-4444-4444-444444444444"; - [SerializeField] private string _userId = "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"; - - [Header("Message Queue Settings")] - [SerializeField] private bool _enableMessageQueue = true; - [SerializeField] private int _maxQueueSize = 100; - - [Header("Live2D Integration")] - [SerializeField] private bool _enableLive2DIntegration = true; - - private bool _isConnected = false; - private bool _isInitialized = false; - private bool _isProcessing = false; - - private WebSocketManager? _webSocketManager; - private AudioManager? _audioManager; - private Live2DCharacterManager? _live2DCharacterManager; - - private readonly Queue _messageQueue = new Queue(); - private readonly object _queueLock = new object(); - - public bool IsConnected => _isConnected; - public bool IsInitialized => _isInitialized; - public int QueueCount => _messageQueue.Count; - - public event Action? OnChatMessageReceived; - public event Action? OnError; - - #region Unity Lifecycle - - private static ChatManager? _instance; - public static ChatManager Instance - { - get - { - if (_instance == null) - { - _instance = FindAnyObjectByType(); - } - return _instance; - } - } - - private void Awake() - { - if (_instance != null && _instance != this) - { - Destroy(gameObject); - return; - } - - _instance = this; - } - - private void Start() - { - // UI ์ปดํฌ๋„ŒํŠธ๋“ค์ด ๋ชจ๋‘ ์ƒ์„ฑ๋œ ํ›„ ์ดˆ๊ธฐํ™” - StartCoroutine(InitializeWhenReady()); - } - - private IEnumerator InitializeWhenReady() - { - float timeout = 5f; - float startTime = Time.realtimeSinceStartup; - - // ChatBubblePanel์ด ์ค€๋น„๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ - while (_chatBubblePanel == null && (Time.realtimeSinceStartup - startTime) < timeout) - { - yield return new WaitForSecondsRealtime(0.1f); - } - - if (_chatBubblePanel == null) - { - Debug.LogError("[ChatManager] ChatBubblePanel ์ดˆ๊ธฐํ™” ํƒ€์ž„์•„์›ƒ"); - yield break; - } - - Initialize(); - } - - private void OnDestroy() - { - if (_webSocketManager != null) - { - _webSocketManager.OnChatMessageReceived -= HandleChatMessageReceived; - } - } - - #endregion - - #region Public Methods - - public void Initialize() - { - if (_isInitialized) - return; - - try - { - // ๊ธฐ๋ณธ ๋งค๋‹ˆ์ €๋“ค ์ดˆ๊ธฐํ™” - _webSocketManager = WebSocketManager.Instance; - _audioManager = AudioManager.Instance; - - // Live2D ๋ชจ๋“ˆ ์ดˆ๊ธฐํ™” - if (_enableLive2DIntegration) - { - _live2DCharacterManager = Live2DCharacterManager.Instance; - } - - if (_webSocketManager != null) - { - _webSocketManager.OnChatMessageReceived += HandleChatMessageReceived; - } - - _isInitialized = true; - _isConnected = true; - Debug.Log("[ChatManager] ์ดˆ๊ธฐํ™” ์™„๋ฃŒ"); - } - catch (Exception ex) - { - Debug.LogError($"[ChatManager] ์ดˆ๊ธฐํ™” ์‹คํŒจ: {ex.Message}"); - OnError?.Invoke($"์ดˆ๊ธฐํ™” ์‹คํŒจ: {ex.Message}"); - } - } - - public async void SendUserMessage(string message) - { - if (!ValidateUserInput(message)) - return; - - try - { - if (_chatBubblePanel != null) - { - _chatBubblePanel.CreateBubble(Actor.User, message); - } - - var chatService = ApiServiceManager.Instance.Chat; - var response = await chatService.SendChatAsync( - message: message, - characterId: _characterId, - userId: _userId - ); - - if (response == null) - { - Debug.LogWarning("[ChatManager] ์ฑ„ํŒ… ์‘๋‹ต์ด null์ž…๋‹ˆ๋‹ค."); - } - } - catch (Exception ex) - { - Debug.LogError($"[ChatManager] ๋ฉ”์‹œ์ง€ ์ „์†ก ์‹คํŒจ: {ex.Message}"); - OnError?.Invoke($"๋ฉ”์‹œ์ง€ ์ „์†ก ์‹คํŒจ: {ex.Message}"); - } - } - - public void ProcessCharacterMessage(ChatMessage chatMessage) - { - if (chatMessage == null) - { - Debug.LogWarning("[ChatManager] ๋นˆ ์ฑ„ํŒ… ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค."); - return; - } - - if (!_enableMessageQueue) - { - ProcessMessageImmediately(chatMessage); - return; - } - - lock (_queueLock) - { - if (_messageQueue.Count >= _maxQueueSize) - { - Debug.LogWarning($"[ChatManager] ๋ฉ”์‹œ์ง€ ํ๊ฐ€ ๊ฐ€๋“ ์ฐผ์Šต๋‹ˆ๋‹ค. (์ตœ๋Œ€: {_maxQueueSize})"); - return; - } - - _messageQueue.Enqueue(chatMessage); - } - - ProcessMessageQueueAsync().Forget(); - } - - public void ClearMessageQueue() - { - lock (_queueLock) - { - _messageQueue.Clear(); - } - } - - #endregion - - #region Private Methods - - private async UniTaskVoid ProcessMessageQueueAsync() - { - if (_isProcessing) - return; - - _isProcessing = true; - - try - { - while (true) - { - ChatMessage message; - - lock (_queueLock) - { - if (_messageQueue.Count == 0) - { - break; - } - message = _messageQueue.Dequeue(); - } - - if (message != null) - { - await ProcessMessageImmediatelyAsync(message); - } - } - } - catch (Exception ex) - { - Debug.LogError($"[ChatManager] ๋ฉ”์‹œ์ง€ ํ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜: {ex.Message}"); - OnError?.Invoke($"๋ฉ”์‹œ์ง€ ํ ์ฒ˜๋ฆฌ ์‹คํŒจ: {ex.Message}"); - } - finally - { - _isProcessing = false; - } - } - - private void ProcessMessageImmediately(ChatMessage chatMessage) - { - try - { - OnChatMessageReceived?.Invoke(chatMessage); - - // ์บ๋ฆญํ„ฐ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฒ„๋ธ”๋กœ ํ‘œ์‹œ - if (_chatBubblePanel != null && !string.IsNullOrEmpty(chatMessage.Text)) - { - _chatBubblePanel.CreateBubble(Actor.Character, chatMessage.Text); - } - - // Live2D ๋ฐ˜์‘ ์ ์šฉ - ApplyLive2DReaction(chatMessage); - - if (chatMessage.VoiceData != null && _audioManager != null) - { - _audioManager.PlayVoice(chatMessage.VoiceData); - } - } - catch (Exception ex) - { - Debug.LogError($"[ChatManager] ์บ๋ฆญํ„ฐ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ ์‹คํŒจ: {ex.Message}"); - OnError?.Invoke($"๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ ์‹คํŒจ: {ex.Message}"); - } - } - - private async UniTask ProcessMessageImmediatelyAsync(ChatMessage chatMessage) - { - try - { - OnChatMessageReceived?.Invoke(chatMessage); - - if (_chatBubblePanel != null && !string.IsNullOrEmpty(chatMessage.Text)) - { - _chatBubblePanel.CreateBubble(Actor.Character, chatMessage.Text); - } - - // Live2D ๋ฐ˜์‘ ์ ์šฉ - ApplyLive2DReaction(chatMessage); - - if (chatMessage.VoiceData != null && _audioManager != null) - { - await _audioManager.PlayVoiceAsync(chatMessage.VoiceData); - } - } - catch (Exception ex) - { - Debug.LogError($"[ChatManager] ์บ๋ฆญํ„ฐ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ ์‹คํŒจ: {ex.Message}"); - OnError?.Invoke($"๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ ์‹คํŒจ: {ex.Message}"); - } - } - - private void ApplyLive2DReaction(ChatMessage chatMessage) - { - if (!_enableLive2DIntegration || _live2DCharacterManager == null) - return; - - try - { - // ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์—์„œ ๊ฐ์ •/ํ–‰๋™ ์ •๋ณด ์ถ”์ถœ - var emotionData = ExtractEmotionData(chatMessage); - var actionData = ExtractActionData(chatMessage); - - // Live2D ๋ฐ˜์‘ ์ ์šฉ - _live2DCharacterManager.ApplyReaction(emotionData, actionData); - - // ์Œ์„ฑ ์žฌ์ƒ ์‹œ์ž‘ ์•Œ๋ฆผ - if (chatMessage.VoiceData != null) - { - _live2DCharacterManager.OnVoiceStarted(); - } - } - catch (Exception ex) - { - Debug.LogError($"[ChatManager] Live2D ๋ฐ˜์‘ ์ ์šฉ ์‹คํŒจ: {ex.Message}"); - } - } - - private EmotionData ExtractEmotionData(ChatMessage chatMessage) - { - // TODO: ์‹ค์ œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์—์„œ ๊ฐ์ • ์ •๋ณด ์ถ”์ถœ - // ํ˜„์žฌ๋Š” ๊ธฐ๋ณธ๊ฐ’ ๋ฐ˜ํ™˜ - return new EmotionData - { - Emotion = "neutral", - Intensity = 0.5f, - DurationMs = 2000 - }; - } - - private ActionData ExtractActionData(ChatMessage chatMessage) - { - // TODO: ์‹ค์ œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์—์„œ ํ–‰๋™ ์ •๋ณด ์ถ”์ถœ - // ํ˜„์žฌ๋Š” ๊ธฐ๋ณธ๊ฐ’ ๋ฐ˜ํ™˜ - return new ActionData - { - Action = "", - Args = null - }; - } - - private bool ValidateUserInput(string message) - { - if (string.IsNullOrWhiteSpace(message)) - { - Debug.LogWarning("[ChatManager] ๋นˆ ๋ฉ”์‹œ์ง€๋Š” ์ „์†กํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); - return false; - } - - if (message.Length > 1000) - { - Debug.LogWarning("[ChatManager] ๋ฉ”์‹œ์ง€๊ฐ€ ๋„ˆ๋ฌด ๊น๋‹ˆ๋‹ค. (์ตœ๋Œ€ 1000์ž)"); - return false; - } - - return true; - } - - private void HandleChatMessageReceived(ChatMessage chatMessage) - { - ProcessCharacterMessage(chatMessage); - } - - #endregion - } -} \ No newline at end of file diff --git a/Assets/Domain/Chat/Service/ChatMessageQueue.cs b/Assets/Domain/Chat/Service/ChatMessageQueue.cs new file mode 100644 index 0000000..d256ada --- /dev/null +++ b/Assets/Domain/Chat/Service/ChatMessageQueue.cs @@ -0,0 +1,141 @@ +#nullable enable +using System; +using System.Collections.Generic; +using UnityEngine; +using Cysharp.Threading.Tasks; +using ProjectVG.Domain.Chat.Model; + +namespace ProjectVG.Domain.Chat.Service +{ + /// + /// ์ฑ„ํŒ… ๋ฉ”์‹œ์ง€ ํ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ํด๋ž˜์Šค + /// + public class ChatMessageQueue + { + private readonly Queue _messageQueue = new Queue(); + private readonly object _queueLock = new object(); + private readonly int _maxQueueSize; + private bool _isProcessing = false; + + public int Count => _messageQueue.Count; + public bool IsProcessing => _isProcessing; + public bool IsEmpty => _messageQueue.Count == 0; + + public event Action? OnMessageProcessed; + public event Action? OnError; + + public ChatMessageQueue(int maxQueueSize = 100) + { + _maxQueueSize = maxQueueSize; + } + + /// + /// ๋ฉ”์‹œ์ง€๋ฅผ ํ์— ์ถ”๊ฐ€ํ•œ๋‹ค + /// + /// ์ถ”๊ฐ€ํ•  ๋ฉ”์‹œ์ง€ + /// ์ถ”๊ฐ€ ์„ฑ๊ณต ์—ฌ๋ถ€ + public bool Enqueue(ChatMessage chatMessage) + { + if (chatMessage == null) + return false; + + lock (_queueLock) { + if (_messageQueue.Count >= _maxQueueSize) { + Debug.LogWarning($"[ChatMessageQueue] ๋ฉ”์‹œ์ง€ ํ๊ฐ€ ๊ฐ€๋“ ์ฐผ์Šต๋‹ˆ๋‹ค. (์ตœ๋Œ€: {_maxQueueSize})"); + return false; + } + + _messageQueue.Enqueue(chatMessage); + } + + return true; + } + + /// + /// ํ์—์„œ ๋ฉ”์‹œ์ง€๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ๋ฐ˜ํ™˜ํ•œ๋‹ค + /// + /// ์ œ๊ฑฐ๋œ ๋ฉ”์‹œ์ง€, ํ๊ฐ€ ๋น„์–ด์žˆ์œผ๋ฉด null + public ChatMessage? Dequeue() + { + lock (_queueLock) { + return _messageQueue.Count > 0 ? _messageQueue.Dequeue() : null; + } + } + + /// + /// ํ์˜ ๋ชจ๋“  ๋ฉ”์‹œ์ง€๋ฅผ ์ œ๊ฑฐํ•œ๋‹ค + /// + public void Clear() + { + lock (_queueLock) { + _messageQueue.Clear(); + } + } + + /// + /// ๋ฉ”์‹œ์ง€ ํ๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค + /// + /// ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ ์•ก์…˜ + /// + public async UniTaskVoid ProcessQueueAsync(Func processAction) + { + if (_isProcessing) + return; + + _isProcessing = true; + + try { + while (true) { + ChatMessage? message = Dequeue(); + + if (message == null) { + break; + } + + try { + await processAction(message); + OnMessageProcessed?.Invoke(message); + } + catch (Exception ex) { + Debug.LogError($"[ChatMessageQueue] ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜: {ex.Message}"); + OnError?.Invoke($"๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ ์‹คํŒจ: {ex.Message}"); + } + } + } + catch (Exception ex) { + Debug.LogError($"[ChatMessageQueue] ํ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜: {ex.Message}"); + OnError?.Invoke($"ํ ์ฒ˜๋ฆฌ ์‹คํŒจ: {ex.Message}"); + } + finally { + _isProcessing = false; + } + } + + /// + /// ํ์˜ ์ƒํƒœ ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค + /// + /// ํ ์ƒํƒœ ์ •๋ณด + public QueueStatus GetStatus() + { + lock (_queueLock) { + return new QueueStatus { + Count = _messageQueue.Count, + MaxSize = _maxQueueSize, + IsProcessing = _isProcessing, + IsFull = _messageQueue.Count >= _maxQueueSize + }; + } + } + } + + /// + /// ํ ์ƒํƒœ ์ •๋ณด + /// + public struct QueueStatus + { + public int Count; + public int MaxSize; + public bool IsProcessing; + public bool IsFull; + } +} diff --git a/Assets/Domain/Chat/Service/ChatMessageQueue.cs.meta b/Assets/Domain/Chat/Service/ChatMessageQueue.cs.meta new file mode 100644 index 0000000..a88db9d --- /dev/null +++ b/Assets/Domain/Chat/Service/ChatMessageQueue.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 30c1af92fefd50c43a58245bea18e854 \ No newline at end of file diff --git a/Assets/Domain/Chat/Service/ChatSystemManager.cs b/Assets/Domain/Chat/Service/ChatSystemManager.cs new file mode 100644 index 0000000..31841a6 --- /dev/null +++ b/Assets/Domain/Chat/Service/ChatSystemManager.cs @@ -0,0 +1,209 @@ +#nullable enable +using System; +using System.Collections; +using UnityEngine; +using Cysharp.Threading.Tasks; +using ProjectVG.Core.Audio; +using ProjectVG.Domain.Chat.Model; +using ProjectVG.Infrastructure.Network.WebSocket; +using ProjectVG.Infrastructure.Network.Services; +using ProjectVG.Domain.Chat.View; +using ProjectVG.Domain.Character.Service; + + +namespace ProjectVG.Domain.Chat.Service +{ + /// + /// ๋Œ€ํ™” ๋ฉ”์‹œ์ง€๋ฅผ ์ „์†กํ•˜๊ณ  ๋ฐ›์œผ๋ฉด ์ฒ˜๋ฆฌํ•œ๋‹ค. + /// ๋Œ€ํ™” ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ• ๋•Œ S์‹œ๊ฐ„ ๋™์•ˆ ์•ก์…˜๊ณผ Voice๋ฅผ ์ ์œ ํ•˜๊ณ  ๋‹ค์Œ ํ–‰๋™์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. + /// + public class ChatSystemManager : MonoBehaviour + { + [Header("Components")] + [SerializeField] private ChatBubblePanel? _chatBubblePanel; + [SerializeField] private CharacterManager? _chracterManager; + + [Header("Chat Settings")] + [SerializeField] private string _characterId = "44444444-4444-4444-4444-444444444444"; + [SerializeField] private string _userId = "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"; + + private WebSocketManager? _webSocketManager; + private AudioManager? _audioManager; + private ChatMessageQueue? _messageQueue; + private ChatApiService? _chatApiService; + + private bool _isInitialized = false; + public bool IsInitialized => _isInitialized; + + public event Action? OnError; + + #region Unity Lifecycle + + private static ChatSystemManager? _instance; + public static ChatSystemManager Instance { + get { + if (_instance == null) { + _instance = FindAnyObjectByType(); + } + return _instance; + } + } + + private void Awake() + { + if (_instance != null && _instance != this) { + Destroy(gameObject); + return; + } + _instance = this; + } + + private void Start() + { + StartCoroutine(InitializeWhenReady()); + } + + private IEnumerator InitializeWhenReady() + { + float timeout = 5f; + float startTime = Time.realtimeSinceStartup; + + while (_chatBubblePanel == null && (Time.realtimeSinceStartup - startTime) < timeout) { + yield return new WaitForSecondsRealtime(0.1f); + } + + if (_chatBubblePanel == null) { + Debug.LogError("[ChatSystemManager] ChatBubblePanel ์ดˆ๊ธฐํ™” ํƒ€์ž„์•„์›ƒ"); + yield break; + } + + Initialize(); + } + public void Initialize() + { + if (_isInitialized) return; + + try { + _webSocketManager = WebSocketManager.Instance; + _audioManager = AudioManager.Instance; + _messageQueue = new ChatMessageQueue(); + _chatApiService = ApiServiceManager.Instance.Chat; + + if (_webSocketManager != null) { + _webSocketManager.OnChatMessageReceived += ProcessCharacterMessage; + } + + _isInitialized = true; + Debug.Log("[ChatSystemManager] ์ดˆ๊ธฐํ™” ์™„๋ฃŒ"); + } + catch (Exception ex) { + Debug.LogError($"[ChatSystemManager] ์ดˆ๊ธฐํ™” ์‹คํŒจ: {ex.Message}"); + } + } + + private void OnDestroy() + { + if (_webSocketManager != null) { + _webSocketManager.OnChatMessageReceived -= ProcessCharacterMessage; + } + } + + #endregion + + #region Public Methods + + /// + /// ์œ ์ € ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค + /// + /// User ๋ฉ”์‹œ์ง€ + public async void SendUserMessage(string message) + { + if (!ValidateUserInput(message)) { return; } + + try { + if (_chatApiService != null) { + var response = await _chatApiService.SendChatAsync( + message: message, + characterId: _characterId, + userId: _userId + ); + if (response == null) { + Debug.LogWarning("[ChatSystemManager] ์ฑ„ํŒ… ์‘๋‹ต์ด null์ž…๋‹ˆ๋‹ค."); + } + } + if (_chatBubblePanel != null) { + _chatBubblePanel.CreateBubble(Actor.User, message); + } + } + catch (Exception ex) { + // todo : ๋ฉ”์‹œ์ง€ ์‹คํŒจ ์ฒ˜๋ฆฌ + Debug.LogError($"[ChatSystemManager] ๋ฉ”์‹œ์ง€ ์ „์†ก ์‹คํŒจ: {ex.Message}"); + } + } + + /// + /// ์บ๋ฆญํ„ฐ ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค + /// + /// ์บ๋ฆญํ„ฐ ๋ฉ”์‹œ์ง€ + public void ProcessCharacterMessage(ChatMessage chatMessage) + { + if (chatMessage == null) { + Debug.LogWarning("[ChatSystemManager] ๋นˆ ์ฑ„ํŒ… ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค."); + return; + } + + if (_messageQueue == null) { + Debug.LogError("[ChatSystemManager] ๋ฉ”์‹œ์ง€ ํ๊ฐ€ ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."); + return; + } + + if (_messageQueue.Enqueue(chatMessage)) { + _messageQueue.ProcessQueueAsync(ProcessMessageAsync).Forget(); + } + } + + #endregion + + #region Private Methods + + /// + /// ๋ฉ”์‹œ์ง€๋ฅผ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค + /// + /// + /// + private async UniTask ProcessMessageAsync(ChatMessage chatMessage) + { + try { + + if (_chatBubblePanel != null && !string.IsNullOrEmpty(chatMessage.Text)) { + _chatBubblePanel.CreateBubble(Actor.Character, chatMessage.Text); + } + + // TODO : ์บ๋ฆญํ„ฐ ๋ฐ˜์‘์„ ์ „๋‹ฌํ•œ๋‹ค. + + if (chatMessage.VoiceData != null && _audioManager != null) { + await _audioManager.PlayVoiceAsync(chatMessage.VoiceData); + } + } + catch (Exception ex) { + Debug.LogError($"[ChatSystemManager] ์บ๋ฆญํ„ฐ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ ์‹คํŒจ: {ex.Message}"); + OnError?.Invoke($"๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ ์‹คํŒจ: {ex.Message}"); + } + } + + private bool ValidateUserInput(string message) + { + if (string.IsNullOrWhiteSpace(message)) { + Debug.LogWarning("[ChatSystemManager] ๋นˆ ๋ฉ”์‹œ์ง€๋Š” ์ „์†กํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + return false; + } + if (message.Length > 1000) { + Debug.LogWarning("[ChatSystemManager] ๋ฉ”์‹œ์ง€๊ฐ€ ๋„ˆ๋ฌด ๊น๋‹ˆ๋‹ค. (์ตœ๋Œ€ 1000์ž)"); + return false; + } + return true; + } + + #endregion + } +} diff --git a/Assets/Domain/Chat/Service/ChatManager.cs.meta b/Assets/Domain/Chat/Service/ChatSystemManager.cs.meta similarity index 100% rename from Assets/Domain/Chat/Service/ChatManager.cs.meta rename to Assets/Domain/Chat/Service/ChatSystemManager.cs.meta diff --git a/Assets/Domain/Chat/View/TextInputView.cs b/Assets/Domain/Chat/View/TextInputView.cs index 142ce66..781fe17 100644 --- a/Assets/Domain/Chat/View/TextInputView.cs +++ b/Assets/Domain/Chat/View/TextInputView.cs @@ -13,7 +13,7 @@ public class TextInputView : MonoBehaviour [SerializeField] private TMP_InputField? _inputField; [SerializeField] private Button? _btnSend; - private ChatManager? _chatManager; + private ChatSystemManager? _chatManager; private bool _isProcessingSubmit = false; public event Action? OnTextMessageSent; @@ -37,7 +37,7 @@ private void Initialize() SetupChatManager(); } - public void SetChatManager(ChatManager chatManager) + public void SetChatManager(ChatSystemManager chatManager) { _chatManager = chatManager; } @@ -77,7 +77,7 @@ private void SetupChatManager() { if (_chatManager == null) { - _chatManager = FindAnyObjectByType(); + _chatManager = FindAnyObjectByType(); if (_chatManager == null) { Debug.LogWarning("[TextInputView] ChatManager๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ˆ˜๋™์œผ๋กœ SetChatManager๋ฅผ ํ˜ธ์ถœํ•ด์ฃผ์„ธ์š”."); diff --git a/Assets/Domain/Chat/View/VoiceInputView.cs b/Assets/Domain/Chat/View/VoiceInputView.cs index 1bbe81b..c5713ce 100644 --- a/Assets/Domain/Chat/View/VoiceInputView.cs +++ b/Assets/Domain/Chat/View/VoiceInputView.cs @@ -19,7 +19,7 @@ public class VoiceInputView : MonoBehaviour [SerializeField] private Button? _btnVoiceStop; - private ChatManager? _chatManager; + private ChatSystemManager? _chatManager; private AudioRecorder? _audioRecorder; private STTService? _sttService; private bool _isRecording = false; @@ -72,7 +72,7 @@ private void Initialize() SetupChatManager(); } - public void SetChatManager(ChatManager chatManager) + public void SetChatManager(ChatSystemManager chatManager) { _chatManager = chatManager; } @@ -239,7 +239,7 @@ private void SetupChatManager() { if (_chatManager == null) { - _chatManager = FindAnyObjectByType(); + _chatManager = FindAnyObjectByType(); if (_chatManager == null) { Debug.LogWarning("[VoiceInputView] ChatManager๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ˆ˜๋™์œผ๋กœ SetChatManager๋ฅผ ํ˜ธ์ถœํ•ด์ฃผ์„ธ์š”."); diff --git a/Assets/Samples.meta b/Assets/Samples.meta deleted file mode 100644 index 1790a84..0000000 --- a/Assets/Samples.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 7e93090ffb9d8aa47ab8da6804c4f7ca -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Samples/Core.meta b/Assets/Samples/Core.meta deleted file mode 100644 index 9210e88..0000000 --- a/Assets/Samples/Core.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 25ed4b2e8a2f9fc48af8e926e17a17c0 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Samples/Core/Managers.meta b/Assets/Samples/Core/Managers.meta deleted file mode 100644 index ce6405e..0000000 --- a/Assets/Samples/Core/Managers.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: bd11cb6c95a02a94faa52c02ded0b378 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Samples/Core/Managers/SampleSystemManager.cs b/Assets/Samples/Core/Managers/SampleSystemManager.cs deleted file mode 100644 index 3a49e09..0000000 --- a/Assets/Samples/Core/Managers/SampleSystemManager.cs +++ /dev/null @@ -1,71 +0,0 @@ -using Live2D.Cubism.Framework.LookAt; -using Live2D.Cubism.Framework.MouthMovement; -using UnityEngine; -using UnityEngine.UI; -using ProjectVG.Core.Audio; - -public class SampleSystemManager : Singleton -{ - [SerializeField] private CubismLookTarget cubismLookTarget = null; - [SerializeField] private Camera mCamera = null; - [SerializeField] private ModelConfig initModelConfig = null; - - - - private GameObject _currentModel = null; - - // TODO : test ์ถ”ํ›„ ์‚ญ์ œ - [SerializeField] private AudioSource voiceSource = null; - [SerializeField] private Button expressionChangeBtn = null; - - private void Start() - { - ScreenTapManager.Instance.Initialize(mCamera); - AudioManager.Instance.Initialize(); - - ModelInit(initModelConfig); - } - - private void ModelInit(ModelConfig modelConfig) - { - if (_currentModel != null) - { - Destroy(_currentModel); - } - - _currentModel = Instantiate(modelConfig.ModelPrefab); - - // LockAt ์„ค์ • - SetLockAt(modelConfig); - - // LipSync ์„ค์ • - SetLipSync(modelConfig); - - // raycast ์„ค์ • - SetRayCast(modelConfig); - } - - private void SetLockAt(ModelConfig modelConfig) - { - var lookController = _currentModel.GetComponent(); - lookController.Target = cubismLookTarget.gameObject; - lookController.Damping = modelConfig.LockAtDamping; - - cubismLookTarget.Initialize(modelConfig); - } - - private void SetLipSync(ModelConfig modelConfig) - { - var mouthController = _currentModel.GetComponent(); - mouthController.AudioInput = voiceSource; - mouthController.Gain = modelConfig.Gain; - mouthController.Smoothing = modelConfig.Smoothing; - } - - private void SetRayCast(ModelConfig modelConfig) - { - var hitHandler = _currentModel.GetComponent(); - hitHandler.Initialize(); - expressionChangeBtn.onClick.AddListener(hitHandler.ExpressionChange_Btn); - } -} diff --git a/Assets/Samples/Core/Managers/SampleSystemManager.cs.meta b/Assets/Samples/Core/Managers/SampleSystemManager.cs.meta deleted file mode 100644 index e960c9c..0000000 --- a/Assets/Samples/Core/Managers/SampleSystemManager.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 9224e8bfc209a8b488ae41e6b188b048 \ No newline at end of file diff --git a/Assets/Voice.mixer b/Assets/Voice.mixer deleted file mode 100644 index 84960c3..0000000 --- a/Assets/Voice.mixer +++ /dev/null @@ -1,69 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!241 &24100000 -AudioMixerController: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_Name: Voice - m_OutputGroup: {fileID: 0} - m_MasterGroup: {fileID: 24300002} - m_Snapshots: - - {fileID: 24500006} - m_StartSnapshot: {fileID: 24500006} - m_SuspendThreshold: -80 - m_EnableSuspend: 1 - m_UpdateMode: 0 - m_ExposedParameters: [] - m_AudioMixerGroupViews: - - guids: - - 0f47c3379c5b66e458d9740a30fc267a - name: View - m_CurrentViewIndex: 0 - m_TargetSnapshot: {fileID: 24500006} ---- !u!243 &24300002 -AudioMixerGroupController: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_Name: Master - m_AudioMixer: {fileID: 24100000} - m_GroupID: 0f47c3379c5b66e458d9740a30fc267a - m_Children: [] - m_Volume: 583a709ad5a7180498f0cda7cd1091b2 - m_Pitch: 7ea55f842e0513b43902be8341087012 - m_Send: 00000000000000000000000000000000 - m_Effects: - - {fileID: 24400004} - m_UserColorIndex: 0 - m_Mute: 0 - m_Solo: 0 - m_BypassEffects: 0 ---- !u!244 &24400004 -AudioMixerEffectController: - m_ObjectHideFlags: 3 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_Name: - m_EffectID: 9f0119b8c97725546ac7b269861f6c67 - m_EffectName: Attenuation - m_MixLevel: b67c869ef3e747e4fad8fd66b2f91bf9 - m_Parameters: [] - m_SendTarget: {fileID: 0} - m_EnableWetMix: 0 - m_Bypass: 0 ---- !u!245 &24500006 -AudioMixerSnapshotController: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_Name: Snapshot - m_AudioMixer: {fileID: 24100000} - m_SnapshotID: 028d43bb053f091429fb3985cb191113 - m_FloatValues: - 583a709ad5a7180498f0cda7cd1091b2: 2.850226 - m_TransitionOverrides: {} diff --git a/Assets/Voice.mixer.meta b/Assets/Voice.mixer.meta deleted file mode 100644 index 3f7bc74..0000000 --- a/Assets/Voice.mixer.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 3bf46f7daa3260847aa1b964ad2b01a7 -NativeFormatImporter: - externalObjects: {} - mainObjectFileID: 24100000 - userData: - assetBundleName: - assetBundleVariant: From 322890ff69197ad418d6d8d3d9c847a3563e847c Mon Sep 17 00:00:00 2001 From: WooSH Date: Thu, 21 Aug 2025 18:12:27 +0900 Subject: [PATCH 10/13] =?UTF-8?q?feat:=20response=20=EA=B0=9D=EC=B2=B4?= =?UTF-8?q?=EC=97=90=20=EC=B6=94=EA=B0=80=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Script/Service/ICharacterActionService.cs | 6 ---- Assets/Domain/Chat/Model/Actor.cs | 2 +- .../Domain/Chat/Model/CharacterActionData.cs | 28 +++++++++++++++++ .../Chat/Model/CharacterActionData.cs.meta | 2 ++ Assets/Domain/Chat/Model/ChatMessage.cs | 19 +++++++++--- Assets/Domain/Chat/Model/CostInfo.cs | 31 +++++++++++++++++++ Assets/Domain/Chat/Model/CostInfo.cs.meta | 2 ++ Assets/Domain/Chat/Model/VoiceData.cs | 4 --- .../Domain/Chat/Service/ChatSystemManager.cs | 30 ++++++++++++++++-- .../Network/DTOs/Chat/ChatResponse.cs | 16 +++++++--- 10 files changed, 116 insertions(+), 24 deletions(-) create mode 100644 Assets/Domain/Chat/Model/CharacterActionData.cs create mode 100644 Assets/Domain/Chat/Model/CharacterActionData.cs.meta create mode 100644 Assets/Domain/Chat/Model/CostInfo.cs create mode 100644 Assets/Domain/Chat/Model/CostInfo.cs.meta diff --git a/Assets/Domain/Character/Script/Service/ICharacterActionService.cs b/Assets/Domain/Character/Script/Service/ICharacterActionService.cs index 42fe9c5..8c3f6a6 100644 --- a/Assets/Domain/Character/Script/Service/ICharacterActionService.cs +++ b/Assets/Domain/Character/Script/Service/ICharacterActionService.cs @@ -30,12 +30,6 @@ public interface ICharacterActionService /// void StopAll(); - /// - /// ์Œ์„ฑ ๊ฒŒ์ดํŠธ๋ฅผ ์„ค์ •ํ•œ๋‹ค. - /// - /// ์Œ์„ฑ์ด ์žฌ์ƒ ์ค‘์ธ์ง€ ์—ฌ๋ถ€ - void SetVoiceGate(bool isVoicePlaying); - /// /// ํ˜„์žฌ ์•ก์…˜ ์ƒํƒœ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. /// diff --git a/Assets/Domain/Chat/Model/Actor.cs b/Assets/Domain/Chat/Model/Actor.cs index a3a0f48..64305f8 100644 --- a/Assets/Domain/Chat/Model/Actor.cs +++ b/Assets/Domain/Chat/Model/Actor.cs @@ -5,4 +5,4 @@ public enum Actor User, Character } -} \ No newline at end of file +} diff --git a/Assets/Domain/Chat/Model/CharacterActionData.cs b/Assets/Domain/Chat/Model/CharacterActionData.cs new file mode 100644 index 0000000..df3f487 --- /dev/null +++ b/Assets/Domain/Chat/Model/CharacterActionData.cs @@ -0,0 +1,28 @@ +#nullable enable +using UnityEngine; + +namespace ProjectVG.Domain.Chat.Model +{ + public class CharacterActionData + { + /// + /// ์บ๋ฆญํ„ฐ๊ฐ€ ์ˆ˜ํ–‰ํ•  ํ–‰๋™ + /// + public string Action { get; set; } = string.Empty; + + /// + /// ํ–‰๋™ ๋ฌธ์ž์—ด๋กœ ์ดˆ๊ธฐํ™” + /// + /// ํ–‰๋™ ๋ฌธ์ž์—ด + public CharacterActionData(string? action = null) + { + Action = action ?? "talk"; + } + + /// + /// ํ–‰๋™์ด ์„ค์ •๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธ + /// + /// ํ–‰๋™์ด ์„ค์ •๋˜์–ด ์žˆ์œผ๋ฉด true + public bool HasAction() => !string.IsNullOrEmpty(Action); + } +} \ No newline at end of file diff --git a/Assets/Domain/Chat/Model/CharacterActionData.cs.meta b/Assets/Domain/Chat/Model/CharacterActionData.cs.meta new file mode 100644 index 0000000..2981f77 --- /dev/null +++ b/Assets/Domain/Chat/Model/CharacterActionData.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 333379fbd87c0304db4ae39689691795 \ No newline at end of file diff --git a/Assets/Domain/Chat/Model/ChatMessage.cs b/Assets/Domain/Chat/Model/ChatMessage.cs index 136d278..e70e31e 100644 --- a/Assets/Domain/Chat/Model/ChatMessage.cs +++ b/Assets/Domain/Chat/Model/ChatMessage.cs @@ -1,6 +1,5 @@ #nullable enable using System; -using System.Collections.Generic; using UnityEngine; using ProjectVG.Infrastructure.Network.DTOs.Chat; @@ -12,17 +11,17 @@ public class ChatMessage public string SessionId { get; set; } = string.Empty; public string? Text { get; set; } public VoiceData? VoiceData { get; set; } + public CharacterActionData? ActionData { get; set; } + public CostInfo? CostInfo { get; set; } public DateTime Timestamp { get; set; } = DateTime.UtcNow; - public Dictionary? Metadata { get; set; } public static ChatMessage FromChatResponse(ChatResponse response) { - var chatMessage = new ChatMessage - { + var chatMessage = new ChatMessage { SessionId = response.SessionId, Text = response.Text, Timestamp = response.Timestamp, - Metadata = response.Metadata + ActionData = new CharacterActionData(response.Action) }; if (!string.IsNullOrEmpty(response.AudioData)) @@ -30,13 +29,23 @@ public static ChatMessage FromChatResponse(ChatResponse response) chatMessage.VoiceData = VoiceData.FromBase64(response.AudioData, response.AudioFormat); } + if ((response.UsedCost ?? 0) > 0 || (response.RemainingCost ?? 0) > 0) + { + chatMessage.CostInfo = new CostInfo(response.UsedCost ?? 0f, response.RemainingCost ?? 0f); + } + return chatMessage; } public bool HasVoiceData() => VoiceData != null && VoiceData.IsPlayable(); public bool HasTextData() => !string.IsNullOrEmpty(Text); + + public bool HasActionData() => ActionData != null && ActionData.HasAction(); + + public bool HasCostInfo() => CostInfo != null && CostInfo.HasCostInfo(); public AudioClip? GetAudioClip() => VoiceData?.AudioClip; + } } \ No newline at end of file diff --git a/Assets/Domain/Chat/Model/CostInfo.cs b/Assets/Domain/Chat/Model/CostInfo.cs new file mode 100644 index 0000000..65811c1 --- /dev/null +++ b/Assets/Domain/Chat/Model/CostInfo.cs @@ -0,0 +1,31 @@ +#nullable enable +using System; + +namespace ProjectVG.Domain.Chat.Model +{ + + [Serializable] + public class CostInfo + { + + public float UsedCost { get; set; } = 0f; + + public float RemainingCost { get; set; } = 0f; + + public CostInfo() { } + + public CostInfo(float usedCost, float remainingCost) + { + UsedCost = usedCost; + RemainingCost = remainingCost; + } + + public bool HasCostInfo() => UsedCost > 0 || RemainingCost > 0; + + + public override string ToString() + { + return $"Used: {UsedCost} Token, Remaining: {RemainingCost} Token"; + } + } +} diff --git a/Assets/Domain/Chat/Model/CostInfo.cs.meta b/Assets/Domain/Chat/Model/CostInfo.cs.meta new file mode 100644 index 0000000..2e9316f --- /dev/null +++ b/Assets/Domain/Chat/Model/CostInfo.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: e8dc30eb8caa6a341bced27e0eb9e218 \ No newline at end of file diff --git a/Assets/Domain/Chat/Model/VoiceData.cs b/Assets/Domain/Chat/Model/VoiceData.cs index 9e8b434..98a41a1 100644 --- a/Assets/Domain/Chat/Model/VoiceData.cs +++ b/Assets/Domain/Chat/Model/VoiceData.cs @@ -17,10 +17,6 @@ public VoiceData(AudioClip audioClip, float length, string format = "wav") Format = format; } - public VoiceData() - { - } - public static VoiceData FromBase64(string base64Data, string format = "wav") { if (string.IsNullOrEmpty(base64Data)) diff --git a/Assets/Domain/Chat/Service/ChatSystemManager.cs b/Assets/Domain/Chat/Service/ChatSystemManager.cs index 31841a6..e4a6f4d 100644 --- a/Assets/Domain/Chat/Service/ChatSystemManager.cs +++ b/Assets/Domain/Chat/Service/ChatSystemManager.cs @@ -36,6 +36,7 @@ public class ChatSystemManager : MonoBehaviour public bool IsInitialized => _isInitialized; public event Action? OnError; + public event Action? OnConversationEnd; #region Unity Lifecycle @@ -174,16 +175,21 @@ public void ProcessCharacterMessage(ChatMessage chatMessage) private async UniTask ProcessMessageAsync(ChatMessage chatMessage) { try { - if (_chatBubblePanel != null && !string.IsNullOrEmpty(chatMessage.Text)) { _chatBubblePanel.CreateBubble(Actor.Character, chatMessage.Text); } - // TODO : ์บ๋ฆญํ„ฐ ๋ฐ˜์‘์„ ์ „๋‹ฌํ•œ๋‹ค. + // TODO : ์บ๋ฆญํ„ฐ ๋ฐ˜์‘์„ ์ „๋‹ฌํ•œ๋‹ค + if (chatMessage.VoiceData != null && _audioManager != null) { - await _audioManager.PlayVoiceAsync(chatMessage.VoiceData); + _audioManager.PlayVoiceAsync(chatMessage.VoiceData).Forget(); } + + float waitTime = CalculateConversationWaitTime(chatMessage); + await UniTask.Delay((int)(waitTime * 1000)); + + OnConversationEnd?.Invoke(); } catch (Exception ex) { Debug.LogError($"[ChatSystemManager] ์บ๋ฆญํ„ฐ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ ์‹คํŒจ: {ex.Message}"); @@ -191,6 +197,24 @@ private async UniTask ProcessMessageAsync(ChatMessage chatMessage) } } + /// + /// ๋Œ€ํ™” ๋Œ€๊ธฐ ์‹œ๊ฐ„์„ ๊ณ„์‚ฐํ•œ๋‹ค + /// + private float CalculateConversationWaitTime(ChatMessage chatMessage) + { + float baseTime = 0f; + + if (chatMessage.VoiceData != null && chatMessage.VoiceData.IsPlayable()) { + baseTime = chatMessage.VoiceData.Length; + } + + if (baseTime <= 0f) { + baseTime = 2f; + } + + return baseTime + 0.5f; + } + private bool ValidateUserInput(string message) { if (string.IsNullOrWhiteSpace(message)) { diff --git a/Assets/Infrastructure/Network/DTOs/Chat/ChatResponse.cs b/Assets/Infrastructure/Network/DTOs/Chat/ChatResponse.cs index d8ddb32..88a1421 100644 --- a/Assets/Infrastructure/Network/DTOs/Chat/ChatResponse.cs +++ b/Assets/Infrastructure/Network/DTOs/Chat/ChatResponse.cs @@ -19,7 +19,10 @@ public class ChatResponse [JsonProperty("text")] public string? Text { get; set; } - + + [JsonProperty("action")] + public string? Action { get; set; } + [JsonProperty("audio_data")] public string? AudioData { get; set; } @@ -28,11 +31,14 @@ public class ChatResponse [JsonProperty("audio_length")] public float? AudioLength { get; set; } - + + [JsonProperty("used_cost")] + public float? UsedCost { get; set; } + + [JsonProperty("remaining_cost")] + public float? RemainingCost { get; set; } + [JsonProperty("timestamp")] public DateTime Timestamp { get; set; } = DateTime.UtcNow; - - [JsonProperty("metadata")] - public Dictionary? Metadata { get; set; } } } \ No newline at end of file From 66170247506c9666e2b912c17e1ff8bca617add7 Mon Sep 17 00:00:00 2001 From: WooSH Date: Thu, 21 Aug 2025 18:49:24 +0900 Subject: [PATCH 11/13] =?UTF-8?q?refactory:=20=EB=AA=A8=EB=8D=B8=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=20=EB=B0=8F=20=EB=8F=99=EC=9E=91=20=EC=97=AD?= =?UTF-8?q?=ED=99=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ResolutionManager.cs | 0 .../ResolutionManager.cs.meta | 0 .../Script/Controller/CharacterActionDTOs.cs | 233 ---------- .../Controller/CharacterActionDTOs.cs.meta | 2 - .../Character/Script/Controller/DTOs.cs | 10 - .../Character/Script/Controller/DTOs.cs.meta | 2 - .../Script/Facade/CharacterManager.cs | 198 --------- .../Script/Facade/ICharacterManager.cs | 83 ---- .../Script/Facade/ICharacterManager.cs.meta | 2 - .../Script/Manager/CharacterManager.cs | 168 ++++++++ .../CharacterManager.cs.meta | 0 .../Script/Manager/CharacterModelLoader.cs | 185 ++++++++ .../Manager/CharacterModelLoader.cs.meta | 2 + .../Script/Manager/CharacterModelManager.cs | 404 ------------------ .../Manager/CharacterModelManager.cs.meta | 2 - .../Script/Manager/ICharacterModelManager.cs | 70 --- .../Manager/ICharacterModelManager.cs.meta | 2 - .../Service/CharacterActionController.cs | 77 ++++ .../Service/CharacterActionController.cs.meta | 2 + .../Service/ICharacterActionResolver.cs | 15 - .../Service/ICharacterActionResolver.cs.meta | 2 - .../Script/Service/ICharacterActionService.cs | 39 -- .../Service/ICharacterActionService.cs.meta | 2 - .../Domain/Chat/Service/ChatSystemManager.cs | 6 +- 24 files changed, 439 insertions(+), 1067 deletions(-) rename Assets/Domain/Character/Script/{Manager => Component}/ResolutionManager.cs (100%) rename Assets/Domain/Character/Script/{Manager => Component}/ResolutionManager.cs.meta (100%) delete mode 100644 Assets/Domain/Character/Script/Controller/CharacterActionDTOs.cs delete mode 100644 Assets/Domain/Character/Script/Controller/CharacterActionDTOs.cs.meta delete mode 100644 Assets/Domain/Character/Script/Controller/DTOs.cs delete mode 100644 Assets/Domain/Character/Script/Controller/DTOs.cs.meta delete mode 100644 Assets/Domain/Character/Script/Facade/CharacterManager.cs delete mode 100644 Assets/Domain/Character/Script/Facade/ICharacterManager.cs delete mode 100644 Assets/Domain/Character/Script/Facade/ICharacterManager.cs.meta create mode 100644 Assets/Domain/Character/Script/Manager/CharacterManager.cs rename Assets/Domain/Character/Script/{Facade => Manager}/CharacterManager.cs.meta (100%) create mode 100644 Assets/Domain/Character/Script/Manager/CharacterModelLoader.cs create mode 100644 Assets/Domain/Character/Script/Manager/CharacterModelLoader.cs.meta delete mode 100644 Assets/Domain/Character/Script/Manager/CharacterModelManager.cs delete mode 100644 Assets/Domain/Character/Script/Manager/CharacterModelManager.cs.meta delete mode 100644 Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs delete mode 100644 Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs.meta create mode 100644 Assets/Domain/Character/Script/Service/CharacterActionController.cs create mode 100644 Assets/Domain/Character/Script/Service/CharacterActionController.cs.meta delete mode 100644 Assets/Domain/Character/Script/Service/ICharacterActionResolver.cs delete mode 100644 Assets/Domain/Character/Script/Service/ICharacterActionResolver.cs.meta delete mode 100644 Assets/Domain/Character/Script/Service/ICharacterActionService.cs delete mode 100644 Assets/Domain/Character/Script/Service/ICharacterActionService.cs.meta diff --git a/Assets/Domain/Character/Script/Manager/ResolutionManager.cs b/Assets/Domain/Character/Script/Component/ResolutionManager.cs similarity index 100% rename from Assets/Domain/Character/Script/Manager/ResolutionManager.cs rename to Assets/Domain/Character/Script/Component/ResolutionManager.cs diff --git a/Assets/Domain/Character/Script/Manager/ResolutionManager.cs.meta b/Assets/Domain/Character/Script/Component/ResolutionManager.cs.meta similarity index 100% rename from Assets/Domain/Character/Script/Manager/ResolutionManager.cs.meta rename to Assets/Domain/Character/Script/Component/ResolutionManager.cs.meta diff --git a/Assets/Domain/Character/Script/Controller/CharacterActionDTOs.cs b/Assets/Domain/Character/Script/Controller/CharacterActionDTOs.cs deleted file mode 100644 index c690b85..0000000 --- a/Assets/Domain/Character/Script/Controller/CharacterActionDTOs.cs +++ /dev/null @@ -1,233 +0,0 @@ -using System; - -namespace ProjectVG.Domain.Character.Service -{ - /// - /// ์บ๋ฆญํ„ฐ ์•ก์…˜ ์š”์ฒญ์„ ๋‚˜ํƒ€๋‚ด๋Š” DTO - /// - [Serializable] - public struct CharacterActionRequest - { - /// - /// ๋Œ€์ƒ ์บ๋ฆญํ„ฐ ID - /// - public string CharacterId; - - /// - /// ์•ก์…˜ ํ‚ค (์˜ˆ: "happy", "wave", "nod") - /// - public string ActionKey; - - /// - /// ์•ก์…˜ ๊ฐ•๋„ (0.0 ~ 1.0) - /// - public float Intensity; - - /// - /// ์•ก์…˜ ์ง€์† ์‹œ๊ฐ„ (๋ฐ€๋ฆฌ์ดˆ, null์ด๋ฉด ๊ธฐ๋ณธ๊ฐ’ ์‚ฌ์šฉ) - /// - public int? DurationMs; - - /// - /// ์•ก์…˜ ์šฐ์„ ์ˆœ์œ„ - /// - public CharacterActionPriority Priority; - - /// - /// ์•ก์…˜ ์ทจ์†Œ ์ •์ฑ… - /// - public CharacterCancelPolicy CancelPolicy; - - public CharacterActionRequest(string characterId, string actionKey, float intensity = 1.0f, int? durationMs = null, CharacterActionPriority priority = CharacterActionPriority.Normal, CharacterCancelPolicy cancelPolicy = CharacterCancelPolicy.Replace) - { - CharacterId = characterId; - ActionKey = actionKey; - Intensity = intensity; - DurationMs = durationMs; - Priority = priority; - CancelPolicy = cancelPolicy; - } - } - - /// - /// ์บ๋ฆญํ„ฐ ์•ก์…˜ ํ•ธ๋“ค (์•ก์…˜ ์‹คํ–‰ ์ถ”์ ์šฉ) - /// - [Serializable] - public struct CharacterActionHandle - { - /// - /// ์•ก์…˜ ๊ณ ์œ  ID - /// - public string ActionId; - - /// - /// ์•ก์…˜ ์ƒํƒœ - /// - public CharacterActionStatus Status; - - public CharacterActionHandle(string actionId, CharacterActionStatus status = CharacterActionStatus.Pending) - { - ActionId = actionId; - Status = status; - } - } - - /// - /// ์บ๋ฆญํ„ฐ ์•ก์…˜ ์ƒํƒœ ์ •๋ณด - /// - [Serializable] - public struct CharacterActionState - { - /// - /// ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ ์•ก์…˜ ID - /// - public string CurrentActionId; - - /// - /// ํ˜„์žฌ ์‹คํ–‰ ์ค‘์ธ ์•ก์…˜ ํ‚ค - /// - public string CurrentActionKey; - - /// - /// ์•ก์…˜์ด ์žฌ์ƒ ์ค‘์ธ์ง€ ์—ฌ๋ถ€ - /// - public bool IsPlaying; - - /// - /// ์Œ์„ฑ ๊ฒŒ์ดํŠธ๊ฐ€ ํ™œ์„ฑํ™”๋˜์–ด ์žˆ๋Š”์ง€ ์—ฌ๋ถ€ - /// - public bool IsVoiceGated; - - public CharacterActionState(string currentActionId = null, string currentActionKey = null, bool isPlaying = false, bool isVoiceGated = false) - { - CurrentActionId = currentActionId; - CurrentActionKey = currentActionKey; - IsPlaying = isPlaying; - IsVoiceGated = isVoiceGated; - } - } - - /// - /// ํ•ด์„๋œ ์บ๋ฆญํ„ฐ ์•ก์…˜ ์ •๋ณด - /// - [Serializable] - public struct CharacterResolvedAction - { - /// - /// ํ‘œํ˜„์‹ ํ‚ค - /// - public string ExpressionKey; - - /// - /// ๋ชจ์…˜ ํ‚ค - /// - public string MotionKey; - - /// - /// ์ง€์† ์‹œ๊ฐ„ (๋ฐ€๋ฆฌ์ดˆ) - /// - public int? DurationMs; - - /// - /// ๋ธ”๋ Œ๋“œ ์ธ ์‹œ๊ฐ„ (์ดˆ) - /// - public float BlendInSec; - - /// - /// ๋ธ”๋ Œ๋“œ ์•„์›ƒ ์‹œ๊ฐ„ (์ดˆ) - /// - public float BlendOutSec; - - public CharacterResolvedAction(string expressionKey = null, string motionKey = null, int? durationMs = null, float blendInSec = 0.3f, float blendOutSec = 0.3f) - { - ExpressionKey = expressionKey; - MotionKey = motionKey; - DurationMs = durationMs; - BlendInSec = blendInSec; - BlendOutSec = blendOutSec; - } - } - - /// - /// ์บ๋ฆญํ„ฐ ์•ก์…˜ ์šฐ์„ ์ˆœ์œ„ - /// - public enum CharacterActionPriority - { - /// - /// ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„ - /// - Low = 0, - - /// - /// ์ผ๋ฐ˜ ์šฐ์„ ์ˆœ์œ„ - /// - Normal = 1, - - /// - /// ๋†’์€ ์šฐ์„ ์ˆœ์œ„ - /// - High = 2, - - /// - /// ์ตœ๊ณ  ์šฐ์„ ์ˆœ์œ„ (์Œ์„ฑ ๋“ฑ) - /// - Critical = 3 - } - - /// - /// ์บ๋ฆญํ„ฐ ์•ก์…˜ ์ทจ์†Œ ์ •์ฑ… - /// - public enum CharacterCancelPolicy - { - /// - /// ๊ธฐ์กด ์•ก์…˜์„ ๋Œ€์ฒด - /// - Replace = 0, - - /// - /// ๊ธฐ์กด ์•ก์…˜์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ - /// - Wait = 1, - - /// - /// ๊ธฐ์กด ์•ก์…˜์„ ์ค‘๋‹จํ•˜๊ณ  ์ฆ‰์‹œ ์‹คํ–‰ - /// - Interrupt = 2, - - /// - /// ๊ธฐ์กด ์•ก์…˜์ด ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋‚ฎ์„ ๋•Œ๋งŒ ๋Œ€์ฒด - /// - ReplaceIfLowerPriority = 3 - } - - /// - /// ์บ๋ฆญํ„ฐ ์•ก์…˜ ์ƒํƒœ - /// - public enum CharacterActionStatus - { - /// - /// ๋Œ€๊ธฐ ์ค‘ - /// - Pending = 0, - - /// - /// ์‹คํ–‰ ์ค‘ - /// - Playing = 1, - - /// - /// ์™„๋ฃŒ๋จ - /// - Completed = 2, - - /// - /// ์ทจ์†Œ๋จ - /// - Cancelled = 3, - - /// - /// ์˜ค๋ฅ˜ ๋ฐœ์ƒ - /// - Error = 4 - } -} diff --git a/Assets/Domain/Character/Script/Controller/CharacterActionDTOs.cs.meta b/Assets/Domain/Character/Script/Controller/CharacterActionDTOs.cs.meta deleted file mode 100644 index 52f628e..0000000 --- a/Assets/Domain/Character/Script/Controller/CharacterActionDTOs.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 94f7891b2539a7446908fcfdddadbe11 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Controller/DTOs.cs b/Assets/Domain/Character/Script/Controller/DTOs.cs deleted file mode 100644 index 4816e2d..0000000 --- a/Assets/Domain/Character/Script/Controller/DTOs.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace ProjectVG.Domain.Character.Service -{ - /// - /// ๊ฐ์ •/ํ–‰๋™ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ์„ ์œ„ํ•œ ๋‹จ์ˆœ DTO. - /// - public struct EmotionData { public string Emotion; public float Intensity; public int DurationMs; } - public struct ActionData { public string Action; public object Args; } -} - - diff --git a/Assets/Domain/Character/Script/Controller/DTOs.cs.meta b/Assets/Domain/Character/Script/Controller/DTOs.cs.meta deleted file mode 100644 index 266eeca..0000000 --- a/Assets/Domain/Character/Script/Controller/DTOs.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 91104183254896440a378f856ed66e43 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Facade/CharacterManager.cs b/Assets/Domain/Character/Script/Facade/CharacterManager.cs deleted file mode 100644 index 621f420..0000000 --- a/Assets/Domain/Character/Script/Facade/CharacterManager.cs +++ /dev/null @@ -1,198 +0,0 @@ -using ProjectVG.Domain.Character.Live2D.Model; -using UnityEngine; -using UnityEngine.TextCore.Text; -using ProjectVG.Domain.Character.Component; -using ProjectVG.Core.Audio; - -namespace ProjectVG.Domain.Character.Service -{ - /// - /// ์บ๋ฆญํ„ฐ ์ œ์–ด๋ฅผ ๋‹จ์ผ ์ง„์ž…์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ํŒŒ์‚ฌ๋“œ ๊ตฌํ˜„์ฒด. - /// - public class CharacterManager : MonoBehaviour, ICharacterManager - { - [SerializeField] private Transform _modelTransform; - [SerializeField] private Live2DModelRegistry _modelRegistry; - - private ICharacterModelManager _modelManager; - private ICharacterActionService _actionService; - private ICharacterActionResolver _actionResolver; - - #region Unity Lifecycle - - void Start() - { - Initialize(); - } - - #endregion - - - /// - /// ํŒŒ์‚ฌ๋“œ๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค. - /// - public void Initialize() - { - _modelManager = GetComponent(); - if (_modelManager == null) - { - _modelManager = gameObject.AddComponent(); - Debug.Log($"[CharacterManager] CharacterModelManager๊ฐ€ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค: {gameObject.name}"); - } - - // AudioManager์—์„œ Voice AudioSource ๊ฐ€์ ธ์˜ค๊ธฐ - var voiceAudioSource = GetVoiceAudioSource(); - - _modelManager.Initialize(_modelTransform, _modelRegistry, voiceAudioSource); - - _modelManager.LoadModel("zero", true); // ์ž„์‹œ ํ™œ์„ฑํ™” - - if (_modelTransform != null) { - var scaler = _modelTransform.GetComponent(); - if (scaler == null) { - scaler = _modelTransform.gameObject.AddComponent(); - Debug.Log($"[CharacterManager] Live2DModelScaler๊ฐ€ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค: {_modelTransform.name}"); - } - } - } - - - /// - /// ์บ๋ฆญํ„ฐ๋ฅผ ๋“ฑ๋กํ•œ๋‹ค. - /// - public void RegisterCharacter(string characterId, bool preload = true) - { - if (_modelManager != null) { - _modelManager.LoadModel(characterId, preload); - } - } - - /// - /// ์บ๋ฆญํ„ฐ ๋“ฑ๋ก์„ ํ•ด์ œํ•œ๋‹ค. - /// - public void UnregisterCharacter(string characterId) - { - if (_modelManager != null) { - _modelManager.UnloadModel(characterId); - } - } - - /// - /// ํ™œ์„ฑ ์บ๋ฆญํ„ฐ๋ฅผ ์„ค์ •ํ•œ๋‹ค. - /// - public void SetActiveCharacter(string characterId) - { - if (_modelManager != null) { - _modelManager.ActivateModel(characterId); - } - } - - /// - /// ํ™œ์„ฑ ์บ๋ฆญํ„ฐ๋ฅผ ํ•ด์ œํ•œ๋‹ค. - /// - public void ClearActiveCharacter() - { - if (_modelManager != null) { - _modelManager.DeactivateModel(); - } - } - - /// - /// ์•ก์…˜์„ ์ ์šฉํ•œ๋‹ค. - /// - public CharacterActionHandle ApplyAction(CharacterActionRequest request) - { - // TODO: ์•ก์…˜ ์„œ๋น„์Šค ๊ตฌํ˜„ ํ›„ ์—ฐ๊ฒฐ - Debug.LogWarning($"[CharacterManager] ApplyAction ์•„์ง ๊ตฌํ˜„๋˜์ง€ ์•Š์Œ: {request.ActionKey}"); - return new CharacterActionHandle("not_implemented", CharacterActionStatus.Error); - } - - /// - /// ์•ก์…˜์„ ์ทจ์†Œํ•œ๋‹ค. - /// - public void CancelAction(string actionId) - { - // TODO: ์•ก์…˜ ์„œ๋น„์Šค ๊ตฌํ˜„ ํ›„ ์—ฐ๊ฒฐ - Debug.LogWarning($"[CharacterManager] CancelAction ์•„์ง ๊ตฌํ˜„๋˜์ง€ ์•Š์Œ: {actionId}"); - } - - /// - /// ๋ชจ๋“  ์•ก์…˜์„ ์ทจ์†Œํ•œ๋‹ค. - /// - public void CancelAllActions() - { - // TODO: ์•ก์…˜ ์„œ๋น„์Šค ๊ตฌํ˜„ ํ›„ ์—ฐ๊ฒฐ - Debug.LogWarning("[CharacterManager] CancelAllActions ์•„์ง ๊ตฌํ˜„๋˜์ง€ ์•Š์Œ"); - } - - /// - /// ์Œ์„ฑ ์žฌ์ƒ ์‹œ์ž‘์„ ์•Œ๋ฆฐ๋‹ค. - /// - public void OnVoiceStarted() - { - // TODO: ์Œ์„ฑ ๊ด€๋ จ ์ฒ˜๋ฆฌ ๊ตฌํ˜„ - } - - /// - /// ์Œ์„ฑ ์žฌ์ƒ ์ข…๋ฃŒ๋ฅผ ์•Œ๋ฆฐ๋‹ค. - /// - public void OnVoiceFinished() - { - // TODO: ์Œ์„ฑ ๊ด€๋ จ ์ฒ˜๋ฆฌ ๊ตฌํ˜„ - } - - /// - /// ํ™œ์„ฑ ์บ๋ฆญํ„ฐ ID๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. - /// - public string GetActiveCharacterId() - { - return _modelManager?.GetActiveModelId(); - } - - /// - /// ์บ๋ฆญํ„ฐ ๋“ฑ๋ก ์—ฌ๋ถ€๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. - /// - public bool IsRegistered(string characterId) - { - return _modelManager?.IsLoaded(characterId) ?? false; - } - - /// - /// ํŒŒ์‚ฌ๋“œ๋ฅผ ์ข…๋ฃŒํ•œ๋‹ค. - /// - public void Shutdown() - { - if (_modelManager != null) { - _modelManager.UnloadAll(); - } - } - - - - /// - /// AudioManager์—์„œ Voice AudioSource๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค. - /// - private AudioSource GetVoiceAudioSource() - { - var audioManager = AudioManager.Instance; - if (audioManager != null && audioManager.IsInitialized) - { - var voiceController = audioManager.GetVoiceController(); - if (voiceController != null) - { - var audioSource = voiceController.GetAudioSource(); - if (audioSource != null) - { - Debug.Log("[CharacterManager] Voice AudioSource ๊ฐ€์ ธ์˜ค๊ธฐ ์™„๋ฃŒ"); - return audioSource; - } - } - } - - Debug.LogWarning("[CharacterManager] AudioManager๊ฐ€ ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์•˜๊ฑฐ๋‚˜ Voice AudioSource๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); - return null; - } - } -} - - diff --git a/Assets/Domain/Character/Script/Facade/ICharacterManager.cs b/Assets/Domain/Character/Script/Facade/ICharacterManager.cs deleted file mode 100644 index 1606058..0000000 --- a/Assets/Domain/Character/Script/Facade/ICharacterManager.cs +++ /dev/null @@ -1,83 +0,0 @@ -namespace ProjectVG.Domain.Character.Service -{ - /// - /// ์บ๋ฆญํ„ฐ ์ œ์–ด๋ฅผ ์œ„ํ•œ ํŒŒ์‚ฌ๋“œ ์ธํ„ฐํŽ˜์ด์Šค - /// - public interface ICharacterManager - { - /// - /// ํŒŒ์‚ฌ๋“œ๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค. - /// - void Initialize(); - - /// - /// ํŒŒ์‚ฌ๋“œ๋ฅผ ์ข…๋ฃŒํ•œ๋‹ค. - /// - void Shutdown(); - - /// - /// ์บ๋ฆญํ„ฐ๋ฅผ ๋“ฑ๋กํ•œ๋‹ค. - /// - /// ์บ๋ฆญํ„ฐ ID - /// ์‚ฌ์ „ ๋กœ๋“œ ์—ฌ๋ถ€ - void RegisterCharacter(string characterId, bool preload = false); - - /// - /// ์บ๋ฆญํ„ฐ ๋“ฑ๋ก์„ ํ•ด์ œํ•œ๋‹ค. - /// - /// ์บ๋ฆญํ„ฐ ID - void UnregisterCharacter(string characterId); - - /// - /// ํ™œ์„ฑ ์บ๋ฆญํ„ฐ๋ฅผ ์„ค์ •ํ•œ๋‹ค. - /// - /// ์บ๋ฆญํ„ฐ ID - void SetActiveCharacter(string characterId); - - /// - /// ํ™œ์„ฑ ์บ๋ฆญํ„ฐ๋ฅผ ํ•ด์ œํ•œ๋‹ค. - /// - void ClearActiveCharacter(); - - /// - /// ์•ก์…˜์„ ์ ์šฉํ•œ๋‹ค. - /// - /// ์•ก์…˜ ์š”์ฒญ - /// ์•ก์…˜ ํ•ธ๋“ค - CharacterActionHandle ApplyAction(CharacterActionRequest request); - - /// - /// ์•ก์…˜์„ ์ทจ์†Œํ•œ๋‹ค. - /// - /// ์•ก์…˜ ID - void CancelAction(string actionId); - - /// - /// ๋ชจ๋“  ์•ก์…˜์„ ์ทจ์†Œํ•œ๋‹ค. - /// - void CancelAllActions(); - - /// - /// ์Œ์„ฑ ์žฌ์ƒ ์‹œ์ž‘์„ ์•Œ๋ฆฐ๋‹ค. - /// - void OnVoiceStarted(); - - /// - /// ์Œ์„ฑ ์žฌ์ƒ ์ข…๋ฃŒ๋ฅผ ์•Œ๋ฆฐ๋‹ค. - /// - void OnVoiceFinished(); - - /// - /// ํ™œ์„ฑ ์บ๋ฆญํ„ฐ ID๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. - /// - /// ํ™œ์„ฑ ์บ๋ฆญํ„ฐ ID - string GetActiveCharacterId(); - - /// - /// ์บ๋ฆญํ„ฐ ๋“ฑ๋ก ์—ฌ๋ถ€๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. - /// - /// ์บ๋ฆญํ„ฐ ID - /// ๋“ฑ๋ก ์—ฌ๋ถ€ - bool IsRegistered(string characterId); - } -} diff --git a/Assets/Domain/Character/Script/Facade/ICharacterManager.cs.meta b/Assets/Domain/Character/Script/Facade/ICharacterManager.cs.meta deleted file mode 100644 index ff16162..0000000 --- a/Assets/Domain/Character/Script/Facade/ICharacterManager.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 5f24a632b53d90a458f448238a1de2d8 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Manager/CharacterManager.cs b/Assets/Domain/Character/Script/Manager/CharacterManager.cs new file mode 100644 index 0000000..21bbff0 --- /dev/null +++ b/Assets/Domain/Character/Script/Manager/CharacterManager.cs @@ -0,0 +1,168 @@ +using ProjectVG.Domain.Character.Live2D.Model; +using UnityEngine; +using ProjectVG.Domain.Character.Component; +using ProjectVG.Core.Audio; +using ProjectVG.Domain.Chat.Model; +using System.Threading.Tasks; + +namespace ProjectVG.Domain.Character.Service +{ + /// + /// ์บ๋ฆญํ„ฐ ์ œ์–ด๋ฅผ ๋‹จ์ผ ์ง„์ž…์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ํŒŒ์‚ฌ๋“œ ๊ตฌํ˜„์ฒด. + /// + public class CharacterManager : MonoBehaviour + { + [SerializeField] private Transform _modelTransform; + [SerializeField] private Live2DModelRegistry _modelRegistry; + + private CharacterModelLoader _modelLoader; + private GameObject _currentCharacter; + private CharacterActionController _currentActionService; + + #region Unity Lifecycle + void Start() + { + Initialize(); + } + + void OnDestroy() + { + Shutdown(); + } + public void Initialize() + { + _modelLoader = GetComponent(); + if (_modelLoader == null) { + _modelLoader = gameObject.AddComponent(); + Debug.Log($"[CharacterManager] CharacterModelLoader๊ฐ€ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค: {gameObject.name}"); + } + + // AudioManager์—์„œ Voice AudioSource ๊ฐ€์ ธ์˜ค๊ธฐ + var voiceAudioSource = GetVoiceAudioSource(); + + _modelLoader.Initialize(_modelRegistry, voiceAudioSource); + + // ์ž„์‹œ๋กœ zero ์บ๋ฆญํ„ฐ ๋กœ๋“œ + LoadCharacter("zero"); + + if (_modelTransform != null) { + var scaler = _modelTransform.GetComponent(); + if (scaler == null) { + scaler = _modelTransform.gameObject.AddComponent(); + Debug.Log($"[CharacterManager] Live2DModelScaler๊ฐ€ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค: {_modelTransform.name}"); + } + } + } + public void Shutdown() + { + UnloadCurrentCharacter(); + } + + #endregion + + #region Public Methods + + /// + /// ์บ๋ฆญํ„ฐ๋ฅผ ๋กœ๋“œํ•˜๊ณ  ํ™œ์„ฑํ™”ํ•œ๋‹ค. + /// + public async Task LoadCharacter(string characterId) + { + if (_modelLoader == null) return; + + // ๊ธฐ์กด ์บ๋ฆญํ„ฐ ์ œ๊ฑฐ + UnloadCurrentCharacter(); + + // ์ƒˆ ์บ๋ฆญํ„ฐ ๋กœ๋“œ + _currentCharacter = await _modelLoader.LoadAndInitializeModelAsync(characterId, _modelTransform); + if (_currentCharacter != null) + { + _currentCharacter.SetActive(true); + _currentActionService = _currentCharacter.GetComponent(); + Debug.Log($"[CharacterManager] ์บ๋ฆญํ„ฐ ๋กœ๋“œ ์™„๋ฃŒ: {characterId}"); + } + } + + /// + /// ํ˜„์žฌ ์บ๋ฆญํ„ฐ๋ฅผ ์–ธ๋กœ๋“œํ•œ๋‹ค. + /// + public void UnloadCurrentCharacter() + { + if (_currentCharacter != null) + { + Destroy(_currentCharacter); + _currentCharacter = null; + _currentActionService = null; + } + } + + /// + /// ์•ก์…˜์„ ์‹คํ–‰ํ•œ๋‹ค. + /// + /// ์•ก์…˜ ๋ฐ์ดํ„ฐ + public void PlayAction(CharacterActionData actionData) + { + if (_currentActionService != null) + { + _currentActionService.PlayAction(actionData); + } + } + + /// + /// ํ˜„์žฌ ์•ก์…˜์„ ์ค‘์ง€ํ•œ๋‹ค. + /// + public void StopCurrentAction() + { + _currentActionService?.StopCurrentAction(); + } + + /// + /// ์•ก์…˜์ด ์žฌ์ƒ ์ค‘์ธ์ง€ ํ™•์ธํ•œ๋‹ค. + /// + /// ์•ก์…˜ ์žฌ์ƒ ์ค‘์ด๋ฉด true + public bool IsActionPlaying() + { + return _currentActionService?.IsPlaying() ?? false; + } + + /// + /// ํ˜„์žฌ ์บ๋ฆญํ„ฐ๊ฐ€ ๋กœ๋“œ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. + /// + /// ์บ๋ฆญํ„ฐ๊ฐ€ ๋กœ๋“œ๋˜์–ด ์žˆ์œผ๋ฉด true + public bool HasCharacter() + { + return _currentCharacter != null; + } + + #endregion + + #region Private Methods + + /// + /// AudioManager์—์„œ Voice AudioSource๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค. + /// + private AudioSource GetVoiceAudioSource() + { + var audioManager = AudioManager.Instance; + if (audioManager != null && audioManager.IsInitialized) + { + var voiceController = audioManager.GetVoiceController(); + if (voiceController != null) + { + var audioSource = voiceController.GetAudioSource(); + if (audioSource != null) + { + Debug.Log("[CharacterManager] Voice AudioSource ๊ฐ€์ ธ์˜ค๊ธฐ ์™„๋ฃŒ"); + return audioSource; + } + } + } + + Debug.LogWarning("[CharacterManager] AudioManager๊ฐ€ ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์•˜๊ฑฐ๋‚˜ Voice AudioSource๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + return null; + } + + #endregion + } +} + + diff --git a/Assets/Domain/Character/Script/Facade/CharacterManager.cs.meta b/Assets/Domain/Character/Script/Manager/CharacterManager.cs.meta similarity index 100% rename from Assets/Domain/Character/Script/Facade/CharacterManager.cs.meta rename to Assets/Domain/Character/Script/Manager/CharacterManager.cs.meta diff --git a/Assets/Domain/Character/Script/Manager/CharacterModelLoader.cs b/Assets/Domain/Character/Script/Manager/CharacterModelLoader.cs new file mode 100644 index 0000000..8d0ae34 --- /dev/null +++ b/Assets/Domain/Character/Script/Manager/CharacterModelLoader.cs @@ -0,0 +1,185 @@ +using Cysharp.Threading.Tasks; +using Live2D.Cubism.Framework; +using Live2D.Cubism.Framework.MouthMovement; +using Live2D.Cubism.Core; +using ProjectVG.Core.Audio; +using ProjectVG.Domain.Character.Live2D.Model; +using System.Collections.Generic; +using UnityEngine; + +namespace ProjectVG.Domain.Character.Service +{ + /// + /// Live2D ๋ชจ๋ธ ๋กœ๋”ฉ ๋ฐ ์ดˆ๊ธฐํ™”๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ๋งค๋‹ˆ์ € + /// + public class CharacterModelLoader : MonoBehaviour + { + + private Live2DModelRegistry _modelRegistry; + private AudioSource _voiceAudioSource; + + #region Unity Lifecycle + + #endregion + + #region Public Methods + + /// + /// ๋กœ๋”๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค + /// + public void Initialize(Live2DModelRegistry modelRegistry, AudioSource voiceAudioSource) + { + _modelRegistry = modelRegistry; + _voiceAudioSource = voiceAudioSource; + } + + /// + /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ๋กœ๋“œํ•˜๊ณ  ์ดˆ๊ธฐํ™”๋œ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค + /// + /// ์บ๋ฆญํ„ฐ ID + /// ๋ถ€๋ชจ Transform + /// ์ดˆ๊ธฐํ™”๋œ ๋ชจ๋ธ ์ธ์Šคํ„ด์Šค + public async UniTask LoadAndInitializeModelAsync(string characterId, Transform parent = null) + { + var config = GetCharacterConfig(characterId); + if (config == null) { + Debug.LogError($"[CharacterModelLoader] ์บ๋ฆญํ„ฐ '{characterId}'์˜ ์„ค์ •์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); + return null; + } + + return await CreateModelInstanceAsync(config, characterId, parent); + } + + #endregion + + #region Private Methods + + /// + /// ์บ๋ฆญํ„ฐ ์„ค์ •์„ ๊ฐ€์ ธ์˜จ๋‹ค + /// + private Live2DModelConfig GetCharacterConfig(string characterId) + { + if (_modelRegistry != null && _modelRegistry.TryGetConfig(characterId, out var config)) { + return config; + } + + return null; + } + + /// + /// ๋ชจ๋ธ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค + /// + private async UniTask CreateModelInstanceAsync(Live2DModelConfig config, string characterId, Transform parent = null) + { + if (config == null || config.CharacterPrefab == null) { + Debug.LogError($"[CharacterModelLoader] ์บ๋ฆญํ„ฐ '{characterId}'์˜ ํ”„๋ฆฌํŒน์ด null์ž…๋‹ˆ๋‹ค."); + return null; + } + + var targetParent = parent != null ? parent : transform; + + var instance = Instantiate(config.CharacterPrefab, targetParent); + instance.name = characterId; + instance.SetActive(false); + + SetupModelComponents(instance, config); + await UniTask.Yield(); + return instance; + } + + /// + /// ๋ชจ๋ธ์— ํ•„์š”ํ•œ ์ปดํฌ๋„ŒํŠธ๋“ค์„ ์„ค์ •ํ•œ๋‹ค + /// + private void SetupModelComponents(GameObject modelInstance, Live2DModelConfig config) + { + SetupLipSync(modelInstance, config); + SetupAutoEyeBlink(modelInstance, config); + SetupActionService(modelInstance); + } + + /// + /// ๋ฆฝ์‹ฑํฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์„ค์ •ํ•œ๋‹ค + /// + private void SetupLipSync(GameObject modelInstance, Live2DModelConfig config) + { + if (!config.UseLipSync) { + Debug.Log($"[CharacterModelLoader] ๋ฆฝ์‹ฑํฌ๊ฐ€ ๋น„ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค: {modelInstance.name}"); + return; + } + + var mouthController = modelInstance.GetComponent(); + if (mouthController == null) { + mouthController = modelInstance.AddComponent(); + Debug.Log($"[CharacterModelLoader] CubismAudioMouthInput ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค: {modelInstance.name}"); + } + + if (_voiceAudioSource == null) { + Debug.LogWarning($"[CharacterModelLoader] Voice AudioSource๊ฐ€ null์ž…๋‹ˆ๋‹ค. ๋ฆฝ์‹ฑํฌ๊ฐ€ ๋™์ž‘ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: {modelInstance.name}"); + } else { + Debug.Log($"[CharacterModelLoader] Voice AudioSource ์„ค์ • ์™„๋ฃŒ: {modelInstance.name}, AudioSource: {_voiceAudioSource.name}"); + } + + mouthController.AudioInput = _voiceAudioSource; + mouthController.Gain = config.Gain; + mouthController.Smoothing = config.Smoothing; + + // Live2D ๋ชจ๋ธ์— Mouth ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ + var model = modelInstance.GetComponent(); + if (model != null) { + var mouthParameter = model.Parameters.FindById("ParamMouthOpenY"); + if (mouthParameter != null) { + Debug.Log($"[CharacterModelLoader] Mouth ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐœ๊ฒฌ: {mouthParameter.Id}, ํ˜„์žฌ๊ฐ’: {mouthParameter.Value}"); + } else { + Debug.LogWarning($"[CharacterModelLoader] Mouth ํŒŒ๋ผ๋ฏธํ„ฐ(ParamMouthOpenY)๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: {modelInstance.name}"); + } + } + + Debug.Log($"[CharacterModelLoader] ๋ฆฝ์‹ฑํฌ ์„ค์ • ์™„๋ฃŒ: {modelInstance.name}, Gain: {config.Gain}, Smoothing: {config.Smoothing}"); + } + + /// + /// ์ž๋™ ๋ˆˆ ๊นœ๋นก์ž„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์„ค์ •ํ•œ๋‹ค + /// + private void SetupAutoEyeBlink(GameObject modelInstance, Live2DModelConfig config) + { + if (!config.UseAutoEyeBlink) { + return; + } + + var eyeBlinkController = modelInstance.GetComponent(); + if (eyeBlinkController == null) { + eyeBlinkController = modelInstance.AddComponent(); + } + + eyeBlinkController.Mean = config.EyeBlinkMean; + eyeBlinkController.MaximumDeviation = config.EyeBlinkMaximumDeviation; + eyeBlinkController.Timescale = config.EyeBlinkTimescale; + eyeBlinkController.SetBlinkingSettings( + config.EyeBlinkClosingSeconds, + config.EyeBlinkClosedSeconds, + config.EyeBlinkOpeningSeconds + ); + } + + /// + /// ์•ก์…˜ ์„œ๋น„์Šค๋ฅผ ์„ค์ •ํ•œ๋‹ค + /// + private void SetupActionService(GameObject modelInstance) + { + var actionService = modelInstance.GetComponent(); + if (actionService == null) { + actionService = modelInstance.AddComponent(); + } + + var animator = modelInstance.GetComponent(); + if (animator != null) { + actionService.Initialize(animator); + Debug.Log($"[CharacterModelLoader] CharacterActionController ์ดˆ๊ธฐํ™” ์™„๋ฃŒ: {modelInstance.name}"); + } else { + Debug.LogWarning($"[CharacterModelLoader] Animator๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: {modelInstance.name}"); + } + } + + #endregion + } +} diff --git a/Assets/Domain/Character/Script/Manager/CharacterModelLoader.cs.meta b/Assets/Domain/Character/Script/Manager/CharacterModelLoader.cs.meta new file mode 100644 index 0000000..7e23c15 --- /dev/null +++ b/Assets/Domain/Character/Script/Manager/CharacterModelLoader.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 739215a2e50c1c64f91ed2aaa05127c2 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Manager/CharacterModelManager.cs b/Assets/Domain/Character/Script/Manager/CharacterModelManager.cs deleted file mode 100644 index 6ae9fc9..0000000 --- a/Assets/Domain/Character/Script/Manager/CharacterModelManager.cs +++ /dev/null @@ -1,404 +0,0 @@ -using Cysharp.Threading.Tasks; -using Live2D.Cubism.Framework; -using Live2D.Cubism.Framework.MouthMovement; -using Live2D.Cubism.Core; -using ProjectVG.Core.Audio; -using ProjectVG.Domain.Character.Live2D.Model; -using System.Collections.Generic; -using UnityEngine; - -namespace ProjectVG.Domain.Character.Service -{ - /// - /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์˜ ์ˆ˜๋ช…์ฃผ๊ธฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋งค๋‹ˆ์ € - /// - public class CharacterModelManager : MonoBehaviour, ICharacterModelManager - { - #region Private Fields - - private Transform _modelRoot; - private Live2DModelRegistry _modelRegistry; - private readonly Dictionary _characterIdToInstance = new Dictionary(); - private string _activeCharacterId; - private AudioSource _voiceAudioSource; - - #endregion - - #region Unity Lifecycle - - #endregion - - #region Public Methods - - /// - /// ๋งค๋‹ˆ์ €๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค - /// - public void Initialize(Transform modelRoot, Live2DModelRegistry modelRegistry, AudioSource voiceAudioSource) - { - _modelRoot = modelRoot; - _modelRegistry = modelRegistry; - _voiceAudioSource = voiceAudioSource; - } - - /// - /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ๋กœ๋“œํ•œ๋‹ค - /// - public void LoadModel(string characterId, bool activateImmediately = false) - { - if (!ValidateLoadRequest(characterId, activateImmediately)) { - return; - } - - var config = GetCharacterConfig(characterId); - if (config == null) { - Debug.LogError($"[CharacterModelManager] ์บ๋ฆญํ„ฐ '{characterId}'์˜ ์„ค์ •์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); - return; - } - - var instance = CreateModelInstance(config, characterId); - if (instance == null) { - return; - } - - RegisterModelInstance(characterId, instance, activateImmediately); - } - - /// - /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ๋น„๋™๊ธฐ๋กœ ๋กœ๋“œํ•œ๋‹ค - /// - public async UniTask LoadModelAsync(string characterId, bool activateImmediately = false) - { - if (!ValidateLoadRequest(characterId, activateImmediately)) { - return; - } - - var config = GetCharacterConfig(characterId); - if (config == null) { - Debug.LogError($"[CharacterModelManager] ์บ๋ฆญํ„ฐ '{characterId}'์˜ ์„ค์ •์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."); - return; - } - - var instance = await CreateModelInstanceAsync(config, characterId); - if (instance == null) { - return; - } - - RegisterModelInstance(characterId, instance, activateImmediately); - } - - /// - /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ์–ธ๋กœ๋“œํ•œ๋‹ค - /// - public void UnloadModel(string characterId) - { - if (!_characterIdToInstance.TryGetValue(characterId, out var instance)) { - return; - } - - if (instance != null) { - Destroy(instance); - } - - _characterIdToInstance.Remove(characterId); - - if (_activeCharacterId == characterId) { - _activeCharacterId = null; - } - } - - /// - /// ๋ชจ๋“  ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ์–ธ๋กœ๋“œํ•œ๋‹ค - /// - public void UnloadAll() - { - var keysToRemove = new List(); - - foreach (var kvp in _characterIdToInstance) { - if (kvp.Value != null) { - Destroy(kvp.Value); - } - keysToRemove.Add(kvp.Key); - } - - foreach (var key in keysToRemove) { - _characterIdToInstance.Remove(key); - } - - _activeCharacterId = null; - } - - /// - /// ์ง€์ •๋œ ์บ๋ฆญํ„ฐ๋ฅผ ํ™œ์„ฑํ™”ํ•œ๋‹ค - /// - public void ActivateModel(string characterId) - { - if (!_characterIdToInstance.TryGetValue(characterId, out var target)) { - Debug.LogWarning($"[CharacterModelManager] ์บ๋ฆญํ„ฐ '{characterId}'๊ฐ€ ๋กœ๋“œ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."); - return; - } - - if (target == null) { - _characterIdToInstance.Remove(characterId); - Debug.LogWarning($"[CharacterModelManager] ์บ๋ฆญํ„ฐ '{characterId}'์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ํŒŒ๊ดด๋˜์—ˆ์Šต๋‹ˆ๋‹ค."); - return; - } - - DeactivateAllModels(); - - target.SetActive(true); - _activeCharacterId = characterId; - } - - /// - /// ํ™œ์„ฑ ๋ชจ๋ธ์„ ๋น„ํ™œ์„ฑํ™”ํ•œ๋‹ค - /// - public void DeactivateModel() - { - if (string.IsNullOrEmpty(_activeCharacterId)) { - return; - } - - if (_characterIdToInstance.TryGetValue(_activeCharacterId, out var current)) { - if (current != null) { - current.SetActive(false); - } else { - _characterIdToInstance.Remove(_activeCharacterId); - } - } - - _activeCharacterId = null; - } - - /// - /// ํ™œ์„ฑ ์บ๋ฆญํ„ฐ ID๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค - /// - public string GetActiveModelId() - { - return _activeCharacterId; - } - - /// - /// ์บ๋ฆญํ„ฐ ๋กœ๋“œ ์—ฌ๋ถ€๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค - /// - public bool IsLoaded(string characterId) - { - return !string.IsNullOrEmpty(characterId) && _characterIdToInstance.ContainsKey(characterId); - } - - - #endregion - - #region Private Methods - - - - /// - /// ๋กœ๋“œ ์š”์ฒญ์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์ฆํ•œ๋‹ค - /// - private bool ValidateLoadRequest(string characterId, bool activateImmediately) - { - if (string.IsNullOrEmpty(characterId)) { - Debug.LogWarning("[CharacterModelManager] ์บ๋ฆญํ„ฐ ID๊ฐ€ null์ด๊ฑฐ๋‚˜ ๋น„์–ด์žˆ์Šต๋‹ˆ๋‹ค."); - return false; - } - - if (_characterIdToInstance.TryGetValue(characterId, out var existing)) { - if (activateImmediately) { - ActivateModel(characterId); - } - return false; - } - - return true; - } - - /// - /// ๋ชจ๋ธ ์ธ์Šคํ„ด์Šค๋ฅผ ๋“ฑ๋กํ•˜๊ณ  ํ•„์š”์‹œ ํ™œ์„ฑํ™”ํ•œ๋‹ค - /// - private void RegisterModelInstance(string characterId, GameObject instance, bool activateImmediately) - { - _characterIdToInstance[characterId] = instance; - - if (activateImmediately) { - ActivateModel(characterId); - } - } - - /// - /// ์บ๋ฆญํ„ฐ ์„ค์ •์„ ๊ฐ€์ ธ์˜จ๋‹ค - /// - private Live2DModelConfig GetCharacterConfig(string characterId) - { - if (_modelRegistry != null && _modelRegistry.TryGetConfig(characterId, out var config)) { - return config; - } - - return null; - } - - /// - /// ๋ชจ๋ธ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค - /// - private GameObject CreateModelInstance(Live2DModelConfig config, string characterId) - { - if (config == null || config.CharacterPrefab == null) { - Debug.LogError($"[CharacterModelManager] ์บ๋ฆญํ„ฐ '{characterId}'์˜ ํ”„๋ฆฌํŒน์ด null์ž…๋‹ˆ๋‹ค."); - return null; - } - - var parent = _modelRoot != null ? _modelRoot : transform; - - var instance = Instantiate(config.CharacterPrefab, parent); - instance.name = characterId; - instance.SetActive(false); - - SetupModelComponents(instance, config); - return instance; - } - - /// - /// ๋ชจ๋ธ ์ธ์Šคํ„ด์Šค๋ฅผ ๋น„๋™๊ธฐ๋กœ ์ƒ์„ฑํ•œ๋‹ค - /// - private async UniTask CreateModelInstanceAsync(Live2DModelConfig config, string characterId) - { - if (config == null || config.CharacterPrefab == null) { - Debug.LogError($"[CharacterModelManager] ์บ๋ฆญํ„ฐ '{characterId}'์˜ ํ”„๋ฆฌํŒน์ด null์ž…๋‹ˆ๋‹ค."); - return null; - } - - var parent = _modelRoot != null ? _modelRoot : transform; - - var instance = Instantiate(config.CharacterPrefab, parent); - instance.name = characterId; - instance.SetActive(false); - - SetupModelComponents(instance, config); - await UniTask.Yield(); - return instance; - } - - /// - /// ๋ชจ๋ธ์— ํ•„์š”ํ•œ ์ปดํฌ๋„ŒํŠธ๋“ค์„ ์„ค์ •ํ•œ๋‹ค - /// - private void SetupModelComponents(GameObject modelInstance, Live2DModelConfig config) - { - SetupLipSync(modelInstance, config); - SetupAutoEyeBlink(modelInstance, config); - } - - /// - /// ๋ฆฝ์‹ฑํฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์„ค์ •ํ•œ๋‹ค - /// - private void SetupLipSync(GameObject modelInstance, Live2DModelConfig config) - { - if (!config.UseLipSync) { - Debug.Log($"[CharacterModelManager] ๋ฆฝ์‹ฑํฌ๊ฐ€ ๋น„ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค: {modelInstance.name}"); - return; - } - - var mouthController = modelInstance.GetComponent(); - if (mouthController == null) { - mouthController = modelInstance.AddComponent(); - Debug.Log($"[CharacterModelManager] CubismAudioMouthInput ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค: {modelInstance.name}"); - } - - if (_voiceAudioSource == null) { - Debug.LogWarning($"[CharacterModelManager] Voice AudioSource๊ฐ€ null์ž…๋‹ˆ๋‹ค. ๋ฆฝ์‹ฑํฌ๊ฐ€ ๋™์ž‘ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: {modelInstance.name}"); - } else { - Debug.Log($"[CharacterModelManager] Voice AudioSource ์„ค์ • ์™„๋ฃŒ: {modelInstance.name}, AudioSource: {_voiceAudioSource.name}"); - } - - mouthController.AudioInput = _voiceAudioSource; - mouthController.Gain = config.Gain; - mouthController.Smoothing = config.Smoothing; - - // Live2D ๋ชจ๋ธ์— Mouth ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ - var model = modelInstance.GetComponent(); - if (model != null) { - var mouthParameter = model.Parameters.FindById("ParamMouthOpenY"); - if (mouthParameter != null) { - Debug.Log($"[CharacterModelManager] Mouth ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐœ๊ฒฌ: {mouthParameter.Id}, ํ˜„์žฌ๊ฐ’: {mouthParameter.Value}"); - } else { - Debug.LogWarning($"[CharacterModelManager] Mouth ํŒŒ๋ผ๋ฏธํ„ฐ(ParamMouthOpenY)๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: {modelInstance.name}"); - } - } - - Debug.Log($"[CharacterModelManager] ๋ฆฝ์‹ฑํฌ ์„ค์ • ์™„๋ฃŒ: {modelInstance.name}, Gain: {config.Gain}, Smoothing: {config.Smoothing}"); - } - - /// - /// ์ž๋™ ๋ˆˆ ๊นœ๋นก์ž„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์„ค์ •ํ•œ๋‹ค - /// - private void SetupAutoEyeBlink(GameObject modelInstance, Live2DModelConfig config) - { - if (!config.UseAutoEyeBlink) { - return; - } - - var eyeBlinkController = modelInstance.GetComponent(); - if (eyeBlinkController == null) { - eyeBlinkController = modelInstance.AddComponent(); - } - - eyeBlinkController.Mean = config.EyeBlinkMean; - eyeBlinkController.MaximumDeviation = config.EyeBlinkMaximumDeviation; - eyeBlinkController.Timescale = config.EyeBlinkTimescale; - eyeBlinkController.SetBlinkingSettings( - config.EyeBlinkClosingSeconds, - config.EyeBlinkClosedSeconds, - config.EyeBlinkOpeningSeconds - ); - } - - /// - /// ๋ชจ๋“  ๋ชจ๋ธ์„ ๋น„ํ™œ์„ฑํ™”ํ•œ๋‹ค - /// - private void DeactivateAllModels() - { - var keysToRemove = new List(); - - foreach (var kvp in _characterIdToInstance) { - if (kvp.Value == null) { - keysToRemove.Add(kvp.Key); - continue; - } - - kvp.Value.SetActive(false); - } - - foreach (var key in keysToRemove) { - _characterIdToInstance.Remove(key); - } - } - - /// - /// ๋ชจ๋“  ๋กœ๋“œ๋œ ๋ชจ๋ธ์˜ ๋ฆฝ์‹ฑํฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์—…๋ฐ์ดํŠธํ•œ๋‹ค - /// - private void UpdateAllLipSyncComponents() - { - var keysToRemove = new List(); - - foreach (var kvp in _characterIdToInstance) { - if (kvp.Value == null) { - keysToRemove.Add(kvp.Key); - continue; - } - - var mouthController = kvp.Value.GetComponent(); - if (mouthController != null) { - mouthController.AudioInput = _voiceAudioSource; - - if (_voiceAudioSource != null) { - Debug.Log($"[CharacterModelManager] ๋ฆฝ์‹ฑํฌ ์—…๋ฐ์ดํŠธ: {kvp.Key}, AudioSource ์žฌ์ƒ์ค‘: {_voiceAudioSource.isPlaying}, ๋ณผ๋ฅจ: {_voiceAudioSource.volume}"); - } - } - } - - foreach (var key in keysToRemove) { - _characterIdToInstance.Remove(key); - } - } - - #endregion - } -} diff --git a/Assets/Domain/Character/Script/Manager/CharacterModelManager.cs.meta b/Assets/Domain/Character/Script/Manager/CharacterModelManager.cs.meta deleted file mode 100644 index cdc29a7..0000000 --- a/Assets/Domain/Character/Script/Manager/CharacterModelManager.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 3d3d2b2f6a9c04348b8fab2f4e3e6a7c \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs b/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs deleted file mode 100644 index d9fe74c..0000000 --- a/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Cysharp.Threading.Tasks; -using ProjectVG.Domain.Character.Live2D.Model; -using UnityEngine; - -namespace ProjectVG.Domain.Character.Service -{ - /// - /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์˜ ์ˆ˜๋ช…์ฃผ๊ธฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค - /// - public interface ICharacterModelManager - { - - /// - /// ๋งค๋‹ˆ์ €๋ฅผ ์ดˆ๊ธฐํ™” ํ•œ๋‹ค. - /// - /// ๋ชจ๋ธ ์œ„์น˜ - /// ๋ชจ๋ธ ๋“ฑ๋ก์ž - /// ์Œ์„ฑ AudioSource (์„ ํƒ์‚ฌํ•ญ) - public void Initialize(Transform modelRoot, Live2DModelRegistry modelRegistry, AudioSource voiceAudioSource); - - /// - /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ๋กœ๋“œํ•œ๋‹ค. - /// - /// ๋กœ๋“œํ•  ์บ๋ฆญํ„ฐ์˜ ๊ณ ์œ  ID - /// ์‚ฌ์ „ ๋กœ๋“œ ์—ฌ๋ถ€ (true๋ฉด ๋กœ๋“œ ํ›„ ์ฆ‰์‹œ ํ™œ์„ฑํ™”) - void LoadModel(string characterId, bool preload = false); - - /// - /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ๋น„๋™๊ธฐ๋กœ ๋กœ๋“œํ•œ๋‹ค. - /// - /// ๋กœ๋“œํ•  ์บ๋ฆญํ„ฐ์˜ ๊ณ ์œ  ID - /// ์‚ฌ์ „ ๋กœ๋“œ ์—ฌ๋ถ€ (true๋ฉด ๋กœ๋“œ ํ›„ ์ฆ‰์‹œ ํ™œ์„ฑํ™”) - UniTask LoadModelAsync(string characterId, bool preload = false); - - /// - /// ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ์–ธ๋กœ๋“œํ•œ๋‹ค. - /// - /// ์–ธ๋กœ๋“œํ•  ์บ๋ฆญํ„ฐ์˜ ๊ณ ์œ  ID - void UnloadModel(string characterId); - - /// - /// ๋ชจ๋“  ์บ๋ฆญํ„ฐ ๋ชจ๋ธ์„ ์–ธ๋กœ๋“œํ•œ๋‹ค. - /// - void UnloadAll(); - - /// - /// ์ง€์ •๋œ ์บ๋ฆญํ„ฐ๋ฅผ ํ™œ์„ฑํ™”ํ•œ๋‹ค. - /// - /// ํ™œ์„ฑํ™”ํ•  ์บ๋ฆญํ„ฐ์˜ ๊ณ ์œ  ID - void ActivateModel(string characterId); - - /// - /// ํ™œ์„ฑ ๋ชจ๋ธ์„ ๋น„ํ™œ์„ฑํ™”ํ•œ๋‹ค. - /// - void DeactivateModel(); - - /// - /// ์บ๋ฆญํ„ฐ ๋กœ๋“œ ์—ฌ๋ถ€๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. - /// - /// ํ™•์ธํ•  ์บ๋ฆญํ„ฐ์˜ ๊ณ ์œ  ID - /// ์บ๋ฆญํ„ฐ๊ฐ€ ๋กœ๋“œ๋˜์–ด ์žˆ์œผ๋ฉด true, ์•„๋‹ˆ๋ฉด false - bool IsLoaded(string characterId); - - /// - /// ํ™œ์„ฑ ์บ๋ฆญํ„ฐ ID๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. - /// - /// ํ™œ์„ฑ ์บ๋ฆญํ„ฐ์˜ ID, ํ™œ์„ฑ ์บ๋ฆญํ„ฐ๊ฐ€ ์—†์œผ๋ฉด null - string GetActiveModelId(); - } -} diff --git a/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs.meta b/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs.meta deleted file mode 100644 index 218a1ad..0000000 --- a/Assets/Domain/Character/Script/Manager/ICharacterModelManager.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 7344ae5c9f3d5af44bbf485b38020449 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Service/CharacterActionController.cs b/Assets/Domain/Character/Script/Service/CharacterActionController.cs new file mode 100644 index 0000000..4ef7e96 --- /dev/null +++ b/Assets/Domain/Character/Script/Service/CharacterActionController.cs @@ -0,0 +1,77 @@ +#nullable enable +using UnityEngine; +using ProjectVG.Domain.Chat.Model; + +namespace ProjectVG.Domain.Character.Service +{ + /// + /// ์บ๋ฆญํ„ฐ ์•ก์…˜ ์‹คํ–‰์„ ๊ด€๋ฆฌํ•˜๋Š” ์„œ๋น„์Šค ๊ตฌํ˜„์ฒด + /// + public class CharacterActionController : MonoBehaviour + { + private Animator? _animator; + private bool _isPlaying = false; + + /// + /// ์„œ๋น„์Šค๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค. + /// + /// ์บ๋ฆญํ„ฐ์˜ Animator + public void Initialize(Animator animator) + { + _animator = animator; + _isPlaying = false; + } + + /// + /// ์•ก์…˜์„ ์‹คํ–‰ํ•œ๋‹ค. + /// + /// ์•ก์…˜ ๋ฐ์ดํ„ฐ + public void PlayAction(CharacterActionData actionData) + { + if (_animator == null) + { + Debug.LogWarning("[CharacterActionController] Animator๊ฐ€ ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."); + return; + } + + if (!actionData.HasAction()) + { + Debug.LogWarning("[CharacterActionController] ์•ก์…˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋น„์–ด์žˆ์Šต๋‹ˆ๋‹ค."); + return; + } + + try + { + _isPlaying = true; + _animator.SetTrigger(actionData.Action); + Debug.Log($"[CharacterActionController] ์•ก์…˜ ์žฌ์ƒ: {actionData.Action}"); + } + catch (System.Exception ex) + { + Debug.LogError($"[CharacterActionController] ์•ก์…˜ ์žฌ์ƒ ์‹คํŒจ: {ex.Message}"); + _isPlaying = false; + } + } + + /// + /// ํ˜„์žฌ ์žฌ์ƒ ์ค‘์ธ ์•ก์…˜์„ ์ค‘์ง€ํ•œ๋‹ค. + /// + public void StopCurrentAction() + { + if (_animator == null) return; + + _animator.SetTrigger("Idle"); + _isPlaying = false; + Debug.Log("[CharacterActionController] ์•ก์…˜ ์ค‘์ง€"); + } + + /// + /// ์•ก์…˜์ด ์žฌ์ƒ ์ค‘์ธ์ง€ ํ™•์ธํ•œ๋‹ค. + /// + /// ์•ก์…˜ ์žฌ์ƒ ์ค‘์ด๋ฉด true + public bool IsPlaying() + { + return _isPlaying && _animator != null; + } + } +} diff --git a/Assets/Domain/Character/Script/Service/CharacterActionController.cs.meta b/Assets/Domain/Character/Script/Service/CharacterActionController.cs.meta new file mode 100644 index 0000000..d2bbf75 --- /dev/null +++ b/Assets/Domain/Character/Script/Service/CharacterActionController.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f43ea94184a478548b8bd29496a6070a \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Service/ICharacterActionResolver.cs b/Assets/Domain/Character/Script/Service/ICharacterActionResolver.cs deleted file mode 100644 index 18a40e9..0000000 --- a/Assets/Domain/Character/Script/Service/ICharacterActionResolver.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace ProjectVG.Domain.Character.Service -{ - /// - /// ์บ๋ฆญํ„ฐ ์•ก์…˜ ์š”์ฒญ์„ ํ•ด์„ํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค - /// - public interface ICharacterActionResolver - { - /// - /// ์•ก์…˜ ์š”์ฒญ์„ ํ•ด์„ํ•˜์—ฌ ๊ตฌ์ฒด์ ์ธ ์•ก์…˜ ์ •๋ณด๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค. - /// - /// ์•ก์…˜ ์š”์ฒญ - /// ํ•ด์„๋œ ์•ก์…˜ ์ •๋ณด - CharacterResolvedAction Resolve(CharacterActionRequest request); - } -} diff --git a/Assets/Domain/Character/Script/Service/ICharacterActionResolver.cs.meta b/Assets/Domain/Character/Script/Service/ICharacterActionResolver.cs.meta deleted file mode 100644 index 6c5bb39..0000000 --- a/Assets/Domain/Character/Script/Service/ICharacterActionResolver.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: cf028008204016d468f696b5120e7109 \ No newline at end of file diff --git a/Assets/Domain/Character/Script/Service/ICharacterActionService.cs b/Assets/Domain/Character/Script/Service/ICharacterActionService.cs deleted file mode 100644 index 8c3f6a6..0000000 --- a/Assets/Domain/Character/Script/Service/ICharacterActionService.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Cysharp.Threading.Tasks; - -namespace ProjectVG.Domain.Character.Service -{ - /// - /// ์บ๋ฆญํ„ฐ ์•ก์…˜ ์‹คํ–‰์„ ๊ด€๋ฆฌํ•˜๋Š” ์„œ๋น„์Šค ์ธํ„ฐํŽ˜์ด์Šค - /// - public interface ICharacterActionService - { - /// - /// ์„œ๋น„์Šค๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค. - /// - void Initialize(); - - /// - /// ์•ก์…˜์„ ํ์— ์ถ”๊ฐ€ํ•œ๋‹ค. - /// - /// ์•ก์…˜ ์š”์ฒญ - /// ์•ก์…˜ ํ•ธ๋“ค - CharacterActionHandle Enqueue(CharacterActionRequest request); - - /// - /// ํŠน์ • ์•ก์…˜์„ ์ค‘์ง€ํ•œ๋‹ค. - /// - /// ์ค‘์ง€ํ•  ์•ก์…˜ ID - void Stop(string actionId); - - /// - /// ๋ชจ๋“  ์•ก์…˜์„ ์ค‘์ง€ํ•œ๋‹ค. - /// - void StopAll(); - - /// - /// ํ˜„์žฌ ์•ก์…˜ ์ƒํƒœ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. - /// - /// ์•ก์…˜ ์ƒํƒœ - CharacterActionState GetState(); - } -} diff --git a/Assets/Domain/Character/Script/Service/ICharacterActionService.cs.meta b/Assets/Domain/Character/Script/Service/ICharacterActionService.cs.meta deleted file mode 100644 index 0989487..0000000 --- a/Assets/Domain/Character/Script/Service/ICharacterActionService.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: db3c66c11864369439cd34ad5e19c9ce \ No newline at end of file diff --git a/Assets/Domain/Chat/Service/ChatSystemManager.cs b/Assets/Domain/Chat/Service/ChatSystemManager.cs index e4a6f4d..6fae4b7 100644 --- a/Assets/Domain/Chat/Service/ChatSystemManager.cs +++ b/Assets/Domain/Chat/Service/ChatSystemManager.cs @@ -179,7 +179,11 @@ private async UniTask ProcessMessageAsync(ChatMessage chatMessage) _chatBubblePanel.CreateBubble(Actor.Character, chatMessage.Text); } - // TODO : ์บ๋ฆญํ„ฐ ๋ฐ˜์‘์„ ์ „๋‹ฌํ•œ๋‹ค + // ์บ๋ฆญํ„ฐ ์•ก์…˜ ์‹คํ–‰ + if (chatMessage.HasActionData() && _chracterManager != null) + { + _chracterManager.PlayAction(chatMessage.ActionData); + } if (chatMessage.VoiceData != null && _audioManager != null) { From 9340da5df4d1618f5fe4ba03ed855f6b88a1d943 Mon Sep 17 00:00:00 2001 From: WooSH Date: Thu, 21 Aug 2025 19:30:35 +0900 Subject: [PATCH 12/13] =?UTF-8?q?feat:=20=EB=8C=80=ED=99=94=20=EC=95=A1?= =?UTF-8?q?=EC=85=98=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/App/Scenes/MainSence.unity | 4 +- .../CharacterActionController.cs | 62 ++++++++++++++----- .../CharacterActionController.cs.meta | 0 .../Script/{Manager => }/CharacterManager.cs | 6 +- .../{Manager => }/CharacterManager.cs.meta | 0 .../{Manager => }/CharacterModelLoader.cs | 18 +++--- .../CharacterModelLoader.cs.meta | 0 .../Domain/Character/Script/Controller.meta | 8 --- Assets/Domain/Character/Script/Facade.meta | 8 --- Assets/Domain/Character/Script/Manager.meta | 8 --- Assets/Domain/Character/Script/Service.meta | 8 --- .../Domain/Chat/Model/CharacterActionData.cs | 37 +++++++++-- .../Domain/Chat/Service/ChatSystemManager.cs | 18 +++++- 13 files changed, 112 insertions(+), 65 deletions(-) rename Assets/Domain/Character/Script/{Service => }/CharacterActionController.cs (55%) rename Assets/Domain/Character/Script/{Service => }/CharacterActionController.cs.meta (100%) rename Assets/Domain/Character/Script/{Manager => }/CharacterManager.cs (95%) rename Assets/Domain/Character/Script/{Manager => }/CharacterManager.cs.meta (100%) rename Assets/Domain/Character/Script/{Manager => }/CharacterModelLoader.cs (92%) rename Assets/Domain/Character/Script/{Manager => }/CharacterModelLoader.cs.meta (100%) delete mode 100644 Assets/Domain/Character/Script/Controller.meta delete mode 100644 Assets/Domain/Character/Script/Facade.meta delete mode 100644 Assets/Domain/Character/Script/Manager.meta delete mode 100644 Assets/Domain/Character/Script/Service.meta diff --git a/Assets/App/Scenes/MainSence.unity b/Assets/App/Scenes/MainSence.unity index a132881..8b1be64 100644 --- a/Assets/App/Scenes/MainSence.unity +++ b/Assets/App/Scenes/MainSence.unity @@ -708,11 +708,9 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: _chatBubblePanel: {fileID: 2017944365} + _chracterManager: {fileID: 873552998} _characterId: 44444444-4444-4444-4444-444444444444 _userId: bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb - _enableMessageQueue: 1 - _maxQueueSize: 100 - _enableLive2DIntegration: 1 --- !u!4 &829067253 Transform: m_ObjectHideFlags: 0 diff --git a/Assets/Domain/Character/Script/Service/CharacterActionController.cs b/Assets/Domain/Character/Script/CharacterActionController.cs similarity index 55% rename from Assets/Domain/Character/Script/Service/CharacterActionController.cs rename to Assets/Domain/Character/Script/CharacterActionController.cs index 4ef7e96..5949ce3 100644 --- a/Assets/Domain/Character/Script/Service/CharacterActionController.cs +++ b/Assets/Domain/Character/Script/CharacterActionController.cs @@ -4,13 +4,19 @@ namespace ProjectVG.Domain.Character.Service { - /// - /// ์บ๋ฆญํ„ฐ ์•ก์…˜ ์‹คํ–‰์„ ๊ด€๋ฆฌํ•˜๋Š” ์„œ๋น„์Šค ๊ตฌํ˜„์ฒด - /// + + public enum CharacterActionType + { + Idle, + Listen, + Talk + } + public class CharacterActionController : MonoBehaviour { private Animator? _animator; private bool _isPlaying = false; + private CharacterActionType _currentAction = CharacterActionType.Idle; /// /// ์„œ๋น„์Šค๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค. @@ -20,13 +26,14 @@ public void Initialize(Animator animator) { _animator = animator; _isPlaying = false; + _currentAction = CharacterActionType.Idle; } /// /// ์•ก์…˜์„ ์‹คํ–‰ํ•œ๋‹ค. /// - /// ์•ก์…˜ ๋ฐ์ดํ„ฐ - public void PlayAction(CharacterActionData actionData) + /// ์•ก์…˜ ํƒ€์ž… + public void PlayAction(CharacterActionType actionType) { if (_animator == null) { @@ -34,17 +41,33 @@ public void PlayAction(CharacterActionData actionData) return; } - if (!actionData.HasAction()) - { - Debug.LogWarning("[CharacterActionController] ์•ก์…˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋น„์–ด์žˆ์Šต๋‹ˆ๋‹ค."); - return; - } - try { - _isPlaying = true; - _animator.SetTrigger(actionData.Action); - Debug.Log($"[CharacterActionController] ์•ก์…˜ ์žฌ์ƒ: {actionData.Action}"); + _currentAction = actionType; + + Debug.Log(actionType); + + switch (actionType) + { + case CharacterActionType.Idle: + _animator.SetTrigger("Idle"); + _animator.SetBool("Talk", false); + _isPlaying = false; + break; + + case CharacterActionType.Listen: + _animator.SetTrigger("Listen"); + _animator.SetBool("Talk", false); + _isPlaying = true; + break; + + case CharacterActionType.Talk: + _animator.SetBool("Talk", true); + _isPlaying = true; + break; + } + + Debug.Log($"[CharacterActionController] ์•ก์…˜ ์žฌ์ƒ: {actionType}"); } catch (System.Exception ex) { @@ -61,7 +84,9 @@ public void StopCurrentAction() if (_animator == null) return; _animator.SetTrigger("Idle"); + _animator.SetBool("Talk", false); _isPlaying = false; + _currentAction = CharacterActionType.Idle; Debug.Log("[CharacterActionController] ์•ก์…˜ ์ค‘์ง€"); } @@ -73,5 +98,14 @@ public bool IsPlaying() { return _isPlaying && _animator != null; } + + /// + /// ํ˜„์žฌ ์•ก์…˜ ํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. + /// + /// ํ˜„์žฌ ์•ก์…˜ ํƒ€์ž… + public CharacterActionType GetCurrentAction() + { + return _currentAction; + } } } diff --git a/Assets/Domain/Character/Script/Service/CharacterActionController.cs.meta b/Assets/Domain/Character/Script/CharacterActionController.cs.meta similarity index 100% rename from Assets/Domain/Character/Script/Service/CharacterActionController.cs.meta rename to Assets/Domain/Character/Script/CharacterActionController.cs.meta diff --git a/Assets/Domain/Character/Script/Manager/CharacterManager.cs b/Assets/Domain/Character/Script/CharacterManager.cs similarity index 95% rename from Assets/Domain/Character/Script/Manager/CharacterManager.cs rename to Assets/Domain/Character/Script/CharacterManager.cs index 21bbff0..d7d0547 100644 --- a/Assets/Domain/Character/Script/Manager/CharacterManager.cs +++ b/Assets/Domain/Character/Script/CharacterManager.cs @@ -101,9 +101,11 @@ public void UnloadCurrentCharacter() /// ์•ก์…˜ ๋ฐ์ดํ„ฐ public void PlayAction(CharacterActionData actionData) { - if (_currentActionService != null) + Debug.Log(_currentActionService != null && actionData.HasAction()); + Debug.Log(actionData.HasAction()); + if (_currentActionService != null && actionData.HasAction()) { - _currentActionService.PlayAction(actionData); + _currentActionService.PlayAction(actionData.ActionType); } } diff --git a/Assets/Domain/Character/Script/Manager/CharacterManager.cs.meta b/Assets/Domain/Character/Script/CharacterManager.cs.meta similarity index 100% rename from Assets/Domain/Character/Script/Manager/CharacterManager.cs.meta rename to Assets/Domain/Character/Script/CharacterManager.cs.meta diff --git a/Assets/Domain/Character/Script/Manager/CharacterModelLoader.cs b/Assets/Domain/Character/Script/CharacterModelLoader.cs similarity index 92% rename from Assets/Domain/Character/Script/Manager/CharacterModelLoader.cs rename to Assets/Domain/Character/Script/CharacterModelLoader.cs index 8d0ae34..f46ca49 100644 --- a/Assets/Domain/Character/Script/Manager/CharacterModelLoader.cs +++ b/Assets/Domain/Character/Script/CharacterModelLoader.cs @@ -94,7 +94,7 @@ private void SetupModelComponents(GameObject modelInstance, Live2DModelConfig co { SetupLipSync(modelInstance, config); SetupAutoEyeBlink(modelInstance, config); - SetupActionService(modelInstance); + SetupActionController(modelInstance); } /// @@ -164,7 +164,7 @@ private void SetupAutoEyeBlink(GameObject modelInstance, Live2DModelConfig confi /// /// ์•ก์…˜ ์„œ๋น„์Šค๋ฅผ ์„ค์ •ํ•œ๋‹ค /// - private void SetupActionService(GameObject modelInstance) + private void SetupActionController(GameObject modelInstance) { var actionService = modelInstance.GetComponent(); if (actionService == null) { @@ -172,13 +172,13 @@ private void SetupActionService(GameObject modelInstance) } var animator = modelInstance.GetComponent(); - if (animator != null) { - actionService.Initialize(animator); - Debug.Log($"[CharacterModelLoader] CharacterActionController ์ดˆ๊ธฐํ™” ์™„๋ฃŒ: {modelInstance.name}"); - } else { - Debug.LogWarning($"[CharacterModelLoader] Animator๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: {modelInstance.name}"); - } - } + if (animator == null) { + Debug.LogWarning($"[CharacterModelLoader] Animator๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: {modelInstance.name}"); + return; + } + actionService.Initialize(animator); + Debug.Log($"[CharacterModelLoader] CharacterActionController ์ดˆ๊ธฐํ™” ์™„๋ฃŒ: {modelInstance.name}"); + } #endregion } diff --git a/Assets/Domain/Character/Script/Manager/CharacterModelLoader.cs.meta b/Assets/Domain/Character/Script/CharacterModelLoader.cs.meta similarity index 100% rename from Assets/Domain/Character/Script/Manager/CharacterModelLoader.cs.meta rename to Assets/Domain/Character/Script/CharacterModelLoader.cs.meta diff --git a/Assets/Domain/Character/Script/Controller.meta b/Assets/Domain/Character/Script/Controller.meta deleted file mode 100644 index 830fe1c..0000000 --- a/Assets/Domain/Character/Script/Controller.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 37c695efe237c2a47888f4ce37fa681e -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Domain/Character/Script/Facade.meta b/Assets/Domain/Character/Script/Facade.meta deleted file mode 100644 index f9a76d2..0000000 --- a/Assets/Domain/Character/Script/Facade.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 1befe0315c2c0b644bf4c75afc6cd9bc -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Domain/Character/Script/Manager.meta b/Assets/Domain/Character/Script/Manager.meta deleted file mode 100644 index d2debce..0000000 --- a/Assets/Domain/Character/Script/Manager.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: a07cad638f33a9e448551011b2f085bf -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Domain/Character/Script/Service.meta b/Assets/Domain/Character/Script/Service.meta deleted file mode 100644 index 375e32d..0000000 --- a/Assets/Domain/Character/Script/Service.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 39450afb9f613d84fab1d2b38836c892 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Domain/Chat/Model/CharacterActionData.cs b/Assets/Domain/Chat/Model/CharacterActionData.cs index df3f487..816e0ba 100644 --- a/Assets/Domain/Chat/Model/CharacterActionData.cs +++ b/Assets/Domain/Chat/Model/CharacterActionData.cs @@ -1,14 +1,24 @@ #nullable enable using UnityEngine; +using ProjectVG.Domain.Character.Service; namespace ProjectVG.Domain.Chat.Model { public class CharacterActionData { /// - /// ์บ๋ฆญํ„ฐ๊ฐ€ ์ˆ˜ํ–‰ํ•  ํ–‰๋™ + /// ์บ๋ฆญํ„ฐ๊ฐ€ ์ˆ˜ํ–‰ํ•  ํ–‰๋™ ํƒ€์ž… /// - public string Action { get; set; } = string.Empty; + public CharacterActionType ActionType { get; set; } = CharacterActionType.Talk; + + /// + /// ์•ก์…˜ ํƒ€์ž…์œผ๋กœ ์ดˆ๊ธฐํ™” + /// + /// ์•ก์…˜ ํƒ€์ž… + public CharacterActionData(CharacterActionType actionType = CharacterActionType.Talk) + { + ActionType = actionType; + } /// /// ํ–‰๋™ ๋ฌธ์ž์—ด๋กœ ์ดˆ๊ธฐํ™” @@ -16,13 +26,32 @@ public class CharacterActionData /// ํ–‰๋™ ๋ฌธ์ž์—ด public CharacterActionData(string? action = null) { - Action = action ?? "talk"; + ActionType = ParseActionString(action); } /// /// ํ–‰๋™์ด ์„ค์ •๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธ /// /// ํ–‰๋™์ด ์„ค์ •๋˜์–ด ์žˆ์œผ๋ฉด true - public bool HasAction() => !string.IsNullOrEmpty(Action); + public bool HasAction() => true; + + /// + /// ๋ฌธ์ž์—ด์„ ์•ก์…˜ ํƒ€์ž…์œผ๋กœ ํŒŒ์‹ฑ + /// + /// ์•ก์…˜ ๋ฌธ์ž์—ด + /// ํŒŒ์‹ฑ๋œ ์•ก์…˜ ํƒ€์ž… + private CharacterActionType ParseActionString(string? actionString) + { + if (string.IsNullOrEmpty(actionString)) + return CharacterActionType.Talk; + + return actionString.ToLower() switch + { + "idle" => CharacterActionType.Idle, + "listen" => CharacterActionType.Listen, + "talk" => CharacterActionType.Talk, + _ => CharacterActionType.Idle + }; + } } } \ No newline at end of file diff --git a/Assets/Domain/Chat/Service/ChatSystemManager.cs b/Assets/Domain/Chat/Service/ChatSystemManager.cs index 6fae4b7..40d9f24 100644 --- a/Assets/Domain/Chat/Service/ChatSystemManager.cs +++ b/Assets/Domain/Chat/Service/ChatSystemManager.cs @@ -122,6 +122,13 @@ public async void SendUserMessage(string message) if (!ValidateUserInput(message)) { return; } try { + // ์œ ์ € ๋ฉ”์‹œ์ง€ ์ „์†ก ์‹œ ์บ๋ฆญํ„ฐ๋ฅผ Listen ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ + if (_chracterManager != null) + { + var listenAction = new CharacterActionData(CharacterActionType.Listen); + _chracterManager.PlayAction(listenAction); + } + if (_chatApiService != null) { var response = await _chatApiService.SendChatAsync( message: message, @@ -179,9 +186,11 @@ private async UniTask ProcessMessageAsync(ChatMessage chatMessage) _chatBubblePanel.CreateBubble(Actor.Character, chatMessage.Text); } + Debug.Log(_chracterManager); // ์บ๋ฆญํ„ฐ ์•ก์…˜ ์‹คํ–‰ - if (chatMessage.HasActionData() && _chracterManager != null) + if (_chracterManager != null) { + Debug.Log(chatMessage.ActionData); _chracterManager.PlayAction(chatMessage.ActionData); } @@ -193,6 +202,13 @@ private async UniTask ProcessMessageAsync(ChatMessage chatMessage) float waitTime = CalculateConversationWaitTime(chatMessage); await UniTask.Delay((int)(waitTime * 1000)); + // ๋Œ€ํ™” ์ข…๋ฃŒ ์‹œ ์บ๋ฆญํ„ฐ๋ฅผ Idle ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ + if (_chracterManager != null) + { + var idleAction = new CharacterActionData(CharacterActionType.Idle); + _chracterManager.PlayAction(idleAction); + } + OnConversationEnd?.Invoke(); } catch (Exception ex) { From 594564516175639ddb29207afe572c7727dc8d53 Mon Sep 17 00:00:00 2001 From: WooSH Date: Fri, 22 Aug 2025 00:15:11 +0900 Subject: [PATCH 13/13] =?UTF-8?q?fix:=20=EC=BD=94=EB=93=9C=EB=A0=88?= =?UTF-8?q?=EB=B9=97=20=EC=88=98=EC=A0=95=20=EC=82=AC=ED=95=AD=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{MainSence.unity => MainScene.unity} | 2 +- ...nSence.unity.meta => MainScene.unity.meta} | 0 Assets/Core/Audio/AudioRecorder.cs | 16 +++++++- Assets/Core/Managers/SystemManager.cs | 2 +- .../Character/Script/CharacterManager.cs | 30 ++++++++++++--- .../Character/Script/CharacterModelLoader.cs | 3 +- .../Script/Config/Live2DModelConfig.cs | 12 ++++-- .../Domain/Chat/Service/ChatMessageQueue.cs | 10 +++-- .../Domain/Chat/Service/ChatSystemManager.cs | 38 ++++++++++--------- ProjectSettings/EditorBuildSettings.asset | 2 +- 10 files changed, 76 insertions(+), 39 deletions(-) rename Assets/App/Scenes/{MainSence.unity => MainScene.unity} (99%) rename Assets/App/Scenes/{MainSence.unity.meta => MainScene.unity.meta} (100%) diff --git a/Assets/App/Scenes/MainSence.unity b/Assets/App/Scenes/MainScene.unity similarity index 99% rename from Assets/App/Scenes/MainSence.unity rename to Assets/App/Scenes/MainScene.unity index 8b1be64..c64d012 100644 --- a/Assets/App/Scenes/MainSence.unity +++ b/Assets/App/Scenes/MainScene.unity @@ -708,7 +708,7 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: _chatBubblePanel: {fileID: 2017944365} - _chracterManager: {fileID: 873552998} + _characterManager: {fileID: 873552998} _characterId: 44444444-4444-4444-4444-444444444444 _userId: bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb --- !u!4 &829067253 diff --git a/Assets/App/Scenes/MainSence.unity.meta b/Assets/App/Scenes/MainScene.unity.meta similarity index 100% rename from Assets/App/Scenes/MainSence.unity.meta rename to Assets/App/Scenes/MainScene.unity.meta diff --git a/Assets/Core/Audio/AudioRecorder.cs b/Assets/Core/Audio/AudioRecorder.cs index 0613383..2f65c7e 100644 --- a/Assets/Core/Audio/AudioRecorder.cs +++ b/Assets/Core/Audio/AudioRecorder.cs @@ -98,7 +98,14 @@ public bool StartRecording() _isRecording = true; _recordingStartTime = Time.time; - _recordingClip = Microphone.Start(_currentDevice != null ? _currentDevice : string.Empty, false, _maxRecordingLength, _sampleRate); + _recordingClip = Microphone.Start(_currentDevice ?? string.Empty, false, _maxRecordingLength, _sampleRate); + if (_recordingClip == null) + { + _isRecording = false; + Debug.LogError("[AudioRecorder] ๋งˆ์ดํฌ ์‹œ์ž‘ ์‹คํŒจ: ๋ฐ˜ํ™˜๋œ AudioClip์ด null์ž…๋‹ˆ๋‹ค."); + OnError?.Invoke("๋งˆ์ดํฌ ์‹œ์ž‘ ์‹คํŒจ"); + return false; + } Debug.Log($"[AudioRecorder] ์Œ์„ฑ ๋…น์Œ ์‹œ์ž‘๋จ (์ตœ๋Œ€ {_maxRecordingLength}์ดˆ, {_sampleRate}Hz)"); OnRecordingStarted?.Invoke(); @@ -156,6 +163,11 @@ public bool StartRecording() _isRecording = false; return null; } + finally + { + // ์ค‘๋ณต ํ˜ธ์ถœ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด ์„ฑ๊ณต ๋ถ„๊ธฐ์—์„œ ์ด๋ฏธ ํ˜ธ์ถœํ–ˆ๋‹ค๋ฉด ์˜ต์ €๋ฒ„ ์ธก์—์„œ idempotent ์ฒ˜๋ฆฌ ๊ฐ€์ • + OnRecordingStopped?.Invoke(); + } } /// @@ -316,7 +328,7 @@ private void InitializeMicrophone() // ์›๋ณธ AudioClip ์ •๋ฆฌํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐฉ์ง€ if (_recordingClip != null) { - DestroyImmediate(_recordingClip); + Destroy(_recordingClip); } _recordingClip = processedClip; diff --git a/Assets/Core/Managers/SystemManager.cs b/Assets/Core/Managers/SystemManager.cs index ddc05c0..b33cc2c 100644 --- a/Assets/Core/Managers/SystemManager.cs +++ b/Assets/Core/Managers/SystemManager.cs @@ -211,7 +211,7 @@ public async UniTask TransitionToMainSceneAsync() { try { - await UnityEngine.SceneManagement.SceneManager.LoadSceneAsync("MainSence"); + await UnityEngine.SceneManagement.SceneManager.LoadSceneAsync("MainScene"); Debug.Log("[SystemManager] MainScene ์ „ํ™˜ ์™„๋ฃŒ"); } catch (System.Exception ex) diff --git a/Assets/Domain/Character/Script/CharacterManager.cs b/Assets/Domain/Character/Script/CharacterManager.cs index d7d0547..d1066ca 100644 --- a/Assets/Domain/Character/Script/CharacterManager.cs +++ b/Assets/Domain/Character/Script/CharacterManager.cs @@ -4,6 +4,7 @@ using ProjectVG.Core.Audio; using ProjectVG.Domain.Chat.Model; using System.Threading.Tasks; +using System.Threading; namespace ProjectVG.Domain.Character.Service { @@ -18,6 +19,7 @@ public class CharacterManager : MonoBehaviour private CharacterModelLoader _modelLoader; private GameObject _currentCharacter; private CharacterActionController _currentActionService; + private int _loadVersion = 0; #region Unity Lifecycle void Start() @@ -37,6 +39,12 @@ public void Initialize() Debug.Log($"[CharacterManager] CharacterModelLoader๊ฐ€ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค: {gameObject.name}"); } + if (_modelRegistry == null) + { + Debug.LogError("[CharacterManager] Live2DModelRegistry๊ฐ€ ํ• ๋‹น๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. Inspector์—์„œ ์„ค์ •ํ•˜์„ธ์š”."); + return; + } + // AudioManager์—์„œ Voice AudioSource ๊ฐ€์ ธ์˜ค๊ธฐ var voiceAudioSource = GetVoiceAudioSource(); @@ -69,15 +77,27 @@ public async Task LoadCharacter(string characterId) { if (_modelLoader == null) return; + // ์š”์ฒญ ๋ฒ„์ „ ์ฆ๊ฐ€(์ƒˆ ์š”์ฒญ ์‹๋ณ„์ž) + var requestId = Interlocked.Increment(ref _loadVersion); + // ๊ธฐ์กด ์บ๋ฆญํ„ฐ ์ œ๊ฑฐ UnloadCurrentCharacter(); // ์ƒˆ ์บ๋ฆญํ„ฐ ๋กœ๋“œ - _currentCharacter = await _modelLoader.LoadAndInitializeModelAsync(characterId, _modelTransform); - if (_currentCharacter != null) + var newCharacter = await _modelLoader.LoadAndInitializeModelAsync(characterId, _modelTransform); + + // ๋” ์ตœ์‹  ์š”์ฒญ์ด ์ง„ํ–‰๋˜์—ˆ๋‹ค๋ฉด ํ˜„์žฌ ๊ฒฐ๊ณผ๋Š” ํ๊ธฐ + if (requestId != _loadVersion) { - _currentCharacter.SetActive(true); + if (newCharacter != null) Destroy(newCharacter); + return; + } + + if (newCharacter != null) + { + _currentCharacter = newCharacter; _currentActionService = _currentCharacter.GetComponent(); + _currentCharacter.SetActive(true); Debug.Log($"[CharacterManager] ์บ๋ฆญํ„ฐ ๋กœ๋“œ ์™„๋ฃŒ: {characterId}"); } } @@ -101,9 +121,7 @@ public void UnloadCurrentCharacter() /// ์•ก์…˜ ๋ฐ์ดํ„ฐ public void PlayAction(CharacterActionData actionData) { - Debug.Log(_currentActionService != null && actionData.HasAction()); - Debug.Log(actionData.HasAction()); - if (_currentActionService != null && actionData.HasAction()) + if (_currentActionService != null && actionData.HasAction()) { _currentActionService.PlayAction(actionData.ActionType); } diff --git a/Assets/Domain/Character/Script/CharacterModelLoader.cs b/Assets/Domain/Character/Script/CharacterModelLoader.cs index f46ca49..c7f4cea 100644 --- a/Assets/Domain/Character/Script/CharacterModelLoader.cs +++ b/Assets/Domain/Character/Script/CharacterModelLoader.cs @@ -117,9 +117,8 @@ private void SetupLipSync(GameObject modelInstance, Live2DModelConfig config) Debug.LogWarning($"[CharacterModelLoader] Voice AudioSource๊ฐ€ null์ž…๋‹ˆ๋‹ค. ๋ฆฝ์‹ฑํฌ๊ฐ€ ๋™์ž‘ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: {modelInstance.name}"); } else { Debug.Log($"[CharacterModelLoader] Voice AudioSource ์„ค์ • ์™„๋ฃŒ: {modelInstance.name}, AudioSource: {_voiceAudioSource.name}"); + mouthController.AudioInput = _voiceAudioSource; } - - mouthController.AudioInput = _voiceAudioSource; mouthController.Gain = config.Gain; mouthController.Smoothing = config.Smoothing; diff --git a/Assets/Domain/Character/Script/Config/Live2DModelConfig.cs b/Assets/Domain/Character/Script/Config/Live2DModelConfig.cs index 369a05f..cee40fa 100644 --- a/Assets/Domain/Character/Script/Config/Live2DModelConfig.cs +++ b/Assets/Domain/Character/Script/Config/Live2DModelConfig.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using UnityEngine; +using UnityEngine.Serialization; namespace ProjectVG.Domain.Character.Live2D.Model { @@ -75,7 +76,7 @@ public class ActionMapping [Space(2)] [Tooltip("์‹œ์„  ์ถ”์  ์‚ฌ์šฉ ์—ฌ๋ถ€")] - [SerializeField] private bool isLockAtActive = true; + [SerializeField] private bool isLookAtActive = true; [Tooltip("์‹œ์„  ๋ฏผ๊ฐ๋„ (๊ฐ’์ด ํด์ˆ˜๋ก ํšŒ์ „์ด ์ปค์ง)")] [Range(0f, 30f)] @@ -83,7 +84,8 @@ public class ActionMapping [Tooltip("์‹œ์„  ๋ฐ˜์‘ ์†๋„ (๊ฐ’์ด ์ž‘์„์ˆ˜๋ก ๋น ๋ฆ„)")] [Range(0f, 5f)] - [SerializeField] private float lockAtDamping = 0.0f; + [SerializeField, FormerlySerializedAs("lockAtDamping")] + private float lookAtDamping = 0.0f; [Space(5)] [Header("โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€")] @@ -152,9 +154,11 @@ public class ActionMapping public List ActionMappings => actionMappings; // ์„ธ๋ถ€ ์„ค์ • - public bool IsLockAtActive => isLockAtActive; + public bool IsLookAtActive => isLookAtActive; public float LookSensitivity => lookSensitivity; - public float LockAtDamping => lockAtDamping; + [Obsolete("Use LookAtDamping instead.")] + public float LockAtDamping => lookAtDamping; + public float LookAtDamping => lookAtDamping; public float Gain => gain; public float Smoothing => smoothing; public bool UseLipSync => useLipSync; diff --git a/Assets/Domain/Chat/Service/ChatMessageQueue.cs b/Assets/Domain/Chat/Service/ChatMessageQueue.cs index d256ada..f96e7ca 100644 --- a/Assets/Domain/Chat/Service/ChatMessageQueue.cs +++ b/Assets/Domain/Chat/Service/ChatMessageQueue.cs @@ -79,10 +79,12 @@ public void Clear() /// public async UniTaskVoid ProcessQueueAsync(Func processAction) { - if (_isProcessing) - return; - - _isProcessing = true; + lock (_queueLock) + { + if (_isProcessing) + return; + _isProcessing = true; + } try { while (true) { diff --git a/Assets/Domain/Chat/Service/ChatSystemManager.cs b/Assets/Domain/Chat/Service/ChatSystemManager.cs index 40d9f24..de919b9 100644 --- a/Assets/Domain/Chat/Service/ChatSystemManager.cs +++ b/Assets/Domain/Chat/Service/ChatSystemManager.cs @@ -1,6 +1,7 @@ #nullable enable using System; using System.Collections; +using System.Threading; using UnityEngine; using Cysharp.Threading.Tasks; using ProjectVG.Core.Audio; @@ -21,7 +22,7 @@ public class ChatSystemManager : MonoBehaviour { [Header("Components")] [SerializeField] private ChatBubblePanel? _chatBubblePanel; - [SerializeField] private CharacterManager? _chracterManager; + [SerializeField] private CharacterManager? _characterManager; [Header("Chat Settings")] [SerializeField] private string _characterId = "44444444-4444-4444-4444-444444444444"; @@ -33,6 +34,7 @@ public class ChatSystemManager : MonoBehaviour private ChatApiService? _chatApiService; private bool _isInitialized = false; + private CancellationTokenSource _cts = new(); public bool IsInitialized => _isInitialized; public event Action? OnError; @@ -90,6 +92,10 @@ public void Initialize() _messageQueue = new ChatMessageQueue(); _chatApiService = ApiServiceManager.Instance.Chat; + if (_messageQueue != null) { + _messageQueue.OnError += (msg) => OnError?.Invoke(msg); + } + if (_webSocketManager != null) { _webSocketManager.OnChatMessageReceived += ProcessCharacterMessage; } @@ -104,6 +110,8 @@ public void Initialize() private void OnDestroy() { + _cts.Cancel(); + _cts.Dispose(); if (_webSocketManager != null) { _webSocketManager.OnChatMessageReceived -= ProcessCharacterMessage; } @@ -123,10 +131,9 @@ public async void SendUserMessage(string message) try { // ์œ ์ € ๋ฉ”์‹œ์ง€ ์ „์†ก ์‹œ ์บ๋ฆญํ„ฐ๋ฅผ Listen ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ - if (_chracterManager != null) - { + if (_characterManager != null) { var listenAction = new CharacterActionData(CharacterActionType.Listen); - _chracterManager.PlayAction(listenAction); + _characterManager.PlayAction(listenAction); } if (_chatApiService != null) { @@ -186,27 +193,22 @@ private async UniTask ProcessMessageAsync(ChatMessage chatMessage) _chatBubblePanel.CreateBubble(Actor.Character, chatMessage.Text); } - Debug.Log(_chracterManager); - // ์บ๋ฆญํ„ฐ ์•ก์…˜ ์‹คํ–‰ - if (_chracterManager != null) - { - Debug.Log(chatMessage.ActionData); - _chracterManager.PlayAction(chatMessage.ActionData); + if (_characterManager != null) { + _characterManager.PlayAction(chatMessage.ActionData); } - + if (chatMessage.VoiceData != null && _audioManager != null) { _audioManager.PlayVoiceAsync(chatMessage.VoiceData).Forget(); } float waitTime = CalculateConversationWaitTime(chatMessage); - await UniTask.Delay((int)(waitTime * 1000)); + await UniTask.Delay(TimeSpan.FromSeconds(waitTime), cancellationToken: _cts.Token); // ๋Œ€ํ™” ์ข…๋ฃŒ ์‹œ ์บ๋ฆญํ„ฐ๋ฅผ Idle ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ - if (_chracterManager != null) - { + if (_characterManager != null) { var idleAction = new CharacterActionData(CharacterActionType.Idle); - _chracterManager.PlayAction(idleAction); + _characterManager.PlayAction(idleAction); } OnConversationEnd?.Invoke(); @@ -223,15 +225,15 @@ private async UniTask ProcessMessageAsync(ChatMessage chatMessage) private float CalculateConversationWaitTime(ChatMessage chatMessage) { float baseTime = 0f; - + if (chatMessage.VoiceData != null && chatMessage.VoiceData.IsPlayable()) { baseTime = chatMessage.VoiceData.Length; } - + if (baseTime <= 0f) { baseTime = 2f; } - + return baseTime + 0.5f; } diff --git a/ProjectSettings/EditorBuildSettings.asset b/ProjectSettings/EditorBuildSettings.asset index 7bb2c5f..aae99c7 100644 --- a/ProjectSettings/EditorBuildSettings.asset +++ b/ProjectSettings/EditorBuildSettings.asset @@ -6,7 +6,7 @@ EditorBuildSettings: serializedVersion: 2 m_Scenes: - enabled: 1 - path: Assets/App/Scenes/MainSence.unity + path: Assets/App/Scenes/MainScene.unity guid: 0845d716b4db3d745a759f81d547dea6 - enabled: 1 path: Assets/App/Scenes/StartSence.unity