Commit 92f679a6 authored by Gavin An's avatar Gavin An

작업 중

parent 1a214bb6
...@@ -26,6 +26,8 @@ var _auto_target_cooldown: float = 0.0 ...@@ -26,6 +26,8 @@ var _auto_target_cooldown: float = 0.0
# 홀드 상태 변수 # 홀드 상태 변수
var _is_holding: bool = false var _is_holding: bool = false
# 이동 상태 변수
var _is_moving: bool = false
# 3D 모델 및 애니메이션 연동 변수 (정적 타이핑 캐싱) # 3D 모델 및 애니메이션 연동 변수 (정적 타이핑 캐싱)
var _model_instance: Node3D = null var _model_instance: Node3D = null
...@@ -87,9 +89,12 @@ func _physics_process(delta: float) -> void: ...@@ -87,9 +89,12 @@ func _physics_process(delta: float) -> void:
var chase_threshold: float = attack_range + buffer var chase_threshold: float = attack_range + buffer
if dist > chase_threshold: if dist > chase_threshold:
_target_monster = null _target_monster = null
if _is_moving and _nav_agent.target_position:
_is_moving = false
# 2. 적 자동 탐색 (타겟이 없고 수동 강제 이동 방지 쿨다운이 완료, 공격 쿨다운이 끝난 경우) # 2. 적 자동 탐색 (타겟이 없고 수동 강제 이동 방지 쿨다운이 완료, 공격 쿨다운이 끝난 경우)
if not _target_monster and _auto_target_cooldown <= 0.0 and _attack_lock_timer <= 0.0: if not _is_moving and not _target_monster and _auto_target_cooldown <= 0.0 and _attack_lock_timer <= 0.0:
_find_nearest_target() _find_nearest_target()
# 3. 상태에 따른 AI 동작 분기 (XZ 수평 속도를 velocity에 세팅) # 3. 상태에 따른 AI 동작 분기 (XZ 수평 속도를 velocity에 세팅)
...@@ -226,33 +231,10 @@ func _try_attack() -> void: ...@@ -226,33 +231,10 @@ func _try_attack() -> void:
# 공격 비주얼 연출 # 공격 비주얼 연출
_show_attack_visual() _show_attack_visual()
# 공격 애니메이션 재생 길이에 맞춘 이동 제어 락 타이머 설정 # 공격 애니메이션 길이와 유닛별 보정 훅을 이용해 공격 락 시간을 계산합니다.
var anim_length: float = 0.5 # 기본값 var anim_length: float = _get_attack_animation_length()
var play_speed: float = 1.5 var play_speed: float = _get_attack_timing_play_speed()
_attack_lock_timer = _get_attack_lock_duration(anim_length, play_speed)
if _model_instance and _model_anim_player:
var model_name: String = _model_instance.name.to_lower()
var library_prefix: String = model_name + "/"
var attack_key: String = "Slash"
if model_name == "ranger":
attack_key = "Draw"
elif model_name == "mage":
attack_key = "Casting"
var attack_anim: String = library_prefix + attack_key
if _model_anim_player.has_animation(attack_anim):
anim_length = _model_anim_player.get_animation(attack_anim).length
elif _model_anim_player.has_animation("Armature|Armature|Basic_Jump|baselayer"):
anim_length = _model_anim_player.get_animation("Armature|Armature|Basic_Jump|baselayer").length
if unit_data.cooldown < 0.6:
play_speed = clampf(0.6 / unit_data.cooldown, 1.0, 3.0)
# 공격 락 시간 계산 (애니메이션 실재생 속도 반영)
_attack_lock_timer = anim_length / play_speed
# 락 타이머가 공격 쿨다운보다 길어지는 것은 방지
if _attack_lock_timer > unit_data.cooldown:
_attack_lock_timer = unit_data.cooldown
# 데미지 적용 및 발사체 발사 처리 분기 # 데미지 적용 및 발사체 발사 처리 분기
if unit_data.is_ranged: if unit_data.is_ranged:
...@@ -271,7 +253,7 @@ func _try_attack() -> void: ...@@ -271,7 +253,7 @@ func _try_attack() -> void:
# 근접 유닛: 공격 애니메이션 중간 즈음(락아웃 시간의 50% 지점)에 데미지 지연 적용 # 근접 유닛: 공격 애니메이션 중간 즈음(락아웃 시간의 50% 지점)에 데미지 지연 적용
var target_ref: Node3D = _target_monster var target_ref: Node3D = _target_monster
var damage_amount: float = unit_data.damage var damage_amount: float = unit_data.damage
var damage_delay: float = _attack_lock_timer * 0.5 var damage_delay: float = _get_attack_damage_delay(_attack_lock_timer)
_set_melee_move_lock_until_hit(damage_delay) _set_melee_move_lock_until_hit(damage_delay)
if damage_delay > 0.0: if damage_delay > 0.0:
...@@ -285,6 +267,52 @@ func _try_attack() -> void: ...@@ -285,6 +267,52 @@ func _try_attack() -> void:
if target_ref.has_method("take_damage"): if target_ref.has_method("take_damage"):
target_ref.call("take_damage", damage_amount) target_ref.call("take_damage", damage_amount)
## 공격 애니메이션 길이를 계산합니다.
## - 자식 클래스는 애니메이션 이름 훅만 바꾸면 공통 공격 로직을 그대로 사용할 수 있습니다.
func _get_attack_animation_length() -> float:
var anim_length: float = 0.5
if not _model_instance or not _model_anim_player:
return anim_length
var model_name: String = _model_instance.name.to_lower()
var attack_anim: String = _get_attack_animation_name(model_name)
if _model_anim_player.has_animation(attack_anim):
anim_length = _model_anim_player.get_animation(attack_anim).length
elif _model_anim_player.has_animation("Armature|Armature|Basic_Jump|baselayer"):
anim_length = _model_anim_player.get_animation("Armature|Armature|Basic_Jump|baselayer").length
return anim_length
## 모델별 기본 공격 애니메이션 이름을 반환합니다.
func _get_attack_animation_name(model_name: String) -> String:
return ''
# var attack_key: String = "Slash"
# if model_name == "ranger":
# attack_key = "Draw"
# elif model_name == "mage":
# attack_key = "Casting"
# return model_name + "/" + attack_key
## 공격 타이밍 계산에 사용할 기본 재생 속도입니다.
func _get_attack_timing_play_speed() -> float:
var play_speed: float = 1.5
if unit_data and unit_data.cooldown < 0.6:
play_speed = clampf(0.6 / unit_data.cooldown, 1.0, 3.0)
return play_speed
## 공격 모션 유지 시간을 계산합니다.
func _get_attack_lock_duration(anim_length: float, play_speed: float) -> float:
var attack_lock_duration: float = anim_length / play_speed
if attack_lock_duration > unit_data.cooldown:
attack_lock_duration = unit_data.cooldown
return attack_lock_duration
## 근접 공격 데미지가 실제로 들어가는 시점을 계산합니다.
func _get_attack_damage_delay(attack_lock_duration: float) -> float:
return attack_lock_duration * 0.5
## 공격 관련 비주얼 이펙트 연출 ## 공격 관련 비주얼 이펙트 연출
func _show_attack_visual() -> void: func _show_attack_visual() -> void:
# 원거리 타격 시 빔 또는 스파크 이펙트 임시 렌더 # 원거리 타격 시 빔 또는 스파크 이펙트 임시 렌더
...@@ -296,6 +324,7 @@ func _show_attack_visual() -> void: ...@@ -296,6 +324,7 @@ func _show_attack_visual() -> void:
## h 버튼을 누르면 유닛을 제자리에 고정하고 사거리 내의 적만 공격하도록 설정 (Hold) ## h 버튼을 누르면 유닛을 제자리에 고정하고 사거리 내의 적만 공격하도록 설정 (Hold)
func hold_position() -> void: func hold_position() -> void:
_is_holding = true _is_holding = true
_is_moving = false
_target_monster = null _target_monster = null
_has_queued_move_after_attack = false _has_queued_move_after_attack = false
_nav_agent.target_position = global_position _nav_agent.target_position = global_position
...@@ -319,6 +348,7 @@ func move_to(target_pos: Vector3) -> void: ...@@ -319,6 +348,7 @@ func move_to(target_pos: Vector3) -> void:
## 실제 수동 이동 상태 전환을 한곳에서 처리합니다. ## 실제 수동 이동 상태 전환을 한곳에서 처리합니다.
## - 근접 공격의 후딜 캔슬과 원거리 카이팅 모두 이 경로를 공유합니다. ## - 근접 공격의 후딜 캔슬과 원거리 카이팅 모두 이 경로를 공유합니다.
func _apply_move_command(target_pos: Vector3) -> void: func _apply_move_command(target_pos: Vector3) -> void:
_is_moving = true
_is_holding = false # 수동 조작 시 홀드 해제 _is_holding = false # 수동 조작 시 홀드 해제
_target_monster = null # 이동 명령 시 강제 공격 해제 _target_monster = null # 이동 명령 시 강제 공격 해제
_attack_lock_timer = 0.0 # 타격 확정 이후의 후딜 또는 원거리 공격 모션은 이동 명령으로 캔슬합니다. _attack_lock_timer = 0.0 # 타격 확정 이후의 후딜 또는 원거리 공격 모션은 이동 명령으로 캔슬합니다.
......
...@@ -41,34 +41,17 @@ func _setup_unit_visuals() -> void: ...@@ -41,34 +41,17 @@ func _setup_unit_visuals() -> void:
if _model_anim_player: if _model_anim_player:
print("DwarfHero: Linked embedded model & AnimationPlayer: ", m_name) print("DwarfHero: Linked embedded model & AnimationPlayer: ", m_name)
## 드워프의 기본 공격 처리 ## 드워프는 일반 유닛의 "dwarf/Slash"가 아니라 FBX 내장 "Attack" 클립을 사용합니다.
## - BaseUnit의 기본 공격 계산은 "dwarf/Slash" 같은 일반 유닛 애니메이션을 찾으므로, func _get_attack_animation_name(_model_name: String) -> String:
## 드워프 전용 "Attack" 클립 길이를 기준으로 공격 락과 데미지 판정 시간을 다시 맞춥니다. return DWARF_ATTACK_ANIM
func _try_attack() -> void:
if _attack_timer > 0.0 or not _target_monster: ## 드워프 Attack 클립은 길이가 길어 공통 계산보다 짧은 락 비율로 보정합니다.
return func _get_attack_lock_duration(_anim_length: float, _play_speed: float) -> float:
return _get_dwarf_attack_lock_duration()
_attack_timer = unit_data.cooldown
_show_attack_visual() ## 도끼가 몸 앞쪽으로 떨어지는 구간에 데미지가 들어가도록 판정 시점을 살짝 앞당깁니다.
func _get_attack_damage_delay(attack_lock_duration: float) -> float:
# 실제 Attack 애니메이션 길이와 현재 쿨다운을 함께 보아 모션 유지 시간을 계산합니다. return attack_lock_duration * DWARF_ATTACK_DAMAGE_RATIO
# 쿨다운 전체를 락으로 쓰지 않고 약간의 회복 구간을 남겨 다음 공격이 딱딱 끊기지 않게 합니다.
_attack_lock_timer = _get_dwarf_attack_lock_duration()
var target_ref: Node3D = _target_monster
var damage_amount: float = unit_data.damage
var damage_delay: float = _attack_lock_timer * DWARF_ATTACK_DAMAGE_RATIO
_set_melee_move_lock_until_hit(damage_delay)
# 도끼가 몸 앞쪽으로 떨어지는 구간에 데미지가 들어가도록 지연시켜 시각적 타격감과 판정을 맞춥니다.
if damage_delay > 0.0:
get_tree().create_timer(damage_delay).timeout.connect(
func() -> void:
if is_instance_valid(target_ref) and not target_ref.get("is_dead"):
if target_ref.has_method("take_damage"):
target_ref.call("take_damage", damage_amount)
)
else:
if target_ref.has_method("take_damage"):
target_ref.call("take_damage", damage_amount)
## 드워프 영웅 애니메이션 갱신 제어 ## 드워프 영웅 애니메이션 갱신 제어
func _update_animations() -> void: func _update_animations() -> void:
......
...@@ -5,6 +5,11 @@ extends BaseUnit ...@@ -5,6 +5,11 @@ extends BaseUnit
## - BaseUnit을 상속받아 마법사 모델 스킨 적용, 애니메이션 라이브러리 로드 및 ## - BaseUnit을 상속받아 마법사 모델 스킨 적용, 애니메이션 라이브러리 로드 및
## 불덩이(Fireball) 특수 효과 발사체 생성을 처리합니다. ## 불덩이(Fireball) 특수 효과 발사체 생성을 처리합니다.
const MAGE_ATTACK_ANIM: String = "CASTING"
const MAGE_RUN_ANIM: String = "Running"
const MAGE_WALK_ANIM: String = "Walking"
const MAGE_IDLE_ANIM: String = "Idle"
## 유닛의 3D 비주얼 모델, 스킨 텍스처, 무기 및 애니메이션 플레이어 연동 설정 ## 유닛의 3D 비주얼 모델, 스킨 텍스처, 무기 및 애니메이션 플레이어 연동 설정
func _setup_unit_visuals() -> void: func _setup_unit_visuals() -> void:
# 자식 노드 중 메이지(Mage) 3D 모델 노드 이름을 명시하여 인스턴스를 확보합니다. # 자식 노드 중 메이지(Mage) 3D 모델 노드 이름을 명시하여 인스턴스를 확보합니다.
...@@ -42,6 +47,10 @@ func _setup_unit_visuals() -> void: ...@@ -42,6 +47,10 @@ func _setup_unit_visuals() -> void:
# 트랙 내 불필요한 루트 모션 경로 정리를 수행합니다. # 트랙 내 불필요한 루트 모션 경로 정리를 수행합니다.
UnitHelper.clean_root_animation_tracks(_model_anim_player) UnitHelper.clean_root_animation_tracks(_model_anim_player)
## 드워프는 일반 유닛의 "dwarf/Slash"가 아니라 FBX 내장 "Attack" 클립을 사용합니다.
func _get_attack_animation_name(_model_name: String) -> String:
return MAGE_ATTACK_ANIM
## 매 프레임 물리에 맞추어 마법사 유닛의 애니메이션 상태를 제어하는 함수 ## 매 프레임 물리에 맞추어 마법사 유닛의 애니메이션 상태를 제어하는 함수
func _update_animations() -> void: func _update_animations() -> void:
if not _model_anim_player: if not _model_anim_player:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment