Skip to content

Instantly share code, notes, and snippets.

@Kellojo
Last active May 26, 2024 12:01
Show Gist options
  • Select an option

  • Save Kellojo/1f13002116cce6ebbc37a1f97d3db722 to your computer and use it in GitHub Desktop.

Select an option

Save Kellojo/1f13002116cce6ebbc37a1f97d3db722 to your computer and use it in GitHub Desktop.

Revisions

  1. Kellojo revised this gist May 26, 2024. 1 changed file with 74 additions and 0 deletions.
    74 changes: 74 additions & 0 deletions PhysicsMoverNetworkTransform.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,74 @@
    using System.Collections;
    using System.Collections.Generic;
    using KinematicCharacterController;
    using Unity.Netcode;
    using UnityEngine;


    [RequireComponent(typeof(PhysicsMover))]
    public abstract class PhysicsMoverNetworkTransform : NetworkBehaviour, IMoverController {

    PhysicsMover PhysicsMover;
    PhysicsMoverState PreviousState;
    float LastUpdateTime;

    [SerializeField] NetworkVariable<PhysicsMoverState> State = new NetworkVariable<PhysicsMoverState>(writePerm: NetworkVariableWritePermission.Owner);

    float TickDuration {
    get {
    return 1f / (float)NetworkManager.Singleton.NetworkTickSystem.TickRate;
    }
    }

    protected virtual void Awake() {
    PhysicsMover = GetComponent<PhysicsMover>();
    PhysicsMover.MoverController = this;
    }

    private void OnEnable() {
    State.OnValueChanged += OnStateChance;
    }

    private void OnDisable() {
    State.OnValueChanged -= OnStateChance;
    }


    private void Update() {
    if (!IsSpawned) return;

    // collect state
    if (IsOwner) {
    State.Value = PhysicsMover.GetState();
    }
    }

    void OnStateChance(PhysicsMoverState oldState, PhysicsMoverState newState) {
    LastUpdateTime = Time.time;
    PreviousState = oldState;
    }

    public virtual void UpdateMovement(out Vector3 goalPosition, out Quaternion goalRotation, float deltaTime) {
    if (IsOwner) {
    // generate movement input, if we're the owner
    UpdateMovementOwner(out goalPosition, out goalRotation, deltaTime);
    } else {

    // apply movement input, if we're not the owner
    if (IsSpawned) {
    var t = (Time.time - LastUpdateTime) / TickDuration;
    var newState = State.Value;
    goalPosition = Vector3.Lerp(PreviousState.Position, newState.Position, t);
    goalRotation = Quaternion.Slerp(PreviousState.Rotation, newState.Rotation, t);
    } else {
    goalPosition = transform.position;
    goalRotation = transform.rotation;
    }

    }
    }

    public abstract void UpdateMovementOwner(out Vector3 goalPosition, out Quaternion goalRotation, float deltaTime);


    }
  2. Kellojo created this gist May 25, 2024.
    145 changes: 145 additions & 0 deletions PlayerNetworkTransform.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,145 @@
    using System.Collections;
    using System.Collections.Generic;
    using KinematicCharacterController;
    using Unity.Netcode;
    using UnityEngine;

    [RequireComponent(typeof(PlayerPhysicsSticker))]
    public class PlayerNetworkTransform : NetworkBehaviour
    {

    public NetworkVariable<PlayerNetworkTransformState> State = new NetworkVariable<PlayerNetworkTransformState>(writePerm: NetworkVariableWritePermission.Owner);
    public KinematicCharacterMotor PlayerMotor;
    PlayerPhysicsSticker PlayerPhysicsSticker;

    PlayerNetworkTransformState PreviousState;
    Rigidbody Rigidbody;

    float LastUpdateTime;
    float TickDuration {
    get {
    return 1f / (float) NetworkManager.Singleton.NetworkTickSystem.TickRate;
    }
    }

    private void Awake() {
    PlayerPhysicsSticker = GetComponent<PlayerPhysicsSticker>();
    Rigidbody = GetComponent<Rigidbody>();
    }

    private void OnEnable() {
    State.OnValueChanged += OnStateChange;
    }
    private void OnDisable() {
    State.OnValueChanged -= OnStateChange;
    }

    public override void OnNetworkSpawn() {
    base.OnNetworkSpawn();

    Rigidbody.isKinematic = !IsLocalPlayer;
    }


    private void LateUpdate() {

    // update state, if we're the owner
    if (IsLocalPlayer) {
    var state = new PlayerNetworkTransformState();
    state.WorldPosition = transform.position;
    state.WorldRotation = transform.rotation;
    state.LocalPosition = Vector3.zero;

    var physicsMover = PlayerPhysicsSticker.CurrentMover;
    state.IsGroundedOnPhysicsMover = physicsMover != null;
    state.IsGrounded = PlayerMotor.GroundingStatus.FoundAnyGround;
    state.SetUsedPhysicsMover(physicsMover);
    if (physicsMover != null) {
    state.LocalPosition = physicsMover.transform.InverseTransformPoint(state.WorldPosition);
    }

    State.Value = state;
    }

    // apply state, if we're not the owner
    if (!IsLocalPlayer) {
    var t = (Time.time - LastUpdateTime) / TickDuration;
    var playerTransform = transform;
    var state = State.Value;

    var newPos = Vector3.Lerp(PreviousState.WorldPosition, state.WorldPosition, t); // world pos
    if (state.IsGroundedOnPhysicsMover) {

    // standard moving case
    var ilocalPos = Vector3.Lerp(PreviousState.LocalPosition, state.LocalPosition, t);
    newPos = state.GetWorldPosOn(ilocalPos);

    // jumping on a mover
    // entering / exiting a mover
    if (!PreviousState.IsGroundedOnPhysicsMover) {
    newPos = Vector3.Lerp(PreviousState.WorldPosition, state.GetWorldPosOn(state.LocalPosition), t);
    }


    }

    playerTransform.position = newPos;
    playerTransform.rotation = Quaternion.Slerp(PreviousState.WorldRotation, state.WorldRotation, t);
    }
    }


    void OnStateChange(PlayerNetworkTransformState oldState, PlayerNetworkTransformState newState) {
    LastUpdateTime = Time.time;

    PreviousState = oldState;
    //Debug.Log(newState);
    }


    [System.Serializable]
    public struct PlayerNetworkTransformState : INetworkSerializable {
    public Vector3 WorldPosition;
    public Quaternion WorldRotation;

    public Vector3 LocalPosition;
    public bool IsGroundedOnPhysicsMover;
    public bool IsGrounded;
    public NetworkObjectReference UsedPhysicsMoverReference;


    public void SetUsedPhysicsMover(PhysicsMover physicsMover) {
    if (physicsMover == null) return;
    UsedPhysicsMoverReference = new NetworkObjectReference(physicsMover.NetworkObject);
    }
    public PhysicsMover GetUsedPhysicsMover() {
    if (!IsGroundedOnPhysicsMover) return null;
    UsedPhysicsMoverReference.TryGet(out NetworkObject physicsMoverObj);
    if (physicsMoverObj == null) return null;

    return physicsMoverObj.GetComponent<PhysicsMover>();
    }

    public Vector3 GetWorldPosOn(Vector3 localPos) {
    var mover = GetUsedPhysicsMover();
    return mover.transform.TransformPoint(localPos);
    }




    public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter {
    serializer.SerializeValue(ref WorldPosition);
    serializer.SerializeValue(ref WorldRotation);
    serializer.SerializeValue(ref LocalPosition);
    serializer.SerializeValue(ref IsGroundedOnPhysicsMover);
    serializer.SerializeValue(ref IsGrounded);
    serializer.SerializeValue(ref UsedPhysicsMoverReference);
    }

    public override string ToString() {
    return $"{WorldPosition}, ${WorldRotation.eulerAngles}, {IsGroundedOnPhysicsMover} on {GetUsedPhysicsMover()} with {LocalPosition}";
    }
    }

    }