After the Third Person course, my way of escaping my issues was trying to improve the current Ledge system, so we can go right and left as well on any given ledge. To do so, I started by creating a new state, and after hours of try and error, eventually I realized it’s best done in the ‘PlayerHangingState.cs’ script itself, but my character still goes off the ledge to infinity, and he can literally still get attached to the air assuming it’s a ledge, if he starts moving left and right from the ledge itself (I cancelled the idea of another Player Ledge state because it was a nightmare to return to the hanging state from there). Where did I go wrong? Here is my current ‘PlayerHangingState.cs’ script:
using UnityEngine;
public class PlayerHangingState : PlayerBaseState
{
internal Vector3 closestPoint; // closest point to hold on to, before falling
internal Vector3 ledgeForward; // direction of the ledge, so our player looks at it whilst climbing
private readonly int HangingHash = Animator.StringToHash("Hanging");
private const float CrossFadeDuration = 0.3f;
public PlayerHangingState(PlayerStateMachine stateMachine, Vector3 ledgeForward, Vector3 closestPoint) : base(stateMachine)
// public PlayerHangingState(PlayerStateMachine stateMachine, Vector3 closestPoint, Vector3 ledgeForward) : base(stateMachine)
{
this.closestPoint = closestPoint;
this.ledgeForward = ledgeForward;
}
public override void Enter()
{
stateMachine.transform.rotation = Quaternion.LookRotation(ledgeForward, Vector3.up);
// Disabling the character controller, prior to hanging (similar to what we did when pulling our player up, when he detected a ledge)
// because the math won't work before deactivating the CharacterController:
stateMachine.CharacterController.enabled = false;
// moving the player when hanging:
stateMachine.transform.position = closestPoint - (stateMachine.LedgeDetector.transform.position - stateMachine.transform.position);
// Enabling the character controller again (we are done with the mathematical formula):
stateMachine.CharacterController.enabled = true;
stateMachine.Animator.CrossFadeInFixedTime(HangingHash, CrossFadeDuration);
}
public override void Tick(float deltaTime)
{
// if you press 'w' while hanging, climb whatever surface you're trying to climb:
if (stateMachine.InputReader.MovementValue.y > 0f) {
// move and resetting force done in 'PlayerPullupState.cs', since we need to finish off the animation first
stateMachine.SwitchState(new PlayerPullupState(stateMachine));
}
// (TEST: Delete if failed): if you press the 'a' or 'd' key to move sideways whilst hanging, do so (by switching to the new 'PlayerMoveSidewaysWhileHanging.cs' State):
if (stateMachine.InputReader.MovementValue.x > 0f || stateMachine.InputReader.MovementValue.x < 0f) {
// stateMachine.SwitchState(new PlayerMoveSidewaysWhileHanging(stateMachine, ledgeForward, closestPoint));
CheckRightOrLeft();
}
// if you press 's' while hanging, just fall:
if (stateMachine.InputReader.MovementValue.y < 0f) {
stateMachine.CharacterController.Move(Vector3.zero);
stateMachine.ForceReceiver.Reset();
stateMachine.SwitchState(new PlayerFallingState(stateMachine));
}
}
public override void Exit() {}
private readonly Vector3 Offset = new Vector3(2, 0, 0);
private readonly int SidewaysRightWhileHangingHash = Animator.StringToHash("Hanging Move Right");
private readonly int SidewaysLeftWhileHangingHash = Animator.StringToHash("Hanging Move Left");
// private const float CrossFadeDuration = 0.3f;
public void CheckRightOrLeft() {
// If you go right:
if (stateMachine.InputReader.MovementValue.x > 0f)
{
// Play the 'going right' animation:
stateMachine.Animator.CrossFadeInFixedTime(SidewaysRightWhileHangingHash, CrossFadeDuration);
// If you're not done yet with the animation, don't proceed:
if (GetNormalizedTime(stateMachine.Animator, "HangingMoveRight") < 1f) return;
// When you're done, disable the controller, move a few steps right, and then enable the controller:
stateMachine.CharacterController.enabled = false;
stateMachine.transform.Translate(Offset, Space.Self);
stateMachine.CharacterController.enabled = true;
}
// If you go left:
else if (stateMachine.InputReader.MovementValue.x < 0f)
{
// Play the 'going left' animation:
stateMachine.Animator.CrossFadeInFixedTime(SidewaysLeftWhileHangingHash, CrossFadeDuration);
// If you're not done yet with the animation, don't proceed:
if (GetNormalizedTime(stateMachine.Animator, "HangingMoveLeft") < 1f) return;
// When you're done, disable the controller, move a few steps left, and then enable the controller:
stateMachine.CharacterController.enabled = false;
stateMachine.transform.Translate(-Offset, Space.Self);
stateMachine.CharacterController.enabled = true;
}
}
}
My alternative (and original) approach to solving the ledge sideways issue, was to create a seperate State Machine script (and I would love to fix that one instead, because it’s easier to debug in the future), and access that instead. If it helps, here’s my attempted ‘PlayerMoveSidewaysWhileHanging.cs’ script (SCROLL RIGHT AT THE BOTTOM OF THE FOLLOWING SCRIPT):
using UnityEngine;
public class PlayerMoveSidewaysWhileHanging : PlayerBaseState
{
private Vector3 closestPoint;
private Vector3 ledgeForward;
private readonly Vector3 Offset = new Vector3(2,0,0);
private readonly int SidewaysRightWhileHangingHash = Animator.StringToHash("Hanging Move Right");
private readonly int SidewaysLeftWhileHangingHash = Animator.StringToHash("Hanging Move Left");
private const float CrossFadeDuration = 0.3f;
public PlayerMoveSidewaysWhileHanging(PlayerStateMachine stateMachine, Vector3 ledgeForward, Vector3 closestPoint) : base(stateMachine) {
this.closestPoint = closestPoint;
this.ledgeForward = ledgeForward;
}
public override void Enter()
{
CheckRightOrLeft();
}
public void CheckRightOrLeft() {
// If you go right:
if (stateMachine.InputReader.MovementValue.x > 0f) {
// Play the 'going right' animation:
stateMachine.Animator.CrossFadeInFixedTime(SidewaysRightWhileHangingHash, CrossFadeDuration);
// If you're not done yet with the animation, don't proceed:
if (GetNormalizedTime(stateMachine.Animator, "HangingMoveRight") < 1f) return;
// When you're done, disable the controller, move a few steps right, and then enable the controller:
stateMachine.CharacterController.enabled = false;
stateMachine.transform.Translate(Offset, Space.Self);
stateMachine.CharacterController.enabled = true;
}
// If you go left:
else if (stateMachine.InputReader.MovementValue.x < 0f) {
// Play the 'going left' animation:
stateMachine.Animator.CrossFadeInFixedTime(SidewaysLeftWhileHangingHash, CrossFadeDuration);
// If you're not done yet with the animation, don't proceed:
if (GetNormalizedTime(stateMachine.Animator, "HangingMoveLeft") < 1f) return;
// When you're done, disable the controller, move a few steps left, and then enable the controller:
stateMachine.CharacterController.enabled = false;
stateMachine.transform.Translate(-Offset, Space.Self);
stateMachine.CharacterController.enabled = true;
}
stateMachine.SwitchState(new PlayerHangingState(stateMachine, ledgeForward, closestPoint)); // this line isn't working, not in enter, neither in exit (putting this in tick is a nightmare) and it's my nightmare right now
}
public override void Tick(float deltaTime) {}
public override void Exit() {}
}
and obviously, don’t forget to add this line in ‘PlayerHangingState.Tick()’ (for the “alternative” approach in this post):
if (stateMachine.InputReader.MovementValue.x > 0f || stateMachine.InputReader.MovementValue.x < 0f) {
stateMachine.SwitchState(new PlayerMoveSidewaysWhileHanging(stateMachine, ledgeForward, closestPoint));
}
The only problem that approach has, is that it doesn’t access any state once the animation is played, hence why I coded the entire thing in one script instead
I will also write down over here the problems I still have with my current 3rd person project (I want to fix these before importing it into the RPG Course), which I’m hoping we can work on one by one (I copied these off my own Udemy questions):
-
I can’t strike my enemy for some reason (i.e: I can’t deal damage), although my weapon has a box collider on it
-
Any target I have, miraculously, instantiates an “Impact” effect after my player performs a combo attack on that target (somehow, I got an impact strike back from a static cube (after a combo attack), let alone my AI enemies)
-
the first 2 states of my combo attack deal absolutely no damage, and the third one outputs only a damage value of 10 (I had to turn off the enemy’s weapon box collider to see this work), regardless of what value I throw into it… This was a past value, and now it deals absolutely no damage
-
When we mix the projects up, we will use the death animations, right? I don’t want Ragdolls in, since it’s just more trouble than help right now