feat: 아군 타게팅 시스템 구현 — SingleAlly 투사체형 치유/보호막

- 치유/보호막 스킬을 즉발 자가시전에서 투사체형 아군 1인 타겟팅으로 전환

- TargetType.SingleAlly 추가, targetOverride 매개변수로 외부 타겟 주입 지원

- PlayerSkillInput: 카메라 레이캐스트 기반 아군 탐지, 서버 검증, RPC 타겟 ID 전달

- AllyTargetIndicator: 호버 아군 위에 디스크 인디케이터 표시, 사거리/초과 색상 변경

- SpawnEffect: 타겟 방향 회전 보정

- 투사체 스폰 이펙트 에셋 생성 (치유/보호막 각각)

- 인디케이터 프리팹 + URP/Unlit 머티리얼 생성

- Player 프리팹에 AllyTargetIndicator 컴포넌트 추가 및 설정

- Input.mousePosition → Mouse.current.position.ReadValue() 수정 (Input System 호환)
This commit is contained in:
2026-03-31 23:06:13 +09:00
parent 2c6a65d406
commit 8cd2623163
16 changed files with 6744 additions and 112 deletions

View File

@@ -65,6 +65,7 @@ namespace Colosseum.Skills
private bool waitingForEndAnimation; // EndAnimation 종료 대기 중
private int currentRepeatCount = 1;
private int currentIterationIndex = 0;
private GameObject currentTargetOverride;
// 쿨타임 추적
private Dictionary<SkillData, float> cooldownTracker = new Dictionary<SkillData, float>();
@@ -80,6 +81,7 @@ namespace Colosseum.Skills
public Animator Animator => animator;
public SkillCancelReason LastCancelReason => lastCancelReason;
public string LastCancelledSkillName => lastCancelledSkillName;
public GameObject CurrentTargetOverride => currentTargetOverride;
private void Awake()
{
@@ -152,6 +154,25 @@ namespace Colosseum.Skills
/// 슬롯 엔트리 기준으로 스킬 시전
/// </summary>
public bool ExecuteSkill(SkillLoadoutEntry loadoutEntry)
{
currentTargetOverride = null;
return ExecuteSkillInternal(loadoutEntry);
}
/// <summary>
/// 타겟 오버라이드와 함께 스킬 시전.
/// SingleAlly 타입 효과에서 외부 타겟을 사용할 때 호출합니다.
/// </summary>
public bool ExecuteSkill(SkillLoadoutEntry loadoutEntry, GameObject targetOverride)
{
currentTargetOverride = targetOverride;
return ExecuteSkillInternal(loadoutEntry);
}
/// <summary>
/// 스킬 시전 공통 로직
/// </summary>
private bool ExecuteSkillInternal(SkillLoadoutEntry loadoutEntry)
{
SkillData skill = loadoutEntry != null ? loadoutEntry.BaseSkill : null;
if (skill == null)
@@ -217,7 +238,7 @@ namespace Colosseum.Skills
continue;
if (debugMode) Debug.Log($"[Skill] Cast start effect: {effect.name} (index {i})");
effect.ExecuteOnCast(gameObject);
effect.ExecuteOnCast(gameObject, currentTargetOverride);
}
if (currentCastStartAbnormalities.Count <= 0)
@@ -266,7 +287,7 @@ namespace Colosseum.Skills
continue;
if (debugMode) Debug.Log($"[Skill] Immediate self effect: {effect.name} (index {i})");
effect.ExecuteOnCast(gameObject);
effect.ExecuteOnCast(gameObject, currentTargetOverride);
}
}
@@ -478,7 +499,7 @@ namespace Colosseum.Skills
effect.DrawDebugRange(gameObject, debugDrawDuration);
}
effect.ExecuteOnCast(gameObject);
effect.ExecuteOnCast(gameObject, currentTargetOverride);
}
ApplyTriggeredAbnormalities(index, effects);
@@ -564,6 +585,7 @@ namespace Colosseum.Skills
currentCastStartAbnormalities.Clear();
currentTriggeredAbnormalities.Clear();
currentTriggeredTargetsBuffer.Clear();
currentTargetOverride = null;
waitingForEndAnimation = false;
currentRepeatCount = 1;
currentIterationIndex = 0;
@@ -589,7 +611,7 @@ namespace Colosseum.Skills
if (effect == null || effect.TargetType == TargetType.Self)
continue;
effect.CollectTargets(gameObject, currentTriggeredTargetsBuffer);
effect.CollectTargets(gameObject, currentTriggeredTargetsBuffer, currentTargetOverride);
}
if (currentTriggeredTargetsBuffer.Count == 0)