Commit 3410532f authored by Gavin An's avatar Gavin An

근접유닛 애니매이션 관련 수정

parent 9939b856
......@@ -32,4 +32,4 @@
에이전트는 아래의 명령을 수행하여 프로젝트를 관리할 수 있습니다.
- `run game`: `godot --main-pack project.pck` 또는 에디터 실행을 통해 게임을 시작합니다.
- `lint`: GDScript 내장 linter를 실행하여 코드 오류를 점검합니다.
- `clean`: `.godot/` 임시 캐시 폴더를 비웁니다.
\ No newline at end of file
- `clean`: `.godot/` 임시 캐시 폴더를 비웁니다.
매 10 라운드마다 보스 출현
......@@ -15,7 +15,7 @@ func _initialize_unit_templates() -> void:
striker.unit_id = "Unit_Striker"
striker.unit_name = "Striker"
striker.damage = 12.0
striker.cooldown = 0.5
striker.cooldown = 1.5
striker.move_speed = 6.0
striker.attack_range = 1.5
striker.is_ranged = false
......
......@@ -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://cq7r51whh6po" path="res://src/core/stage_manager.gd" id="2_stage_mgr"]
[ext_resource type="PackedScene" path="res://src/entities/monsters/noble_man/noble_man_unit.tscn" id="3_base_monster"]
[ext_resource type="PackedScene" uid="uid://dimlohck6ebg7" path="res://src/entities/monsters/noble_man/noble_man_unit.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="PackedScene" uid="uid://bqga1rvcjyydy" path="res://src/ui/hud.tscn" id="5_hud"]
......
......@@ -4,7 +4,7 @@ extends PathFollow3D
## 기본 몬스터 클래스 (Path3D 상하위에서 경로를 따라 회전하며 피격/사망 처리됨)
# 몬스터 능력치 설정
@export var speed: float = 6.0
@export var speed: float = 2.0
var max_hp: float = 100.0
var current_hp: float = 100.0
var gold_reward: int = 15
......@@ -158,7 +158,7 @@ func _create_hp_bar() -> void:
# 3. Sprite3D 생성 (Viewport의 2D 렌더링 텍스처를 3D 공간에 띄움)
var sprite: Sprite3D = Sprite3D.new()
sprite.billboard = BaseMaterial3D.BILLBOARD_ENABLED # 카메라를 항상 마주하도록 설정
sprite.position = Vector3(0.0, 1.2, 0.0) # 몬스터 머리 위 오프셋
sprite.position = Vector3(0.0, 0.15, 0.0) # 몬스터 발밑 오프셋
sprite.texture = viewport.get_texture()
sprite.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF # 그림자 연산 제외
sprite.no_depth_test = false # 지형이나 다른 장막 뒤에 가려지도록 설정
......
This diff is collapsed.
......@@ -2,7 +2,6 @@ class_name UnitData
extends Resource
## 유닛의 기획 능력치를 정의하는 커스텀 리소스 (UnitData)
@export var unit_id: String = ""
@export var unit_name: String = ""
@export var damage: float = 10.0
......
class_name UnitHelper
extends Object
## 유닛 외형 렌더링 및 3D 장착 어태치먼트 제어를 위한 정적 유틸리티 헬퍼
## 자식 메쉬에 지정된 텍스처를 씌우는 재귀 함수
static func apply_skin(node: Node, texture_path: String) -> void:
if not ResourceLoader.exists(texture_path):
return
var texture: Texture2D = load(texture_path) as Texture2D
if not texture:
return
_apply_skin_recursive(node, texture)
static func _apply_skin_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.5
mesh_inst.material_override = mat
for child: Node in node.get_children():
_apply_skin_recursive(child, texture)
## 자식 노드 중 AnimationPlayer 탐색
static 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
## 자식 노드 중 Skeleton3D 탐색
static func find_skeleton(node: Node) -> Skeleton3D:
if node is Skeleton3D:
return node as Skeleton3D
for child: Node in node.get_children():
var res: Skeleton3D = find_skeleton(child)
if res:
return res
return null
## 유닛의 오른손에 지정된 무기 모델을 런타임 장착
static func attach_weapon(model_instance: Node3D, unit_data: UnitData) -> void:
if not model_instance or not unit_data:
return
# 스켈레톤 노드 탐색
var skeleton: Skeleton3D = find_skeleton(model_instance)
if not skeleton:
return
# 오른손 본("RightHand")이 존재하는지 확인
var bone_idx: int = skeleton.find_bone("RightHand")
if bone_idx == -1:
print("UnitHelper: RightHand bone not found in skeleton.")
return
# 이미 장착된 무기가 있는지 확인하여 제거 (중복 장착 방지)
var existing_attachment: Node = skeleton.get_node_or_null("RightHandAttachment")
if existing_attachment:
existing_attachment.queue_free()
# 1. BoneAttachment3D 노드 동적 생성 및 본 동기화 설정
var bone_attachment: BoneAttachment3D = BoneAttachment3D.new()
bone_attachment.name = "RightHandAttachment"
bone_attachment.bone_name = "RightHand"
skeleton.add_child(bone_attachment)
# 2. 유닛 종류에 따른 무기 설정 분기
var weapon_scene_path: String = ""
var weapon_scale: Vector3 = Vector3.ONE
var weapon_rotation: Vector3 = Vector3.ZERO
var weapon_position: Vector3 = Vector3.ZERO
var texture_path: String = ""
if unit_data.unit_id == "Unit_Guardian":
# 가디언 창 (azure_spear)
weapon_scene_path = "res://assets/models/items/weapons/azure_spear.fbx"
texture_path = "res://assets/models/items/weapons/azure_spear_0.png"
else:
# 기본값: Striker 검 (azure_blade)
weapon_scene_path = "res://assets/models/items/weapons/azure_blade.fbx"
texture_path = "res://assets/models/items/weapons/azure_blade_0.png"
# 3. 무기 로드 및 인스턴스화
if ResourceLoader.exists(weapon_scene_path):
var weapon_scene: PackedScene = load(weapon_scene_path) as PackedScene
if weapon_scene:
var weapon_inst: Node3D = weapon_scene.instantiate() as Node3D
weapon_inst.scale = weapon_scale
weapon_inst.rotation_degrees = weapon_rotation
weapon_inst.position = weapon_position
# 무기 텍스처 매핑
apply_skin(weapon_inst, texture_path)
bone_attachment.add_child(weapon_inst)
print("UnitHelper: Attached weapon successfully. Unit: ", unit_data.unit_name, ", Weapon: ", weapon_scene_path.get_file())
## 애니메이션 내에서 모델의 로컬 정면 방향을 강제 리셋하는 루트(Armature) 트랙 정화
static func clean_root_animation_tracks(anim_player: AnimationPlayer) -> void:
if not anim_player:
return
# AnimationPlayer에 바인딩된 모든 애니메이션 라이브러리의 트랙 정화
for library_name: StringName in anim_player.get_animation_library_list():
var lib: AnimationLibrary = anim_player.get_animation_library(library_name)
if not lib:
continue
for anim_name: StringName in lib.get_animation_list():
var anim: Animation = lib.get_animation(anim_name)
if anim:
var tracks_to_remove: Array[int] = []
for track_idx: int in range(anim.get_track_count()):
var path_str: String = str(anim.track_get_path(track_idx))
# 최상위 루트 노드(Armature 등)의 회전/위치 강제 덮어쓰기 트랙 검출
# Skeleton3D 하위의 실제 뼈대 애니메이션 트랙은 유지하고 오직 Armature 루트 트랜스폼 트랙만 제거
if path_str == "Armature:rotation" or path_str == "Armature:position" or \
path_str.begins_with("Armature:") and not "Skeleton3D" in path_str:
tracks_to_remove.append(track_idx)
# 역순으로 정렬하여 트랙 제거 시 인덱스 밀림 방지
tracks_to_remove.reverse()
for track_idx: int in tracks_to_remove:
anim.remove_track(track_idx)
print("UnitHelper: Cleaned root and Armature transform tracks to preserve character body rotation.")
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