Sure, thanks for this. Here is the total Mover.cs attached. This is all working fine if have the character placed in the scene, just not if they’re spawned in
using RPG.Core;
using UnityEngine;
using UnityEngine.AI;
using GameDevTV.Saving;
using RPG.Attributes;
using System.Collections;
using UnityEngine.Events;
using RootMotion.FinalIK;
using System;
namespace RPG.Movement
{
public class Mover : MonoBehaviour, IAction, ISaveable
{
[SerializeField] float maxSpeed = 6f;
[SerializeField] float injuredMinSpeed = 2f;
[SerializeField] float maxNavPathLength = 40f;
[SerializeField] float accelleration = 2f;
[SerializeField] AnimationCurve stoppingDistance;
NavMeshAgent navMeshAgent;
Health health;
Animator animator;
float desiredSpeed;
float layerWeightVelocity;
float currentHealthPoints;
float healthPercentage;
private GameObject followTarget;
private bool isFollowing = false;
//Knockback
bool knockback;
Vector3 direction;
public float knockbackForce = 1f;
//Debug
public Vector3 debugDestinationPosition;
public GameObject waypointClickPrefab = null;
private void Awake()
{
navMeshAgent = GetComponent<NavMeshAgent>();
health = GetComponent<Health>();
animator = GetComponent<Animator>();
knockback = false;
}
void Update()
{
navMeshAgent.enabled = !health.IsDead();
UpdateAnimator();
SetMaxSpeed();
SetInjuredAnimLayerWeight();
UpdatePlayerStoppingDistance();
FollowTarget(followTarget);
}
private void UpdatePlayerStoppingDistance()
{
var velocity = navMeshAgent.velocity.magnitude;
navMeshAgent.stoppingDistance = stoppingDistance.Evaluate(velocity);
//Debug.Log(velocity);
}
void FixedUpdate()
{
if (knockback)
{
navMeshAgent.velocity = direction * knockbackForce;
}
}
public void StartMoveAction(Vector3 destination, float speedFraction)
{
GetComponent<ActionScheduler>().StartAction(this);
//disabled look towards clickpoint as it was stopping block direction rotation if it was interupted
//if(waypointClickPrefab != null && GetDistanceToMovePoint(destination) > 1.5f) LookAtIK(navMeshAgent.gameObject, destination);
MoveTo(destination, speedFraction);
}
private float GetDistanceToMovePoint(Vector3 destination)
{
float distance = Vector3.Distance(transform.position, destination);
return distance;
}
private void LookAtIK(GameObject callingController, Vector3 destination)
{
LookAtController lookAt = callingController.GetComponent<LookAtController>();
if (lookAt != null && waypointClickPrefab != null)
{
waypointClickPrefab.transform.position = destination;
lookAt.target = waypointClickPrefab.transform;
lookAt.weight = 1f;
lookAt.offset = new Vector3(0,.75f,0);
StartCoroutine(RemoveLookAtTarget(1f, lookAt));
}
}
IEnumerator RemoveLookAtTarget(float duration, LookAtController lookAtController)
{
yield return new WaitForSeconds(duration);
lookAtController.target = null;
lookAtController.weight = 0f;
}
public bool CanMoveTo(Vector3 destination)
{
//Prevent walkable cursor from showing if nav meshes do not connect
NavMeshPath path = new NavMeshPath();
bool hasPath = NavMesh.CalculatePath(transform.position, destination, NavMesh.AllAreas, path);
if (!hasPath) return false;
if (path.status != NavMeshPathStatus.PathComplete) return false;
//Prevent walkable if Path is too long
if (GetPathLength(path) > maxNavPathLength) return false;
return true;
}
private float GetPathLength(NavMeshPath path)
{
float total = 0;
if (path.corners.Length < 2) return total;
for (int i = 0; i < path.corners.Length - 1; i++)
{
total += Vector3.Distance(path.corners[i], path.corners[i + 1]);
}
return total;
}
public bool HasCompletedPath(NavMeshPath path)
{
float distance = GetPathLength(path);
if (distance != Mathf.Infinity && navMeshAgent.pathStatus == NavMeshPathStatus.PathComplete && navMeshAgent.remainingDistance == 0)
{
return true;
}
else return false;
}
public void MoveTo(Vector3 destination, float speedFraction)
{
if(navMeshAgent.enabled)
{
navMeshAgent.destination = destination;
//SetAccelleration();
navMeshAgent.speed = desiredSpeed * Mathf.Clamp01(speedFraction);
navMeshAgent.isStopped = false;
debugDestinationPosition = destination;
}
}
public void FollowTarget(GameObject target)
{
if(isFollowing && followTarget!= null)
{
Debug.Log("Following");
float followRange = 2f;
float closeEnoughToTarget = 2f;
float distanceToTarget = Vector3.Distance(this.transform.position, target.transform.position);
if (distanceToTarget > followRange)
{
MoveTo(target.transform.position, 1f);
}
}
}
public void SetFollowTarget(GameObject target)
{
followTarget = target;
isFollowing = true;
Debug.Log("following " + target);
}
public void SetIsFollowing(bool value)
{
isFollowing = value;
}
private void SetAccelleration()
{
if (animator.GetBool("Angry")) navMeshAgent.acceleration = accelleration * 2;
else navMeshAgent.acceleration = accelleration;
}
private void SetMaxSpeed()
{
currentHealthPoints = health.GetHealthPoints();
healthPercentage = currentHealthPoints / 100;
if(currentHealthPoints <= 100)
{
float newSpeed = maxSpeed * healthPercentage;
if(newSpeed <= injuredMinSpeed)
{
newSpeed = injuredMinSpeed;
}
desiredSpeed = newSpeed;
}
else
{
desiredSpeed = maxSpeed;
}
}
private void SetInjuredAnimLayerWeight()
{
int injuredAnimLayerIndex = animator.GetLayerIndex("Injured");
float currentInjuredLayerWeight = animator.GetLayerWeight(injuredAnimLayerIndex);
float targetLayerWeight = 1 - healthPercentage;
animator.SetLayerWeight(injuredAnimLayerIndex, Mathf.SmoothDamp(currentInjuredLayerWeight, targetLayerWeight, ref layerWeightVelocity, 0.2f));
}
public void Cancel()
{
if (!knockback)
{
StopAllCoroutines();
navMeshAgent.isStopped = true;
}
}
private void UpdateAnimator()
{
Vector3 velocity = navMeshAgent.velocity;
Vector3 localVelocity = transform.InverseTransformDirection(velocity);
float speed = localVelocity.z;
GetComponent<Animator>().SetFloat("forwardSpeed", speed);
}
[System.Serializable]
struct MoverSaveData
{
public SerializableVector3 position;
public SerializableVector3 rotation;
}
public void Knockback()
{
direction = -transform.forward;
knockbackForce = Mathf.Min(health.damageLastTaken * .25f, 3f);
StartCoroutine(Impact());
}
IEnumerator Impact()
{
knockback = true;
navMeshAgent.speed = 100f;
navMeshAgent.angularSpeed = 0; //Keeps the enemy facing forward ranter than spinning
navMeshAgent.acceleration = 100f;
yield return new WaitForSeconds(0.1f); //Only knock the enemy back for a short time
//Reset to default values
navMeshAgent.speed = maxSpeed;
navMeshAgent.angularSpeed = 2000f;
navMeshAgent.acceleration = 5f;
knockback = false;
}
public object CaptureState()
{
MoverSaveData data = new MoverSaveData();
data.position = new SerializableVector3(transform.position);
data.rotation = new SerializableVector3(transform.eulerAngles);
return data;
}
public void RestoreState(object state)
{
MoverSaveData data = (MoverSaveData)state;
NavMeshAgent navMeshAgent = GetComponent<NavMeshAgent>();
if (IsNewPositionOnNavMesh(data.position.ToVector()))
{
navMeshAgent.enabled = false;
transform.position = data.position.ToVector();
transform.eulerAngles = data.rotation.ToVector();
navMeshAgent.enabled = true;
}
}
private bool IsNewPositionOnNavMesh(Vector3 targetDestination)
{
NavMeshHit hit;
if(NavMesh.SamplePosition(targetDestination, out hit, 1f, NavMesh.AllAreas))
{
return true;
}
return false;
}
}
}