Commit 9168d476 authored by Gavin An's avatar Gavin An

스킨 입혀보기

parent 9af0b9d6
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://ddqbm4y6brray"
path="res://.godot/imported/striker.fbx-bc1f4af7c3cd42cf4653a106a2b4cd60.scn"
[deps]
source_file="res://assets/models/units/striker.fbx"
dest_files=["res://.godot/imported/striker.fbx-bc1f4af7c3cd42cf4653a106a2b4cd60.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=true
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
fbx/importer=0
fbx/allow_geometry_helper_nodes=false
fbx/embedded_image_handling=1
fbx/naming_version=2
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ccilxf3k1hhpb"
path.s3tc="res://.godot/imported/striker_0.png-33c061e5427709c43bfd101c9c356d76.s3tc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
generator_parameters={
"md5": "4bf449ff8612570375bc5092fac3ec8b"
}
[deps]
source_file="res://assets/models/units/striker_0.png"
dest_files=["res://.godot/imported/striker_0.png-33c061e5427709c43bfd101c9c356d76.s3tc.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
...@@ -17,4 +17,15 @@ ldefense/ ...@@ -17,4 +17,15 @@ ldefense/
│ │ └── units/ # 플레이어 유닛 관련 씬 및 스크립트 │ │ └── units/ # 플레이어 유닛 관련 씬 및 스크립트
│ ├── ui/ # UI 관련 씬 및 스크립트 │ ├── ui/ # UI 관련 씬 및 스크립트
│ └── utils/ # 기타 유틸리티 함수 │ └── utils/ # 기타 유틸리티 함수
├── assets/
│ ├── models/ # 3D 모델 (.glb, .gltf, .obj) 및 애니메이션
│ │ ├── units/ # 플레이어 유닛 3D 애셋 (Striker, Ranger 등)
│ │ ├── monsters/ # 몬스터 3D 애셋
│ │ └── environment/ # 지형 장애물, 소품, 타일 등
│ ├── textures/ # 3D 맵핑용 재질 텍스처, 노이즈 맵, 하늘(Skybox) HDRI
│ ├── ui/ # 버튼, 테두리, 아이콘 등 2D 스프라이트 이미지 (.png)
│ ├── audio/ # 사운드 파일
│ │ ├── sfx/ # 효과음 (즉시 재생을 위해 .wav 확장자 권장)
│ │ └── music/ # 배경음악 (스트리밍 재생을 위해 .ogg 또는 .mp3 권장)
│ └── fonts/ # UI 텍스트용 외부 폰트 (.ttf, .otf)
└── project.godot └── project.godot
This diff is collapsed.
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
[ext_resource type="Script" uid="uid://dd1xgi6hnpuvs" path="res://src/core/rts_camera.gd" id="1_rts_cam"] [ext_resource type="Script" uid="uid://dd1xgi6hnpuvs" path="res://src/core/rts_camera.gd" id="1_rts_cam"]
[ext_resource type="Script" uid="uid://cq7r51whh6po" path="res://src/core/stage_manager.gd" id="2_stage_mgr"] [ext_resource type="Script" uid="uid://cq7r51whh6po" path="res://src/core/stage_manager.gd" id="2_stage_mgr"]
[ext_resource type="PackedScene" path="res://src/entities/monsters/base_monster.tscn" id="3_base_monster"] [ext_resource type="PackedScene" uid="uid://bslqp550s3j0s" path="res://src/entities/monsters/base_monster.tscn" id="3_base_monster"]
[ext_resource type="Script" uid="uid://dkplkt6q8e3b5" path="res://src/core/unit_controller.gd" id="4_unit_ctrl"] [ext_resource type="Script" uid="uid://dkplkt6q8e3b5" path="res://src/core/unit_controller.gd" id="4_unit_ctrl"]
[ext_resource type="PackedScene" uid="uid://bqga1rvcjyydy" path="res://src/ui/hud.tscn" id="5_hud"] [ext_resource type="PackedScene" uid="uid://bqga1rvcjyydy" path="res://src/ui/hud.tscn" id="5_hud"]
......
...@@ -21,6 +21,11 @@ var _auto_target_cooldown: float = 0.0 ...@@ -21,6 +21,11 @@ var _auto_target_cooldown: float = 0.0
@onready var _selection_ring: MeshInstance3D = $SelectionRing @onready var _selection_ring: MeshInstance3D = $SelectionRing
@onready var _visual_mesh: MeshInstance3D = $VisualMesh @onready var _visual_mesh: MeshInstance3D = $VisualMesh
# 3D 모델 및 애니메이션 연동 변수 (정적 타이핑 캐싱)
var _model_instance: Node3D = null
var _model_anim_player: AnimationPlayer = null
var _last_anim_state: String = ""
func _ready() -> void: func _ready() -> void:
# 유닛 그룹에 등록 # 유닛 그룹에 등록
add_to_group("units") add_to_group("units")
...@@ -36,6 +41,17 @@ func _ready() -> void: ...@@ -36,6 +41,17 @@ func _ready() -> void:
if unit_data: if unit_data:
_apply_unit_style() _apply_unit_style()
# 씬 트리에 조립된 Striker 3D 모델 탐색 및 초기화
var striker_node: Node3D = get_node_or_null("striker") as Node3D
if striker_node:
_model_instance = striker_node
_apply_striker_skin(_model_instance)
_model_anim_player = _find_animation_player(_model_instance)
if _model_anim_player:
print("BaseUnit: Linked pre-configured striker model & AnimationPlayer.")
func _physics_process(delta: float) -> void: func _physics_process(delta: float) -> void:
if GameManager.is_game_over: if GameManager.is_game_over:
velocity = Vector3.ZERO velocity = Vector3.ZERO
...@@ -61,6 +77,9 @@ func _physics_process(delta: float) -> void: ...@@ -61,6 +77,9 @@ func _physics_process(delta: float) -> void:
else: else:
_process_normal_logic(delta) _process_normal_logic(delta)
# 4. 3D 애니메이션 재생 업데이트
_update_animations()
## 1. 공격 타겟이 존재할 때의 동작 로직 ## 1. 공격 타겟이 존재할 때의 동작 로직
func _process_attack_logic(_delta: float) -> void: func _process_attack_logic(_delta: float) -> void:
if not _target_monster: if not _target_monster:
...@@ -200,3 +219,54 @@ func deselect() -> void: ...@@ -200,3 +219,54 @@ func deselect() -> void:
is_selected = false is_selected = false
if _selection_ring: if _selection_ring:
_selection_ring.visible = false _selection_ring.visible = false
## FBX 모델의 자식 메쉬에 striker_0.png 텍스처를 씌우는 재귀 함수
func _apply_striker_skin(node: Node) -> void:
if node is MeshInstance3D:
var mesh_inst: MeshInstance3D = node as MeshInstance3D
var texture: Texture2D = load("res://assets/models/units/striker_0.png") as Texture2D
if texture:
var mat: StandardMaterial3D = StandardMaterial3D.new()
mat.albedo_texture = texture
mat.roughness = 0.5
mesh_inst.material_override = mat
for child: Node in node.get_children():
_apply_striker_skin(child)
## 모델 자식 노드 중 AnimationPlayer 탐색
func _find_animation_player(node: Node) -> AnimationPlayer:
if node is AnimationPlayer:
return node as AnimationPlayer
for child: Node in node.get_children():
var res: AnimationPlayer = _find_animation_player(child)
if res:
return res
return null
## 상태(대기, 이동, 공격)별 3D 애니메이션 트랙 제어
func _update_animations() -> void:
if not _model_anim_player:
return
# 사용할 애니메이션 클립 이름
var anim_name: String = "Armature|Armature|Basic_Jump|baselayer"
if not _model_anim_player.has_animation(anim_name):
return
# 1. 공격 중 상태: 공격 타이머 차감 후 쿨타임 구간의 초입 0.4초간은 공격 애니메이션 우선 재생
# Striker의 공격 속도(cooldown)는 0.5초이므로, 공격 직후 0.4초 동안 2.5배속으로 순간 재생하면 활기찬 펀칭 타격을 표현함
if _attack_timer > (unit_data.cooldown - 0.4):
if _last_anim_state != "attack":
_last_anim_state = "attack"
_model_anim_player.play(anim_name, -1, 2.5)
# 2. 이동 중 상태: 속도 벡터가 존재할 때 (이동 상태)
elif velocity.length() > 0.1:
if _last_anim_state != "move":
_last_anim_state = "move"
_model_anim_player.play(anim_name, -1, 1.5)
# 3. 대기(Idle) 상태: 정지 상태일 때 애니메이션 정지 처리 (T-Pose 상태로 대기)
else:
if _last_anim_state != "idle":
_last_anim_state = "idle"
_model_anim_player.stop()
[gd_scene format=3 uid="uid://dwvbc5m3myqbk"]
[ext_resource type="Script" uid="uid://daq4ko8ryry3w" path="res://src/entities/units/base_unit.gd" id="1_base_unit"]
[ext_resource type="PackedScene" uid="uid://ddqbm4y6brray" path="res://assets/models/units/striker.fbx" id="2_striker_fbx"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_body"]
radius = 0.4
height = 1.2
[sub_resource type="CapsuleMesh" id="CapsuleMesh_unit"]
radius = 0.4
height = 1.2
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_ring"]
albedo_color = Color(0.098039, 0.901961, 0.098039, 1)
emission_enabled = true
emission = Color(0.098039, 0.901961, 0.098039, 1)
emission_energy_multiplier = 2.0
[sub_resource type="TorusMesh" id="TorusMesh_selection"]
material = SubResource("StandardMaterial3D_ring")
inner_radius = 0.65
outer_radius = 0.7
[node name="StrikerUnit" type="CharacterBody3D" unique_id=1844747242]
script = ExtResource("1_base_unit")
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=332342782]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0)
shape = SubResource("CapsuleShape3D_body")
[node name="VisualMesh" type="MeshInstance3D" parent="." unique_id=758090532]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0)
visible = false
mesh = SubResource("CapsuleMesh_unit")
[node name="SelectionRing" type="MeshInstance3D" parent="." unique_id=1378906746]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.02, 0)
mesh = SubResource("TorusMesh_selection")
[node name="NavigationAgent3D" type="NavigationAgent3D" parent="." unique_id=462079662]
[node name="striker" parent="." unique_id=1562791213 instance=ExtResource("2_striker_fbx")]
...@@ -55,7 +55,7 @@ func _initialize_unit_templates() -> void: ...@@ -55,7 +55,7 @@ func _initialize_unit_templates() -> void:
# 2. Guardian (밸런스형 근접 유닛) # 2. Guardian (밸런스형 근접 유닛)
var guardian: UnitData = UnitData.new() var guardian: UnitData = UnitData.new()
guardian.unit_id = "Unit_Guardian" guardian.unit_id = "Unit_Striker"
guardian.unit_name = "Guardian" guardian.unit_name = "Guardian"
guardian.damage = 25.0 guardian.damage = 25.0
guardian.cooldown = 1.0 guardian.cooldown = 1.0
...@@ -67,7 +67,7 @@ func _initialize_unit_templates() -> void: ...@@ -67,7 +67,7 @@ func _initialize_unit_templates() -> void:
# 3. Ranger (원거리 유닛) # 3. Ranger (원거리 유닛)
var ranger: UnitData = UnitData.new() var ranger: UnitData = UnitData.new()
ranger.unit_id = "Unit_Ranger" ranger.unit_id = "Unit_Striker"
ranger.unit_name = "Ranger" ranger.unit_name = "Ranger"
ranger.damage = 18.0 ranger.damage = 18.0
ranger.cooldown = 0.8 ranger.cooldown = 0.8
...@@ -79,7 +79,7 @@ func _initialize_unit_templates() -> void: ...@@ -79,7 +79,7 @@ func _initialize_unit_templates() -> void:
# 4. Slayer (고화력 대형 유닛) # 4. Slayer (고화력 대형 유닛)
var slayer: UnitData = UnitData.new() var slayer: UnitData = UnitData.new()
slayer.unit_id = "Unit_Slayer" slayer.unit_id = "Unit_Striker"
slayer.unit_name = "Slayer" slayer.unit_name = "Slayer"
slayer.damage = 55.0 slayer.damage = 55.0
slayer.cooldown = 1.8 slayer.cooldown = 1.8
...@@ -91,7 +91,7 @@ func _initialize_unit_templates() -> void: ...@@ -91,7 +91,7 @@ func _initialize_unit_templates() -> void:
# 5. Archmage (극단적 극딜 마법 유닛) # 5. Archmage (극단적 극딜 마법 유닛)
var archmage: UnitData = UnitData.new() var archmage: UnitData = UnitData.new()
archmage.unit_id = "Unit_Archmage" archmage.unit_id = "Unit_Striker"
archmage.unit_name = "Archmage" archmage.unit_name = "Archmage"
archmage.damage = 110.0 archmage.damage = 110.0
archmage.cooldown = 3.0 archmage.cooldown = 3.0
...@@ -136,8 +136,14 @@ func _on_spawn_pressed() -> void: ...@@ -136,8 +136,14 @@ func _on_spawn_pressed() -> void:
var random_index: int = randi() % _unit_templates.size() var random_index: int = randi() % _unit_templates.size()
var chosen_template: UnitData = _unit_templates[random_index] var chosen_template: UnitData = _unit_templates[random_index]
# 유닛 객체 스폰 # 유닛 객체 스폰 (Striker 전용 씬 분기 분할)
var new_unit: Node = base_unit_scene.instantiate() var target_scene: PackedScene = base_unit_scene
if chosen_template.unit_id == "Unit_Striker":
var striker_scene: PackedScene = load("res://src/entities/units/striker/striker_unit.tscn") as PackedScene
if striker_scene:
target_scene = striker_scene
var new_unit: Node = target_scene.instantiate()
var parent_scene: Node = get_tree().current_scene var parent_scene: Node = get_tree().current_scene
# 맵 중앙 스폰 존(0, 0) 기준 분산 스폰 좌표 지정 # 맵 중앙 스폰 존(0, 0) 기준 분산 스폰 좌표 지정
......
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