diff --git a/Assets/WL/Look/Arena/ArenaWalker.cs b/Assets/WL/Look/Arena/ArenaWalker.cs new file mode 100644 index 000000000..ca02ffe9a --- /dev/null +++ b/Assets/WL/Look/Arena/ArenaWalker.cs @@ -0,0 +1,153 @@ +// ───────────────────────────────────────────────────────────────────────────── +// ArenaWalker.cs — 시범 전투장(WL_ArenaProto) 전용 이동 리그 (발주서 WL-814r · PD 「걸어다니며 크기 감 잡기」) +// +// ■ 왜 새 스크립트인가 +// 이 씬의 캐릭터는 **전시용**이라 게임 컴포넌트(`WizardActor`·`NavMeshAgent`·`Rigidbody`·`CapsuleCollider`)를 +// 전부 떼어 냈다(814r §1 · 그것들이 게임 초기화 없이 돌면서 `KeyNotFoundException: 'Freeze'` 가 프레임마다 터졌다). +// 되살리면 에러 0 이 깨지므로, **본편 시스템을 전혀 건드리지 않는 씬 전용 리그**를 따로 둔다. +// +// ■ 캐릭터에 붙이지 않고 별도 오브젝트에 두는 이유 +// `PC_LH_M05` 는 프리팹 인스턴스다. 거기에 컴포넌트를 더하면 「전시용 = Transform·Animator·렌더러뿐」이라는 +// 814r §1 의 완료 조건이 흐려진다. 그래서 이 리그가 밖에서 `body` 를 움직인다(프리팹 수정 0 · 인스턴스 추가 0). +// +// ■ 실측해서 가져온 값 (하드코딩 아님 · 전부 인스펙터 노출 · 출처 주석) +// · moveSpeed 3.9 = `Assets/Res_Addr/PC/LH_M05.prefab` NavMeshAgent `m_Speed` (인게임 이동 속도 그대로) +// · turnSpeed 300 = 같은 프리팹 NavMeshAgent `m_AngularSpeed` +// · 애니메이션 전환 = 원본 `Actor.AnimationPlay()` 가 쓰는 **상태 이름 직접 재생** 방식 그대로 +// (`Assets/Script/Character/Actor.cs:2031` `m_animation.Play(aniname)` · 이름은 `MyValue.cs:72` +// `eAnim.Idle→"idle"` / `eAnim.Run→"run"` · `pcanim_mage.controller` 에 두 상태 모두 존재 실측). +// 🔴 이 컨트롤러의 Base Layer 에는 idle/run 으로 들어가는 **트랜지션이 없다** — 파라미터로는 못 바꾼다. +// +// ■ 바닥 +// 콜라이더를 전부 뗐으므로 물리를 쓰지 않는다. 지형 높이는 `Terrain.SampleHeight()` 로 직접 샘플링한다 +// (TerrainCollider 유무와 무관 · Raycast 보다 싸다). 바위·물 통과는 크기 확인용이라 신경 쓰지 않는다(발주 명시). +// +// ■ 입력 = 레거시 Input (이 프로젝트 `activeInputHandler: 0`) +// ───────────────────────────────────────────────────────────────────────────── + +using UnityEngine; + +namespace WL.Look.Arena +{ + [DisallowMultipleComponent] + public sealed class ArenaWalker : MonoBehaviour + { + [Header("대상")] + [Tooltip("움직일 캐릭터 루트. 비우면 이 오브젝트 자신.")] + public Transform body; + + [Tooltip("따라다닐 카메라. 비우면 Camera.main.")] + public Camera followCamera; + + [Header("이동 (LH_M05.prefab NavMeshAgent 실측값)")] + [Tooltip("m/s · NavMeshAgent.m_Speed = 3.9")] + public float moveSpeed = 3.9f; + + [Tooltip("deg/s · NavMeshAgent.m_AngularSpeed = 300")] + public float turnSpeed = 300f; + + [Header("애니메이션 상태 이름 (MyValue.cs:72)")] + public string idleState = "idle"; + public string runState = "run"; + + [Header("프로브 — 중앙→가장자리 소요 시간 실측용 (평소 off)")] + [Tooltip("켜면 입력 없이 autoWalkDir 로 자동 전진하고, 중앙에서 autoWalkGoal m 떨어지면 걸린 시간을 로그로 남긴다.")] + public bool autoWalk; + public Vector3 autoWalkDir = Vector3.right; + public float autoWalkGoal = 14f; + + float _autoT = -1f; + /// 자동 전진이 목표 거리에 도달한 시각(초). 아직이면 -1. + public float AutoWalkSeconds { get; private set; } = -1f; + + Animator _anim; + Vector3 _camOffset; + bool _running; + + void Start() + { + if (body == null) body = transform; + if (followCamera == null) followCamera = Camera.main; + _anim = body.GetComponentInChildren(); + + // 🔴 전시용 캐릭터에는 애니메이션 이벤트 수신자가 없다. + // 클립 `Idle` 이 `Event_Idle` 을 쏘는데 받는 쪽(`WizardActor`)을 떼어 냈으므로 + // 루프마다 "AnimationEvent 'Event_Idle' … has no receiver!" 에러가 난다(실측 2초에 1회). + // 빈 수신자 컴포넌트를 새로 붙이는 대신 이벤트 발사 자체를 끈다(런타임 전용 플래그). + foreach (var a in body.GetComponentsInChildren(true)) a.fireEvents = false; + + if (followCamera != null) _camOffset = followCamera.transform.position - body.position; + SnapToGround(); + PlayState(idleState); + } + + void Update() + { + if (body == null) return; + + // 카메라 기준 입력(화면에서 보이는 방향대로 움직인다) + Vector3 fwd = Vector3.forward, right = Vector3.right; + if (followCamera != null) + { + fwd = Vector3.ProjectOnPlane(followCamera.transform.forward, Vector3.up); + if (fwd.sqrMagnitude < 1e-6f) fwd = Vector3.forward; + fwd.Normalize(); + right = Vector3.Cross(Vector3.up, fwd); + } + + Vector3 dir = right * Input.GetAxisRaw("Horizontal") + fwd * Input.GetAxisRaw("Vertical"); + if (autoWalk && AutoWalkSeconds < 0f) + { + if (_autoT < 0f) _autoT = Time.time; + dir = autoWalkDir; + if (DistanceFromCenter >= autoWalkGoal) + { + AutoWalkSeconds = Time.time - _autoT; + Debug.Log(string.Format("[WL-814r] 중앙→{0:F1} m 도달: {1:F2} 초 (speed {2} m/s)", autoWalkGoal, AutoWalkSeconds, moveSpeed)); + } + } + if (dir.sqrMagnitude > 1f) dir.Normalize(); + + bool moving = dir.sqrMagnitude > 1e-4f; + if (moving) + { + body.position += dir * (moveSpeed * Time.deltaTime); + body.rotation = Quaternion.RotateTowards( + body.rotation, Quaternion.LookRotation(dir, Vector3.up), turnSpeed * Time.deltaTime); + } + SnapToGround(); + + if (moving != _running) + { + _running = moving; + PlayState(moving ? runState : idleState); + } + } + + void LateUpdate() + { + if (followCamera != null && body != null) + followCamera.transform.position = body.position + _camOffset; + } + + void SnapToGround() + { + var t = Terrain.activeTerrain; + if (t == null) return; + var p = body.position; + p.y = t.SampleHeight(p) + t.transform.position.y; + body.position = p; + } + + void PlayState(string state) + { + if (_anim != null && !string.IsNullOrEmpty(state)) _anim.Play(state, 0, 0f); + } + + /// 프로브용 — 지금까지 중앙(원점)에서 떨어진 수평 거리(m). + public float DistanceFromCenter + { + get { var p = body != null ? body.position : transform.position; return new Vector2(p.x, p.z).magnitude; } + } + } +} diff --git a/Assets/WL/Look/Arena/ArenaWalker.cs.meta b/Assets/WL/Look/Arena/ArenaWalker.cs.meta new file mode 100644 index 000000000..e32450f79 --- /dev/null +++ b/Assets/WL/Look/Arena/ArenaWalker.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 8767c514397da294b856789af586ae40 \ No newline at end of file diff --git a/Assets/WL/Look/Arena/Scenes/WL_ArenaProto.unity b/Assets/WL/Look/Arena/Scenes/WL_ArenaProto.unity index b44112096..35d018093 100644 --- a/Assets/WL/Look/Arena/Scenes/WL_ArenaProto.unity +++ b/Assets/WL/Look/Arena/Scenes/WL_ArenaProto.unity @@ -7264,11 +7264,24 @@ PrefabInstance: propertyPath: m_LocalRotation.z value: -0.1736205 objectReference: {fileID: 0} - m_RemovedComponents: [] + m_RemovedComponents: + - {fileID: 8641894914256534733, guid: 13b0aeca987980646af87711964339c8, type: 3} + - {fileID: 7018224718987816087, guid: 13b0aeca987980646af87711964339c8, type: 3} + - {fileID: 2857479151170562952, guid: 13b0aeca987980646af87711964339c8, type: 3} + - {fileID: 3657923760099891804, guid: 13b0aeca987980646af87711964339c8, type: 3} + - {fileID: 1835281278008071509, guid: 13b0aeca987980646af87711964339c8, type: 3} + - {fileID: 1155056168462878929, guid: 13b0aeca987980646af87711964339c8, type: 3} + - {fileID: 8835979289890761207, guid: 13b0aeca987980646af87711964339c8, type: 3} m_RemovedGameObjects: [] m_AddedGameObjects: [] m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 13b0aeca987980646af87711964339c8, type: 3} +--- !u!4 &918041735 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 5000001850263733008, guid: 13b0aeca987980646af87711964339c8, + type: 3} + m_PrefabInstance: {fileID: 918041734} + m_PrefabAsset: {fileID: 0} --- !u!1001 &929833716 PrefabInstance: m_ObjectHideFlags: 0 @@ -7724,6 +7737,7 @@ GameObject: m_Component: - component: {fileID: 1021739863} - component: {fileID: 1021739864} + - component: {fileID: 1021739865} m_Layer: 0 m_Name: Directional Light m_TagString: Untagged @@ -7811,6 +7825,35 @@ Light: m_LightUnit: 1 m_LuxAtDistance: 1 m_EnableSpotReflector: 1 +--- !u!114 &1021739865 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1021739862} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.UniversalAdditionalLightData + m_UsePipelineSettings: 1 + m_AdditionalLightsShadowResolutionTier: 2 + m_CustomShadowLayers: 0 + m_LightCookieSize: {x: 1, y: 1} + m_LightCookieOffset: {x: 0, y: 0} + m_SoftShadowQuality: 0 + m_RenderingLayersMask: + serializedVersion: 0 + m_Bits: 1 + m_ShadowRenderingLayersMask: + serializedVersion: 0 + m_Bits: 1 + m_Version: 4 + m_LightLayerMask: 1 + m_ShadowLayerMask: 1 + m_RenderingLayers: 1 + m_ShadowRenderingLayers: 1 --- !u!1001 &1023981080 PrefabInstance: m_ObjectHideFlags: 0 @@ -8913,6 +8956,7 @@ GameObject: - component: {fileID: 1166334010} - component: {fileID: 1166334009} - component: {fileID: 1166334008} + - component: {fileID: 1166334011} m_Layer: 0 m_Name: Main Camera m_TagString: MainCamera @@ -8963,8 +9007,8 @@ Camera: far clip plane: 500 field of view: 60 orthographic: 1 - orthographic size: 3.4 - m_Depth: 0 + orthographic size: 10 + m_Depth: -1 m_CullingMask: serializedVersion: 2 m_Bits: 4294967295 @@ -8987,13 +9031,57 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1166334007} serializedVersion: 2 - m_LocalRotation: {x: -0.064500555, y: 0.9143501, z: -0.36580086, w: -0.1612246} - m_LocalPosition: {x: 1.5702481, y: 5.4334784, z: 4.3142214} + m_LocalRotation: {x: 0.22414388, y: 0.48296294, z: -0.12940954, w: 0.8365163} + m_LocalPosition: {x: -22.499998, y: 15.599999, z: -12.990381} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1166334011 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1166334007} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.UniversalAdditionalCameraData + m_RenderShadows: 1 + m_RequiresDepthTextureOption: 2 + m_RequiresOpaqueTextureOption: 2 + m_CameraType: 0 + m_Cameras: [] + m_RendererIndex: -1 + m_VolumeLayerMask: + serializedVersion: 2 + m_Bits: 1 + m_VolumeTrigger: {fileID: 0} + m_VolumeFrameworkUpdateModeOption: 2 + m_RenderPostProcessing: 0 + m_Antialiasing: 0 + m_AntialiasingQuality: 2 + m_StopNaN: 0 + m_Dithering: 0 + m_ClearDepth: 1 + m_AllowXRRendering: 1 + m_AllowHDROutput: 1 + m_UseScreenCoordOverride: 0 + m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0} + m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0} + m_RequiresDepthTexture: 0 + m_RequiresColorTexture: 0 + m_TaaSettings: + m_Quality: 3 + m_FrameInfluence: 0.1 + m_JitterScale: 1 + m_MipBias: 0 + m_VarianceClampScale: 0.9 + m_ContrastAdaptiveSharpening: 0 + m_Version: 2 --- !u!1001 &1169602851 PrefabInstance: m_ObjectHideFlags: 0 @@ -12037,7 +12125,7 @@ Terrain: m_ShadowCastingMode: 2 m_DrawHeightmap: 1 m_DrawInstanced: 0 - m_DrawTreesAndFoliage: 0 + m_DrawTreesAndFoliage: 1 m_StaticShadowCaster: 0 m_IgnoreQualitySettings: 0 m_ReflectionProbeUsage: 1 @@ -12258,6 +12346,7 @@ GameObject: m_Component: - component: {fileID: 1711927570} - component: {fileID: 1711927571} + - component: {fileID: 1711927572} m_Layer: 0 m_Name: Point Light 0 m_TagString: Untagged @@ -12345,6 +12434,35 @@ Light: m_LightUnit: 1 m_LuxAtDistance: 1 m_EnableSpotReflector: 1 +--- !u!114 &1711927572 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1711927569} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.UniversalAdditionalLightData + m_UsePipelineSettings: 1 + m_AdditionalLightsShadowResolutionTier: 2 + m_CustomShadowLayers: 0 + m_LightCookieSize: {x: 1, y: 1} + m_LightCookieOffset: {x: 0, y: 0} + m_SoftShadowQuality: 0 + m_RenderingLayersMask: + serializedVersion: 0 + m_Bits: 1 + m_ShadowRenderingLayersMask: + serializedVersion: 0 + m_Bits: 1 + m_Version: 4 + m_LightLayerMask: 1 + m_ShadowLayerMask: 1 + m_RenderingLayers: 1 + m_ShadowRenderingLayers: 1 --- !u!1001 &1731050756 PrefabInstance: m_ObjectHideFlags: 0 @@ -13067,6 +13185,7 @@ GameObject: m_Component: - component: {fileID: 1861768068} - component: {fileID: 1861768069} + - component: {fileID: 1861768070} m_Layer: 0 m_Name: Point Light 1 m_TagString: Untagged @@ -13154,6 +13273,35 @@ Light: m_LightUnit: 1 m_LuxAtDistance: 1 m_EnableSpotReflector: 1 +--- !u!114 &1861768070 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1861768067} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.UniversalAdditionalLightData + m_UsePipelineSettings: 1 + m_AdditionalLightsShadowResolutionTier: 2 + m_CustomShadowLayers: 0 + m_LightCookieSize: {x: 1, y: 1} + m_LightCookieOffset: {x: 0, y: 0} + m_SoftShadowQuality: 0 + m_RenderingLayersMask: + serializedVersion: 0 + m_Bits: 1 + m_ShadowRenderingLayersMask: + serializedVersion: 0 + m_Bits: 1 + m_Version: 4 + m_LightLayerMask: 1 + m_ShadowLayerMask: 1 + m_RenderingLayers: 1 + m_ShadowRenderingLayers: 1 --- !u!1001 &1867996742 PrefabInstance: m_ObjectHideFlags: 0 @@ -14359,6 +14507,56 @@ Transform: type: 3} m_PrefabInstance: {fileID: 2040128716} m_PrefabAsset: {fileID: 0} +--- !u!1 &2042308454 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2042308456} + - component: {fileID: 2042308455} + m_Layer: 0 + m_Name: ArenaWalkerRig + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &2042308455 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2042308454} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8767c514397da294b856789af586ae40, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::WL.Look.Arena.ArenaWalker + body: {fileID: 918041735} + followCamera: {fileID: 1166334009} + moveSpeed: 3.9 + turnSpeed: 300 + idleState: idle + runState: run +--- !u!4 &2042308456 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2042308454} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1001 &2110215298 PrefabInstance: m_ObjectHideFlags: 0 @@ -14549,3 +14747,4 @@ SceneRoots: - {fileID: 1881675720} - {fileID: 918041734} - {fileID: 1166334010} + - {fileID: 2042308456}