using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace BulletHellTemplate
{
///
/// Handles advanced character movement, including walking, jumping, and being pushed.
/// Requires a CharacterController component to function correctly.
///
[RequireComponent(typeof(CharacterController))]
public class AdvancedCharacterController : MonoBehaviour
{
public float moveSpeed = 5f; // Speed at which the character moves
public float pushForce = 10f; // Force applied when being pushed
public float rotationSpeed = 720f; // Speed of character rotation
public float jumpHeight = 2f; // Height the character can jump
public float gravity = 9.81f; // Gravity applied to the character
public float maxFallVelocity = 40f; // Maximum velocity when falling
private CharacterController characterController; // The CharacterController component
private Vector3 moveDirection = Vector3.zero; // Current direction of movement
private Vector3 pushDirection = Vector3.zero; // Direction of any applied push force
private float verticalVelocity; // Vertical velocity for gravity and jumping
private bool isJumping = false; // Indicates if the character is currently jumping
private bool isMovementStopped = false;
[HideInInspector]public bool CanRotateWhileStopped { get; private set; } = false;
void Start()
{
// Initialize the CharacterController component
characterController = GetComponent();
}
void Update()
{
// Apply gravity and move the character each frame
ApplyGravity();
MoveCharacter();
}
///
/// Moves the character in a specified direction.
///
/// Direction vector for movement.
public void Move(Vector3 direction)
{
if (GameplayManager.Singleton.IsPaused())
{
moveDirection = Vector3.zero;
return;
}
if (isMovementStopped)
{
if (CanRotateWhileStopped && direction.magnitude > 0)
{
float targetAngle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
float angle = Mathf.SmoothDampAngle(transform.eulerAngles.y, targetAngle, ref rotationSpeed, 0.1f);
transform.rotation = Quaternion.Euler(0, angle, 0);
}
moveDirection = Vector3.zero;
return;
}
if (direction.magnitude > 0)
{
float targetAngle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
float angle = Mathf.SmoothDampAngle(transform.eulerAngles.y, targetAngle, ref rotationSpeed, 0.1f);
transform.rotation = Quaternion.Euler(0, angle, 0);
moveDirection = direction.normalized * moveSpeed;
}
else
{
moveDirection = Vector3.zero;
}
}
///
/// Makes the character jump if grounded.
///
public void Jump()
{
if (characterController.isGrounded)
{
isJumping = true;
verticalVelocity = Mathf.Sqrt(2 * jumpHeight * gravity);
}
}
///
/// Applies a force to push the character.
///
/// The force to apply.
public void Push(Vector3 force)
{
pushDirection = force;
}
///
/// Alters the character's movement speed.
///
/// The new speed value.
public void AlterSpeed(float newSpeed)
{
moveSpeed = newSpeed;
}
///
/// Gets the current speed of the character.
///
/// The magnitude of the character's movement velocity.
public float GetCurrentSpeed()
{
Vector3 horizontalVelocity = new Vector3(moveDirection.x + pushDirection.x, 0, moveDirection.z + pushDirection.z);
return horizontalVelocity.magnitude;
}
private void ApplyGravity()
{
if (characterController.isGrounded)
{
if (!isJumping)
{
verticalVelocity = -gravity * Time.deltaTime;
}
else
{
verticalVelocity -= gravity * Time.deltaTime;
isJumping = false;
}
}
else
{
verticalVelocity -= gravity * Time.deltaTime;
if (verticalVelocity < -maxFallVelocity)
{
verticalVelocity = -maxFallVelocity;
}
}
}
private void MoveCharacter()
{
if (isMovementStopped)
{
characterController.Move(Vector3.zero);
return;
}
Vector3 velocity = moveDirection + pushDirection;
velocity.y = verticalVelocity;
if (GameplayManager.Singleton.IsPaused())
{
characterController.Move(Vector3.zero);
}
else
{
characterController.Move(velocity * Time.deltaTime);
}
// Gradually reduce push force over time
pushDirection = Vector3.Lerp(pushDirection, Vector3.zero, Time.deltaTime * pushForce);
}
///
/// Stops the movement of the character temporarily, allowing optional rotation.
///
/// Whether rotation is allowed while movement is stopped.
public void StopMovement(bool allowRotation = false)
{
isMovementStopped = true;
CanRotateWhileStopped = allowRotation;
}
///
/// Resumes the movement of the character.
///
public void ResumeMovement()
{
isMovementStopped = false;
CanRotateWhileStopped = false;
}
///
/// Performs a dash in the specified direction while ensuring that the character does not pass through walls.
/// The dash increases the movement speed temporarily, locks the movement direction, and ensures the character faces the dash direction.
///
/// The speed to dash at.
/// The duration of the dash in seconds.
/// The direction vector in which to dash, typically coming from the joystick input.
public IEnumerator Dash(Vector3 direction, float dashSpeed, float dashDuration)
{
// Normalize the dash direction
Vector3 dashDirection = direction.normalized;
// Rotate character to face dash direction
if (dashDirection.magnitude > 0)
{
float targetAngle = Mathf.Atan2(dashDirection.x, dashDirection.z) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0, targetAngle, 0); // Instantly rotate to dash direction
}
// Store the original speed and temporarily disable normal movement
float originalSpeed = moveSpeed;
moveSpeed = 0f;
float elapsedTime = 0f;
// Perform the dash by temporarily setting the dash speed
while (elapsedTime < dashDuration)
{
// Apply dash movement in the locked dash direction using CharacterController
Vector3 dashMovement = dashDirection * dashSpeed * Time.deltaTime;
characterController.Move(dashMovement);
// Increase the elapsed time
elapsedTime += Time.deltaTime;
yield return null;
}
// After the dash, return to the original movement speed
moveSpeed = originalSpeed;
}
}
}