The issue I am experiencing is that CharacterController.isGrounded flickers between true and false when the player holds down the ground pound button, even if the player is already grounded. Here is my code:
using UnityEngine;
using UnityEngine.InputSystem;
public class MovementController : MonoBehaviour
{
// declare reference variables
PlayerInput playerInput;
CharacterController characterController;
// variables to store player input values
Vector2 currentMovementInput;
Vector3 currentMovement;
Vector3 currentRunMovement;
Vector3 appliedMovement;
bool isMovementPressed;
bool isRunPressed;
//constants
float rotationFactorPerFrame = 15.0f;
public float runMultiplier = 3.0f;
public float walkSpeed = 1.5f;
// gravity variables
public float gravity = -9.8f;
public float groundedGravity = -3.00f;
public float terminalVelocity = -20.0f;
// jumping variables
bool isJumpPressed = false;
float initialJumpVelocity;
public float maxJumpHeight = 2.0f;
public float maxJumpTime = 0.75f;
public int jumpCount = 0;
bool isJumping = false;
// groundPound variables
public bool isGroundPoundPressed = false;
public bool isGroundPounding = false;
public float groundPoundSpeed = -20.0f;
float previousHeight = 0.0f;
public float knockbackForce = 5.0f; // You can tweak this to get the desired knockback force
// Awake is called earlier than Start in unity's event life cycle
void Awake()
{
//initially set reference variables
playerInput = new PlayerInput();
characterController = GetComponent<CharacterController>();
// set the player input callbacks
playerInput.CharacterControls.Move.started += onMovementInput;
playerInput.CharacterControls.Move.canceled += onMovementInput;
playerInput.CharacterControls.Move.performed += onMovementInput;
playerInput.CharacterControls.Run.started += onRun;
playerInput.CharacterControls.Run.canceled += onRun;
playerInput.CharacterControls.Jump.started += onJump;
playerInput.CharacterControls.Jump.canceled += onJump;
playerInput.CharacterControls.GroundPound.started += onGroundPound;
playerInput.CharacterControls.GroundPound.canceled += onGroundPound;
setupJumpVariables();
}
void setupJumpVariables()
{
float timeToApex = maxJumpTime / 2;
gravity = (-2 * maxJumpHeight) / Mathf.Pow(timeToApex, 2);
initialJumpVelocity = (2 * maxJumpHeight) / timeToApex;
}
void onJump(InputAction.CallbackContext context)
{
isJumpPressed = context.ReadValueAsButton();
}
void onGroundPound(InputAction.CallbackContext context)
{
isGroundPoundPressed = context.ReadValueAsButton();
}
void onRun(InputAction.CallbackContext context)
{
isRunPressed = context.ReadValueAsButton();
}
// handler function to set the player input values
void onMovementInput(InputAction.CallbackContext context)
{
currentMovementInput = context.ReadValue<Vector2>();
currentMovement.x = currentMovementInput.x * walkSpeed;
currentMovement.z = currentMovementInput.y * walkSpeed;
currentRunMovement.x = currentMovementInput.x * runMultiplier;
currentRunMovement.z = currentMovementInput.y * runMultiplier;
isMovementPressed = currentMovementInput.x != 0 || currentMovementInput.y != 0;
}
void HandleRotation()
{
Vector3 positionToLookAt;
// the change in position our character should point to
positionToLookAt.x = currentMovement.x;
positionToLookAt.y = 0.0f;
positionToLookAt.z = currentMovement.z;
// the current rotation of our character
Quaternion currentRotation = transform.rotation;
if (isMovementPressed)
{
// creates a new rotation based on where the player is currently pressing
Quaternion targetRotation = Quaternion.LookRotation(positionToLookAt);
transform.rotation = Quaternion.Slerp(currentRotation, targetRotation, rotationFactorPerFrame * Time.deltaTime);
}
}
void HandleJump()
{
if (characterController.isGrounded)
{
// Reset jump variables when grounded
isJumping = false;
jumpCount = 0; // Reset the jump count on the ground
currentMovement.y = groundedGravity; // Reset vertical velocity
}
if (isJumpPressed && jumpCount < 2)
{
isJumping = true;
currentMovement.y = initialJumpVelocity;
appliedMovement.y = initialJumpVelocity;
jumpCount++; // Increment jump count after the jump is initiated
isJumpPressed = false; // Prevent multiple increments in the same frame
}
}
void HandleGroundPound()
{
// Check if the player is grounded
if (characterController.isGrounded)
{
if (isGroundPounding)
{
// If the player is grounded and we were in a ground pound, complete it
isGroundPounding = false;
Debug.Log("Ground Pound Complete");
}
return; // If grounded, no ground pound should happen
}
// If the player is not grounded and ground pound button is pressed
if (isGroundPoundPressed && !isGroundPounding)
{
// Store the player's height before the ground pound
previousHeight = transform.position.y;
isGroundPounding = true;
currentMovement.y = groundPoundSpeed; // Apply downward speed for the ground pound
appliedMovement.y = groundPoundSpeed;
Debug.Log("Ground Pound Initiated");
}
}
void HandleGravity()
{
bool isFalling = currentMovement.y <= 0.0f || !isJumpPressed;
float fallMultiplier = 2.0f;
// apply proper gravity depending on if the character is grounded or not
if (characterController.isGrounded)
{
currentMovement.y = groundedGravity;
appliedMovement.y = groundedGravity;
if (isGroundPounding)
{
isGroundPounding = false; // Reset ground pounding
// Apply the knockback effect
float knockbackHeight = Mathf.Max(0.0f, previousHeight - transform.position.y); // Calculate how far they've fallen
currentMovement.y = knockbackForce * knockbackHeight; // Apply force based on height
appliedMovement.y = currentMovement.y;
}
}
else if (isFalling)
{
float previousYVelocity = currentMovement.y;
currentMovement.y = currentMovement.y + (gravity * fallMultiplier * Time.deltaTime);
appliedMovement.y = Mathf.Max((previousYVelocity + currentMovement.y) * 0.5f, terminalVelocity);
}
else
{
float previousYVelocity = currentMovement.y;
currentMovement.y = currentMovement.y + (gravity * Time.deltaTime);
appliedMovement.y = (previousYVelocity + currentMovement.y) * 0.5f;
}
}
// Update is called once per frame
void Update()
{
HandleRotation();
if (isRunPressed)
{
appliedMovement.x = currentRunMovement.x;
appliedMovement.z = currentRunMovement.z;
}
else
{
appliedMovement.x = currentMovement.x;
appliedMovement.z = currentMovement.z;
}
characterController.Move(appliedMovement * Time.deltaTime);
HandleGravity();
HandleJump();
HandleGroundPound();
}
void OnEnable()
{
// enable the character controls action map
playerInput.CharacterControls.Enable();
}
void OnDisable()
{
// Disable the character controls action map
playerInput.CharacterControls.Disable();
}
}