Add practice range: shooting, AK47, intro cinematic, dressed warehouse scene
- PortalTestDestination practice range: Valorant-style hitscan rifle (recoil, spray/bloom, reload, controller support), debug HUD, practice-dummy targets with head/body hitboxes, hit/death/respawn animations, procedural SFX/FX - AK47 model (Quaternius, CC0) held via Animator IK, with fire/reload/equip anims - Intro cinematic (camera shots, title card, target beam-in, GO hand-off) - Scene dressing editor pass: CC0 textures, props, warehouse roof, lighting and post-processing; custom stylized skybox shader - CharacterMovement: camera recoil, shoulder offset, cinematic control lock - Unity CLI pipeline package; plus in-progress chat/Rive/player changes Asset credits: Assets/Models/Props/PROPS_CREDITS.txt (3 CC-BY 3.0 props), Assets/Textures/Range/TEXTURES_LICENSE.txt, Assets/Resources/*/..._LICENSE.txt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
c674d76549
commit
5fc9ebf1e6
@@ -8,6 +8,18 @@ namespace QuickStart
|
||||
public TextMesh playerNameText;
|
||||
public GameObject floatingInfo;
|
||||
|
||||
[Header("Camera")]
|
||||
public float cameraDistance = 4.5f;
|
||||
public float cameraPivotHeight = 1.6f;
|
||||
public float cameraPitchDefault = 15f;
|
||||
public float cameraPitchMin = -10f;
|
||||
public float cameraPitchMax = 65f;
|
||||
public float mouseSensitivity = 2.5f;
|
||||
|
||||
private float cameraYaw;
|
||||
private float cameraPitch;
|
||||
private bool wantsCursorLocked = true;
|
||||
|
||||
private Material playerMaterialClone;
|
||||
|
||||
[SyncVar(hook = nameof(OnNameChanged))]
|
||||
@@ -31,8 +43,17 @@ namespace QuickStart
|
||||
|
||||
public override void OnStartLocalPlayer()
|
||||
{
|
||||
Camera.main.transform.SetParent(transform);
|
||||
Camera.main.transform.localPosition = new Vector3(0, 0, 0);
|
||||
// Camera is NOT parented to the player anymore: it follows the player's position
|
||||
// every frame (see LateUpdate/UpdateCameraTransform) but its rotation is driven
|
||||
// independently by the mouse, so turning the player body no longer snaps the
|
||||
// camera around with it.
|
||||
Camera.main.transform.SetParent(null, true);
|
||||
cameraYaw = transform.eulerAngles.y;
|
||||
cameraPitch = cameraPitchDefault;
|
||||
UpdateCameraTransform();
|
||||
|
||||
wantsCursorLocked = true;
|
||||
ApplyCursorLockState();
|
||||
|
||||
// Move the floatingInfo (player name) above the player instead of below.
|
||||
// Adjust Y to fit your model height (2.0f is a good starting point for standard humanoid scale).
|
||||
@@ -128,5 +149,81 @@ namespace QuickStart
|
||||
transform.Rotate(0f, rotationDelta, 0f, Space.Self);
|
||||
transform.Translate(0f, 0f, forwardDelta, Space.Self);
|
||||
}
|
||||
|
||||
// Camera follow/look runs in LateUpdate so it reacts to the final position for the
|
||||
// frame and isn't a frame behind the player's own Update movement.
|
||||
void LateUpdate()
|
||||
{
|
||||
if (!isLocalPlayer) return;
|
||||
|
||||
var kb = UnityEngine.InputSystem.Keyboard.current;
|
||||
if (kb != null && kb.escapeKey.wasPressedThisFrame)
|
||||
{
|
||||
if (ChatUI.IsInputFieldFocused)
|
||||
{
|
||||
ChatUI.Instance?.ExitChatFocus();
|
||||
wantsCursorLocked = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
wantsCursorLocked = !wantsCursorLocked;
|
||||
}
|
||||
}
|
||||
|
||||
// Re-assert the lock state every frame rather than trusting Cursor.lockState to
|
||||
// have stuck: the Editor Game view (and some platforms) silently drop the lock on
|
||||
// focus loss, and gating mouse-look on reading it back meant one dropped lock
|
||||
// permanently stopped the camera from responding to the mouse.
|
||||
bool shouldLockNow = wantsCursorLocked && !ChatUI.IsInputFieldFocused;
|
||||
ApplyCursorLockState(shouldLockNow);
|
||||
|
||||
if (shouldLockNow && Application.isFocused)
|
||||
{
|
||||
float mouseX;
|
||||
float mouseY;
|
||||
|
||||
var mouse = UnityEngine.InputSystem.Mouse.current;
|
||||
if (mouse != null)
|
||||
{
|
||||
Vector2 delta = mouse.delta.ReadValue();
|
||||
mouseX = delta.x * mouseSensitivity * Time.deltaTime;
|
||||
mouseY = delta.y * mouseSensitivity * Time.deltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback to legacy input if the new Input System isn't available
|
||||
mouseX = Input.GetAxis("Mouse X") * mouseSensitivity;
|
||||
mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity;
|
||||
}
|
||||
|
||||
cameraYaw += mouseX;
|
||||
cameraPitch = Mathf.Clamp(cameraPitch - mouseY, cameraPitchMin, cameraPitchMax);
|
||||
}
|
||||
|
||||
UpdateCameraTransform();
|
||||
}
|
||||
|
||||
void ApplyCursorLockState() => ApplyCursorLockState(wantsCursorLocked);
|
||||
|
||||
void ApplyCursorLockState(bool locked)
|
||||
{
|
||||
Cursor.lockState = locked ? CursorLockMode.Locked : CursorLockMode.None;
|
||||
Cursor.visible = !locked;
|
||||
}
|
||||
|
||||
// Orbits the camera around a pivot above the player at a fixed distance, using
|
||||
// cameraYaw/cameraPitch — never the player's own transform.rotation.
|
||||
void UpdateCameraTransform()
|
||||
{
|
||||
var cam = Camera.main;
|
||||
if (cam == null) return;
|
||||
|
||||
Quaternion orbitRotation = Quaternion.Euler(cameraPitch, cameraYaw, 0f);
|
||||
Vector3 pivot = transform.position + Vector3.up * cameraPivotHeight;
|
||||
|
||||
cam.transform.SetPositionAndRotation(
|
||||
pivot + orbitRotation * new Vector3(0f, 0f, -cameraDistance),
|
||||
orbitRotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user