using System;
using System.Linq;
using MLAPI;
using UnityEngine;
using UnityEngine.AI;
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(NavMeshAgent))]
[RequireComponent(typeof(NetworkCharacterState))]
public class ServerCharacterMovement : NetworkedBehaviour
{
private NavMeshAgent navMeshAgent;
private NetworkCharacterState networkCharacterState;
private NavMeshPath path;
private MovementState movementState;
[SerializeField]
private float movementSpeed; // TODO this should be assigned based on character definition
public override void NetworkStart()
{
if (!IsServer)
{
// Disable server component on clients
enabled = false;
return;
}
// On the server enable navMeshAgent and initialize
navMeshAgent.enabled = true;
networkCharacterState.OnReceivedClientInput += SetMovementTarget;
path = new NavMeshPath();
}
private void SetMovementTarget(Vector3 position)
{
movementState = MovementState.PathFollowing;
// Recalculate navigation path only on target change.
navMeshAgent.CalculatePath(position, path);
}
private void Awake()
{
navMeshAgent = GetComponent();
networkCharacterState = GetComponent();
}
private void FixedUpdate()
{
if (movementState == MovementState.PathFollowing)
{
Movement();
}
// Send new position values to the client
networkCharacterState.NetworkPosition.Value = transform.position;
networkCharacterState.NetworkRotationY.Value = transform.rotation.eulerAngles.y;
networkCharacterState.NetworkMovementSpeed.Value = movementState == MovementState.Idle ? 0 : movementSpeed;
}
private void Movement()
{
var corners = path.corners;
// If we don't have a movement path stop moving
if (!corners.Any())
{
movementState = MovementState.Idle;
return;
}
var desiredMovementAmount = movementSpeed * Time.fixedDeltaTime;
// If there is less distance to move left in the path than our desired amount
if (Vector3.SqrMagnitude(corners[corners.Length - 1] - transform.position) < (desiredMovementAmount * desiredMovementAmount))
{
// Set to destination and stop moving
transform.position = corners[corners.Length - 1];
movementState = MovementState.Idle;
return;
}
// Get the direction to move along based on the calculated path.
var direction = corners.Length > 1
? (corners[1] - corners[0]).normalized
: throw new InvalidOperationException("Navigation path should have a start and end position");
var movementVector = direction * desiredMovementAmount;
navMeshAgent.Move(movementVector);
transform.rotation = Quaternion.LookRotation(movementVector);
navMeshAgent.CalculatePath(corners[corners.Length - 1], path);
}
}
}