OK so… I’m trying to fix this bug, by introducing a boolean that gets triggered when the player mounts or dismounts an animal, and I can use that to classify who gets triggered to quit using their patrol paths, and who does not (this whole thing is a big surprise in and of itself).
To do that, I created this script and placed it on each animal individually:
using UnityEngine;
namespace RPG.MalbersAccess
{
public class UniqueAnimalProperties : MonoBehaviour
{
[Tooltip("Is this Animal Dead?")]
public bool isThisAnimalDead = false;
[Tooltip("Is this Animal Mounted?")]
public bool isThisAnimalMounted = false;
public void MarkThisAnimalAsDead() // Used in 'AnimalDeathState.cs', to block the player from mounting dead animals
{
isThisAnimalDead = true;
}
public void MarkThisAnimalAsAlive() // Will be used in 'AnimalRespawnManager.cs', to allow mounting of respawned animals
{
isThisAnimalDead = false;
}
public bool ThisAnimalIsDead() // Is this animal dead?
{
return isThisAnimalDead;
}
public void MarkThisAnimalAsMounted()
{
isThisAnimalMounted = true;
}
public void MarkThisAnimalAsUnmounted()
{
isThisAnimalMounted = false;
}
public bool ThisAnimalIsMounted()
{
return isThisAnimalMounted;
}
}
}
then, I initiated it in ‘AnimalStateMachine.cs’, as such (this is my current ‘AnimalStateMachine.cs’ script):
using RPG.States;
using UnityEngine;
using RPG.Attributes;
using RPG.Animals;
using UnityEngine.AI;
using RPG.States.Enemies;
using RPG.Stats;
using RPG.Movement;
using RPG.Control;
using RPG.MalbersAccess;
public class AnimalStateMachine : StateMachine
{
[field: SerializeField] public Animator Animator {get; private set;}
[field: SerializeField] public Health Health {get; private set;}
[field: SerializeField] public Animal ThisAnimal {get; private set;}
[field: SerializeField] public NavMeshAgent Agent {get; private set;}
[field: SerializeField] public BaseStats BaseStats {get; private set;}
[field: SerializeField] public Rigidbody Rigidbody {get; private set;}
[field: SerializeField] public CharacterController CharacterController {get; private set;}
[field: SerializeField] public ForceReceiver ForceReceiver {get; private set;}
[field: SerializeField] public PatrolPath PatrolPath {get; private set;}
[field: SerializeField] public UniqueAnimalProperties UniqueAnimalProperties {get; private set;}
[field: SerializeField] public float FreeLookRotationSpeed {get; private set;} = 15.0f;
[field: SerializeField] public float MovementSpeed {get; private set;} = 2.0f;
[field: SerializeField] public float CrossFadeDuration {get; private set;} = 0.1f;
[field: SerializeField] public float AnimatorDampTime {get; private set;} = 0.1f;
public Blackboard Blackboard = new Blackboard();
[field: Header("AUTOMATICALLY UPDATED VARIABLE,\nDO NOT TOUCH!\nWILLD DELETE WHEN TESTING\nISDONE")]
[field: Tooltip("Temporary holder for the animal's Patrol Path, so that when the Player mounts the animal, he can control it by deleting the patrol path, and when it's dismounted, the animal knows it's Patrol Path again")]
[field: SerializeField] public PatrolPath PatrolPathHolder { get; private set; } // will hold the patrol path for when the player drives the animal
private void OnValidate()
{
if (Animator == null) Animator = GetComponent<Animator>();
if (Health == null) Health = GetComponent<Health>();
if (ThisAnimal == null) ThisAnimal = GetComponent<Animal>();
if (Agent == null) Agent = GetComponentInChildren<NavMeshAgent>();
if (BaseStats == null) BaseStats = GetComponent<BaseStats>();
if (Rigidbody == null) Rigidbody = GetComponent<Rigidbody>();
if (CharacterController == null) CharacterController = GetComponent<CharacterController>();
if (ForceReceiver == null) ForceReceiver = GetComponent<ForceReceiver>();
// Patrol Path must be manually assigned from the scene to the gameObject
if (UniqueAnimalProperties == null) UniqueAnimalProperties = GetComponent<UniqueAnimalProperties>();
}
private void Start()
{
Health.onDie.AddListener(() =>
{
SwitchState(new AnimalDeathState(this));
});
SwitchState(new AnimalIdleState(this));
Blackboard["Level"] = BaseStats.GetLevel();
// The animals rely on the rigidbody for collision detection, so you can't
// turn off 'isKinematic' BY CODE in here (don't try with the hierarchy, it'll fail!).
// So, instead, turn off the 'detectCollisions' for 'CharacterController', that'll
// solve the problem!
CharacterController.detectCollisions = false;
}
public PatrolPath GetAssignedPatrolPath()
{
return PatrolPath;
}
public void AssignPatrolPath(PatrolPath newPatrolPath)
{
this.PatrolPath = newPatrolPath;
}
public PatrolPath GetPatrolPathHolder()
{
return PatrolPathHolder;
}
public void SetPatrolPathHolder(PatrolPath PatrolPathHolder)
{
this.PatrolPathHolder = PatrolPathHolder;
}
}
and when patrolling or dwelling, I try enabling it, and disabling it (when in idle, so it can return to patrolling when dismounted (IT’S FOR TEST PURPOSES. THE FINAL MECHANIC WILL MAKE THE ANIMAL YOUR PET WHEN DISMOUNTED, NOW THAT YOU OWN IT)) accordingly.
Here’s the Patrolling part where we set it up:
// in 'Enter()':
AnimalMountManager.OnAnimalMounted += RemoveAnimalPatrolPath;
// in 'Exit()':
AnimalMountManager.OnAnimalMounted -= RemoveAnimalPatrolPath;
// Removing the Patrol Path to make the animal idle:
private void RemoveAnimalPatrolPath()
{
stateMachine.UniqueAnimalProperties.MarkThisAnimalAsMounted();
if (stateMachine.UniqueAnimalProperties.ThisAnimalIsMounted())
{
stateMachine.SetPatrolPathHolder(stateMachine.PatrolPath);
stateMachine.AssignPatrolPath(null);
stateMachine.SwitchState(new AnimalIdleState(stateMachine));
}
}
and the exact same thing happens when dwelling to be able to drive the animal
Moving on, in ‘Idle’, making them patrol again does the opposite effect, as follows:
// in 'Enter()':
AnimalMountManager.OnAnimalDismounted += ReassignAnimalPatrolPath;
// in 'Exit()':
AnimalMountManager.OnAnimalDismounted -= ReassignAnimalPatrolPath;
// in 'ReassignAnimalPatrolPath()':
private void ReassignAnimalPatrolPath()
{
stateMachine.ThisAnimal.GetComponent<UniqueAnimalProperties>().MarkThisAnimalAsUnmounted();
if (!stateMachine.UniqueAnimalProperties.ThisAnimalIsMounted())
{
// DUMMY CODE JUST TO TEST THE ANIMALS PATROL STATE
// DELETE AFTER ALL ANIMALS SUCCESSFULLY BE GOOD AT
// NAVIGATION
stateMachine.AssignPatrolPath(stateMachine.GetPatrolPathHolder());
stateMachine.SwitchState(new AnimalIdleState(stateMachine));
stateMachine.SetPatrolPathHolder(null);
}
}
The problem I have is, is there’s two animals in my scene (for now), a horse and a Raven
When you mount the horse, it triggers correctly as expected
When you mount the Raven, it triggers THE HORSE correctly as expected… and does NOTHING to the Raven itself, and I’m very confused as of why
Worst thing is, they both think they’re the ones being driven, regardless of who I’m driving, and I really can’t tell why. I just know it’s a flaw to do with the patrolling system
Please help @Brian_Trotter or anyone else who sees this
and the same problem goes on when trying to check for the animal’s death:
using RPG.Animals;
using RPG.MalbersAccess;
using RPG.States.Animals;
using UnityEngine;
public class AnimalDeathState : AnimalBaseState
{
private static readonly int FirstHorseDeathHash = Animator.StringToHash("Horse Death 1");
private static readonly int SecondHorseDeathHash = Animator.StringToHash("Horse Death 1 Mirrored");
private static readonly int ThirdHorseDeathHash = Animator.StringToHash("Horse Death 2");
private static readonly int FourthHorseDeathHash = Animator.StringToHash("Horse Death 2 Mirrored");
// Set up the Raven death has here as well
public AnimalDeathState(AnimalStateMachine stateMachine) : base(stateMachine) {}
public override void Enter()
{
Debug.Log($"Animal has entered death state");
if (stateMachine.ThisAnimal.Type == AnimalType.Horse)
{
// in 'AnimalBaseState.cs'
ActivateNonMalbersLayerWeight(0, 0);
ActivateNonMalbersLayerWeight(1, 0);
ActivateNonMalbersLayerWeight(2, 0);
ActivateNonMalbersLayerWeight(3, 0);
ActivateNonMalbersLayerWeight(4, 1);
}
if (stateMachine.ThisAnimal.Type == AnimalType.Raven)
{
// in 'AnimalBaseState.cs' (Will be implemented in the future)
ActivateNonMalbersLayerWeight(0, 0);
ActivateNonMalbersLayerWeight(1, 0);
ActivateNonMalbersLayerWeight(2, 0);
ActivateNonMalbersLayerWeight(3, 0);
ActivateNonMalbersLayerWeight(4, 0);
ActivateNonMalbersLayerWeight(5, 1);
}
ChooseRandomDeathAnimation();
// Solution to disable Malbers' MountTrigger.OnTriggerEnter()' when the animal is dead
var uniqueAnimalProperties = stateMachine.ThisAnimal.GetComponent<UniqueAnimalProperties>();
if (uniqueAnimalProperties != null)
{
uniqueAnimalProperties.MarkThisAnimalAsDead();
}
}
public override void Tick(float deltaTime)
{
// There's no 'Move(deltaTime)' here
}
public override void Exit()
{
if (stateMachine.ThisAnimal.Type == AnimalType.Horse)
{
// in 'AnimalBaseState.cs'
ActivateNonMalbersLayerWeight(0, 1);
ActivateNonMalbersLayerWeight(1, 1);
ActivateNonMalbersLayerWeight(2, 1);
ActivateNonMalbersLayerWeight(3, 1);
ActivateNonMalbersLayerWeight(4, 0);
}
if (stateMachine.ThisAnimal.Type == AnimalType.Raven)
{
// in 'AnimalBaseState.cs' (Will be implemented in the future)
ActivateNonMalbersLayerWeight(0, 1);
ActivateNonMalbersLayerWeight(1, 1);
ActivateNonMalbersLayerWeight(2, 1);
ActivateNonMalbersLayerWeight(3, 1);
ActivateNonMalbersLayerWeight(4, 1);
ActivateNonMalbersLayerWeight(5, 0);
}
}
private void ChooseRandomDeathAnimation()
{
if (stateMachine.ThisAnimal.Type == AnimalType.Horse)
{
var randomAnimationSelector = Random.Range(0, 4); // includes the zero, excludes the 4
switch (randomAnimationSelector)
{
case 0:
stateMachine.Animator.CrossFadeInFixedTime(FirstHorseDeathHash, AnimatorDampTime);
break;
case 1:
stateMachine.Animator.CrossFadeInFixedTime(SecondHorseDeathHash, AnimatorDampTime);
break;
case 2:
stateMachine.Animator.CrossFadeInFixedTime(ThirdHorseDeathHash, AnimatorDampTime);
break;
case 3:
stateMachine.Animator.CrossFadeInFixedTime(FourthHorseDeathHash, AnimatorDampTime);
break;
}
}
// Repeat for other animals here
}
}
For this one, the horse doesn’t get marked as dead for the bird’s death, but the bird doesn’t get marked dead either…
What’s driving me to pure insanity, is where is this link coming from? How do I seperate each animal to take it’s own variables without tangling them up?