Converted to third person
This commit is contained in:
parent
109df7ef19
commit
36fd1709c1
6
.idea/.idea.RizzeBattleRoyale/.idea/vcs.xml
generated
Normal file
6
.idea/.idea.RizzeBattleRoyale/.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -87,15 +87,27 @@ MonoBehaviour:
|
|||||||
_Owner: {fileID: 0}
|
_Owner: {fileID: 0}
|
||||||
_cameraPivot: {fileID: 5401675310905547977}
|
_cameraPivot: {fileID: 5401675310905547977}
|
||||||
_cameraHandle: {fileID: 2117859135794692440}
|
_cameraHandle: {fileID: 2117859135794692440}
|
||||||
|
_minPitch: -40
|
||||||
|
_maxPitch: 70
|
||||||
|
_yawRotateSpeed: 120
|
||||||
|
_cameraDistance: 2.26
|
||||||
|
_cameraDistanceMin: 1
|
||||||
|
_cameraDistanceMax: 5
|
||||||
|
_shoulderOffset: {x: -1.19, y: 0.67, z: 0.27}
|
||||||
|
_cameraCollisionMask:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 4294967295
|
||||||
|
_cameraCollisionRadius: 0.15
|
||||||
_moveSpeed: 6
|
_moveSpeed: 6
|
||||||
_upGravity: -18
|
_upGravity: -18
|
||||||
_downGravity: -25
|
_downGravity: -25
|
||||||
_maxCameraAngle: 75
|
|
||||||
_jumpImpulse: 6
|
_jumpImpulse: 6
|
||||||
_groundAcceleration: 55
|
_groundAcceleration: 55
|
||||||
_groundDeceleration: 25
|
_groundDeceleration: 25
|
||||||
_airAcceleration: 25
|
_airAcceleration: 25
|
||||||
_airDeceleration: 1.3
|
_airDeceleration: 1.3
|
||||||
|
_turnSpeed: 1
|
||||||
|
_faceMoveDirection: 1
|
||||||
--- !u!114 &8517683973242102361
|
--- !u!114 &8517683973242102361
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@ -366,7 +378,7 @@ Transform:
|
|||||||
m_GameObject: {fileID: 1315163349561248969}
|
m_GameObject: {fileID: 1315163349561248969}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
m_LocalPosition: {x: -0.9, y: 2.16, z: -2.06}
|
m_LocalPosition: {x: 0, y: 1.38, z: 0}
|
||||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
m_ConstrainProportionsScale: 0
|
m_ConstrainProportionsScale: 0
|
||||||
m_Children:
|
m_Children:
|
||||||
|
@ -4,152 +4,210 @@ using Fusion.Addons.SimpleKCC;
|
|||||||
|
|
||||||
namespace Projectiles
|
namespace Projectiles
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Main script handling player agent. It provides access to common components and handles movement input processing and camera.
|
/// Third-person PlayerAgent for Photon Fusion + SimpleKCC.
|
||||||
/// </summary>
|
/// - Camera-relative movement
|
||||||
[DefaultExecutionOrder(-5)]
|
/// - Character rotation toward move/aim direction
|
||||||
[RequireComponent(typeof(Weapons), typeof(Health), typeof(SimpleKCC))]
|
/// - Shoulder camera with wall-collision
|
||||||
public class PlayerAgent : ContextBehaviour
|
/// </summary>
|
||||||
{
|
[DefaultExecutionOrder(-5)]
|
||||||
// PUBLIC MEMBERS
|
[RequireComponent(typeof(Weapons), typeof(Health), typeof(SimpleKCC))]
|
||||||
|
public class PlayerAgent : ContextBehaviour
|
||||||
|
{
|
||||||
|
// --- Networked / components ------------------------------------------------
|
||||||
|
|
||||||
[Networked]
|
[Networked] public Player Owner { get; set; }
|
||||||
public Player Owner { get; set; }
|
public Weapons Weapons { get; private set; }
|
||||||
public Weapons Weapons { get; private set; }
|
public Health Health { get; private set; }
|
||||||
public Health Health { get; private set; }
|
public SimpleKCC KCC { get; private set; }
|
||||||
public SimpleKCC KCC { get; private set; }
|
public PlayerInput Input { get; private set; }
|
||||||
public PlayerInput Input { get; private set; }
|
|
||||||
|
|
||||||
public bool InputBlocked => Health.IsAlive == false;
|
public bool InputBlocked => Health.IsAlive == false;
|
||||||
|
|
||||||
// PRIVATE MEMBERS
|
// --- Camera rig (reuse your existing references) ---------------------------
|
||||||
|
|
||||||
[SerializeField]
|
[SerializeField] private Transform _cameraPivot; // yaw/pitch anchor above character
|
||||||
private Transform _cameraPivot;
|
[SerializeField] private Transform _cameraHandle; // optional visual/weapon anchor
|
||||||
[SerializeField]
|
|
||||||
private Transform _cameraHandle;
|
|
||||||
|
|
||||||
[Header("Movement")]
|
[Header("Third-Person Camera")]
|
||||||
[SerializeField]
|
[SerializeField] private float _minPitch = -40f;
|
||||||
private float _moveSpeed = 6f;
|
[SerializeField] private float _maxPitch = 70f;
|
||||||
[SerializeField]
|
[SerializeField] private float _yawRotateSpeed = 720f; // deg/sec when snapping to aim
|
||||||
public float _upGravity = 15f;
|
[SerializeField] private float _cameraDistance = 3.5f; // default boom length
|
||||||
[SerializeField]
|
[SerializeField] private float _cameraDistanceMin = 1.0f;
|
||||||
public float _downGravity = 25f;
|
[SerializeField] private float _cameraDistanceMax = 5.0f;
|
||||||
[SerializeField]
|
[SerializeField] private Vector3 _shoulderOffset = new Vector3(0.4f, 1.6f, 0f);
|
||||||
private float _maxCameraAngle = 75f;
|
[SerializeField] private LayerMask _cameraCollisionMask = ~0; // collide with world
|
||||||
[SerializeField]
|
[SerializeField] private float _cameraCollisionRadius = 0.15f;
|
||||||
private float _jumpImpulse = 6f;
|
|
||||||
[SerializeField]
|
|
||||||
public float _groundAcceleration = 55f;
|
|
||||||
[SerializeField]
|
|
||||||
public float _groundDeceleration = 25f;
|
|
||||||
[SerializeField]
|
|
||||||
public float _airAcceleration = 25f;
|
|
||||||
[SerializeField]
|
|
||||||
public float _airDeceleration = 1.3f;
|
|
||||||
|
|
||||||
[Networked]
|
// --- Movement --------------------------------------------------------------
|
||||||
private Vector3 _moveVelocity { get; set; }
|
|
||||||
|
|
||||||
private Vector2 _lastFUNLookRotation;
|
[Header("Movement")]
|
||||||
|
[SerializeField] private float _moveSpeed = 6f;
|
||||||
|
[SerializeField] public float _upGravity = 15f;
|
||||||
|
[SerializeField] public float _downGravity = 25f;
|
||||||
|
[SerializeField] private float _jumpImpulse = 6f;
|
||||||
|
[SerializeField] public float _groundAcceleration = 55f;
|
||||||
|
[SerializeField] public float _groundDeceleration = 25f;
|
||||||
|
[SerializeField] public float _airAcceleration = 25f;
|
||||||
|
[SerializeField] public float _airDeceleration = 1.3f;
|
||||||
|
|
||||||
// NetworkBehaviour INTERFACE
|
[Header("Character Facing")]
|
||||||
|
[SerializeField] private float _turnSpeed = 720f; // deg/sec turning toward desired facing
|
||||||
|
[SerializeField] private bool _faceMoveDirection = true; // if false, faces camera forward (aiming)
|
||||||
|
|
||||||
public override void Spawned()
|
[Networked] private Vector3 _moveVelocity { get; set; }
|
||||||
{
|
|
||||||
name = Object.InputAuthority.ToString();
|
|
||||||
|
|
||||||
// Only local player needs networked properties (move velocity).
|
// Local cache of most recent FUN (FixedUpdateNetwork) look to build on in Render/LateUpdate
|
||||||
// This saves network traffic by not synchronizing networked properties to other clients except local player.
|
private Vector2 _lastFUNLookRotation; // (pitch,yaw) from KCC
|
||||||
ReplicateToAll(false);
|
|
||||||
ReplicateTo(Object.InputAuthority, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Despawned(NetworkRunner runner, bool hasState)
|
// --- Fusion lifecycle ------------------------------------------------------
|
||||||
{
|
|
||||||
Owner = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void FixedUpdateNetwork()
|
public override void Spawned()
|
||||||
{
|
{
|
||||||
if (Owner != null && Health.IsAlive == true)
|
name = Object.InputAuthority.ToString();
|
||||||
{
|
|
||||||
ProcessMovementInput();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setting camera pivot rotation
|
// Only local player needs networked props replicated (bandwidth saver)
|
||||||
var pitchRotation = KCC.GetLookRotation(true, false);
|
ReplicateToAll(false);
|
||||||
_cameraPivot.localRotation = Quaternion.Euler(pitchRotation);
|
ReplicateTo(Object.InputAuthority, true);
|
||||||
|
}
|
||||||
|
|
||||||
_lastFUNLookRotation = KCC.GetLookRotation();
|
public override void Despawned(NetworkRunner runner, bool hasState)
|
||||||
}
|
{
|
||||||
|
Owner = null;
|
||||||
|
}
|
||||||
|
|
||||||
// MONOBEHAVIOUR
|
public override void FixedUpdateNetwork()
|
||||||
|
{
|
||||||
|
if (Owner != null && Health.IsAlive)
|
||||||
|
{
|
||||||
|
ProcessMovementInput();
|
||||||
|
}
|
||||||
|
|
||||||
protected void Awake()
|
// Keep pivot pitch synced to KCC look (we set it in LateUpdate on local)
|
||||||
{
|
var pitchYaw = KCC.GetLookRotation(true, true); // (x=pitch, y=yaw)
|
||||||
KCC = GetComponent<SimpleKCC>();
|
_cameraPivot.localRotation = Quaternion.Euler(pitchYaw.x, 0f, 0f);
|
||||||
Weapons = GetComponent<Weapons>();
|
|
||||||
Health = GetComponent<Health>();
|
|
||||||
Input = GetComponent<PlayerInput>();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void LateUpdate()
|
_lastFUNLookRotation = KCC.GetLookRotation();
|
||||||
{
|
}
|
||||||
if (HasInputAuthority == true && Owner != null && Health.IsAlive == true)
|
|
||||||
{
|
|
||||||
// For responsive look experience we use last FUN look + accumulated look rotation delta
|
|
||||||
KCC.SetLookRotation(_lastFUNLookRotation + Input.AccumulatedLook, -_maxCameraAngle, _maxCameraAngle);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update camera pitch
|
// --- Unity lifecycle -------------------------------------------------------
|
||||||
// Camera pivot influences also weapon rotation so it needs to be set on proxies as well
|
|
||||||
var pitchRotation = KCC.GetLookRotation(true, false);
|
|
||||||
_cameraPivot.localRotation = Quaternion.Euler(pitchRotation);
|
|
||||||
|
|
||||||
if (HasInputAuthority == true)
|
private void Awake()
|
||||||
{
|
{
|
||||||
var cameraTransform = Context.Camera.transform;
|
KCC = GetComponent<SimpleKCC>();
|
||||||
|
Weapons = GetComponent<Weapons>();
|
||||||
|
Health = GetComponent<Health>();
|
||||||
|
Input = GetComponent<PlayerInput>();
|
||||||
|
}
|
||||||
|
|
||||||
// Setting base camera transform based on handle
|
private void LateUpdate()
|
||||||
cameraTransform.position = _cameraHandle.position;
|
{
|
||||||
cameraTransform.rotation = _cameraHandle.rotation;
|
// 1) UPDATE LOOK ROTATION (LOCAL ONLY): accumulate mouse/controller deltas
|
||||||
}
|
if (HasInputAuthority && Owner != null && Health.IsAlive)
|
||||||
}
|
{
|
||||||
|
// Accumulate pitch/yaw with clamp on pitch only; yaw is free
|
||||||
|
var look = _lastFUNLookRotation + Input.AccumulatedLook;
|
||||||
|
look.x = Mathf.Clamp(look.x, _minPitch, _maxPitch);
|
||||||
|
KCC.SetLookRotation(look, _minPitch, _maxPitch);
|
||||||
|
}
|
||||||
|
|
||||||
// PRIVATE METHODS
|
// 2) Apply current pitch to pivot for everyone (affects remote weapon aim too)
|
||||||
|
var pitchOnly = KCC.GetLookRotation(true, false);
|
||||||
|
_cameraPivot.localRotation = Quaternion.Euler(pitchOnly);
|
||||||
|
|
||||||
private void ProcessMovementInput()
|
// 3) Position the actual camera (LOCAL ONLY) with shoulder offset + collision
|
||||||
{
|
if (HasInputAuthority)
|
||||||
if (GetInput(out GameplayInput input) == false)
|
{
|
||||||
return;
|
var cam = Context.Camera.transform;
|
||||||
|
|
||||||
KCC.AddLookRotation(input.LookRotationDelta, -_maxCameraAngle, _maxCameraAngle);
|
// Desired camera target position (pivot in character space + shoulder offset)
|
||||||
|
Vector3 pivotWorld = _cameraPivot.TransformPoint(_shoulderOffset);
|
||||||
|
|
||||||
// It feels better when player falls quicker
|
// Camera wants to sit behind pivot along its backward vector
|
||||||
KCC.SetGravity(KCC.RealVelocity.y >= 0f ? _upGravity : _downGravity);
|
Quaternion yawWorld = Quaternion.Euler(0f, KCC.GetLookRotation(false, true).y, 0f);
|
||||||
|
Vector3 desiredOffset = yawWorld * new Vector3(0f, 0f, -_cameraDistance);
|
||||||
|
Vector3 desiredPos = pivotWorld + desiredOffset;
|
||||||
|
|
||||||
// Calculate input direction based on recently updated look rotation (the change propagates internally also to KCC.TransformRotation)
|
// Simple collision test: spherecast from pivot toward desired
|
||||||
var inputDirection = KCC.TransformRotation * new Vector3(input.MoveDirection.x, 0f, input.MoveDirection.y);
|
Vector3 dir = (desiredPos - pivotWorld);
|
||||||
|
float len = dir.magnitude;
|
||||||
|
Vector3 finalPos = desiredPos;
|
||||||
|
|
||||||
var desiredMoveVelocity = inputDirection * _moveSpeed;
|
if (len > 0.0001f)
|
||||||
float acceleration = 1f;
|
{
|
||||||
|
dir /= len;
|
||||||
|
if (Physics.SphereCast(pivotWorld, _cameraCollisionRadius, dir, out RaycastHit hit, len, _cameraCollisionMask, QueryTriggerInteraction.Ignore))
|
||||||
|
{
|
||||||
|
finalPos = pivotWorld + dir * Mathf.Max(hit.distance - 0.05f, _cameraDistanceMin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (desiredMoveVelocity == Vector3.zero)
|
// Clamp within min/max boom
|
||||||
{
|
float dist = Mathf.Clamp(Vector3.Distance(pivotWorld, finalPos), _cameraDistanceMin, _cameraDistanceMax);
|
||||||
// No desired move velocity - we are stopping.
|
finalPos = pivotWorld + dir * dist;
|
||||||
acceleration = KCC.IsGrounded == true ? _groundDeceleration : _airDeceleration;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
acceleration = KCC.IsGrounded == true ? _groundAcceleration : _airAcceleration;
|
|
||||||
}
|
|
||||||
|
|
||||||
_moveVelocity = Vector3.Lerp(_moveVelocity, desiredMoveVelocity, acceleration * Runner.DeltaTime);
|
cam.position = finalPos;
|
||||||
|
cam.rotation = Quaternion.Euler(pitchOnly.x, KCC.GetLookRotation(false, true).y, 0f);
|
||||||
|
|
||||||
float jumpImpulse = input.Buttons.WasPressed(Input.PreviousButtons, EInputButton.Jump) && KCC.IsGrounded ? _jumpImpulse : 0f;
|
// Keep your optional handle aligned to the camera (muzzle/crosshair visuals)
|
||||||
KCC.Move(_moveVelocity, jumpImpulse);
|
_cameraHandle.position = cam.position;
|
||||||
}
|
_cameraHandle.rotation = cam.rotation;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Input → Movement / Facing --------------------------------------------
|
||||||
|
|
||||||
|
private void ProcessMovementInput()
|
||||||
|
{
|
||||||
|
if (!GetInput(out GameplayInput input))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 1) Update look from input deltas (affects yaw for camera-relative movement)
|
||||||
|
KCC.AddLookRotation(input.LookRotationDelta, _minPitch, _maxPitch);
|
||||||
|
|
||||||
|
// 2) Gravity preference: faster fall feels better
|
||||||
|
KCC.SetGravity(KCC.RealVelocity.y >= 0f ? _upGravity : _downGravity);
|
||||||
|
|
||||||
|
// 3) CAMERA-RELATIVE MOVE on XZ plane
|
||||||
|
// Build forward/right from current yaw only (ignore pitch)
|
||||||
|
float yaw = KCC.GetLookRotation(false, true).y;
|
||||||
|
Quaternion yawRot = Quaternion.Euler(0f, yaw, 0f);
|
||||||
|
Vector3 camForward = yawRot * Vector3.forward;
|
||||||
|
Vector3 camRight = yawRot * Vector3.right;
|
||||||
|
Vector3 inputDir = (camForward * input.MoveDirection.y + camRight * input.MoveDirection.x);
|
||||||
|
inputDir.y = 0f;
|
||||||
|
if (inputDir.sqrMagnitude > 1f) inputDir.Normalize();
|
||||||
|
|
||||||
|
// Desired velocity and acceleration/deceleration
|
||||||
|
Vector3 desiredVel = inputDir * _moveSpeed;
|
||||||
|
float accel = 1f;
|
||||||
|
if (desiredVel == Vector3.zero)
|
||||||
|
accel = KCC.IsGrounded ? _groundDeceleration : _airDeceleration;
|
||||||
|
else
|
||||||
|
accel = KCC.IsGrounded ? _groundAcceleration : _airAcceleration;
|
||||||
|
|
||||||
|
_moveVelocity = Vector3.Lerp(_moveVelocity, desiredVel, accel * Runner.DeltaTime);
|
||||||
|
|
||||||
|
// 4) Character facing: toward move dir OR toward camera forward (aim)
|
||||||
|
Vector3 faceDir = _faceMoveDirection
|
||||||
|
? (_moveVelocity.sqrMagnitude > 0.01f ? _moveVelocity : (yawRot * Vector3.forward))
|
||||||
|
: (yawRot * Vector3.forward);
|
||||||
|
faceDir.y = 0f;
|
||||||
|
if (faceDir.sqrMagnitude > 0.0001f)
|
||||||
|
{
|
||||||
|
Quaternion target = Quaternion.LookRotation(faceDir, Vector3.up);
|
||||||
|
// Rotate the KCC transform smoothly (turnSpeed deg/sec)
|
||||||
|
Quaternion newRot = Quaternion.RotateTowards(KCC.TransformRotation, target, _turnSpeed * Runner.DeltaTime);
|
||||||
|
KCC.SetLookRotation(newRot);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5) Jump
|
||||||
|
float jumpImpulse = (input.Buttons.WasPressed(Input.PreviousButtons, EInputButton.Jump) && KCC.IsGrounded)
|
||||||
|
? _jumpImpulse : 0f;
|
||||||
|
|
||||||
|
// 6) Move
|
||||||
|
KCC.Move(_moveVelocity, jumpImpulse);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user