Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
L
ldefense
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Packages
Packages
Container Registry
Analytics
CI / CD Analytics
Repository Analytics
Value Stream Analytics
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Gavin An
ldefense
Commits
92f679a6
Commit
92f679a6
authored
Jul 16, 2026
by
Gavin An
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
작업 중
parent
1a214bb6
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
79 additions
and
57 deletions
+79
-57
src/entities/units/base_unit.gd
src/entities/units/base_unit.gd
+59
-29
src/entities/units/hero/dwarf/dwarf_hero.gd
src/entities/units/hero/dwarf/dwarf_hero.gd
+11
-28
src/entities/units/mage/mage_unit.gd
src/entities/units/mage/mage_unit.gd
+9
-0
No files found.
src/entities/units/base_unit.gd
View file @
92f679a6
...
@@ -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
# 타격 확정 이후의 후딜 또는 원거리 공격 모션은 이동 명령으로 캔슬합니다.
...
...
src/entities/units/hero/dwarf/dwarf_hero.gd
View file @
92f679a6
...
@@ -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
:
...
...
src/entities/units/mage/mage_unit.gd
View file @
92f679a6
...
@@ -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
:
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment