Project_WL/Assets/WL/Feel/WLHitFeelSettings.cs

168 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using UnityEngine;
/// <summary>
/// PD #797 — 타격 시점 임팩트 연출 수치의 단일 출처(SOT · 조직 C45).
///
/// 에셋 경로(고정): Assets/WL/Feel/Settings/Resources/WL/WLHitFeelSettings.asset
/// → WLHitFeel 이 Resources.Load&lt;WLHitFeelSettings&gt;("WL/WLHitFeelSettings") 로 1회 로드한다.
/// 에셋이 없거나 enabled = false 면 Actor.Get_Damage 의 훅이 false 를 반환하고
/// 기존 RealCamera.ShakeCamera(1) 경로가 그대로 살아난다 = 100% 기존 동작. 롤백 1순위 스위치.
///
/// 【기본값의 근거 — Feel v5.9.1 공식 데모 실측】
/// Assets/Feel/FeelDemos/Barbarians/Prefabs/FeelBarbarianEnemy.prefab
/// = PD 참고 이미지(탑다운 캐릭터가 무리를 베는 순간)와 같은 구성의 More Mountains 공식 프리셋.
/// FreezeFrameDuration 0.02 · MinimumTimescaleThreshold 0.1
/// CinemachineImpulse m_ImpulseDuration 0.2 · Position(넉백) 0.3 · Blink On/Off 각 0.03
/// → 전체 화면 플래시(MMF_Flash) 는 이 프리셋에 **없다**. 참고 이미지의 '번쩍임' 은
/// 몹 Blink + 히트 파티클이며 풀스크린 플래시가 아니다. 따라서 flashEnabled 기본 false.
/// MMF_FreezeFrame.cs 의 툴팁 원문도 "I usually go with 0.01s or 0.02s" 로 0.02 대를 권장한다.
///
/// 【힘민지 실측 제약 — 이 수치를 함부로 올리면 안 되는 이유】
/// · 근접 스윙 1회가 군집에서 몹 5~7 마리를 같은 프레임에 때린다 → throttleSeconds 로 스윙당 1발.
/// · 콤보 Knight@Attack1_S/2_S/3_S 간격 0.42~0.47 s → 히트스톱이 0.08 s 를 넘으면 리듬이 깨진다.
/// · ProjectileBase.cs:305/308 이 transform.localScale.x > 2f 로 **보스를 판정**한다
/// → 스케일 펀치가 그 경계를 넘나들면 보스 판정이 흔들린다(WLHitFeel 이 자동으로 건너뛴다).
/// · RealCamera 는 자기 출력을 다시 입력으로 읽는 저역통과 필터(smooth 4 · τ≈0.25 s)다
/// → 셰이크는 WLHitFeelCameraShake 가 필터 밖에서 가감산한다(해당 파일 주석 참조).
/// </summary>
[CreateAssetMenu(fileName = "WLHitFeelSettings", menuName = "WL/Hit Feel Settings")]
public class WLHitFeelSettings : ScriptableObject
{
// ─────────────────────────────────────────── 전체 스위치
[Header("전체 스위치")]
[Tooltip("끄면 Actor 의 훅이 false 를 반환해 기존 RealCamera.ShakeCamera(1) 경로가 그대로 살아난다. 롤백 1순위.")]
public bool enabled = true;
[Tooltip("스윙 1회당 1발로 묶는 간격(초 · unscaled).\n" +
"근접 스윙 1회가 군집에서 5~7 마리를 같은 프레임에 때리므로 반드시 필요하다.\n" +
"콤보 간격 0.42~0.47 s 보다 충분히 작아야 3 히트가 각각 발동한다. 0.08 = 발주 규정값.")]
public float throttleSeconds = 0.08f;
// ─────────────────────────────────────────── ① 히트스톱
public enum eHitStopMode
{
WLSafe = 0, // WL 자체 구현 — 직전 timeScale 을 저장했다가 '아무도 안 건드렸을 때만' 복원
FeelFreezeFrame = 1, // Feel 의 MMFreezeFrameEvent + MMTimeManager 경로
}
[Header("① 히트스톱 (타격 순간의 정지)")]
public bool hitStopEnabled = true;
[Tooltip("WLSafe(기본) = 직전 timeScale 을 기억했다가 '그 사이 아무도 timeScale 을 안 바꿨을 때만' 복원한다.\n" +
" → 일시정지(0)·서버 팝업(0)·보스 연출과 절대 싸우지 않는다.\n" +
"FeelFreezeFrame = Feel 의 MMFreezeFrameEvent 를 쏘고 MMTimeManager 가 처리한다.\n" +
" → MMTimeManager 가 timeScale 을 전역 소유하며 해제 시 항상 NormalTimeScale(1) 로 되돌린다.")]
public eHitStopMode hitStopMode = eHitStopMode.WLSafe;
[Tooltip("히트스톱 길이(초 · unscaled). Feel 공식 데모 = 0.02.\n" +
"0.08 을 넘으면 콤보(0.42~0.47 s) 리듬이 깨진다 — hitStopMax 로 강제 제한된다.")]
public float hitStopDuration = 0.03f;
[Tooltip("킬(이 타격으로 몹이 죽음) 시의 히트스톱 길이(초). 킬 강조를 끄려면 hitStopDuration 과 같게.")]
public float hitStopKillDuration = 0.05f;
[Tooltip("히트스톱 동안의 timeScale. 0 = 완전 정지(Feel 과 동일). 0.05 = 힘민지 기존 TimeMgr 와 동일한 느낌.")]
[Range(0f, 0.5f)] public float hitStopTimeScale = 0f;
[Tooltip("안전 상한(초). 이 값을 넘는 히트스톱은 강제로 잘린다. 콤보 보호용 — 함부로 올리지 말 것.")]
public float hitStopMax = 0.08f;
[Tooltip("현재 timeScale 이 이 값보다 낮으면 히트스톱을 발동하지 않는다.\n" +
"이미 멈춰 있거나(일시정지 0) 느려진 상태를 덮어쓰지 않기 위한 것. Feel 기본값과 동일한 0.1.")]
public float hitStopMinTimescale = 0.1f;
// ─────────────────────────────────────────── ② 카메라 셰이크
[Header("② 카메라 셰이크")]
public bool cameraShakeEnabled = true;
[Tooltip("셰이크 진폭(m). 카메라-주시점 거리 5.677 m · FOV 60 실측 기준\n" +
" 화면 세로 비율 = 진폭 / (2 × 5.677 × tan30°) = 진폭 × 15.3 %/m\n" +
" 0.10 m ≒ 화면 세로의 1.5 % (세로 화면에서는 가로 폭 대비 더 크게 보인다)\n" +
"발주 규정 범위 0.08~0.12.")]
public float shakeAmplitude = 0.10f;
[Tooltip("셰이크 길이(초 · unscaled). 발주 규정 범위 0.10~0.12.\n" +
"기존 RealCamera.ShakeCamera(1) 은 0.25 s 로 콤보 간격의 절반을 차지해 늘어져 보였다.")]
public float shakeDuration = 0.12f;
[Tooltip("셰이크 주파수(Hz 상당). 높을수록 잘게 떤다. 32 = 짧고 날카로운 타격감.")]
public float shakeFrequency = 32f;
[Tooltip("카메라 **로컬** 축별 가중치 (x = 화면 좌우, y = 화면 상하, z = 시선 앞뒤).\n" +
"세로 서비스라 상하 흔들림은 화면 세로가 길어 더 크게 체감된다 → y 를 낮춘다.\n" +
"기본 (1, 0.35, 0.6) = 좌우 위주 + 상하 소량 + 앞뒤(줌) 소량.")]
public Vector3 shakeAxisWeight = new Vector3(1f, 0.35f, 0.6f);
[Tooltip("킬 시 진폭 배수. 1 이면 킬 강조 없음.")]
public float shakeKillMultiplier = 1.35f;
[Tooltip("기존 Actor.cs 의 RealCamera.ShakeCamera(1)(진폭 0.1 · 0.25 s) 를 대체한다.\n" +
"끄면 둘이 **동시에** 걸려 과하게 흔들린다 — 켜 두는 것이 '살짝' 요구에 맞다.")]
public bool suppressLegacyShake = true;
// ─────────────────────────────────────────── ③ 피격 몹 스케일 펀치
[Header("③ 피격 몹 스케일 펀치")]
public bool scalePunchEnabled = true;
[Tooltip("최대 배수. 1.12 = 발주 규정값. 몹의 테이블 기본 스케일(f_DefaultScale)에 곱한다.")]
public float scalePunch = 1.12f;
[Tooltip("펀치 길이(초 · unscaled). 0 → 최대 → 0 으로 한 번 튀고 돌아온다.")]
public float scalePunchDuration = 0.10f;
[Tooltip("보스에게는 펀치를 걸지 않는다. 보스는 덩치가 커서 같은 배수도 훨씬 과하게 보이고,\n" +
"ProjectileBase 의 localScale.x > 2f 보스 판정과 경계가 겹친다.")]
public bool skipPunchOnBoss = true;
[Tooltip("이 타격으로 몹이 죽으면 펀치를 걸지 않는다.\n" +
"죽은 몹은 곧 풀로 반환되므로 펀치 복원이 재사용된 객체에 쓰일 위험이 있다.")]
public bool skipPunchOnKill = true;
// ─────────────────────────────────────────── ④ 화면 플래시 (기본 OFF)
[Header("④ 화면 플래시 — 기본 OFF")]
[Tooltip("Feel 공식 Barbarians 프리셋에는 풀스크린 플래시가 없다. 기본 false 를 권장한다.\n" +
"켜면 런타임에 오버레이 Canvas + Image 1 장(드로우 +1)을 만들고, 꺼져 있는 동안은 아무것도 만들지 않는다.")]
public bool flashEnabled = false;
[Tooltip("플래시 색. 알파 ≤ 0.12 (발주 규정 상한).")]
public Color flashColor = new Color(1f, 1f, 1f, 0.10f);
[Tooltip("플래시 길이(초 · unscaled). 발주 규정 0.06.")]
public float flashDuration = 0.06f;
// ─────────────────────────────────────────── ⑤ 진동 (기본 OFF)
[Header("⑤ 진동 — 기본 OFF (모바일 배터리)")]
[Tooltip("NiceVibrations 햅틱. 모바일 배터리 때문에 기본 false.")]
public bool hapticEnabled = false;
// ─────────────────────────────────────────── ⑥ 피격 몹 경직 (스태거)
[Header("⑥ 피격 몹 경직 — 맞은 몹이 아주 짧게 멈춘다")]
[Tooltip("PD #816zb — 맞은 몹이 넉백으로 밀린 직후 바로 걸어 나와 타격감이 죽는 문제.\n" +
"직접 타격(HitConfirmed) 마다 NavMeshAgent 속도를 0 으로 잠그고 애니메이터를 멈춘다(WL.Combat.Reaction.MobStagger).\n" +
"끄면 그 구독자가 아무 것도 하지 않는다 = 기존 동작 100 %. 롤백 스위치.")]
public bool mobStaggerEnabled = true;
[Tooltip("일반 몹 경직 길이(초 · unscaled). 콤보 간격 0.42~0.47 s 의 절반 이하로 둔다 — 0.18 = 발주 규정값.\n" +
"넉백 중에 맞으면 넉백이 끝난 시점부터 이 길이를 1회 다시 준다(밀리다 말고 곧장 걷지 않게).")]
public float mobStaggerSeconds = 0.18f;
[Tooltip("엘리트 경직 길이(초). 일반보다 짧게 — 압박감을 유지한다.")]
public float mobStaggerSecondsElite = 0.12f;
[Tooltip("보스 경직 길이(초). 가장 짧게 — 보스 패턴이 끊기면 안 된다.")]
public float mobStaggerSecondsBoss = 0.08f;
// ─────────────────────────────────────────── 진단
[Header("진단 (검증용 · 배포 시 false)")]
[Tooltip("타격/발동 시각과 스로틀 결과를 Debug.Log 로 남긴다.")]
public bool logHits = false;
}