Project_WL/Assets/Script/Mgr/MobControlMgr.cs

228 lines
11 KiB
C#
Raw Normal View History

2024-12-15 01:39:39 +00:00
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
public class MobControlMgr : MyCoroutine
{
public int SpawnerID;
public float MobGenRange = 10f;
public TextMeshPro text_lv;
MonsterAppearData m_MonsterAppearData;
Dictionary<int, List<Actor>> dic_actors = new Dictionary<int, List<Actor>>();
int orderIndex, killmobcount;
bool m_LoadComplete;
2024-12-15 01:39:39 +00:00
WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
// #764 — 이 맵의 스폰 규칙. 프리팹 계층에서 찾고(같은 맵 프리팹 루트), 없으면 현재 로드된 맵의 MapData 를 쓴다.
MapData m_MapCfg;
MapData Get_MapCfg()
{
if (DSUtil.CheckNull(m_MapCfg)) m_MapCfg = GetComponentInParent<MapData>(true);
if (DSUtil.CheckNull(m_MapCfg) && MapData.isIns) m_MapCfg = MapData.Ins;
return DSUtil.CheckNull(m_MapCfg) ? null : m_MapCfg;
}
2024-12-15 01:39:39 +00:00
IEnumerator Start()
{
m_LoadComplete = false;
2024-12-15 01:39:39 +00:00
text_lv.text = "Lv.???";
m_MonsterAppearData = table_monsterappear.Ins.Get_Data_orNull(SpawnerID);
2024-12-18 02:15:45 +00:00
if (!DSUtil.CheckNull(m_MonsterAppearData))
2024-12-15 01:39:39 +00:00
{
text_lv.text = DSUtil.Format("Lv.{0}", m_MonsterAppearData.n_AppearMonsterLv);
WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
// #764 — 맵이 요구하면 스포너 원점이 플레이어 시작 지점에서 도달 가능한지 1회 확인한다.
// NavMesh 연결 성분 안에서 도달성은 대칭·추이적이므로, 원점이 도달 가능하면
// "원점에서 완전 경로가 나오는 표집점" 도 전부 도달 가능하다 → 몹마다 경로를 두 번 계산할 필요가 없다.
var cfg = Get_MapCfg();
if (cfg != null && cfg.spawnRequireReachableFromPlayer)
yield return StartCoroutine(Co_CheckSpawnerReachable());
var list_make_mobs = Get_Mobs();
2024-12-15 01:39:39 +00:00
for (int i = 0; i < list_make_mobs.Count; i++)
{
WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
var rdnpos = Get_SpawnPos();
var mobData = list_make_mobs[i];
yield return StartCoroutine(Make_Mob(mobData, i, rdnpos));
}
// 생성한 몬스터를 m_MonsterAppearData.n_MaxMonsterCount 만큼 활성화
for (int i = 0; i < m_MonsterAppearData.n_MaxMonsterCount; i++)
dic_actors[0][i].On_Regen();
m_LoadComplete = true;
}
}
public bool Get_LoadComplete() { return m_LoadComplete; }
WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
/// <summary>
/// #764 — 몬스터 1마리의 생성 위치. 맵이 요구할 때만 엄격 표집을 쓰고, 아니면 기존 동작 그대로다.
/// 반경·재시도 수치는 코드 상수가 아니라 MapData 에서 읽는다(C45).
/// </summary>
Vector3 Get_SpawnPos()
{
var cfg = Get_MapCfg();
if (cfg == null || !cfg.spawnRequireReachableFromPlayer)
return DSUtil.Get_RandomPos_onNavMesh(transform.position, 0f, MobGenRange);
return DSUtil.Get_RandomPos_onNavMesh_Reachable(
transform.position, 0f, MobGenRange,
cfg.spawnSnapRadius,
cfg.spawnSampleTries > 0 ? cfg.spawnSampleTries : 50);
}
/// <summary>#764 — 스포너 원점이 플레이어 시작 지점(포탈)에서 도달 가능한지 확인하고, 아니면 경고를 남긴다.</summary>
IEnumerator Co_CheckSpawnerReachable()
{
if (!LoadMapMgr.isIns) yield break;
bool got = false;
Vector3 startpos = Vector3.zero;
LoadMapMgr.Ins.Get_PortalPos(MyValue.MyChoiceMapData, p => { startpos = p; got = true; });
// 맵 로딩이 끝날 때까지 기다린다. 안전 상한 — 못 받으면 확인을 건너뛰고 스폰은 정상 진행한다.
float wait = 0f;
while (!got && wait < 10f) { wait += Time.deltaTime; yield return null; }
if (!got) { Debug.LogWarning($"[#764] Spawner {SpawnerID}: 시작 지점을 받지 못해 도달성 확인을 건너뛴다"); yield break; }
if (!DSUtil.IsReachable_onNavMesh(startpos, transform.position))
Debug.LogWarning($"[#764] Spawner {SpawnerID} 원점이 플레이어 시작 지점에서 도달 불가 " +
$"(spawner {transform.position.ToString("F1")} / start {startpos.ToString("F1")}) — 스포너 위치 재배치가 필요하다");
}
List<MonsterTableData> Get_Mobs()
{
var list_make_mobs = new List<MonsterTableData>();
switch (m_MonsterAppearData.e_SpawnerType)
{ // 생성할 몹 정보
case eSpawnerType.Random: // s_AppearMonsterID에 지정된 몬스터가 n_MaxMonsterCount 수량만큼 Spawner 범위 내 랜덤으로 생성되는 방식
for (int i = 0; i < m_MonsterAppearData.n_MaxMonsterCount * 2; i++)
list_make_mobs.Add(m_MonsterAppearData.Get_MobData_orNull());
dic_actors.Add(0, new List<Actor>());
break;
case eSpawnerType.Order: // s_AppearMonsterID에 지정 된 몬스터가 n_MaxMonsterCount 수량만큼 Spawner 범위 내 순서대로 생성되는 방식 (예: 1001|1002|1003 → 1001|1002|1003 → 순서를 반복해 생성)
int index = 0;
for (int i = 0; i < m_MonsterAppearData.n_MaxMonsterCount * 2; i++)
{
list_make_mobs.Add(m_MonsterAppearData.Get_MobData_orNull(index));
++index;
if (index >= m_MonsterAppearData.Get_MobTypeCount()) index = 0;
}
dic_actors.Add(0, new List<Actor>());
orderIndex = m_MonsterAppearData.n_MaxMonsterCount - 1;
break;
case eSpawnerType.Phase:
// s_AppearMonsterID에 지정 된 몬스터가 n_MaxMonsterCount 수량만큼 Spawner 범위 한 종류씩 순차적으로 생성되는 방식
// (예: 1001|1002|1003일 경우 1001번 몬스터가 사망하면 1002번 몬스터가 n_MaxMonsterCount 만큼 f_SpawnerDelay 후 생성)
case eSpawnerType.PhaseAllKill:
// s_AppearMonsterID에 지정 된 몬스터가 n_MaxMonsterCount 수량만큼 Spawner 범위 한 종류씩 순차적으로 생성되고, 모든 몬스터가 제거되면 다음 페이즈가 발생되는 방식 (※주로 컨텐츠에서 활용될 예정)
// (예: 1001|1002|1003일 경우 1001번 몬스터를 전부 제거해야 1002번 몬스터가 n_MaxMonsterCount 만큼 f_SpawnerDelay 후 일괄 생성)
for (int i = 0; i < m_MonsterAppearData.Get_MobTypeCount(); i++)
{
dic_actors.Add(i, new List<Actor>());
for (int j = 0; j < m_MonsterAppearData.n_MaxMonsterCount; j++)
list_make_mobs.Add(m_MonsterAppearData.Get_MobData_orNull(i));
}
break;
}
return list_make_mobs;
}
2024-12-15 01:39:39 +00:00
IEnumerator Make_Mob(MonsterTableData mobData, int i, Vector3 rdnpos)
{
bool makeComplete = false;
AddrResourceMgr.Ins.LoadObject<GameObject>(mobData.s_MonsterPrefab, handle =>
{
var mob = DSUtil.Get_Clone<MobActor>(handle.Result, transform);
AddrResourceMgr.Ins.Set_AddressableReleaseSelf(mob.gameObject);
ActorInfo.Ins.Add_Actor(mob, eRole.Mob, SpawnerID, mobData.s_MonsterPrefab);
2024-12-15 01:39:39 +00:00
switch (m_MonsterAppearData.e_SpawnerType)
{
case eSpawnerType.Random:
case eSpawnerType.Order:
dic_actors[0].Add(mob);
break;
case eSpawnerType.Phase:
case eSpawnerType.PhaseAllKill:
var list_ids = m_MonsterAppearData.Get_MobIds();
int findindex = list_ids.FindIndex(f => f == mobData.n_MonsterID);
if (findindex >= 0 && findindex < dic_actors.Count) // 인덱스가 유효한지 체크
dic_actors[findindex].Add(mob);
else
Debug.LogError($"Invalid findindex: {findindex}, MonsterID: {mobData.n_MonsterID}");
break;
2024-12-15 01:39:39 +00:00
}
mob.Set(true, null, null, m_MonsterAppearData.n_SpawnerID, false);
mob.Set(mobData, rdnpos, actDie, i);
mob.Off();
mob.MagicID = 0;
makeComplete = true;
});
2024-12-15 01:39:39 +00:00
while (!makeComplete) yield return null;
2024-12-15 01:39:39 +00:00
}
private void actDie(int mobid)
{
if (m_MonsterAppearData != null)
{
++killmobcount;
switch (m_MonsterAppearData.e_SpawnerType)
{ // 사망 후 리젠
case eSpawnerType.Random:
Set_Coroutine(() =>
{
var deadmobs = dic_actors[0].FindAll(f => !f.isActiveAndEnabled);
deadmobs[Random.Range(0, deadmobs.Count)].On_Regen();
}, m_MonsterAppearData.f_SpawnerDelay);
break;
case eSpawnerType.Order:
Set_Coroutine(() =>
{
if (dic_actors[0].Count > 0)
2024-12-15 01:39:39 +00:00
{
orderIndex = orderIndex % dic_actors[0].Count;
while (true)
2024-12-15 01:39:39 +00:00
{
var mob = dic_actors[0][orderIndex++];
if (!mob.isActiveAndEnabled)
{
mob.On_Regen();
break;
}
orderIndex = orderIndex % dic_actors[0].Count;
2024-12-15 01:39:39 +00:00
}
}
}, m_MonsterAppearData.f_SpawnerDelay);
break;
case eSpawnerType.Phase:
Set_Coroutine(() =>
{
var ids = m_MonsterAppearData.Get_MobIds();
var index = ids.FindIndex(f => f == mobid);
var nextindex = index + 1;
if (nextindex >= dic_actors.Count) nextindex = 0;
dic_actors[nextindex].Find(f => !f.isActiveAndEnabled).On_Regen();
}, m_MonsterAppearData.f_SpawnerDelay);
break;
case eSpawnerType.PhaseAllKill:
if (killmobcount == m_MonsterAppearData.n_MaxMonsterCount)
{
Set_Coroutine(() =>
{
killmobcount = 0;
++orderIndex; if (orderIndex >= dic_actors.Count) orderIndex = 0;
for (int i = 0; i < dic_actors[orderIndex].Count; i++)
dic_actors[orderIndex][i].On_Regen();
}, m_MonsterAppearData.f_SpawnerDelay);
}
break;
}
}
}
}