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; } } }