sigh… I tried everything I know, but I really can’t get that Ranger Aiming State to work, so I’ll probably keep it aside for the time being, until I get a better idea or something (the goal of that system was, quite literally, just to make freelook range fights happen). If anyone wants to help me fix it, here’s everything I did so far:
-
Create a ‘RangerAimingVirtualCamera’ in the hierarchy, assign it all it needs (but no ‘Follow’ or ‘LookAt’, because it’s under the player on the hierarchy), and give it a tag called ‘RangerAimingCamera’
-
Assign this into my Cinemachine Brain’s ‘Custom Blends’, between the freelook camera and the new ‘RangerAimingVirtualCamera’, with a timer of about 0.3 seconds
-
Create an Input for it, which will be the right mouse button, with all the procedures in ‘InputReader.cs’ (which will have one boolean, ‘IsRangerAiming’ and 2 events: one for entering the ranger state, and one for exiting it), and a new boolean in ‘WeaponConfig.cs’ called ‘isRangerAimWeapon’. Because this and the blocking state share the same button, this boolean was necessary, to make sure ranged weapons can’t go into blocking state, only that new state
-
Reference it in ‘PlayerFreeLookState’ and subscribe and unsubscribe to it as well, as follows:
// Subscribe in 'Enter()'
stateMachine.InputReader.RangerAimEvent += InputReader_HandleRangerAimingEvent;
// Usubscribe in 'Exit()'
stateMachine.InputReader.RangerAimEvent -= InputReader_HandleRangerAimingEvent;
// the 'InputReader_HandleRangerAimingEvent' function
// TEST - Player Ranger Aiming State:
private void InputReader_HandleRangerAimingEvent()
{
if (stateMachine.Fighter.GetCurrentWeaponConfig().GetIsRangerAimWeapon())
{
stateMachine.SwitchState(new PlayerRangerAimingState(stateMachine));
}
}
- The function itself, ‘PlayerRangerAimingState.cs’:
using System.Collections;
using RPG.States.Player;
using UnityEngine;
public class PlayerRangerAimingState : PlayerBaseState
{
private float mouseX;
private float mouseY;
private float sensitivity = 100f;
private float maxYRotation = 80f;
private float minYRotation = -80f;
private readonly int RangerStateBowLoadHash = Animator.StringToHash("RangerStateBowLoad");
public PlayerRangerAimingState(PlayerStateMachine stateMachine) : base(stateMachine) {}
public override void Enter()
{
Debug.Log($"Player has entered Ranger Aiming State");
stateMachine.InputReader.RangerAimCancelEvent += CancelAim;
stateMachine.rangerAimingCamera.m_Priority = 11;
stateMachine.Animator.CrossFadeInFixedTime(RangerStateBowLoadHash, stateMachine.CrossFadeDuration);
// Capture the initial camera rotation
mouseX = stateMachine.transform.eulerAngles.y;
mouseY = stateMachine.transform.eulerAngles.x;
}
public override void Tick(float deltaTime)
{
if (stateMachine.InputReader.IsAttacking)
{
stateMachine.SwitchState(new PlayerRangerFiringState(stateMachine));
return;
}
HandleRotation(deltaTime);
Move(deltaTime);
}
public override void Exit()
{
Debug.Log($"IsAttacking: {stateMachine.InputReader.IsAttacking}");
Debug.Log($"Player has exited Ranger Aiming State");
stateMachine.InputReader.RangerAimCancelEvent -= CancelAim;
stateMachine.rangerAimingCamera.m_Priority = 9;
if (stateMachine.InputReader.IsAttacking) { return; }
else // if you're not attacking, reset the rotation
// (because you're going back to FreeLook State, from 'CancelAim')
{
// Reset the y-axis rotation to zero:
Quaternion resetYRotation = Quaternion.Euler(0, stateMachine.transform.eulerAngles.y, 0);
stateMachine.transform.rotation = resetYRotation;
}
}
private void HandleRotation(float deltaTime)
{
float mouseXDelta = Input.GetAxis("Mouse X") * sensitivity * deltaTime;
float mouseYDelta = Input.GetAxis("Mouse Y") * sensitivity * deltaTime;
mouseX += mouseXDelta;
mouseY -= mouseYDelta;
mouseY = Mathf.Clamp(mouseY, minYRotation, maxYRotation);
// Quaternion targetRotation = initialCameraRotation * Quaternion.Euler(mouseY, mouseX, 0);
Quaternion targetRotation = Quaternion.Euler(mouseY, mouseX, 0);
stateMachine.transform.rotation = targetRotation;
}
private void CancelAim()
{
SetLocomotionState();
}
}
- When you’re firing, there’s a ‘PlayerRangerFiringState.cs’ for that:
using System.Collections;
using RPG.States.Player;
using UnityEngine;
public class PlayerRangerFiringState : PlayerBaseState
{
private readonly int RangerStateBowFireHash = Animator.StringToHash("RangerStateBowFire");
private bool hasFired;
public PlayerRangerFiringState(PlayerStateMachine stateMachine) : base(stateMachine)
{
hasFired = false;
}
public override void Enter()
{
Debug.Log($"Player Entered Ranger Firing State");
stateMachine.Animator.CrossFadeInFixedTime(RangerStateBowFireHash, stateMachine.CrossFadeDuration);
stateMachine.rangerAimingCamera.m_Priority = 11;
hasFired = false;
stateMachine.InputReader.RangerAimCancelEvent += CancelAim;
}
public override void Tick(float deltaTime)
{
Move(deltaTime);
if (!stateMachine.InputReader.IsRangerAiming)
{
ReturnToFreeLookState();
return;
}
if (!hasFired && stateMachine.Animator.GetCurrentAnimatorStateInfo(0).IsName("RangerStateBowFire") && stateMachine.Animator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1.0f)
{
// animation in layer 0 (i.e: the animation layer this animation is getting its info from), has finished playing
// (i.e: normalized time > 1.0f)
hasFired = true;
ReturnToRangerAimingState();
}
}
public override void Exit()
{
if (!stateMachine.InputReader.IsRangerAiming) // returning to freelook from firing
{
stateMachine.rangerAimingCamera.m_Priority = 9;
}
else
{
stateMachine.rangerAimingCamera.m_Priority = 11; // returning to aiming state
}
// Reset the y-axis rotation to zero:
Quaternion resetYRotation = Quaternion.Euler(0, stateMachine.transform.eulerAngles.y, 0);
stateMachine.transform.rotation = resetYRotation;
stateMachine.InputReader.RangerAimCancelEvent -= CancelAim;
}
private void ReturnToRangerAimingState()
{
stateMachine.SwitchState(new PlayerRangerAimingState(stateMachine));
}
private void ReturnToFreeLookState()
{
stateMachine.SwitchState(new PlayerFreeLookState(stateMachine));
/* // Reset the y-axis rotation to zero:
Quaternion resetRotation = Quaternion.Euler(0, stateMachine.transform.eulerAngles.y, 0);
stateMachine.transform.rotation = resetRotation; */
}
private void CancelAim()
{
ReturnToFreeLookState();
}
}
I got stuck because I was unable to get the correct rotation when exiting the firing state to the aiming state again, and I am absolutely terrible when it comes to rotations… They genuinely confuse me… So I’m seeking help, please, or I’ll just wait until Brian returns and tries to help me with this (and honestly, it was introducing so many bugs I can’t even get to fix because I can’t even fix the rotation issue…!)
and somehow, it was also colliding with my horse riding system… I don’t know what miracle was going on there, but with the Virtual camera in the scene, sometimes it will, quite literally, work on its own the moment I ride the horse (i.e: When I deactivate my player state machine to activate the state machine for mounting animals)
(It’s really sad for me to quit on this one tbh, because I really wanted this system into my project!)
I just hope we can fix this system up, because frankly speaking, it is an important one
Random errors:
There’s also this error that sometimes happens when I launch my game, ever since I introduced my Cinemachine Virtual Camera:
NullReferenceException: Object reference not set to an instance of an object
Cinemachine.Editor.CinemachineSceneToolUtility+<>c.<.cctor>b__21_0 () (at Library/PackageCache/com.unity.cinemachine@2.9.7/Editor/Utility/CinemachineSceneTools.cs:272)
UnityEditor.EditorApplication.Internal_CallUpdateFunctions () (at <57a8ad0d1492436d8cfee9ba8e6618f8>:0)
I have absolutely no clue where it’s coming from