209 lines
14 KiB
C#
209 lines
14 KiB
C#
using UnityEngine;
|
||
|
||
namespace WL.Combat
|
||
{
|
||
/// <summary>검 궤적 이펙트를 그리는 방식 (PD #718)</summary>
|
||
public enum SlashMode
|
||
{
|
||
/// <summary>NamuFX 프리팹을 스윙 호에 맞춰 1회 배치한다</summary>
|
||
PrefabArc = 0,
|
||
/// <summary>검이 지나간 자리를 매 프레임 리본 메시로 그린다(궤적 일치도 최고)</summary>
|
||
BladeTrail = 1
|
||
}
|
||
|
||
/// <summary>
|
||
/// 플레이어·몬스터 전투 수치의 단일 출처(SOT). 코드 상수 금지(C45) — 모든 값은 이 에셋에서 읽는다.
|
||
/// 에셋 경로: Assets/WL/Settings/CombatSettings.asset
|
||
///
|
||
/// 현재 값은 전부 PD 지시 기준의 임시값이며, 추후 테이블(데이터 시트)로 대체할 예정이다.
|
||
/// </summary>
|
||
[CreateAssetMenu(fileName = "CombatSettings", menuName = "WL/Combat Settings")]
|
||
public class CombatSettings : ScriptableObject
|
||
{
|
||
[Header("■ 플레이어 공격 (PD 지시 · 임시 3~5)")]
|
||
[Tooltip("1회 공격 최소 피해량 · 임시 3")]
|
||
public int playerAttackMin = 3;
|
||
[Tooltip("1회 공격 최대 피해량(이 값 포함) · 임시 5")]
|
||
public int playerAttackMax = 5;
|
||
|
||
[Header("■ 플레이어 체력 (PD 지시 ⑥ · 아직 사망하지 않는다)")]
|
||
[Tooltip("표시·계산용 최대 체력 · 임시 100")]
|
||
public float playerMaxHp = 100f;
|
||
[Tooltip("체력 하한. 이 값 아래로 내려가지 않아 사망하지 않는다 · 임시 1")]
|
||
public float playerMinHp = 1f;
|
||
[Tooltip("피격 모션 길이를 Animator 에서 못 읽을 때 쓰는 대체 시간(초)")]
|
||
public float playerHitFallbackDuration = 1.07f;
|
||
[Tooltip("피격 모션 크로스페이드 시간(초)")]
|
||
public float playerHitBlendTime = 0.06f;
|
||
|
||
[Header("■ 플레이어 콤보 공격 (PD #704 추가지시 1·2 · 임시)")]
|
||
[Tooltip("콤보 단계 수. 3 이면 Attack1 -> Attack2 -> Attack3 (3히트) · PD 지시 3")]
|
||
public int comboStepCount = 3;
|
||
[Tooltip("현재 공격 클립의 이 정규화 시각(0~1)을 넘으면 다음 콤보 단계로 넘어간다 · 임시 0.78")]
|
||
[Range(0.1f, 1f)] public float comboAdvanceNormalizedTime = 0.78f;
|
||
[Tooltip("콤보 단계 사이 크로스페이드 시간(초) · 임시 0.10")]
|
||
public float comboBlendTime = 0.10f;
|
||
[Tooltip("공격 클립 길이 대비 피해가 들어가는 정규화 시각(0~1). 각 단계마다 1회씩 판정 · 임시 0.35")]
|
||
[Range(0f, 1f)] public float comboHitNormalizedTime = 0.35f;
|
||
|
||
[Header("■ 검 궤적 VFX — 방식 선택 (PD #718)")]
|
||
[Tooltip("BladeTrail = 검이 지나간 자리를 매 프레임 리본으로 그린다(궤적 일치도 최고) · PrefabArc = NamuFX 프리팹을 호에 맞춰 1회 배치")]
|
||
public SlashMode slashMode = SlashMode.BladeTrail;
|
||
[Tooltip("공격 클립에서 스윙이 시작되는 정규화 시각(0~1) · 임시 0.10")]
|
||
[Range(0f, 1f)] public float slashSwingStartNormalized = 0.10f;
|
||
[Tooltip("공격 클립에서 스윙이 끝나는 정규화 시각(0~1) · 임시 0.60")]
|
||
[Range(0f, 1f)] public float slashSwingEndNormalized = 0.60f;
|
||
|
||
[Header("■ 검날 트레일 (slashMode = BladeTrail · 임시)")]
|
||
[Tooltip("트레일 1겹 — NamuFX Slash_B 의 가산 머티리얼(M_slashB_Add)")]
|
||
public Material trailMaterial;
|
||
[Tooltip("트레일 2겹 — NamuFX Slash_B 의 알파 머티리얼(M_slashB_Alp). 원본과 같은 2겹 구성 · PD #718")]
|
||
public Material trailMaterialAlpha;
|
||
[Tooltip("리본 폭 비율. 1 = 손잡이~칼끝 전체 · 원본 SlashMesh_00 실측 안쪽/바깥 반지름 = 0.47/1.0 이므로 0.53 이 원본과 같은 두께")]
|
||
[Range(0.05f, 1f)] public float trailWidthScale = 0.53f;
|
||
[Tooltip("꼬리(오래된 쪽) 폭 비율. 1 = 균일, 작을수록 꼬리가 얇아진다(원본 크레센트의 테이퍼) · 임시 0.25")]
|
||
[Range(0f, 1f)] public float trailTailWidthRatio = 0.25f;
|
||
[Tooltip("스윙이 끝난 검끝 위치에 원본의 fx·dust 만 터뜨려 '튀는' 느낌을 유지한다 · PD #718 ④")]
|
||
public bool trailSpawnBurst = true;
|
||
[Tooltip("스윙이 끝난 뒤 트레일이 사라지는 시간(초) · 임시 0.18")]
|
||
public float trailFadeSeconds = 0.18f;
|
||
[Tooltip("페이드 중 알파를 그대로 유지하는 구간 비율(0~1). 원본 Custom2 그라디언트 실측 = 0.51 까지 알파 1 유지 후 0 까지 감소")]
|
||
[Range(0f, 0.95f)] public float trailFadeHoldRatio = 0.51f;
|
||
[Tooltip("트레일이 기억하는 최대 프레임 수 · 임시 48")]
|
||
public int trailMaxSamples = 48;
|
||
|
||
[Header("■ 트레일 색·UV (NamuFX 원본 실측값 · PD #718 마무리)")]
|
||
[Tooltip("가산 겹(M_slashB_Add) 색(HDR). 원본은 파티클 Custom Data 2 로 색을 넣는다 — 실측 피크 = (0.086, 1.553, 2.024)")]
|
||
[ColorUsage(true, true)] public Color trailColorAdd = new Color(0.086f, 1.553f, 2.024f, 1f);
|
||
[Tooltip("알파 겹(M_slashB_Alp) 색(HDR). 원본 Custom Data 2 그라디언트 시작값 실측 = (0, 0.762, 1.582)")]
|
||
[ColorUsage(true, true)] public Color trailColorAlpha = new Color(0f, 0.762f, 1.582f, 1f);
|
||
[Tooltip("가산 겹 텍스처(T_SlashSheet01)는 3x3 시트다. 한 칸만 쓰도록 u 범위를 지정한다 — 프레임 2(가장 밝은 크레센트) = 0.667~1.0")]
|
||
[Range(0f, 1f)] public float trailUvUMin = 0.6667f;
|
||
[Range(0f, 1f)] public float trailUvUMax = 1f;
|
||
[Tooltip("가산 겹 v 범위(0 = 손잡이 쪽, 1 = 칼끝 쪽). 프레임 2 = 0.667~1.0 · 코어(밝은 띠)는 v 위쪽에 있다")]
|
||
[Range(0f, 1f)] public float trailUvVMin = 0.6667f;
|
||
[Range(0f, 1f)] public float trailUvVMax = 1f;
|
||
[Tooltip("알파 겹의 마스크(SlashMaskTest)는 시트가 아니라 한 장이다 — 켜면 알파 겹만 UV 0~1 전체를 쓴다")]
|
||
public bool trailAlphaUvFull = true;
|
||
[Tooltip("알파 겹의 디졸브 진행량(원본 Custom Data 1 실측 = 수명 동안 0 → 0.98). 페이드 구간에서 이 값까지 올린다")]
|
||
[Range(0f, 1f)] public float trailDissolveMax = 0.98f;
|
||
|
||
[Header("■ 검 궤적 VFX (PD #712 ② · 임시)")]
|
||
[Tooltip("타격마다 1회 재생할 슬래시 프리팹. 비우면 재생하지 않는다 (NamuFX Slash_B_recolor_1 등)")]
|
||
public GameObject slashVfxPrefab;
|
||
[Tooltip("미리 만들어 돌려 쓰는 개수. 콤보 3타가 겹쳐도 모자라지 않게 · 임시 3")]
|
||
public int slashVfxPoolSize = 3;
|
||
[Tooltip("검날 프레임(전방 = 검날 방향) 기준 추가 위치 오프셋(m) · 임시 (0, 0, 0)")]
|
||
public Vector3 slashLocalOffset = Vector3.zero;
|
||
[Tooltip("검날 길이의 몇 지점에서 호가 시작할지(0=손잡이, 1=칼끝) · PD #716 ④ · 임시 0.15")]
|
||
[Range(0f, 1.5f)] public float slashBladeFraction = 0.15f;
|
||
[Tooltip("호가 칼끝까지 덮도록 검날 길이에 맞춰 크기를 자동 보정한다 · PD #716 ④")]
|
||
public bool slashFitToBlade = true;
|
||
[Tooltip("콤보 단계별 회전 오프셋(도). 배열 길이가 모자라면 마지막 값을 쓴다 · 임시 1타 가로 / 2타 반대 가로 / 3타 세로")]
|
||
public Vector3[] slashRotationOffsets = new Vector3[]
|
||
{
|
||
new Vector3(0f, 0f, 0f),
|
||
new Vector3(0f, 0f, 180f),
|
||
new Vector3(0f, 0f, 90f)
|
||
};
|
||
[Tooltip("슬래시 크기 배수 · 임시 1.0")]
|
||
public float slashScale = 1f;
|
||
[Tooltip("재생 후 이 시간(초)이 지나면 회수한다. 파티클 길이보다 넉넉하게 · 임시 2.0")]
|
||
public float slashAutoReturnSeconds = 2f;
|
||
[Tooltip("소켓으로 쓸 트랜스폼 이름 후보(위에서부터 탐색). 못 찾으면 Humanoid 오른손 본을 쓴다")]
|
||
public string[] slashSocketNames = new string[] { "Kazuko_claymore", "Weapon_Target_Hand_R", "Sword" };
|
||
// slashParentToSocket 은 PD #718 로 폐기 — 검 본에 붙이면 이펙트가 검을 따라 끌려다녀
|
||
// "궤적과 다른 방향으로 보인다"는 증상의 원인이 됐다(PM 실측). 이제 항상 무부모 월드 스폰이다.
|
||
|
||
[Header("■ 플레이어 사망 (PD #704 범위확정 4 · 현재 비활성)")]
|
||
[Tooltip("체크하면 체력이 0 이 될 때 사망 모션을 재생한다. 기본 false = 하한 1 유지(기존 규칙)")]
|
||
public bool playerCanDie = false;
|
||
[Tooltip("사망 클립 길이를 Animator 에서 못 읽을 때 쓰는 대체 시간(초) · 임시 1.0")]
|
||
public float playerDeathFallbackDuration = 1.0f;
|
||
[Tooltip("사망 모션 크로스페이드 시간(초) · 임시 0.10")]
|
||
public float playerDeathBlendTime = 0.10f;
|
||
|
||
[Header("■ 몬스터 스탯 (PD 지시 · 임시 · 추후 테이블)")]
|
||
[Tooltip("몬스터 최대 체력 · 임시 10")]
|
||
public float enemyHp = 10f;
|
||
[Tooltip("이 반경(m) 안에 플레이어가 들어오면 추적한다 · 임시 6")]
|
||
public float enemySight = 6f;
|
||
[Tooltip("이 반경(m) 안이면 공격한다 · 임시 1.5")]
|
||
public float enemyAttackRange = 1.5f;
|
||
[Tooltip("추적을 포기하는 거리(m). 시야보다 크게 잡아 경계에서 깜빡이지 않게 한다 · 임시 8")]
|
||
public float enemyLoseSight = 8f;
|
||
[Tooltip("공격 간격(초) · 임시 1.5")]
|
||
public float enemyCooldown = 1.5f;
|
||
[Tooltip("1회 공격이 플레이어에게 주는 피해량 · 임시 1")]
|
||
public int enemyDamage = 1;
|
||
[Tooltip("추적 이동 속도(m/s) · 임시 1.6")]
|
||
public float enemyMoveSpeed = 1.6f;
|
||
[Tooltip("플레이어를 향해 도는 속도(도/초) · 임시 360")]
|
||
public float enemyTurnSpeed = 360f;
|
||
[Tooltip("공격 모션 시작 후 피해가 들어가는 시간(초) · 임시 0.3")]
|
||
public float enemyAttackHitDelay = 0.3f;
|
||
[Tooltip("공격 모션 길이를 Animator 에서 못 읽을 때 쓰는 대체 시간(초)")]
|
||
public float enemyAttackFallbackDuration = 0.7f;
|
||
[Tooltip("피격 반응 모션 길이(초). 0 이면 재생하지 않는다 · 임시 0.35")]
|
||
public float enemyDamageReactionSeconds = 0.35f;
|
||
[Tooltip("중력 가속도(m/s²)")]
|
||
public float enemyGravity = -19.62f;
|
||
|
||
[Header("■ 몬스터 사망 연출 (PD 지시 ⑤ · 임시)")]
|
||
[Tooltip("Die 클립 길이를 Animator 에서 못 읽을 때 쓰는 대체 시간(초)")]
|
||
public float deathDieFallbackDuration = 1.2f;
|
||
[Tooltip("Die 클립 종료 후 깜빡이기 시작할 때까지 대기(초) · 임시 0.15")]
|
||
public float deathDieHoldSeconds = 0.15f;
|
||
[Tooltip("깜빡이는 총 시간(초) · 임시 0.9")]
|
||
public float deathBlinkSeconds = 0.9f;
|
||
[Tooltip("깜빡이는 횟수(꺼짐 기준) · 임시 6")]
|
||
public int deathBlinkCount = 6;
|
||
[Tooltip("알파 페이드아웃 시간(초) · 임시 1.0")]
|
||
public float deathFadeSeconds = 1.0f;
|
||
|
||
[Header("■ 데미지 숫자 연출 (PD #702-① · 힘민지 HUDDMGUI 규약 이식)")]
|
||
[Tooltip("숫자가 화면에 떠 있는 시간(초) · 힘민지 destroyTime 0.9 동일")]
|
||
public float dmgPopupLifetime = 0.9f;
|
||
[Tooltip("초당 위로 올라가는 캔버스 단위 거리 · 임시 90")]
|
||
public float dmgPopupRiseSpeed = 90f;
|
||
[Tooltip("알파 감쇠 속도 계수(Lerp) · 힘민지 alphaSpeed 0.5 대비 가독성 위해 상향 · 임시 2.2")]
|
||
public float dmgPopupFadeSpeed = 2.2f;
|
||
[Tooltip("겹침 방지용 위치 흔들기 반경(캔버스 단위) · 힘민지 ±25 동일")]
|
||
public float dmgPopupJitter = 25f;
|
||
[Tooltip("대상 위로 띄우는 화면 높이 보정(캔버스 단위) · 임시 20")]
|
||
public float dmgPopupScreenOffsetY = 20f;
|
||
[Tooltip("적이 맞았을 때 숫자 색")]
|
||
public Color dmgPopupColorToEnemy = new Color(1f, 0.95f, 0.35f, 1f);
|
||
[Tooltip("플레이어가 맞았을 때 숫자 색")]
|
||
public Color dmgPopupColorToPlayer = new Color(1f, 0.35f, 0.35f, 1f);
|
||
[Tooltip("숫자 글자 크기(캔버스 단위) · 임시 46")]
|
||
public float dmgPopupFontSize = 72f;
|
||
[Tooltip("굵게 표시한다 (PD #703)")]
|
||
public bool dmgPopupBold = true;
|
||
[Tooltip("검은 아웃라인 두께 0~1 · 임시 0.22 (PD #703)")]
|
||
[Range(0f, 1f)] public float dmgPopupOutlineWidth = 0.22f;
|
||
[Tooltip("아웃라인 색 · 기본 검정")]
|
||
public Color dmgPopupOutlineColor = Color.black;
|
||
[Tooltip("글자 두께 보정 −1~1. 아웃라인에 획이 먹히지 않게 살짝 굵힌다 · 임시 0.1")]
|
||
[Range(-1f, 1f)] public float dmgPopupFaceDilate = 0.1f;
|
||
[Tooltip("풀 초기 개수. 모자라면 런타임에 증설한다 · 임시 12")]
|
||
public int dmgPopupPoolSize = 12;
|
||
|
||
[Header("■ 피격 하이라이트 (PD #702-① · MaterialPropertyBlock)")]
|
||
[Tooltip("피격 시 몬스터 전신에 입히는 색 · 임시 주황빨강")]
|
||
public Color hitFlashColor = new Color(1f, 0.30f, 0.22f, 1f);
|
||
[Tooltip("피격 색이 원래 색으로 돌아오는 시간(초) · 임시 0.15")]
|
||
public float hitFlashSeconds = 0.15f;
|
||
[Tooltip("피격 색 세기 0~1. 1 이면 전부 flashColor 로 덮는다 · 임시 0.85")]
|
||
[Range(0f, 1f)] public float hitFlashStrength = 0.85f;
|
||
|
||
/// <summary>플레이어 1회 공격 피해량. min~max 정수 균등 난수(max 포함).</summary>
|
||
public int RollPlayerDamage()
|
||
{
|
||
int lo = Mathf.Min(playerAttackMin, playerAttackMax);
|
||
int hi = Mathf.Max(playerAttackMin, playerAttackMax);
|
||
return Random.Range(lo, hi + 1);
|
||
}
|
||
}
|
||
}
|