using System; using System.Collections.Generic; using UnityEngine; #if UNITY_EDITOR using System.IO; using System.Text.RegularExpressions; using UnityEditor; #endif namespace Colosseum.Skills { /// /// 프로젝트의 모든 스킬 데이터를 중앙에서 관리하는 ScriptableObject. /// 이 에셋을 프리팹이나 씬에서 참조하면, 등록된 모든 스킬이 빌드에 자동 포함됩니다. /// [CreateAssetMenu(fileName = "SkillRegistry", menuName = "Colosseum/SkillRegistry")] public class SkillRegistry : ScriptableObject { private const string PlayerSkillSearchFolder = "Assets/_Game/Data/Skills"; private const string PlayerSkillNameFilter = "_Skill_Player_"; [Header("Player Skills")] [Tooltip("모든 플레이어 스킬 데이터 (에디터에서 자동 수집됨)")] [SerializeField] private List playerSkills = new(); /// /// 등록된 모든 플레이어 스킬 목록 /// public IReadOnlyList PlayerSkills => playerSkills; /// /// 스킬 이름으로 플레이어 스킬을 조회합니다. /// public SkillData GetPlayerSkill(string skillName) { if (string.IsNullOrEmpty(skillName)) return null; for (int i = 0; i < playerSkills.Count; i++) { SkillData skill = playerSkills[i]; if (skill != null && string.Equals(skill.SkillName, skillName, StringComparison.Ordinal)) return skill; } return null; } /// /// 에셋 이름으로 플레이어 스킬을 조회합니다. /// public SkillData GetPlayerSkillByAssetName(string assetName) { if (string.IsNullOrEmpty(assetName)) return null; for (int i = 0; i < playerSkills.Count; i++) { SkillData skill = playerSkills[i]; if (skill != null && string.Equals(skill.name, assetName, StringComparison.Ordinal)) return skill; } return null; } /// /// 이름에 지정 문자열이 포함된 첫 번째 플레이어 스킬을 조회합니다. /// public SkillData FindPlayerSkillByNameContains(string fragment) { if (string.IsNullOrEmpty(fragment)) return null; for (int i = 0; i < playerSkills.Count; i++) { SkillData skill = playerSkills[i]; if (skill != null && skill.name.IndexOf(fragment, StringComparison.OrdinalIgnoreCase) >= 0) return skill; } return null; } #if UNITY_EDITOR /// /// "_Skill_Player_" 이름이 포함된 모든 SkillData를 에디터에서 자동 수집합니다. /// OnValidate에서 호출되어 에셋 변경 시 자동 동기화됩니다. /// public void AutoCollectPlayerSkills() { if (!AssetDatabase.IsValidFolder(PlayerSkillSearchFolder)) { Debug.LogWarning($"[SkillRegistry] 스킬 검색 폴더가 없습니다: {PlayerSkillSearchFolder}", this); return; } string[] guids = AssetDatabase.FindAssets("t:SkillData", new[] { PlayerSkillSearchFolder }); var skills = new List(); foreach (string guid in guids) { string path = AssetDatabase.GUIDToAssetPath(guid); string assetName = Path.GetFileNameWithoutExtension(path); if (assetName.IndexOf(PlayerSkillNameFilter, StringComparison.OrdinalIgnoreCase) >= 0) { SkillData skill = AssetDatabase.LoadAssetAtPath(path); if (skill != null) skills.Add(skill); } } skills.Sort((a, b) => string.Compare(a.name, b.name, StringComparison.Ordinal)); bool changed = playerSkills.Count != skills.Count; if (!changed) { for (int i = 0; i < skills.Count; i++) { if (playerSkills[i] != skills[i]) { changed = true; break; } } } if (changed) { playerSkills.Clear(); playerSkills.AddRange(skills); Debug.Log($"[SkillRegistry] 자동 수집: {skills.Count}개 Player 스킬", this); } } private void OnValidate() { AutoCollectPlayerSkills(); } #endif } }