Commit 474297e4 authored by Gavin An's avatar Gavin An

유닛, 몬스터 상속 작업 전

parent 711b84d0
...@@ -4,9 +4,12 @@ extends Node ...@@ -4,9 +4,12 @@ extends Node
# 유닛 리소스 템플릿 목록 # 유닛 리소스 템플릿 목록
var unit_templates: Array[UnitData] = [] var unit_templates: Array[UnitData] = []
# 몬스터 리소스 템플릿 목록
var monster_templates: Array[MonsterData] = []
func _ready() -> void: func _ready() -> void:
_initialize_unit_templates() _initialize_unit_templates()
_initialize_monster_templates()
## 5종의 유닛 능력치 데이터 초기 셋업 ## 5종의 유닛 능력치 데이터 초기 셋업
func _initialize_unit_templates() -> void: func _initialize_unit_templates() -> void:
...@@ -45,36 +48,52 @@ func _initialize_unit_templates() -> void: ...@@ -45,36 +48,52 @@ func _initialize_unit_templates() -> void:
ranger.is_ranged = true ranger.is_ranged = true
ranger.unit_color = Color(0.12, 0.75, 0.25, 1.0) # 녹색 ranger.unit_color = Color(0.12, 0.75, 0.25, 1.0) # 녹색
unit_templates.append(ranger) unit_templates.append(ranger)
## 몬스터 능력치 데이터 초기 셋업
func _initialize_monster_templates() -> void:
# 1. Noble Man (기본 밸런스형 몬스터)
var noble_man: MonsterData = MonsterData.new()
noble_man.monster_id = "noble_man"
noble_man.monster_name = "Noble Man"
noble_man.hp = 100.0
noble_man.speed = 2.0
noble_man.gold_reward = 15
noble_man.model_name = "noble_man"
monster_templates.append(noble_man)
# 4. Slayer (고화력 대형 유닛) # 2. Zombie (고체력 저속형 몬스터)
#var slayer: UnitData = UnitData.new() var zombie: MonsterData = MonsterData.new()
#slayer.unit_id = "Unit_Slayer" zombie.monster_id = "zombie"
#slayer.unit_name = "Slayer" zombie.monster_name = "Zombie"
#slayer.damage = 55.0 zombie.hp = 150.0
#slayer.cooldown = 1.8 zombie.speed = 1.5
#slayer.move_speed = 3.5 zombie.gold_reward = 20
#slayer.attack_range = 1.6 zombie.model_name = "zombie"
#slayer.is_ranged = false monster_templates.append(zombie)
#slayer.unit_color = Color(0.85, 0.12, 0.12, 1.0) # 진적색
#unit_templates.append(slayer)
# 5. Archmage (극단적 극딜 마법 유닛)
#var archmage: UnitData = UnitData.new()
#archmage.unit_id = "Unit_Archmage"
#archmage.unit_name = "Archmage"
#archmage.damage = 110.0
#archmage.cooldown = 3.0
#archmage.move_speed = 3.0
#archmage.attack_range = 12.0
#archmage.is_ranged = true
#archmage.unit_color = Color(0.55, 0.12, 0.85, 1.0) # 보라색
#unit_templates.append(archmage)
## 특정 스테이지에 따른 몬스터 체력(HP) 반환 ## 특정 스테이지 스케일이 반영된 완성된 MonsterData 인스턴스를 반환
func get_monster_hp(stage: int) -> float: func get_stage_monster_data(monster_id: String, stage: int) -> MonsterData:
return 100.0 + (stage - 1) * 50.0 var template: MonsterData = null
for t in monster_templates:
if t.monster_id == monster_id:
template = t
break
if not template:
push_error("DataManager: Monster template not found for ID: " + monster_id)
return null
# 복제하여 런타임용 데이터 독립 인스턴스 생성
var stage_data = template.duplicate(true) as MonsterData
# 체력 스케일링: 기본 체력 + (스테이지 - 1) * 50
stage_data.hp = template.hp + (stage - 1) * 50.0
# 처치 골드 리워드 스케일링
stage_data.gold_reward = get_monster_gold_reward(stage)
return stage_data
## 특정 스테이지에 따른 몬스터 처치 보상 골드 반환 ## 특정 스테이지에 따른 몬스터 처치 보상 골드 반환 (기본 연산용)
func get_monster_gold_reward(stage: int) -> int: func get_monster_gold_reward(stage: int) -> int:
if stage >= 20: return 100 if stage >= 20: return 100
elif stage >= 15: return 60 + (stage - 15) * 8 elif stage >= 15: return 60 + (stage - 15) * 8
......
...@@ -91,13 +91,16 @@ func _on_spawn_timer_timeout() -> void: ...@@ -91,13 +91,16 @@ func _on_spawn_timer_timeout() -> void:
var monster_instance: Node = selected_scene.instantiate() var monster_instance: Node = selected_scene.instantiate()
_path_node.add_child(monster_instance) _path_node.add_child(monster_instance)
# 몬스터 초기화 (HP, 보상 골드 설정) # 몬스터 초기화 (리소스 ID 판정 및 스테이지 데이터 획득)
var stage: int = GameManager.current_stage var stage: int = GameManager.current_stage
var monster_hp: float = DataManager.get_monster_hp(stage) var monster_id: String = "noble_man" # 기본값
var monster_gold: int = DataManager.get_monster_gold_reward(stage) var scene_path: String = selected_scene.resource_path.to_lower()
if "zombie" in scene_path:
if monster_instance.has_method("initialize_monster"): monster_id = "zombie"
monster_instance.call("initialize_monster", monster_hp, monster_gold)
var monster_data: MonsterData = DataManager.get_stage_monster_data(monster_id, stage)
if monster_data and monster_instance.has_method("initialize_monster"):
monster_instance.call("initialize_monster", monster_data)
GameManager.increment_monster_count() GameManager.increment_monster_count()
......
class_name BaseMonster class_name BaseMonster
extends PathFollow3D extends PathFollow3D
## 기본 몬스터 클래스 (Path3D 상하위에서 경로를 따라 회전하며 피격/사망 처리됨) ## 기본 몬스터 클래스 (MonsterData를 기반으로 스펙 및 모델을 설정하고 이동/피격 처리)
# 몬스터 능력치 설정 @export var monster_data: MonsterData
# 몬스터 실시간 스펙 (MonsterData 주입 수치를 따름)
@export var speed: float = 2.0 @export var speed: float = 2.0
var max_hp: float = 100.0 var max_hp: float = 100.0
var current_hp: float = 100.0 var current_hp: float = 100.0
...@@ -21,57 +23,64 @@ func _ready() -> void: ...@@ -21,57 +23,64 @@ func _ready() -> void:
# PathFollow3D 루프 설정 활성화 (경로 끝 도달 시 처음으로 돌아감) # PathFollow3D 루프 설정 활성화 (경로 끝 도달 시 처음으로 돌아감)
loop = true loop = true
# 자식 노드 중 3D 모델(noble_man 등) 노드 자동 탐색 및 초기화 (base_unit.gd 매커니즘 준수) # 만약 씬 내부에 monster_data 리소스가 사전 설정되어 있다면 바로 로드 실행
#var model_names: Array[String] = ["noble_man", "zombie"] if monster_data:
var model_names: Array[String] = ["zombie"] _setup_monster_from_data()
var detected_model_node: Node3D = null
var detected_model_name: String = "" ## 몬스터 데이터 기반 모델 및 충돌, 스킨/애니메이션 정렬 셋업
func _setup_monster_from_data() -> void:
for m_name: String in model_names: if not monster_data:
var node: Node3D = get_node_or_null(m_name) as Node3D
if node:
detected_model_node = node
detected_model_name = m_name
break
if not detected_model_node:
return return
var m_name: String = monster_data.model_name
var node: Node3D = get_node_or_null(m_name) as Node3D
if not node:
push_error("BaseMonster: Model node '" + m_name + "' not found under children.")
return
_model_instance = node
_model_instance = detected_model_node print("BaseMonster: Identified model [", m_name, "] from MonsterData.")
#GridMap 타일 높이(Y=0.21)에 맞춰 모델 및 충돌 판정 영역 오프셋 보정
#_model_instance.position.y = 0 if m_name == "noble_man":
var monster_area = get_node_or_null("MonsterArea")
#if monster_area and monster_area is Node3D:
#monster_area.position.y = 0.71
if detected_model_name == "noble_man":
_apply_noble_man_skin(_model_instance) _apply_noble_man_skin(_model_instance)
elif detected_model_name == "zombie": elif m_name == "zombie":
_apply_zombie_skin(_model_instance) _apply_zombie_skin(_model_instance)
_model_anim_player = _find_animation_player(_model_instance) _model_anim_player = _find_animation_player(_model_instance)
if not _model_anim_player: if _model_anim_player:
return print("BaseMonster: Linked model AnimationPlayer: ", m_name)
print("BaseMonster: Linked model AnimationPlayer: ", detected_model_name)
# 모델명_animations.tres 또는 모델명_anims.tres 라이브러리를 동적으로 로드하여 등록
var anim_lib_path: String = "res://assets/models/monsters/" + m_name + "/" + m_name + "_animations.tres"
var anim_lib_path_alt: String = "res://assets/models/monsters/" + m_name + "/" + m_name + "_anims.tres"
# 모델명_animations.tres 또는 모델명_anims.tres 라이브러리를 동적으로 로드하여 등록 (base_unit.gd 방식 준수) var path_to_load: String = ""
# 단, 씬 파일에서 이미 기본 라이브러리("")로 등록된 경우 중복 등록 방지 if ResourceLoader.exists(anim_lib_path):
var anim_lib_path: String = "res://assets/models/monsters/" + detected_model_name + "/" + detected_model_name + "_animations.tres" path_to_load = anim_lib_path
elif ResourceLoader.exists(anim_lib_path_alt):
if anim_lib_path != "": path_to_load = anim_lib_path_alt
var lib: AnimationLibrary = load(anim_lib_path) as AnimationLibrary
if lib and not _model_anim_player.has_animation_library(detected_model_name):
_model_anim_player.add_animation_library(detected_model_name, lib)
print("BaseMonster: Loaded and registered ", detected_model_name, " animations library successfully.")
else:
print("BaseMonster: Animation library already present (scene-bound or already added).")
# 초기 Idle 애니메이션 재생 시도
# 탐색 순서: 기본 라이브러리("") -> 동적 등록(모델명/) -> FBX 내장
var init_idle: String = _resolve_anim_name(_model_anim_player, detected_model_name, "Idle")
if init_idle != "":
_model_anim_player.play(init_idle)
print("BaseMonster: Playing initial Idle: ", init_idle)
if path_to_load != "":
var lib: AnimationLibrary = load(path_to_load) as AnimationLibrary
if lib and not _model_anim_player.has_animation_library(m_name):
var already_registered: bool = false
for existing_lib_name in _model_anim_player.get_animation_library_list():
if _model_anim_player.get_animation_library(existing_lib_name) == lib:
already_registered = true
break
if not already_registered:
_model_anim_player.add_animation_library(m_name, lib)
print("BaseMonster: Loaded and registered ", m_name, " animations library successfully.")
else:
print("BaseMonster: Animation library already registered on model under another name.")
# 초기 Idle 애니메이션 재생 시도
var init_idle: String = _resolve_anim_name(_model_anim_player, m_name, "Idle")
if init_idle != "":
_model_anim_player.play(init_idle)
print("BaseMonster: Playing initial Idle: ", init_idle)
## 몬스터 자식 메쉬에 noble_man_0.png 텍스처를 입히는 재귀 함수 ## 몬스터 자식 메쉬에 noble_man_0.png 텍스처를 입히는 재귀 함수
func _apply_noble_man_skin(node: Node) -> void: func _apply_noble_man_skin(node: Node) -> void:
if node is MeshInstance3D: if node is MeshInstance3D:
...@@ -116,12 +125,19 @@ func _physics_process(delta: float) -> void: ...@@ -116,12 +125,19 @@ func _physics_process(delta: float) -> void:
_update_monster_animation() _update_monster_animation()
## 몬스터 초기화 (StageManager에 의해 스폰 시 호출됨) ## 몬스터 초기화 (StageManager에 의해 스폰 시 호출됨)
func initialize_monster(hp: float, reward: int) -> void: func initialize_monster(data: MonsterData) -> void:
max_hp = hp monster_data = data
current_hp = hp
gold_reward = reward max_hp = data.hp
current_hp = data.hp
speed = data.speed
gold_reward = data.gold_reward
is_dead = false is_dead = false
# _ready() 시점에 데이터가 비어 모델 셋업을 보류했던 경우 여기서 실행
if not _model_instance:
_setup_monster_from_data()
# 체력바 생성 # 체력바 생성
_create_hp_bar() _create_hp_bar()
...@@ -199,17 +215,14 @@ func _update_monster_animation() -> void: ...@@ -199,17 +215,14 @@ func _update_monster_animation() -> void:
return return
var model_name: String = "" var model_name: String = ""
if _model_instance: if monster_data:
model_name = monster_data.model_name
elif _model_instance:
model_name = _model_instance.name model_name = _model_instance.name
# 걷기 애니메이션 탐색 순서:
# 1. 씬 바인딩 기본 라이브러리("") -> "Walking"
# 2. 동적 등록 라이브러리 -> "모델명/Walking"
# 3. FBX 내장 트랙 -> "Armature|Walking"
var anim_name: String = _resolve_anim_name(_model_anim_player, model_name, "Walking") var anim_name: String = _resolve_anim_name(_model_anim_player, model_name, "Walking")
if anim_name != "": if anim_name != "":
# 걷는 애니메이션 루프 활성화
var anim: Animation = _model_anim_player.get_animation(anim_name) var anim: Animation = _model_anim_player.get_animation(anim_name)
if anim: if anim:
anim.loop_mode = Animation.LOOP_LINEAR anim.loop_mode = Animation.LOOP_LINEAR
...@@ -230,16 +243,12 @@ func _find_animation_player(node: Node) -> AnimationPlayer: ...@@ -230,16 +243,12 @@ func _find_animation_player(node: Node) -> AnimationPlayer:
return null return null
## 헬퍼: 애니메이션 이름 탐색 (기본 라이브러리 -> 모델명 라이브러리 -> FBX 내장 순) ## 헬퍼: 애니메이션 이름 탐색 (기본 라이브러리 -> 모델명 라이브러리 -> FBX 내장 순)
## anim_base_name: "Walking", "Idle", "Running" 등 라이브러리 키 없는 기본 이름
func _resolve_anim_name(player: AnimationPlayer, model_name: String, anim_base_name: String) -> String: func _resolve_anim_name(player: AnimationPlayer, model_name: String, anim_base_name: String) -> String:
# 1순위: 씬에서 기본 라이브러리("")로 바인딩된 경우 (noble_man.tscn 방식)
if player.has_animation(anim_base_name): if player.has_animation(anim_base_name):
return anim_base_name return anim_base_name
# 2순위: 동적으로 등록된 모델명 라이브러리
var lib_anim: String = model_name + "/" + anim_base_name var lib_anim: String = model_name + "/" + anim_base_name
if player.has_animation(lib_anim): if player.has_animation(lib_anim):
return lib_anim return lib_anim
# 3순위: FBX 내장 Armature| 접두사 트랙
var fbx_anim: String = "Armature|" + anim_base_name var fbx_anim: String = "Armature|" + anim_base_name
if player.has_animation(fbx_anim): if player.has_animation(fbx_anim):
return fbx_anim return fbx_anim
......
class_name MonsterData
extends Resource
## 몬스터의 기획 능력치 및 메쉬 정보를 정의하는 커스텀 리소스 (MonsterData)
@export var monster_id: String = ""
@export var monster_name: String = ""
@export var hp: float = 100.0
@export var speed: float = 2.0
@export var gold_reward: int = 15
@export var model_name: String = "" # 자식 노드로 들어갈 3D 모델 노드 이름 (예: "noble_man", "zombie")
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