using MLAPI;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Assertions;
namespace BossRoom.Server
{
public enum MovementState
{
Idle = 0,
PathFollowing = 1,
}
///
/// Component responsible for moving a character on the server side based on inputs.
///
[RequireComponent(typeof(NetworkCharacterState), typeof(NavMeshAgent), typeof(ServerCharacter)), RequireComponent(typeof(Rigidbody))]
public class ServerCharacterMovement : NetworkedBehaviour
{
private NavMeshAgent m_NavMeshAgent;
private Rigidbody m_Rigidbody;
private NetworkCharacterState m_NetworkCharacterState;
private NavigationSystem m_NavigationSystem;
private DynamicNavPath m_NavPath;
private MovementState m_MovementState;
private ServerCharacter m_CharLogic;
[SerializeField]
private float m_MovementSpeed; // TODO [GOMPS-86] this should be assigned based on character definition
private void Awake()
{
m_NavMeshAgent = GetComponent();
m_NetworkCharacterState = GetComponent();
m_CharLogic = GetComponent();
m_Rigidbody = GetComponent();
m_NavigationSystem = GameObject.FindGameObjectWithTag(NavigationSystem.NavigationSytemTag).GetComponent();
}
public override void NetworkStart()
{
if (!IsServer)
{
// Disable server component on clients
enabled = false;
return;
}
// On the server enable navMeshAgent and initialize
m_NavMeshAgent.enabled = true;
m_NetworkCharacterState.OnReceivedClientInput += OnReceivedClientInput;
m_NavPath = new DynamicNavPath(m_NavMeshAgent, m_NavigationSystem);
}
private void OnReceivedClientInput(Vector3 position)
{
m_CharLogic.ClearActions(); //a fresh movement request trumps whatever we were doing before.
SetMovementTarget(position);
}
///
/// Sets a movement target. We will path to this position, avoiding static obstacles.
///
/// Position in world space to path to.
public void SetMovementTarget(Vector3 position)
{
m_MovementState = MovementState.PathFollowing;
m_NavPath.SetTargetPosition(position);
}
///
/// Follow the given transform until it is reached.
///
/// The transform to follow
public void FollowTransform(Transform followTransform)
{
m_MovementState = MovementState.PathFollowing;
m_NavPath.FollowTransform(followTransform);
}
///
/// Cancels any moves that are currently in progress.
///
public void CancelMove()
{
m_NavPath.Clear();
m_MovementState = MovementState.Idle;
}
private void FixedUpdate()
{
if (m_MovementState == MovementState.PathFollowing)
{
Movement();
}
// Send new position values to the client
m_NetworkCharacterState.NetworkPosition.Value = transform.position;
m_NetworkCharacterState.NetworkRotationY.Value = transform.rotation.eulerAngles.y;
m_NetworkCharacterState.NetworkMovementSpeed.Value =
m_MovementState == MovementState.Idle ? 0 : m_MovementSpeed;
}
private void OnValidate()
{
if (gameObject.scene.rootCount > 1) // Hacky way for checking if this is a scene object or a prefab instance and not a prefab definition.
{
Assert.IsTrue(
GameObject.FindGameObjectWithTag(NavigationSystem.NavigationSytemTag)?.GetComponent() != null,
$"NavigationSystem not found. Is there a NavigationSystem Behaviour in the Scene and does its GameObject have the {NavigationSystem.NavigationSytemTag} tag? {gameObject.scene.name}"
);
}
}
private void OnDestroy()
{
m_NavPath.Dispose();
}
private void Movement()
{
var desiredMovementAmount = m_MovementSpeed * Time.fixedDeltaTime;
var movementVector = m_NavPath.MoveAlongPath(desiredMovementAmount);
// If we didn't move stop moving.
if (movementVector == Vector3.zero)
{
m_MovementState = MovementState.Idle;
return;
}
m_NavMeshAgent.Move(movementVector);
transform.rotation = Quaternion.LookRotation(movementVector);
// After moving adjust the position of the dynamic rigidbody.
m_Rigidbody.position = transform.position;
m_Rigidbody.rotation = transform.rotation;
}
}
}