feat: 드로그 콤보 스킬 자산을 타격 단위로 재구성

- RebuildDrogCombatAssets를 갱신해 기본기/강타/발구르기 콤보를 타격 단위 스킬 시퀀스로 생성하도록 변경
- 외부 후보 모션을 드로그 로컬 애니메이션 클립으로 복제하고 콤보 관련 스킬을 루트모션 사용 기준으로 재생하도록 정리
- 레거시 단일 콤보 스킬/클립/이펙트를 제거하고 드로그 리뷰 씬 삭제를 함께 반영
This commit is contained in:
2026-04-06 15:07:18 +09:00
parent 48c629bf17
commit daaf54169a
100 changed files with 767817 additions and 135437 deletions

View File

@@ -23,6 +23,13 @@ namespace Colosseum.Editor
private const string PatternsFolder = "Assets/_Game/Data/Patterns";
private const string EffectsFolder = "Assets/_Game/Data/Skills/Effects";
private const string ExecutionTelegraphAbnormalityPath = "Assets/_Game/Data/Abnormalities/Data_Abnormality_Drog_집행준비.asset";
private const string LightCombo01BSourcePath = "Assets/External/Animations/AnimationSwordCombat/Animations/Sidekick/Attack/LightCombo01/A_MOD_SWD_Attack_LightCombo01B_RM_Neut.fbx";
private const string HeavyCombo01BSourcePath = "Assets/External/Animations/AnimationSwordCombat/Animations/Sidekick/Attack/HeavyCombo01/A_MOD_SWD_Attack_HeavyCombo01B_RM_Neut.fbx";
private const string HeavyCombo01CSourcePath = "Assets/External/Animations/AnimationSwordCombat/Animations/Sidekick/Attack/HeavyCombo01/A_MOD_SWD_Attack_HeavyCombo01C_RM_Neut.fbx";
private const string ZweihanderAttack013SourcePath = "Assets/External/Animations/Knight_Zweihander_Animset/Animation/Attack/Root/Zweihander_Attack01_3_Root.FBX";
private const string PunchLSourcePath = "Assets/External/Animations/Mixamo/펀치L.fbx";
private const string PunchRSourcePath = "Assets/External/Animations/Mixamo/펀치R.fbx";
private const string KickRSourcePath = "Assets/External/Animations/Mixamo/킥R.fbx";
[MenuItem("Tools/Colosseum/Rebuild Drog Combat Assets")]
private static void Rebuild()
@@ -35,6 +42,7 @@ namespace Colosseum.Editor
EnsureFolder("Assets/_Game/Data/Skills");
EnsureFolder("Assets/_Game/Data/Patterns");
EnsureFolder("Assets/_Game/Data/Skills/Effects");
DeleteLegacyComboAssets();
AbnormalityData executionTelegraph = AssetDatabase.LoadAssetAtPath<AbnormalityData>(ExecutionTelegraphAbnormalityPath);
if (executionTelegraph == null)
@@ -43,12 +51,26 @@ namespace Colosseum.Editor
return;
}
AnimationClip combo1Clip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_타1_0.anim");
AnimationClip combo2Clip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_연타2_0.anim");
AnimationClip slamClip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_강타_0.anim");
AnimationClip combo3Clip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_연타3_0.anim");
AnimationClip combo4Clip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_연타4_0.anim");
AnimationClip stompClip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_발구르기_0.anim");
AnimationClip comboBasic1Hit1Clip0 = EnsureClipFromSource($"{AnimationsFolder}/Anim_Drog_콤보-기본기1_1_0.anim", $"{AnimationsFolder}/Anim_Drog_타1R_0.anim");
AnimationClip comboBasic1Hit1Clip1 = EnsureClipFromSource($"{AnimationsFolder}/Anim_Drog_콤보-기본기1_1_1.anim", $"{AnimationsFolder}/Anim_Drog_평타1R_1.anim");
AnimationClip comboBasic1Hit2Clip = EnsureClipFromSource($"{AnimationsFolder}/Anim_Drog_콤보-기본기1_2_0.anim", LightCombo01BSourcePath, "A_MOD_SWD_Attack_LightCombo01B_RM_Neut");
AnimationClip comboBasic2Hit1Clip0 = EnsureClipFromSource($"{AnimationsFolder}/Anim_Drog_콤보-기본기2_1_0.anim", $"{AnimationsFolder}/Anim_Drog_평타2R_0.anim");
AnimationClip comboBasic2Hit1Clip1 = EnsureClipFromSource($"{AnimationsFolder}/Anim_Drog_콤보-기본기2_1_1.anim", $"{AnimationsFolder}/Anim_Drog_평타2R_1.anim");
AnimationClip comboBasic2Hit1Clip2 = EnsureClipFromSource($"{AnimationsFolder}/Anim_Drog_콤보-기본기2_1_2.anim", $"{AnimationsFolder}/Anim_Drog_평타2R_2.anim");
AnimationClip comboBasic2Hit2Clip = EnsureClipFromSource($"{AnimationsFolder}/Anim_Drog_콤보-기본기2_2_0.anim", HeavyCombo01CSourcePath, "A_MOD_SWD_Attack_HeavyCombo01C_RM_Neut");
AnimationClip comboBasic3Hit1Clip = EnsureClipFromSource($"{AnimationsFolder}/Anim_Drog_콤보-기본기3_1_0.anim", PunchLSourcePath, "mixamo.com");
AnimationClip comboBasic3Hit2Clip = EnsureClipFromSource($"{AnimationsFolder}/Anim_Drog_콤보-기본기3_2_0.anim", PunchRSourcePath, "mixamo.com");
AnimationClip comboBasic3Hit3Clip = EnsureClipFromSource($"{AnimationsFolder}/Anim_Drog_콤보-기본기3_3_0.anim", KickRSourcePath, "mixamo.com");
AnimationClip comboSlamHit1Clip = EnsureClipFromSource($"{AnimationsFolder}/Anim_Drog_콤보-강타_1_0.anim", HeavyCombo01BSourcePath, "A_MOD_SWD_Attack_HeavyCombo01B_RM_Neut");
AnimationClip comboSlamHit2Clip = EnsureClipFromSource($"{AnimationsFolder}/Anim_Drog_콤보-강타_2_0.anim", LightCombo01BSourcePath, "A_MOD_SWD_Attack_LightCombo01B_RM_Neut");
AnimationClip slamClip = EnsureClipFromSource($"{AnimationsFolder}/Anim_Drog_강타_0.anim", $"{AnimationsFolder}/Anim_Drog_강타R_0.anim");
AnimationClip comboStompHit1Clip = EnsureClipFromSource($"{AnimationsFolder}/Anim_Drog_콤보-발구르기_1_0.anim", ZweihanderAttack013SourcePath, "Zweihander_Attack01_3_Root");
AnimationClip comboStompHit2Clip = EnsureClipFromSource($"{AnimationsFolder}/Anim_Drog_콤보-발구르기_2_0.anim", HeavyCombo01CSourcePath, "A_MOD_SWD_Attack_HeavyCombo01C_RM_Neut");
AnimationClip stompClip = EnsureClipFromSource($"{AnimationsFolder}/Anim_Drog_발구르기_0.anim", $"{AnimationsFolder}/Anim_Drog_발구르기_0.anim");
AnimationClip leapPrepareClip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_도약_준비_0.anim");
AnimationClip leapAirClip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_도약_공중_0.anim");
AnimationClip leapLandingClip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_도약_착지_0.anim");
@@ -60,11 +82,11 @@ namespace Colosseum.Editor
AnimationClip executionHit2Clip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_집행_연타2_0.anim");
AnimationClip executionHit3Clip = EnsurePlaceholderClip($"{AnimationsFolder}/Anim_Drog_집행_연타3_0.anim");
DamageEffect combo1Damage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_연타1_0_데미지.asset",
24f,
DamageEffect comboBasic1Hit1Damage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_콤보-기본기1_1_0_데미지.asset",
22f,
DamageType.Physical,
0.75f,
0.65f,
AreaShapeType.Fan,
3.25f,
1.25f,
@@ -72,11 +94,23 @@ namespace Colosseum.Editor
42f,
AreaCenterType.Caster);
DamageEffect combo2Damage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_연타2_0_데미지.asset",
30f,
DamageEffect comboBasic1Hit2Damage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_콤보-기본기1_2_0_데미지.asset",
16f,
DamageType.Physical,
0.9f,
0.5f,
AreaShapeType.Fan,
3.3f,
1.25f,
3.3f,
40f,
AreaCenterType.Caster);
DamageEffect comboBasic2Hit1Damage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_콤보-기본기2_1_0_데미지.asset",
26f,
DamageType.Physical,
0.8f,
AreaShapeType.Fan,
3.5f,
1.35f,
@@ -84,6 +118,18 @@ namespace Colosseum.Editor
46f,
AreaCenterType.Caster);
DamageEffect comboBasic2Hit2Damage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_콤보-기본기2_2_0_데미지.asset",
20f,
DamageType.Physical,
0.6f,
AreaShapeType.Fan,
3.6f,
1.35f,
3.6f,
42f,
AreaCenterType.Caster);
DamageEffect slamDamage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_강타_0_데미지.asset",
48f,
@@ -106,28 +152,88 @@ namespace Colosseum.Editor
32f,
AreaCenterType.Caster);
DamageEffect combo3Damage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_연타3_0_데미지.asset",
26f,
DamageEffect comboBasic3Hit1Damage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_콤보-기본기3_1_0_데미지.asset",
12f,
DamageType.Physical,
0.8f,
0.35f,
AreaShapeType.Fan,
2.6f,
1.1f,
2.6f,
55f,
AreaCenterType.Caster);
DamageEffect comboBasic3Hit2Damage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_콤보-기본기3_2_0_데미지.asset",
12f,
DamageType.Physical,
0.35f,
AreaShapeType.Fan,
2.6f,
1.1f,
2.6f,
55f,
AreaCenterType.Caster);
DamageEffect comboBasic3Hit3Damage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_콤보-기본기3_3_0_데미지.asset",
18f,
DamageType.Physical,
0.55f,
AreaShapeType.Fan,
3.1f,
1.15f,
3.1f,
68f,
AreaCenterType.Caster);
DamageEffect comboSlamHit1Damage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_콤보-강타_1_0_데미지.asset",
20f,
DamageType.Physical,
0.6f,
AreaShapeType.Fan,
3.6f,
1.3f,
3.6f,
55f,
52f,
AreaCenterType.Caster);
DamageEffect combo4Damage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_연타4_0_데미지.asset",
28f,
DamageEffect comboSlamHit2Damage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_콤보-강타_2_0_데미지.asset",
16f,
DamageType.Physical,
0.85f,
0.45f,
AreaShapeType.Fan,
3.8f,
3.2f,
1.15f,
3.2f,
42f,
AreaCenterType.Caster);
DamageEffect comboStompHit1Damage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_콤보-발구르기_1_0_데미지.asset",
22f,
DamageType.Physical,
0.65f,
AreaShapeType.Fan,
3.9f,
1.35f,
3.8f,
58f,
3.9f,
60f,
AreaCenterType.Caster);
DamageEffect comboStompHit2Damage = CreateDamageEffect(
$"{EffectsFolder}/Data_SkillEffect_Drog_콤보-발구르기_2_0_데미지.asset",
18f,
DamageType.Physical,
0.5f,
AreaShapeType.Fan,
3.6f,
1.25f,
3.6f,
44f,
AreaCenterType.Caster);
DamageEffect stompDamage = CreateDamageEffect(
@@ -251,75 +357,159 @@ namespace Colosseum.Editor
180f,
AreaCenterType.Caster);
SkillData combo1Skill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_연타1.asset",
"연타1",
"기본 루프의 첫 타격입니다.",
combo1Clip,
SkillData comboBasic1Hit1Skill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_콤보-기본기1_1.asset",
"콤보-기본기1 1타",
"기본기 콤보1의 첫 타격입니다.",
new[] { comboBasic1Hit1Clip0, comboBasic1Hit1Clip1 },
1f,
SkillCastTargetTrackingMode.FaceTarget,
false,
true,
true,
false,
combo1Damage);
comboBasic1Hit1Damage);
SkillData combo2Skill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_연타2.asset",
"연타2",
"기본 루프의 두 번째 타격입니다.",
combo2Clip,
SkillData comboBasic1Hit2Skill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_콤보-기본기1_2.asset",
"콤보-기본기1 2타",
"기본기 콤보1의 후속 타격입니다.",
new[] { comboBasic1Hit2Clip },
1f,
SkillCastTargetTrackingMode.FaceTarget,
false,
true,
true,
false,
combo2Damage);
comboBasic1Hit2Damage);
SkillData comboBasic2Hit1Skill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_콤보-기본기2_1.asset",
"콤보-기본기2 1타",
"기본기 콤보2의 시작 타격입니다.",
new[] { comboBasic2Hit1Clip0, comboBasic2Hit1Clip1, comboBasic2Hit1Clip2 },
1f,
SkillCastTargetTrackingMode.FaceTarget,
true,
true,
false,
comboBasic2Hit1Damage);
SkillData comboBasic2Hit2Skill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_콤보-기본기2_2.asset",
"콤보-기본기2 2타",
"기본기 콤보2의 마무리 타격입니다.",
new[] { comboBasic2Hit2Clip },
1f,
SkillCastTargetTrackingMode.FaceTarget,
true,
true,
false,
comboBasic2Hit2Damage);
SkillData slamSkill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_강타.asset",
"강타",
"정면 관리 실패를 응징하는 강한 일격입니다.",
slamClip,
new[] { slamClip },
1f,
SkillCastTargetTrackingMode.FaceTarget,
false,
true,
true,
false,
slamDamage,
slamDown);
SkillData combo3Skill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_연타3.asset",
"연타3",
"강타로 이어지는 선행 타격입니다.",
combo3Clip,
SkillData comboBasic3Hit1Skill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_콤보-기본기3_1.asset",
"콤보-기본기3 1타",
"기본기 콤보3의 첫 번째 타격입니다.",
new[] { comboBasic3Hit1Clip },
1f,
SkillCastTargetTrackingMode.FaceTarget,
false,
true,
true,
false,
combo3Damage);
comboBasic3Hit1Damage);
SkillData combo4Skill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_연타4.asset",
"연타4",
"발구르기로 이어지는 압박용 선행 타격입니다.",
combo4Clip,
SkillData comboBasic3Hit2Skill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_콤보-기본기3_2.asset",
"콤보-기본기3 2타",
"기본기 콤보3의 두 번째 타격입니다.",
new[] { comboBasic3Hit2Clip },
1f,
SkillCastTargetTrackingMode.FaceTarget,
false,
true,
true,
false,
combo4Damage);
comboBasic3Hit2Damage);
SkillData comboBasic3Hit3Skill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_콤보-기본기3_3.asset",
"콤보-기본기3 3타",
"기본기 콤보3의 발차기 마무리입니다.",
new[] { comboBasic3Hit3Clip },
1f,
SkillCastTargetTrackingMode.FaceTarget,
true,
true,
false,
comboBasic3Hit3Damage);
SkillData comboSlamHit1Skill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_콤보-강타_1.asset",
"콤보-강타 1타",
"강타 콤보의 첫 선행 타격입니다.",
new[] { comboSlamHit1Clip },
1f,
SkillCastTargetTrackingMode.FaceTarget,
true,
true,
false,
comboSlamHit1Damage);
SkillData comboSlamHit2Skill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_콤보-강타_2.asset",
"콤보-강타 2타",
"강타로 이어지는 두 번째 선행 타격입니다.",
new[] { comboSlamHit2Clip },
1f,
SkillCastTargetTrackingMode.FaceTarget,
true,
true,
false,
comboSlamHit2Damage);
SkillData comboStompHit1Skill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_콤보-발구르기_1.asset",
"콤보-발구르기 1타",
"발구르기 콤보의 첫 선행 타격입니다.",
new[] { comboStompHit1Clip },
1f,
SkillCastTargetTrackingMode.FaceTarget,
true,
true,
false,
comboStompHit1Damage);
SkillData comboStompHit2Skill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_콤보-발구르기_2.asset",
"콤보-발구르기 2타",
"발구르기로 연결되는 두 번째 선행 타격입니다.",
new[] { comboStompHit2Clip },
1f,
SkillCastTargetTrackingMode.FaceTarget,
true,
true,
false,
comboStompHit2Damage);
SkillData stompSkill = CreateSkill(
$"{SkillsFolder}/Data_Skill_Drog_발구르기.asset",
"발구르기",
"근접 측후방 전체를 흔드는 광역 압박입니다.",
stompClip,
new[] { stompClip },
1f,
SkillCastTargetTrackingMode.None,
false,
true,
true,
false,
stompDamage,
@@ -329,7 +519,7 @@ namespace Colosseum.Editor
$"{SkillsFolder}/Data_Skill_Drog_도약_준비.asset",
"도약 준비",
"원거리 이탈 대상에게 시선을 고정합니다.",
leapPrepareClip,
new[] { leapPrepareClip },
1f,
SkillCastTargetTrackingMode.FaceTarget,
false,
@@ -340,7 +530,7 @@ namespace Colosseum.Editor
$"{SkillsFolder}/Data_Skill_Drog_도약_공중.asset",
"도약 공중",
"대상 위치로 도약하는 이동 스텝입니다.",
leapAirClip,
new[] { leapAirClip },
1f,
SkillCastTargetTrackingMode.MoveTowardTarget,
true,
@@ -351,7 +541,7 @@ namespace Colosseum.Editor
$"{SkillsFolder}/Data_Skill_Drog_도약_착지.asset",
"도약 착지",
"도약 종료 시 주변에 피해와 넉백을 줍니다.",
leapLandingClip,
new[] { leapLandingClip },
1f,
SkillCastTargetTrackingMode.None,
false,
@@ -364,7 +554,7 @@ namespace Colosseum.Editor
$"{SkillsFolder}/Data_Skill_Drog_밟기.asset",
"밟기",
"다운된 대상을 후속 압박으로 처벌합니다.",
stepClip,
new[] { stepClip },
1f,
SkillCastTargetTrackingMode.FaceTarget,
false,
@@ -377,7 +567,7 @@ namespace Colosseum.Editor
$"{SkillsFolder}/Data_Skill_Drog_투척.asset",
"투척",
"부활 시전자나 원거리 대상을 견제하는 유틸리티 공격입니다.",
throwClip,
new[] { throwClip },
1f,
SkillCastTargetTrackingMode.FaceTarget,
false,
@@ -389,7 +579,7 @@ namespace Colosseum.Editor
$"{SkillsFolder}/Data_Skill_Drog_포효.asset",
"포효",
"Phase 3 진입을 알리는 전환 신호입니다.",
roarClip,
new[] { roarClip },
0.9f,
SkillCastTargetTrackingMode.None,
false,
@@ -400,7 +590,7 @@ namespace Colosseum.Editor
$"{SkillsFolder}/Data_Skill_Drog_집행_준비.asset",
"집행 준비",
"집행 돌입 전 자세를 고정합니다.",
executionReadyClip,
new[] { executionReadyClip },
0.85f,
SkillCastTargetTrackingMode.None,
false,
@@ -411,7 +601,7 @@ namespace Colosseum.Editor
$"{SkillsFolder}/Data_Skill_Drog_집행_연타1.asset",
"집행 연타1",
"집행의 첫 압박 타격입니다.",
executionHit1Clip,
new[] { executionHit1Clip },
1f,
SkillCastTargetTrackingMode.None,
false,
@@ -423,7 +613,7 @@ namespace Colosseum.Editor
$"{SkillsFolder}/Data_Skill_Drog_집행_연타2.asset",
"집행 연타2",
"집행의 두 번째 압박 타격입니다.",
executionHit2Clip,
new[] { executionHit2Clip },
1.1f,
SkillCastTargetTrackingMode.None,
false,
@@ -435,7 +625,7 @@ namespace Colosseum.Editor
$"{SkillsFolder}/Data_Skill_Drog_집행_연타3.asset",
"집행 연타3",
"집행의 세 번째 압박 타격입니다.",
executionHit3Clip,
new[] { executionHit3Clip },
1.2f,
SkillCastTargetTrackingMode.None,
false,
@@ -444,8 +634,8 @@ namespace Colosseum.Editor
executionHit3);
CreatePattern(
$"{PatternsFolder}/Data_Pattern_Drog_연타1.asset",
"연타1",
$"{PatternsFolder}/Data_Pattern_Drog_콤보-기본기1.asset",
"콤보-기본기1",
PatternCategory.Basic,
false,
true,
@@ -453,23 +643,39 @@ namespace Colosseum.Editor
2.5f,
1,
false,
PatternStepDefinition.CreateSkillStep(combo1Skill));
PatternStepDefinition.CreateSkillStep(comboBasic1Hit1Skill),
PatternStepDefinition.CreateSkillStep(comboBasic1Hit2Skill));
CreatePattern(
$"{PatternsFolder}/Data_Pattern_Drog_연타2.asset",
"연타2",
$"{PatternsFolder}/Data_Pattern_Drog_콤보-기본기2.asset",
"콤보-기본기2",
PatternCategory.Basic,
false,
true,
TargetResolveMode.HighestThreat,
2.75f,
3f,
1,
false,
PatternStepDefinition.CreateSkillStep(combo2Skill));
PatternStepDefinition.CreateSkillStep(comboBasic2Hit1Skill),
PatternStepDefinition.CreateSkillStep(comboBasic2Hit2Skill));
CreatePattern(
$"{PatternsFolder}/Data_Pattern_Drog_연타3-강타.asset",
"연타3-강타",
$"{PatternsFolder}/Data_Pattern_Drog_콤보-기본기3.asset",
"콤보-기본기3",
PatternCategory.Basic,
false,
true,
TargetResolveMode.HighestThreat,
3.25f,
1,
false,
PatternStepDefinition.CreateSkillStep(comboBasic3Hit1Skill),
PatternStepDefinition.CreateSkillStep(comboBasic3Hit2Skill),
PatternStepDefinition.CreateSkillStep(comboBasic3Hit3Skill));
CreatePattern(
$"{PatternsFolder}/Data_Pattern_Drog_콤보-강타.asset",
"콤보-강타",
PatternCategory.Basic,
false,
true,
@@ -477,13 +683,14 @@ namespace Colosseum.Editor
4.5f,
1,
false,
PatternStepDefinition.CreateSkillStep(combo3Skill),
PatternStepDefinition.CreateWaitStep(0.15f),
PatternStepDefinition.CreateSkillStep(comboSlamHit1Skill),
PatternStepDefinition.CreateSkillStep(comboSlamHit2Skill),
PatternStepDefinition.CreateWaitStep(0.1f),
PatternStepDefinition.CreateSkillStep(slamSkill));
CreatePattern(
$"{PatternsFolder}/Data_Pattern_Drog_연타4-발구르기.asset",
"연타4-발구르기",
$"{PatternsFolder}/Data_Pattern_Drog_콤보-발구르기.asset",
"콤보-발구르기",
PatternCategory.Basic,
false,
true,
@@ -491,8 +698,9 @@ namespace Colosseum.Editor
5f,
1,
false,
PatternStepDefinition.CreateSkillStep(combo4Skill),
PatternStepDefinition.CreateWaitStep(0.15f),
PatternStepDefinition.CreateSkillStep(comboStompHit1Skill),
PatternStepDefinition.CreateSkillStep(comboStompHit2Skill),
PatternStepDefinition.CreateWaitStep(0.1f),
PatternStepDefinition.CreateSkillStep(stompSkill));
CreatePattern(
@@ -579,6 +787,36 @@ namespace Colosseum.Editor
AssetDatabase.CreateFolder(parent, Path.GetFileName(path));
}
/// <summary>
/// 이전 단일 스킬 기반 콤보 자산을 제거합니다.
/// </summary>
private static void DeleteLegacyComboAssets()
{
string[] legacyPaths =
{
$"{AnimationsFolder}/Anim_Drog_콤보-기본기1_0.anim",
$"{AnimationsFolder}/Anim_Drog_콤보-기본기2_0.anim",
$"{AnimationsFolder}/Anim_Drog_콤보-기본기3_0.anim",
$"{AnimationsFolder}/Anim_Drog_콤보-발구르기_선행_0.anim",
$"{SkillsFolder}/Data_Skill_Drog_콤보-기본기1.asset",
$"{SkillsFolder}/Data_Skill_Drog_콤보-기본기2.asset",
$"{SkillsFolder}/Data_Skill_Drog_콤보-기본기3.asset",
$"{SkillsFolder}/Data_Skill_Drog_콤보-발구르기_선행.asset",
$"{EffectsFolder}/Data_SkillEffect_Drog_콤보-기본기1_0_데미지.asset",
$"{EffectsFolder}/Data_SkillEffect_Drog_콤보-기본기2_0_데미지.asset",
$"{EffectsFolder}/Data_SkillEffect_Drog_콤보-기본기3_0_데미지.asset",
$"{EffectsFolder}/Data_SkillEffect_Drog_콤보-발구르기_선행_0_데미지.asset",
};
for (int i = 0; i < legacyPaths.Length; i++)
{
if (AssetDatabase.LoadMainAssetAtPath(legacyPaths[i]) == null)
continue;
AssetDatabase.DeleteAsset(legacyPaths[i]);
}
}
/// <summary>
/// 지정 경로의 ScriptableObject를 읽거나 새로 생성합니다.
/// </summary>
@@ -613,6 +851,108 @@ namespace Colosseum.Editor
return clip;
}
/// <summary>
/// 소스 클립을 독립 .anim 자산으로 복제하거나 기존 자산을 갱신합니다.
/// </summary>
private static AnimationClip EnsureClipFromSource(string targetPath, string sourcePath, string sourceClipName = null)
{
AnimationClip sourceClip = LoadClipFromPath(sourcePath, sourceClipName);
if (sourceClip == null)
{
Debug.LogWarning($"[DrogCombatAssets] 소스 클립을 찾지 못해 플레이스홀더를 유지합니다: {sourcePath} ({sourceClipName})");
return EnsurePlaceholderClip(targetPath);
}
AnimationClip clonedClip = CloneClip(sourceClip, Path.GetFileNameWithoutExtension(targetPath));
AnimationClip existingClip = AssetDatabase.LoadAssetAtPath<AnimationClip>(targetPath);
if (existingClip == null)
{
AssetDatabase.CreateAsset(clonedClip, targetPath);
return clonedClip;
}
EditorUtility.CopySerialized(clonedClip, existingClip);
existingClip.name = Path.GetFileNameWithoutExtension(targetPath);
EditorUtility.SetDirty(existingClip);
UnityEngine.Object.DestroyImmediate(clonedClip);
return existingClip;
}
/// <summary>
/// 경로에서 AnimationClip을 읽습니다. FBX인 경우 지정 이름의 서브 클립을 우선 사용합니다.
/// </summary>
private static AnimationClip LoadClipFromPath(string path, string preferredClipName = null)
{
AnimationClip directClip = AssetDatabase.LoadAssetAtPath<AnimationClip>(path);
if (directClip != null && (string.IsNullOrEmpty(preferredClipName) || directClip.name == preferredClipName))
return directClip;
UnityEngine.Object[] subAssets = AssetDatabase.LoadAllAssetsAtPath(path);
AnimationClip fallbackClip = null;
for (int i = 0; i < subAssets.Length; i++)
{
if (subAssets[i] is not AnimationClip clip)
continue;
if (!string.IsNullOrEmpty(preferredClipName) && clip.name == preferredClipName)
return clip;
if (fallbackClip == null && !clip.name.StartsWith("__preview__", StringComparison.Ordinal))
fallbackClip = clip;
}
return fallbackClip;
}
/// <summary>
/// 소스 클립의 커브와 이벤트를 복사한 독립 AnimationClip을 생성합니다.
/// </summary>
private static AnimationClip CloneClip(AnimationClip sourceClip, string targetName)
{
AnimationClip clonedClip = new AnimationClip
{
name = targetName,
frameRate = sourceClip.frameRate,
legacy = sourceClip.legacy,
wrapMode = sourceClip.wrapMode,
localBounds = sourceClip.localBounds,
};
EditorCurveBinding[] bindings = AnimationUtility.GetCurveBindings(sourceClip);
for (int i = 0; i < bindings.Length; i++)
{
AnimationCurve curve = AnimationUtility.GetEditorCurve(sourceClip, bindings[i]);
if (curve == null || curve.keys.Length == 0)
continue;
clonedClip.SetCurve(bindings[i].path, bindings[i].type, bindings[i].propertyName, curve);
}
AnimationEvent[] sourceEvents = AnimationUtility.GetAnimationEvents(sourceClip);
if (sourceEvents.Length > 0)
{
var clonedEvents = new AnimationEvent[sourceEvents.Length];
for (int i = 0; i < sourceEvents.Length; i++)
{
clonedEvents[i] = new AnimationEvent
{
time = sourceEvents[i].time,
functionName = sourceEvents[i].functionName,
floatParameter = sourceEvents[i].floatParameter,
intParameter = sourceEvents[i].intParameter,
stringParameter = sourceEvents[i].stringParameter,
objectReferenceParameter = sourceEvents[i].objectReferenceParameter,
messageOptions = sourceEvents[i].messageOptions,
};
}
AnimationUtility.SetAnimationEvents(clonedClip, clonedEvents);
}
return clonedClip;
}
/// <summary>
/// Effect/Skill/Pattern의 공통 Object 리스트를 설정합니다.
/// </summary>
@@ -768,7 +1108,7 @@ namespace Colosseum.Editor
string path,
string skillName,
string description,
AnimationClip clip,
IReadOnlyList<AnimationClip> clips,
float animationSpeed,
SkillCastTargetTrackingMode trackingMode,
bool useRootMotion,
@@ -799,7 +1139,17 @@ namespace Colosseum.Editor
serializedObject.FindProperty("maxGemSlotCount").intValue = 0;
serializedObject.FindProperty("triggeredEffects").arraySize = 0;
SetObjectList(serializedObject, "animationClips", new UnityEngine.Object[] { clip });
var clipObjects = new List<UnityEngine.Object>();
if (clips != null)
{
for (int i = 0; i < clips.Count; i++)
{
if (clips[i] != null)
clipObjects.Add(clips[i]);
}
}
SetObjectList(serializedObject, "animationClips", clipObjects);
var effectObjects = new List<UnityEngine.Object>();
if (castStartEffects != null)
@@ -813,6 +1163,7 @@ namespace Colosseum.Editor
SetObjectList(serializedObject, "castStartEffects", effectObjects);
serializedObject.ApplyModifiedPropertiesWithoutUndo();
skill.RefreshAnimationClips();
EditorUtility.SetDirty(skill);
return skill;