using RebootKit.Engine.Extensions; using RebootKit.Engine.Foundation; using RebootKit.Engine.Main; using RebootKit.Engine.Services.Simulation; using RebootKit.Engine.Simulation; using Unity.Collections; using Unity.Mathematics; using Unity.Netcode; using UnityEngine; using Logger = RebootKit.Engine.Foundation.Logger; namespace RebootReality.jelycho.Player { public struct PlayerActorState : INetworkSerializable { public Vector3 Position; public Vector3 Velocity; public float LookPitch; public float LookYaw; public bool IsGrounded; public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter { serializer.SerializeValue(ref Position); serializer.SerializeValue(ref Velocity); serializer.SerializeValue(ref LookPitch); serializer.SerializeValue(ref LookYaw); serializer.SerializeValue(ref IsGrounded); } } public class PlayerActor : NetworkBehaviour { static readonly Logger s_Logger = new Logger(nameof(PlayerActor)); [SerializeField] Animator m_Animator; [Header("Movement")] [SerializeField] PlayerFPPLocomotion m_Locomotion; [Header("Camera")] [SerializeField] FPPCamera m_Camera; [SerializeField] CameraSpring m_CameraSpring; [SerializeField, Range(0.0f, 1.0f), Tooltip("Percentage of run speed")] float m_EnableCameraBobbingPercentThreshold = 0.5f; [SerializeField] float m_SprintCameraBobbing = 1.0f; [SerializeField] float m_RunCameraBobbing = 0.5f; [SerializeField] float m_IdleCameraBobbing = 0.0f; [SerializeField] float m_CameraBobbingTransitionSpeed = 5.0f; float m_TargetCameraBobbing = 0.0f; float m_CurrentCameraBobbing = 0.0f; [Header("Character")] [SerializeField] Transform m_CharacterRootTransform; [SerializeField] Transform m_HeadBoneTransform; [SerializeField] Transform m_HeadAimTargetTransform; [SerializeField] Transform m_CharacterForwardTransform; [SerializeField, Range(0.0f, 90.0f)] float m_CharacterRotateDeadAngle = 5.0f; [SerializeField, Range(0.0f, 90.0f)] float m_CharacterRotateSoftAngle = 90.0f; [SerializeField] float m_CharacterRotateSpeed = 180.0f; [SerializeField] float m_CharacterRotateFastSpeed = 720.0f; float m_CharacterTurnVelocity = 0.0f; [Header("Dragging")] [SerializeField] Transform m_DragGutStartPosition; [SerializeField] PhysicsObjectDragger m_PhysicsDragger; [SerializeField] FloatRange m_DragDistanceRange = new FloatRange(1.0f, 5.0f); [Header("Network")] [SerializeField] float m_MinTeleportDistance = 0.5f; public float3 LookDirection { get { float pitchRad = math.radians(-m_Camera.Pitch); float yawRad = math.radians(m_Camera.Yaw); return new float3(math.sin(yawRad) * math.cos(pitchRad), math.sin(pitchRad), math.cos(yawRad) * math.cos(pitchRad)); } } void Start() { m_CameraSpring.Initialize(); } public override void OnNetworkSpawn() { base.OnNetworkSpawn(); if (IsOwner) { SetupAsLocalPlayer(); RR.ClientTick += OnClientTick; } else { SetupAsRemotePlayer(); } } public override void OnNetworkDespawn() { base.OnNetworkDespawn(); if (IsOwner) { RR.ClientTick -= OnClientTick; } } void SetupAsLocalPlayer() { m_Camera.enabled = true; m_Camera.Camera.enabled = true; m_Locomotion.enabled = true; if (TryGetComponent(out Rigidbody rbody)) { rbody.isKinematic = false; } } void SetupAsRemotePlayer() { m_Camera.enabled = false; m_Camera.Camera.enabled = false; m_Locomotion.enabled = false; if (TryGetComponent(out Rigidbody rbody)) { rbody.isKinematic = true; } } public override void OnGainedOwnership() { base.OnGainedOwnership(); SetupAsLocalPlayer(); } public override void OnLostOwnership() { base.OnLostOwnership(); SetupAsRemotePlayer(); } void Update() { if (!IsSpawned) { return; } if (IsOwner) { OwnerTick(); } else { RemoteTick(); } // Character rotation float3 targetCharacterForward = math.normalize(LookDirection.With(y: 0.0f)); float3 currentCharacterForward = math.normalize(m_CharacterForwardTransform.forward.With(y: 0.0f)); float angleRad = math.acos(math.clamp(math.dot(targetCharacterForward, currentCharacterForward) / (math.length(targetCharacterForward) * math.length(currentCharacterForward)), -1f, 1f)); float angleDeg = math.degrees(angleRad); bool rotateCharacter = false; float rotateCharacterSpeed = m_CharacterRotateSpeed; m_CharacterTurnVelocity = 0.0f; if (math.abs(angleDeg) > m_CharacterRotateDeadAngle) { if (math.abs(angleDeg) < m_CharacterRotateSoftAngle) { rotateCharacter = true; } else { rotateCharacter = true; rotateCharacterSpeed = m_CharacterRotateFastSpeed; } } float velocityForward = m_Locomotion.Velocity.z; if (!rotateCharacter && math.abs(velocityForward) > 0.01f) { rotateCharacter = true; } if (rotateCharacter) { m_CharacterTurnVelocity = rotateCharacterSpeed * Time.deltaTime; Vector3 newForward = Vector3.RotateTowards(currentCharacterForward, targetCharacterForward, math.radians(m_CharacterTurnVelocity), 0.0f); m_CharacterForwardTransform.forward = newForward; } // Aim Target adjustment m_HeadAimTargetTransform.position = (float3) m_HeadBoneTransform.position + LookDirection * 5.0f; } void OwnerTick() { // Camera Stuff m_Camera.Tick(); if (m_Locomotion.IsGrounded && m_Locomotion.SpeedXZ >= m_Locomotion.runSpeed * m_EnableCameraBobbingPercentThreshold) { if (m_Locomotion.IsSprinting) { m_TargetCameraBobbing = m_SprintCameraBobbing; } else { m_TargetCameraBobbing = m_RunCameraBobbing; } } else { m_TargetCameraBobbing = m_IdleCameraBobbing; } m_CurrentCameraBobbing = Mathf.MoveTowards(m_CurrentCameraBobbing, m_TargetCameraBobbing, m_CameraBobbingTransitionSpeed * Time.deltaTime); m_Camera.SetBobbing(m_CurrentCameraBobbing); m_CameraSpring.UpdateSpring(Time.deltaTime, m_CharacterForwardTransform.up, m_CharacterForwardTransform.right, m_CharacterForwardTransform.forward); UpdateAnimator(m_Locomotion.Velocity); } void RemoteTick() { Vector3 targetPosition = m_NetworkState.Position; if ((transform.position - m_NetworkState.Position).sqrMagnitude < m_MinTeleportDistance * m_MinTeleportDistance) { targetPosition = Vector3.MoveTowards(transform.position, m_NetworkState.Position, m_Locomotion.runSpeed * Time.deltaTime); } m_Locomotion.WarpTo(targetPosition); m_Camera.Pitch = m_NetworkState.LookPitch; m_Camera.Yaw = m_NetworkState.LookYaw; UpdateAnimator(m_NetworkState.Velocity); } void OnClientTick() { PlayerActorState state = new PlayerActorState { Position = transform.position, LookPitch = m_Camera.Pitch, LookYaw = m_Camera.Yaw, IsGrounded = m_Locomotion.IsGrounded, Velocity = m_Locomotion.Velocity }; UpdatePlayerStateRpc(state); } PlayerActorState m_NetworkState; [Rpc(SendTo.NotMe)] void UpdatePlayerStateRpc(PlayerActorState state) { if (IsOwner) { return; } m_NetworkState = state; } [ServerRpc(RequireOwnership = false)] public void WarpToServerRpc(Vector3 position) { WarpToClientRpc(position); } [ClientRpc] void WarpToClientRpc(Vector3 position) { if (IsOwner) { m_Locomotion.WarpTo(position); } else { transform.position = position; } } public void SetSprint(bool isSprinting) { m_Locomotion.SetSprint(isSprinting); } public void Jump() { m_Locomotion.Jump(); } public void Look(Vector2 input) { m_Camera.Rotate(input.x, input.y); } public void SetMoveInput(Vector2 input) { float3 direction = Quaternion.AngleAxis(m_Camera.Yaw, Vector3.up) * new float3(input.x, 0.0f, input.y); m_Locomotion.SetWishDirection(direction); } public void StartDrag() { GameObject pickedGameObject = m_Camera.Sensor.Sense(); if (pickedGameObject != null && pickedGameObject.TryGetComponent(out Rigidbody rigidbody)) { m_PhysicsDragger.Grab(rigidbody); } } public void StopDrag() { m_PhysicsDragger.Drop(); } public void PrimaryAction() { m_Animator.SetTrigger(AnimatorParamHashes.Attack); } public void SecondaryAction() { m_Animator.SetTrigger(AnimatorParamHashes.Block); } struct AnimatorParamHashes { public static readonly int VelocityForwardNormalized = Animator.StringToHash("VelocityForwardNormalized"); public static readonly int VelocityRightNormalized = Animator.StringToHash("VelocityRightNormalized"); public static readonly int TurnVelocity = Animator.StringToHash("TurnVelocity"); public static readonly int IsGrounded = Animator.StringToHash("IsGrounded"); public static readonly int Attack = Animator.StringToHash("Attack"); public static readonly int Block = Animator.StringToHash("Block"); } void UpdateAnimator(Vector3 velocity) { Vector3 localVelocity = m_CharacterForwardTransform.InverseTransformDirection(velocity); float forwardNormalized = localVelocity.z / m_Locomotion.runSpeed; float rightNormalized = localVelocity.x / m_Locomotion.runSpeed; float turnVelocity = m_CharacterTurnVelocity; if (math.abs(forwardNormalized) > 0.01f || math.abs(rightNormalized) > 0.01f || !m_Locomotion.IsGrounded) { turnVelocity = 0.0f; } m_Animator.SetFloat(AnimatorParamHashes.VelocityForwardNormalized, forwardNormalized); m_Animator.SetFloat(AnimatorParamHashes.VelocityRightNormalized, rightNormalized); m_Animator.SetFloat(AnimatorParamHashes.TurnVelocity, turnVelocity); m_Animator.SetBool(AnimatorParamHashes.IsGrounded, m_Locomotion.IsGrounded); } } }