Commit 71d60e4a authored by Gavin An's avatar Gavin An

작업중

parent 23cc7db1
......@@ -59,7 +59,7 @@
| `Unit_Guardian` | `20%` | `25.0` | `1.0초` | `4.0` | 가장 표준적이고 균형 잡힌 밸런스형 근접 유닛 |
| `Unit_Ranger` | `20%` | `18.0` | `0.8초` | `4.5` | 원거리에서 경로 위의 몬스터를 안전하게 타격 |
| `Unit_Slayer` | `20%` | `55.0` | `1.8초` | `3.5` | 공격 속도는 느리지만 한 방이 강력한 대형 유닛 |
| `Unit_Archmage` | `20%` | `110.0` | `3.0초` | `3.0` | 극단적으로 느린 쿨타임을 가진 고화력 마법 유닛 |
| `Unit_Mage` | `20%` | `110.0` | `3.0초` | `3.0` | 극단적으로 느린 쿨타임을 가진 고화력 마법 유닛 |
---
......
......@@ -43,7 +43,7 @@
- [x] 우클릭 지점 대한 유닛 분산 격자 정렬 이동 및 몬스터 대상 타겟 어택 명령 하달 구현
### [x] 5단계: 유닛 클래스 정의 및 전투 구현
- [x] 5종 유닛(Striker, Guardian, Ranger, Slayer, Archmage) 제원을 정의하는 커스텀 리소스 스키마 (`src/entities/units/unit_data.gd`) 작성
- [x] 5종 유닛(Striker, Guardian, Ranger, Slayer, Mage) 제원을 정의하는 커스텀 리소스 스키마 (`src/entities/units/unit_data.gd`) 작성
- [x] 유닛 뼈대 캐릭터 씬 및 선택 링 하이라이트 생성 (`src/entities/units/base_unit.tscn`)
- [x] 유닛 행동 및 전투 인공지능 스크립트 작성 (`src/entities/units/base_unit.gd`)
- 네비게이션 에이전트(`NavigationAgent3D`)를 활용한 맵 경로 이동
......@@ -96,7 +96,7 @@
- 카메라 줌 조절 범위 **`5.0 ~ 18.0`** (기본 10.0), 스크롤 이탈 경계 **`(±8.0, ±8.0)`**, 유닛 탐지 반경 **`15.0m`**로 소형 맵 최적화 완료.
- [x] **원거리 유닛 3D 발사체 이펙트 시스템 탑재**
- 원거리 유도 비행 발사체 엔티티 스크립트 작성 (`src/entities/units/projectile.gd`).
- Ranger와 Archmage 공격 시, 즉발 대미지 대신 해당 유도 탄환을 어깨 높이에서 생성 발사하며 충돌 시점에 대미지가 연산되도록 구현.
- Ranger와 Mage 공격 시, 즉발 대미지 대신 해당 유도 탄환을 어깨 높이에서 생성 발사하며 충돌 시점에 대미지가 연산되도록 구현.
- 유닛 스펙 컬러에 동기화되는 빛 구슬 이펙트 머티리얼 적용. 컴파일 순환 참조 오류 우회를 위해 프리로드 동적 주입 기법 적용.
- [x] **RTS 조작 편의성 추가 (Shift 선택)**
- `UnitController` 선택 루프 시 `Input.is_key_pressed(KEY_SHIFT)`를 확인하여, Shift 입력 상태에서는 선택을 초기화하지 않고 신규 대상을 중복 없이 `append`하는 추가/누적 선택 시스템 구현.
......@@ -108,7 +108,7 @@
### [ ] 9단계: 사운드 피드백 및 3D 이펙트 연출 보강
- [ ] 몬스터 피격 시 간단한 3D 파티클 스파크 연출 구현
- [ ] 유닛 소환, 몬스터 타격, 사망, 스테이지 클리어 시 2D/3D 효과 오디오 플레이어 추가
- [ ] 원거리 유닛(Archmage 등)의 마법 광역 빔 또는 낙하 이펙트 추가
- [ ] 원거리 유닛(Mage 등)의 마법 광역 빔 또는 낙하 이펙트 추가
### [ ] 10단계: 밸런싱 및 게임 모드 세분화
- [ ] 라운드 증가에 따른 몬스터 외관 다형화 (크기 변형, 색상 변형)
......
extends SceneTree
func _init() -> void:
var path = "res://assets/models/projectiles/arrow/arrow.glb"
if ResourceLoader.exists(path):
var scene = load(path) as PackedScene
if scene:
var inst = scene.instantiate()
print("--- ARROW.GLB STRUCTURE ---")
_print_node_tree(inst, "")
print("--- END ---")
else:
print("Failed to load scene")
else:
print("File not found")
quit()
func _print_node_tree(node: Node, indent: String) -> void:
print(indent + node.name + " (" + node.get_class() + ")")
for child in node.get_children():
_print_node_tree(child, indent + " ")
[gd_scene format=3]
[ext_resource type="PackedScene" path="res://assets/models/projectiles/arrow/arrow.glb" id="1_arrow_glb"]
[ext_resource type="Texture2D" path="res://assets/models/projectiles/arrow/arrow_0.jpg" id="2_arrow_tex"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_arrow"]
albedo_texture = ExtResource("2_arrow_tex")
roughness = 0.4
metallic = 0.3
[node name="ArrowEffect" type="Node3D"]
[node name="arrow" parent="." instance=ExtResource("1_arrow_glb")]
transform = Transform3D(0.333, 0, 0, 0, 0.333, 0, 0, 0, 0.333, 0, 0, 0)
[node name="Mesh" parent="arrow" index="0"]
material_override = SubResource("StandardMaterial3D_arrow")
[editable path="arrow"]
......@@ -7,31 +7,31 @@
transform = Transform3D(-1, 0, -8.742278e-08, 0, 1, 0, 8.742278e-08, 0, -1, 0, 0, 0)
[node name="Skeleton3D" parent="Armature" parent_id_path=PackedInt32Array(252089910) index="0" unique_id=1518187286]
bones/0/position = Vector3(-0.02604381, -0.028485257, 0.63779396)
bones/0/rotation = Quaternion(0.65149087, -0.55722827, -0.35027638, 0.37731016)
bones/1/rotation = Quaternion(0.66806746, 0.20288229, -0.44615895, 0.5598811)
bones/2/rotation = Quaternion(0.70584315, 0.0888539, -0.077750064, 0.69845927)
bones/3/rotation = Quaternion(-0.6454012, -0.03186803, 0.19655482, 0.7374334)
bones/4/rotation = Quaternion(-0.4340241, -0.03172542, -0.002525541, 0.900339)
bones/5/rotation = Quaternion(0.7572378, -0.03645872, -0.052318677, 0.65001893)
bones/6/rotation = Quaternion(0.6427112, -0.07624496, 0.02701758, 0.76182616)
bones/7/rotation = Quaternion(-0.46594104, 0.14889856, -0.09954564, 0.8664982)
bones/8/rotation = Quaternion(-0.55541927, 0.10270871, 0.0380777, 0.8243243)
bones/9/rotation = Quaternion(-0.15820718, 0.21109919, -0.0410106, 0.9637043)
bones/10/rotation = Quaternion(-0.00087597984, 0.010519488, -0.04606504, 0.9988828)
bones/11/rotation = Quaternion(-0.07751671, -0.0020707778, -0.0069086505, 0.996965)
bones/12/rotation = Quaternion(0.57735246, 0.52391493, -0.32330808, 0.5363295)
bones/13/rotation = Quaternion(0.56553626, 0.27505675, 0.083389126, 0.7730192)
bones/14/rotation = Quaternion(-0.29044855, 0.075124785, 0.20163701, 0.93238324)
bones/15/rotation = Quaternion(0.09490285, 0.19491741, -0.22610243, 0.94967276)
bones/16/rotation = Quaternion(-0.5168209, 0.5474174, -0.26478106, -0.60259557)
bones/17/rotation = Quaternion(0.60044354, -0.1461899, -0.015048357, 0.7860469)
bones/18/rotation = Quaternion(-0.71120346, -0.06691303, -0.3304293, 0.61687016)
bones/19/rotation = Quaternion(0.2303776, -0.5326554, -0.10615074, 0.80742586)
bones/20/rotation = Quaternion(0.23474479, 0.01655447, -0.042023707, 0.9710072)
bones/21/rotation = Quaternion(0.008689158, 0.23618911, 0.026608352, 0.97130394)
bones/22/rotation = Quaternion(-0.16522469, -4.013281e-08, 7.0822594e-08, 0.98625606)
bones/23/rotation = Quaternion(0.6332754, 6.4982146e-08, 1.7569252e-07, 0.77392656)
bones/0/position = Vector3(-0.0023139038, -0.04537203, 0.6605864)
bones/0/rotation = Quaternion(0.7145293, -0.48455107, -0.382065, 0.3296733)
bones/1/rotation = Quaternion(0.3307851, 0.33738077, -0.6725466, 0.5695932)
bones/2/rotation = Quaternion(0.7067081, 0.08689259, -0.07896507, 0.6976947)
bones/3/rotation = Quaternion(-0.53251225, -0.023492958, 0.12944964, 0.8361349)
bones/4/rotation = Quaternion(-0.42130682, -0.03375016, 0.0073792883, 0.90625995)
bones/5/rotation = Quaternion(0.74349564, -0.051068917, 0.010168323, 0.66671044)
bones/6/rotation = Quaternion(0.57505196, -0.06973591, 0.017017843, 0.8149618)
bones/7/rotation = Quaternion(-0.39386502, 0.06483811, -0.18611832, 0.8977898)
bones/8/rotation = Quaternion(-0.54875773, 0.10837063, 0.026212737, 0.82851297)
bones/9/rotation = Quaternion(-0.050720684, 0.16700955, -0.062440544, 0.982668)
bones/10/rotation = Quaternion(0.06643845, -0.00746454, -0.020565799, 0.9975506)
bones/11/rotation = Quaternion(-0.06757774, 0.003977116, -0.0060686064, 0.99768764)
bones/12/rotation = Quaternion(0.59593344, 0.522913, -0.40855047, 0.45222998)
bones/13/rotation = Quaternion(0.3893922, 0.15657456, -0.028612759, 0.90721524)
bones/14/rotation = Quaternion(-0.4517099, 0.0879032, 0.29052076, 0.83894515)
bones/15/rotation = Quaternion(0.040262908, -0.16301575, 0.25573364, 0.95205307)
bones/16/rotation = Quaternion(-0.6208552, 0.49839443, -0.42374864, -0.4319479)
bones/17/rotation = Quaternion(0.4354482, -0.32904673, -0.22806014, 0.8062888)
bones/18/rotation = Quaternion(-0.30833024, -0.006909792, -0.14794686, 0.9396789)
bones/19/rotation = Quaternion(0.44285464, -0.5550535, -0.16461429, 0.68461484)
bones/20/rotation = Quaternion(-0.07609447, 0.18107513, -0.02338326, 0.98024213)
bones/21/rotation = Quaternion(0.074729495, 0.2748683, -0.09209712, 0.9541389)
bones/22/rotation = Quaternion(-0.16522469, -2.9472538e-08, 8.9251216e-08, 0.98625606)
bones/23/rotation = Quaternion(0.6332754, 1.2142786e-07, 1.4286297e-07, 0.7739267)
[node name="AnimationPlayer" parent="." index="1" unique_id=1849508717]
libraries/ = ExtResource("2_mage_anims")
......@@ -2,29 +2,20 @@ class_name Projectile
extends Node3D
## 원거리 유닛이 발사하는 3D 발사체 개체
## - Ranger: azure_bow/arrow.glb 모델을 사용한 화살
## - 기타: 기존 구체 빛 구슬 발사체
## - custom_visual_scene이 제공되면 해당 씬을 인스턴싱해 비주얼로 사용
## - 제공되지 않으면 기본 구체 빛 구슬 발사체를 사용
var speed: float = 24.0
var damage: float = 0.0
var target: Node3D = null
var color: Color = Color(1.0, 0.9, 0.2, 1.0) # 기본 노란색 발사체
# 화살 모드 여부 (Ranger 전용)
var use_arrow_model: bool = false
# 커스텀 비주얼 씬 (마법사 불덩이 등 외부 씬 연동 지원)
# 커스텀 비주얼 씬 (마법사 불덩이, 레인저 화살 등 외부 씬 연동 지원)
var custom_visual_scene: PackedScene = null
# 화살 모델 경로
const ARROW_MODEL_PATH: String = "res://assets/models/projectiles/arrow/arrow.glb"
const ARROW_TEXTURE_PATH: String = "res://assets/models/projectiles/arrow/arrow_0.jpg"
func _ready() -> void:
if custom_visual_scene:
_setup_custom_visual()
elif use_arrow_model and ResourceLoader.exists(ARROW_MODEL_PATH):
_setup_arrow_visual()
else:
_setup_sphere_visual()
......@@ -34,37 +25,6 @@ func _setup_custom_visual() -> void:
add_child(visual_inst)
## 화살 모델 비주얼 설정
func _setup_arrow_visual() -> void:
var arrow_scene: PackedScene = load(ARROW_MODEL_PATH) as PackedScene
if not arrow_scene:
_setup_sphere_visual()
return
var arrow_inst: Node3D = arrow_scene.instantiate() as Node3D
# 화살 텍스처 적용 (재귀)
if ResourceLoader.exists(ARROW_TEXTURE_PATH):
var tex: Texture2D = load(ARROW_TEXTURE_PATH) as Texture2D
if tex:
_apply_texture_recursive(arrow_inst, tex)
# 화살 스케일 및 초기 회전 (모델 좌표계에 따라 조정)
arrow_inst.scale = Vector3(0.333, 0.333, 0.333)
add_child(arrow_inst)
## 텍스처 재귀 적용
func _apply_texture_recursive(node: Node, texture: Texture2D) -> void:
if node is MeshInstance3D:
var mesh_inst: MeshInstance3D = node as MeshInstance3D
var mat: StandardMaterial3D = StandardMaterial3D.new()
mat.albedo_texture = texture
mat.roughness = 0.4
mat.metallic = 0.3
mesh_inst.material_override = mat
for child: Node in node.get_children():
_apply_texture_recursive(child, texture)
## 구체 빛 구슬 비주얼 설정 (Ranger 이외 유닛 또는 arrow 로드 실패 시 Fallback)
func _setup_sphere_visual() -> void:
var mesh_instance: MeshInstance3D = MeshInstance3D.new()
......
......@@ -91,8 +91,10 @@ func _spawn_projectile(fire_position: Vector3) -> void:
var proj: Node3D = projectile_script.new() as Node3D
proj.set("damage", unit_data.damage)
proj.set("target", _target_monster)
proj.set("color", unit_data.unit_color)
proj.set("use_arrow_model", true)
# 레인저 화살 비주얼 씬 장착
var arrow_effect = preload("res://src/entities/projectiles/arrow/arrow_effect.tscn")
proj.set("custom_visual_scene", arrow_effect)
get_tree().current_scene.add_child(proj)
proj.global_position = fire_position
......@@ -3,28 +3,28 @@ extends Control
## 소환사 선택 페이지 스크립트 (SummonerSelection)
## 게임 시작 시 사용자가 소환사를 선택하도록 하는 독립된 씬 페이지입니다.
@onready var btn_archmage: Button = $VBoxContainer/CardsHBox/ArchmageCard/SelectArchmage
@onready var btn_warlock: Button = $VBoxContainer/CardsHBox/WarlockCard/SelectWarlock
@onready var btn_mage: Button = $VBoxContainer/CardsHBox/MageCard/SelectMage
@onready var btn_warrior: Button = $VBoxContainer/CardsHBox/WarriorCard/SelectWarrior
@onready var port_archmage: TextureRect = $VBoxContainer/CardsHBox/ArchmageCard/Portrait
@onready var port_warlock: TextureRect = $VBoxContainer/CardsHBox/WarlockCard/Portrait
@onready var port_mage: TextureRect = $VBoxContainer/CardsHBox/MageCard/Portrait
@onready var port_warrior: TextureRect = $VBoxContainer/CardsHBox/WarriorCard/Portrait
func _ready() -> void:
btn_archmage.pressed.connect(_on_select_archmage_pressed)
btn_warlock.pressed.connect(_on_select_warlock_pressed)
btn_mage.pressed.connect(_on_select_mage_pressed)
btn_warrior.pressed.connect(_on_select_warrior_pressed)
# 초상화 텍스처 동적 로딩 (에셋 미비 대비)
var arch_path: String = "res://assets/ui/summoner/portraits/mage_summoner.png"
if ResourceLoader.exists(arch_path) and port_archmage:
port_archmage.texture = load(arch_path) as Texture2D
if ResourceLoader.exists(arch_path) and port_mage:
port_mage.texture = load(arch_path) as Texture2D
var war_path: String = "res://assets/ui/summoner/portraits/warrior_summoner.png"
if ResourceLoader.exists(war_path) and port_warlock:
port_warlock.texture = load(war_path) as Texture2D
if ResourceLoader.exists(war_path) and port_warrior:
port_warrior.texture = load(war_path) as Texture2D
func _on_select_archmage_pressed() -> void:
func _on_select_mage_pressed() -> void:
var data := SummonerData.new()
data.summoner_name = "Archmage"
data.summoner_name = "Mage"
data.max_mana = 100.0
data.mana_regen = 3.0
var arch_path: String = "res://assets/ui/summoner/portraits/mage_summoner.png"
......@@ -34,9 +34,9 @@ func _on_select_archmage_pressed() -> void:
GameManager.selected_summoner_data = data
_transition_to_main_game()
func _on_select_warlock_pressed() -> void:
func _on_select_warrior_pressed() -> void:
var data := SummonerData.new()
data.summoner_name = "Warlock"
data.summoner_name = "Warrior"
data.max_mana = 130.0
data.mana_regen = 1.8
var war_path: String = "res://assets/ui/summoner/portraits/warrior_summoner.png"
......
......@@ -47,25 +47,25 @@ layout_mode = 2
theme_override_constants/separation = 80
alignment = 1
[node name="ArchmageCard" type="VBoxContainer" parent="VBoxContainer/CardsHBox" unique_id=838762373]
[node name="MageCard" type="VBoxContainer" parent="VBoxContainer/CardsHBox" unique_id=838762373]
custom_minimum_size = Vector2(260, 0)
layout_mode = 2
theme_override_constants/separation = 15
alignment = 1
[node name="Portrait" type="TextureRect" parent="VBoxContainer/CardsHBox/ArchmageCard" unique_id=947627767]
[node name="Portrait" type="TextureRect" parent="VBoxContainer/CardsHBox/MageCard" unique_id=947627767]
custom_minimum_size = Vector2(240, 240)
layout_mode = 2
expand_mode = 1
stretch_mode = 6
[node name="Name" type="Label" parent="VBoxContainer/CardsHBox/ArchmageCard" unique_id=594177406]
[node name="Name" type="Label" parent="VBoxContainer/CardsHBox/MageCard" unique_id=594177406]
layout_mode = 2
theme_override_font_sizes/font_size = 24
text = "Mage"
horizontal_alignment = 1
[node name="Description" type="Label" parent="VBoxContainer/CardsHBox/ArchmageCard" unique_id=409919037]
[node name="Description" type="Label" parent="VBoxContainer/CardsHBox/MageCard" unique_id=409919037]
layout_mode = 2
theme_override_font_sizes/font_size = 14
text = "Max Mana: 100
......@@ -73,31 +73,31 @@ Mana Regen: 3.0 / s
Spells: Haste, Smite"
horizontal_alignment = 1
[node name="SelectArchmage" type="Button" parent="VBoxContainer/CardsHBox/ArchmageCard" unique_id=1450594011]
[node name="SelectMage" type="Button" parent="VBoxContainer/CardsHBox/MageCard" unique_id=1450594011]
custom_minimum_size = Vector2(180, 45)
layout_mode = 2
theme_override_font_sizes/font_size = 16
text = "Select Archmage"
text = "Select Mage"
[node name="WarlockCard" type="VBoxContainer" parent="VBoxContainer/CardsHBox" unique_id=1025813844]
[node name="WarriorCard" type="VBoxContainer" parent="VBoxContainer/CardsHBox" unique_id=1025813844]
custom_minimum_size = Vector2(260, 0)
layout_mode = 2
theme_override_constants/separation = 15
alignment = 1
[node name="Portrait" type="TextureRect" parent="VBoxContainer/CardsHBox/WarlockCard" unique_id=1881168350]
[node name="Portrait" type="TextureRect" parent="VBoxContainer/CardsHBox/WarriorCard" unique_id=1881168350]
custom_minimum_size = Vector2(240, 240)
layout_mode = 2
expand_mode = 1
stretch_mode = 6
[node name="Name" type="Label" parent="VBoxContainer/CardsHBox/WarlockCard" unique_id=1845912094]
[node name="Name" type="Label" parent="VBoxContainer/CardsHBox/WarriorCard" unique_id=1845912094]
layout_mode = 2
theme_override_font_sizes/font_size = 24
text = "Warrior"
horizontal_alignment = 1
[node name="Description" type="Label" parent="VBoxContainer/CardsHBox/WarlockCard" unique_id=1584843641]
[node name="Description" type="Label" parent="VBoxContainer/CardsHBox/WarriorCard" unique_id=1584843641]
layout_mode = 2
theme_override_font_sizes/font_size = 14
text = "Max Mana: 130
......@@ -105,8 +105,8 @@ Mana Regen: 1.8 / s
Spells: Haste, Smite"
horizontal_alignment = 1
[node name="SelectWarlock" type="Button" parent="VBoxContainer/CardsHBox/WarlockCard" unique_id=1403911531]
[node name="SelectWarrior" type="Button" parent="VBoxContainer/CardsHBox/WarriorCard" unique_id=1403911531]
custom_minimum_size = Vector2(180, 45)
layout_mode = 2
theme_override_font_sizes/font_size = 16
text = "Select Warlock"
text = "Select Warrior"
......@@ -36,9 +36,9 @@
## [x] 5단계: 유닛 클래스 정의 및 전투 구현
- [x] 유닛 기본 스키마 (`src/entities/units/base_unit.gd`) 작성 (정적 타이핑 적용)
- [x] 5종의 유닛(Striker, Guardian, Ranger, Slayer, Archmage) 스펙 정의 (커스텀 리소스 사용 또는 스크립트 분기)
- [x] 5종의 유닛(Striker, Guardian, Ranger, Slayer, Mage) 스펙 정의 (커스텀 리소스 사용 또는 스크립트 분기)
- [x] 적 감지 범위 (`Area3D`) 내의 몬스터를 자동 검색 및 추격, 사거리 진입 시 쿨타임에 따른 자동 공격 구현
- [x] 투사체(Ranger, Archmage용) 또는 근접 공격 이펙트 처리
- [x] 투사체(Ranger, Mage용) 또는 근접 공격 이펙트 처리
## [x] 6단계: UI 및 소환 시스템
- [x] 메인 HUD UI 씬 (`src/ui/hud.tscn`) 구성
......
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