Commit 98017115 authored by Gavin An's avatar Gavin An

공격 방식 - 범위 내 들어오면 자동으로 공격하도록 변경

parent 68ca2daf
<component name="libraryTable">
<library name="GdSdk" type="GdScript">
<CLASSES />
<JAVADOC />
<SOURCES>
<root url="file://$APPLICATION_PLUGINS_DIR$/GdScript/extracted/Master" />
</SOURCES>
</library>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
\ No newline at end of file
...@@ -68,4 +68,4 @@ ...@@ -68,4 +68,4 @@
- **RTS식 유닛 컨트롤:** - **RTS식 유닛 컨트롤:**
- **선택:** 마우스 왼쪽 버튼 클릭으로 단일 유닛 선택, 또는 드래그(Box Selection)로 여러 유닛을 동시에 선택할 수 있습니다. - **선택:** 마우스 왼쪽 버튼 클릭으로 단일 유닛 선택, 또는 드래그(Box Selection)로 여러 유닛을 동시에 선택할 수 있습니다.
- **이동:** 유닛을 선택한 상태에서 맵의 바닥을 마우스 오른쪽 버튼으로 클릭하면 해당 위치로 이동합니다. - **이동:** 유닛을 선택한 상태에서 맵의 바닥을 마우스 오른쪽 버튼으로 클릭하면 해당 위치로 이동합니다.
- **공격:** 유닛을 선택한 상태에서 무한 루프 경로를 돌고 있는 몬스터를 마우스 오른쪽 버튼으로 클릭하면, 해당 몬스터를 추격하며 사거리 내에 들어올 시 자동으로 공격을 시작합니다. - **공격:** 유닛을 선택한 상태에서 무한 루프 경로를 돌고 있는 몬스터 근처로 가면, 해당 몬스터를 추격하며 공격 사거리 내에 들어올 시 자동으로 공격을 시작합니다.
\ No newline at end of file \ No newline at end of file
...@@ -6,10 +6,12 @@ ...@@ -6,10 +6,12 @@
- [ ] 프로젝트 기본 폴더 구조 생성 (`src/core`, `src/autoload`, `src/entities`, `src/ui`, `src/utils` 등) - [ ] 프로젝트 기본 폴더 구조 생성 (`src/core`, `src/autoload`, `src/entities`, `src/ui`, `src/utils` 등)
- [ ] 메인 게임 씬 (`src/core/main.tscn`) 생성 - [ ] 메인 게임 씬 (`src/core/main.tscn`) 생성
- [ ] 3D 기본 지형 구축 (지상 바닥 및 조명, 환경 설정) - [ ] 3D 기본 지형 구축 (지상 바닥 및 조명, 환경 설정)
- 맵은 해상도에 맞게 최대로 만들어짐
- [ ] 몬스터 이동 경로 (`Path3D`) 배치 (맵 가장자리를 도는 루프 형태) - [ ] 몬스터 이동 경로 (`Path3D`) 배치 (맵 가장자리를 도는 루프 형태)
- [ ] RTS 스타일 3D 카메라 구현 (`src/core/rts_camera.gd`) - [ ] RTS 스타일 3D 카메라 구현 (`src/core/rts_camera.gd`)
- WASD / 방향키를 통한 화면 이동
- 마우스 휠을 통한 줌인/줌아웃 - 마우스 휠을 통한 줌인/줌아웃
- 맵이 줌인 되었을 때 WASD / 방향키를 통한 화면 이동
- 맵이 최대로 줌 아웃 되었을 때는 화면 이동 없음
- 화면 가장자리에 마우스 위치 시 카메라 스크롤 (선택 사항) - 화면 가장자리에 마우스 위치 시 카메라 스크롤 (선택 사항)
## [ ] 2단계: 글로벌 게임 매니저와 스테이지 시스템 ## [ ] 2단계: 글로벌 게임 매니저와 스테이지 시스템
......
...@@ -10,7 +10,7 @@ signal game_lost ...@@ -10,7 +10,7 @@ signal game_lost
signal game_victory signal game_victory
# 글로벌 플레이어 상태 변수 (정적 타이핑 적용) # 글로벌 플레이어 상태 변수 (정적 타이핑 적용)
var player_gold: int = 300: var player_gold: int = 30000:
set(value): set(value):
player_gold = value player_gold = value
gold_changed.emit(player_gold) gold_changed.emit(player_gold)
......
...@@ -20,7 +20,7 @@ func _ready() -> void: ...@@ -20,7 +20,7 @@ func _ready() -> void:
# 화면 전체를 덮도록 설정 # 화면 전체를 덮도록 설정
anchor_right = 1.0 anchor_right = 1.0
anchor_bottom = 1.0 anchor_bottom = 1.0
mouse_filter = MOUSE_FILTER_PASS # 마우스 입력을 통과시켜 카메라 등으로 전달 가능하게 함 mouse_filter = MOUSE_FILTER_IGNORE # UI가 아닌 unhandled input으로 동작하게 통과 처리
# 런타임에 NavigationRegion3D를 찾아 맵을 베이크시킴 (4.2 구현 보완) # 런타임에 NavigationRegion3D를 찾아 맵을 베이크시킴 (4.2 구현 보완)
var nav_region: NavigationRegion3D = get_node_or_null("../NavigationRegion3D") as NavigationRegion3D var nav_region: NavigationRegion3D = get_node_or_null("../NavigationRegion3D") as NavigationRegion3D
...@@ -28,7 +28,7 @@ func _ready() -> void: ...@@ -28,7 +28,7 @@ func _ready() -> void:
nav_region.bake_navigation_mesh() nav_region.bake_navigation_mesh()
print("UnitController: Navigation mesh baked successfully on start.") print("UnitController: Navigation mesh baked successfully on start.")
func _gui_input(event: InputEvent) -> void: func _unhandled_input(event: InputEvent) -> void:
if GameManager.is_game_over: if GameManager.is_game_over:
return return
...@@ -110,7 +110,6 @@ func _perform_selection(start: Vector2, end: Vector2) -> void: ...@@ -110,7 +110,6 @@ func _perform_selection(start: Vector2, end: Vector2) -> void:
selected_units.append(unit) selected_units.append(unit)
if unit.has_method("select"): if unit.has_method("select"):
unit.call("select") unit.call("select")
print("UnitController: Selected ", selected_units.size(), " units via drag.") print("UnitController: Selected ", selected_units.size(), " units via drag.")
## 우클릭 액션 (이동 목적지 지정 또는 적 공격 타겟팅) ## 우클릭 액션 (이동 목적지 지정 또는 적 공격 타겟팅)
...@@ -178,14 +177,18 @@ func _raycast_from_mouse(mouse_pos: Vector2) -> Dictionary: ...@@ -178,14 +177,18 @@ func _raycast_from_mouse(mouse_pos: Vector2) -> Dictionary:
func _get_unit_node(collider: Node) -> Node: func _get_unit_node(collider: Node) -> Node:
if not collider: if not collider:
return null return null
# 유닛의 충돌체(Area3D)가 자식일 경우 부모 노드를 추적 if collider is BaseUnit:
if collider.is_in_group("units"):
return collider return collider
var parent: Node = collider.get_parent() var parent: Node = collider.get_parent()
if parent and parent.is_in_group("units"): if parent and parent is BaseUnit:
return parent return parent
# 한 단계 더 위를 체크 (CharacterBody3D의 CollisionShape 구조 등)
var grandparent: Node = parent.get_parent() if parent else null var grandparent: Node = parent.get_parent() if parent else null
if grandparent and grandparent is BaseUnit:
return grandparent
if collider.is_in_group("units"):
return collider
if parent and parent.is_in_group("units"):
return parent
if grandparent and grandparent.is_in_group("units"): if grandparent and grandparent.is_in_group("units"):
return grandparent return grandparent
return null return null
...@@ -194,12 +197,18 @@ func _get_unit_node(collider: Node) -> Node: ...@@ -194,12 +197,18 @@ func _get_unit_node(collider: Node) -> Node:
func _get_monster_node(collider: Node) -> Node: func _get_monster_node(collider: Node) -> Node:
if not collider: if not collider:
return null return null
if collider.is_in_group("monsters"): if collider is BaseMonster:
return collider return collider
var parent: Node = collider.get_parent() var parent: Node = collider.get_parent()
if parent and parent.is_in_group("monsters"): if parent and parent is BaseMonster:
return parent return parent
var grandparent: Node = parent.get_parent() if parent else null var grandparent: Node = parent.get_parent() if parent else null
if grandparent and grandparent is BaseMonster:
return grandparent
if collider.is_in_group("monsters"):
return collider
if parent and parent.is_in_group("monsters"):
return parent
if grandparent and grandparent.is_in_group("monsters"): if grandparent and grandparent.is_in_group("monsters"):
return grandparent return grandparent
return null return null
...@@ -12,6 +12,8 @@ var gold_reward: int = 15 ...@@ -12,6 +12,8 @@ var gold_reward: int = 15
var is_dead: bool = false var is_dead: bool = false
func _ready() -> void: func _ready() -> void:
# 몬스터 그룹 등록
add_to_group("monsters")
# PathFollow3D 루프 설정 활성화 (경로 끝 도달 시 처음으로 돌아감) # PathFollow3D 루프 설정 활성화 (경로 끝 도달 시 처음으로 돌아감)
loop = true loop = true
......
...@@ -11,8 +11,10 @@ var is_selected: bool = false ...@@ -11,8 +11,10 @@ var is_selected: bool = false
var _target_monster: Node3D = null var _target_monster: Node3D = null
var _attack_timer: float = 0.0 var _attack_timer: float = 0.0
# 자동 탐색 범위 # 자동 탐색 범위 (맵 가장자리를 회전하는 적들을 스폰존에서 인식 가능하도록 30.0m로 확대)
@export var detection_range: float = 16.0 @export var detection_range: float = 30.0
var _auto_target_cooldown: float = 0.0
# 노드 캐싱 # 노드 캐싱
@onready var _nav_agent: NavigationAgent3D = $NavigationAgent3D @onready var _nav_agent: NavigationAgent3D = $NavigationAgent3D
...@@ -39,15 +41,21 @@ func _physics_process(delta: float) -> void: ...@@ -39,15 +41,21 @@ func _physics_process(delta: float) -> void:
velocity = Vector3.ZERO velocity = Vector3.ZERO
return return
# 공격 타이머 차감 # 공격 타이머 및 자동 타겟팅 쿨타임 차감
if _attack_timer > 0.0: if _attack_timer > 0.0:
_attack_timer -= delta _attack_timer -= delta
if _auto_target_cooldown > 0.0:
_auto_target_cooldown -= delta
# 1. 공격 대상이 유효한지 검증 (사망했거나 삭제되었는지 체크) # 1. 공격 대상이 유효한지 검증 (사망했거나 삭제되었는지 체크)
if _target_monster and (not is_instance_valid(_target_monster) or _target_monster.get("is_dead")): if _target_monster and (not is_instance_valid(_target_monster) or _target_monster.get("is_dead")):
_target_monster = null _target_monster = null
# 2. 상태에 따른 AI 동작 분기 # 2. 적 자동 탐색 (타겟이 없고 수동 강제 이동 방지 쿨다운이 완료된 경우)
if not _target_monster and _auto_target_cooldown <= 0.0:
_find_nearest_target()
# 3. 상태에 따른 AI 동작 분기
if _target_monster: if _target_monster:
_process_attack_logic(delta) _process_attack_logic(delta)
else: else:
...@@ -78,8 +86,6 @@ func _process_normal_logic(_delta: float) -> void: ...@@ -78,8 +86,6 @@ func _process_normal_logic(_delta: float) -> void:
_navigate_to_target(_delta) _navigate_to_target(_delta)
else: else:
velocity = Vector3.ZERO velocity = Vector3.ZERO
# 정지 상태에서 주변 몬스터 탐지
_find_nearest_target()
## 네비게이션 타겟 방향으로 캐릭터 이동 ## 네비게이션 타겟 방향으로 캐릭터 이동
func _navigate_to_target(_delta: float) -> void: func _navigate_to_target(_delta: float) -> void:
...@@ -152,6 +158,7 @@ func _show_attack_visual() -> void: ...@@ -152,6 +158,7 @@ func _show_attack_visual() -> void:
## 외부에서 호출하는 수동 이동 명령 ## 외부에서 호출하는 수동 이동 명령
func move_to(target_pos: Vector3) -> void: func move_to(target_pos: Vector3) -> void:
_target_monster = null # 이동 명령 시 강제 공격 해제 _target_monster = null # 이동 명령 시 강제 공격 해제
_auto_target_cooldown = 0.5 # 수동 강제 이동 시 0.5초 동안 자동 공격 타겟 지정을 비활성화하여 피신을 허용
_nav_agent.target_position = target_pos _nav_agent.target_position = target_pos
## 외부에서 호출하는 특정 몬스터 강제 공격 지정 ## 외부에서 호출하는 특정 몬스터 강제 공격 지정
......
...@@ -9,6 +9,7 @@ anchor_right = 1.0 ...@@ -9,6 +9,7 @@ anchor_right = 1.0
anchor_bottom = 1.0 anchor_bottom = 1.0
grow_horizontal = 2 grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
mouse_filter = 2
script = ExtResource("1_hud") script = ExtResource("1_hud")
[node name="TopBar" type="HBoxContainer" parent="."] [node name="TopBar" type="HBoxContainer" parent="."]
......
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