FirstPersonController
unknown
plain_text
6 months ago
14 kB
9
Indexable
using UnityEngine;
using System.Collections;
using UnityEngine.InputSystem;
using UnityEngine.Rendering;
public class FirstPersonController : MonoBehaviour
{
[Header("Movement Settings")]
[SerializeField] private float walkSpeed = 40f;
[SerializeField] private float airControlMultiplier = 0.25f;
[SerializeField] private float knockbackControlMultiplier = 0.05f;
[SerializeField] private float acceleration = 1f; //Mess with later
[SerializeField] private float jumpForce = 5f;
[SerializeField] private float gravityMultiplier = 1.0f;
[SerializeField] private float mouseSensitivity = 0.1f;
[SerializeField] public float controllerSensitivityMultiplier = 8f;
[SerializeField] private float groundDrag = 5f; //Higher Values slows the Rigidbody faster.
[SerializeField] private float airDrag = 0.5f;
//[SerializeField] private float timeFrozen = 1f; //How long you can't move after using bouncepad
[Header("References")]
[SerializeField] public Camera mainCamera;
[SerializeField] private PlayerController pController;
[SerializeField] public PlayerInput playerInput;
private Rigidbody rb;
private float verticalRotation;
private bool isGrounded;
private bool allowMovement = true;
private bool beingExploded = false;
[Header("Ground Check")]
[SerializeField] private float groundCheckDistance = 1.6f; //Useless I think
[SerializeField] private float footRadius = 0.45f;
[SerializeField] private float footOffset = 0.9f;
[SerializeField] private LayerMask groundMask;
public bool groundedCheck = true;
public bool cantThrowNades = false;
public bool BounceNadeAvailable = true;
public bool C4Available = true;
public bool BouncePadAvailable = true;
public bool SuckStarAvailable = true;
public float BounceNadeCooldown = 7f;
public float C4Cooldown = 8f;
public float BPadCooldown = 8f;
public float SuckStarCooldown = 10f;
public bool bouncePadArmed = false;
public bool C4InstantThrowCooldown = false;
public int lives = 3;
private void Awake()
{
}
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
rb = GetComponent<Rigidbody>();
rb.freezeRotation = true; // prevent physics tilt
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
//Vector3 spawnPos = NadeScript.NadeSpawnPos(mainCamera);
}
// Update is called once per frame
void Update()
{
HandleRotation();
}
private void FixedUpdate() // Because this is consistent across frame rates, use this for phsycis!
{
HandleMovement();
ApplyGravityMultiplier();
CheckGround();
HandleJump();
Throw1();
Throw2();
Throw3();
Throw4();
rb.linearDamping = isGrounded ? groundDrag : airDrag; //This gives a natural stop when you release movement. Drag doesn’t affect vertical motion.
}
private void CheckGround()
{
//V3
Vector3 footPos = transform.position + Vector3.down * footOffset;
isGrounded = Physics.CheckSphere(footPos, footRadius, groundMask);
//Todo To check ground: use slope angle. if angle < 30: ground = true
}
private void HandleMovement()
{
if (!allowMovement) return;
Vector3 inputDirection = new Vector3(pController.MovementInput.x, 0f, pController.MovementInput.y); //Where you want to go
Vector3 currentHorizontalVelocity = CurrentHorizantalVelocity();
Vector3 desiredVelocity = DesiredVelocity();
if (isGrounded && inputDirection.magnitude > 0.1f) //If you're grounded, trying to move (If no input detected, no physics applied)
{
Vector3 velocityChange = desiredVelocity - currentHorizontalVelocity;
rb.AddForce(velocityChange * acceleration, ForceMode.Acceleration);
//Lets say you're moving at 5, and desire to move at 10. 10 - 5 = 5 movement added to your speed
}
if (isGrounded==false && beingExploded==false) //if you're airborn because you jumped
{
// Limited control in air
Vector3 velocityChange = desiredVelocity - currentHorizontalVelocity;
rb.AddForce(velocityChange * acceleration * airControlMultiplier, ForceMode.Acceleration);
//Lets say you're moving at 15, and desire to move at 10. 15 - 10 = 5 * 0.5 = 2.5 slowdown
}
if (isGrounded==false && beingExploded==true) //if you're airborn because you just were hit by a knockback or explosion
{
// SUPER Limited control in air
Vector3 velocityChange = desiredVelocity - currentHorizontalVelocity;
rb.AddForce(velocityChange * acceleration * knockbackControlMultiplier, ForceMode.Acceleration);
//Lets say you're moving at 15, and desire to move at 10. 15 - 10 = 5 * 0.5 = 2.5 slowdown
}
}
public Vector3 CurrentHorizantalVelocity()
{
Vector3 horizontalVelocity = new Vector3(rb.linearVelocity.x, 0f, rb.linearVelocity.z);
return horizontalVelocity;
}
public Vector3 DesiredVelocity()
{
Vector3 inputDirection = new Vector3(pController.MovementInput.x, 0f, pController.MovementInput.y); //Where you want to go
Vector3 moveDirection = transform.TransformDirection(inputDirection.normalized); //converts local direction (Relative to player) into world space.
if (isGrounded && inputDirection.magnitude > 0.1f) //If on Slope:
{
if (Physics.Raycast(transform.position, Vector3.down, out RaycastHit hit, 1.5f))
{
moveDirection = Vector3.ProjectOnPlane(moveDirection, hit.normal).normalized; //hit.normal = the slope’s perpendicular (up) vector.
}//ProjectOnPlane removes the component of your movement that goes into the slope. Normalizing ensures you don’t get faster speed on steep slopes.
}
//How does this function work? Imagine walking up a hill.
//Without projection → your movement vector points straight forward, but part of it points into the hill, so you move slower.
//With projection → the vector is “flattened” onto the slope plane, so you move along the slope consistently.
//todo causes a little jump when leaving a ramp. ChatGPT says it's cause of the IsGrounded function not liking the quick change.
Vector3 desiredVelocity = moveDirection * walkSpeed;
return desiredVelocity;
}
public void ApplyUpwardBoop()
{
//rb.AddExplosionForce(100f, Vector3.up, 1f, 0, ForceMode.Impulse);
rb.linearVelocity = new Vector3(0, 7.5f, 0);
//Debug.Log("Boopup");
}
public void ApplyFixedPush(Vector3 pushVector)
{
rb.linearVelocity = new Vector3(0, 0, 0);
Debug.Log("start");
//StartCoroutine(RestoreControlAfter(3f));
//StartCoroutine(noGroundedCheck(0.5f));
StartCoroutine(wasJustExploded(3f));
Debug.Log($"Push Vector: " + pushVector);
rb.AddForce(pushVector, ForceMode.Impulse);
//Player is given no control over their airborn direction for 1 second
}
//Todo add a 1 second delay on being able to throw suck stars, after being launched by knockback. Prevents player from instantly sucking themselves to ground.
private IEnumerator wasJustExploded(float tFrozen)
{
beingExploded = true;
yield return new WaitForSeconds(tFrozen);
beingExploded = false;
}
private void HandleJump()
{
if (pController.JumpTriggered && isGrounded)
{
rb.linearVelocity = new Vector3(rb.linearVelocity.x, 0f, rb.linearVelocity.z); // reset Y
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
}
}
private void ApplyGravityMultiplier()
{
if (!isGrounded)
{
rb.AddForce(Physics.gravity * gravityMultiplier, ForceMode.Acceleration);
}
}
private void HandleRotation()
{
float sensitivity = (playerInput.currentControlScheme == "Gamepad") ? mouseSensitivity * controllerSensitivityMultiplier : mouseSensitivity;
//“If Gamepad is the current controlscheme, use mouseSensitivity * controllerSensitivityMultiplier. Otherwise, just use mouseSensivity"
float XRotation = pController.LookInput.x * sensitivity;
float YRotation = pController.LookInput.y * sensitivity;
transform.Rotate(0, XRotation, 0);
verticalRotation = Mathf.Clamp(verticalRotation - YRotation, -89f, 89f); //Max Distance you're allowed to look up or down
mainCamera.transform.localRotation = Quaternion.Euler(verticalRotation, 0, 0);
}
public void Death()
{
lives -= 1;
}
private void Throw1()
{
if (pController.Throw1Triggered && cantThrowNades == false && BounceNadeAvailable) //when throw1 is pressed, if nades aren't on cooldown,
{
NadeScript nadeScript = GetComponent<NadeScript>();
// Pass the camera reference to NadeScript for NadeSpawnPos() and Vector3 nadeAngleThrown
nadeScript.SetCamera(mainCamera);
nadeScript.BounceNadeThrown();
StartCoroutine(BounceNadeCooldownTimer(BounceNadeCooldown));
StartCoroutine(NadeSpamPreventer(1));
}
}
private void Throw2()
{
NadeScript nadeScript = GetComponent<NadeScript>();
if (pController.Throw2Triggered && cantThrowNades==false && nadeScript.C4Exists==false && C4InstantThrowCooldown==false && C4Available)
{
nadeScript.SetCamera(mainCamera);
nadeScript.C4Thrown();
StartCoroutine(C4CooldownTimer(C4Cooldown));
StartCoroutine(NadeSpamPreventer(1));
}
if (pController.Throw2Triggered && nadeScript.C4Armed && nadeScript.C4Exists)
{
nadeScript.C4Thrown();
StartCoroutine(SpecialC4Cooldown(1));
}
}
private void Throw3()
{
if (pController.Throw3Triggered && cantThrowNades == false && BouncePadAvailable) //when throw1 is pressed, if nades aren't on cooldown,
{
NadeScript nadeScript = GetComponent<NadeScript>();
// Pass the camera reference to NadeScript for NadeSpawnPos() and Vector3 nadeAngleThrown
nadeScript.SetCamera(mainCamera);
nadeScript.BPadThrown();
StartCoroutine(BPadCooldownTimer(BPadCooldown));
StartCoroutine(NadeSpamPreventer(1));
}
}
private void Throw4()
{
if (pController.Throw4Triggered && cantThrowNades == false && SuckStarAvailable) //when throw1 is pressed, if nades aren't on cooldown,
{
NadeScript nadeScript = GetComponent<NadeScript>();
// Pass the camera reference to NadeScript for NadeSpawnPos() and Vector3 nadeAngleThrown
nadeScript.SetCamera(mainCamera);
nadeScript.SuckStarThrown();
StartCoroutine(SuckStarCooldownTimer(SuckStarCooldown));
StartCoroutine(NadeSpamPreventer(1));
}
}
public IEnumerator BounceNadeCooldownTimer(float cooldown) //Time between BounceNade throws
{
BounceNadeAvailable = false;
yield return new WaitForSeconds(cooldown);
BounceNadeAvailable = true;
}
public IEnumerator C4CooldownTimer(float cooldown) //Time between C4 throws. Starts when C4 is thrown, not explodes.
{
C4Available = false;
yield return new WaitForSeconds(cooldown);
C4Available = true;
}
public IEnumerator BPadCooldownTimer(float cooldown) //Time between C4 throws. Starts when C4 is thrown, not explodes.
{
BouncePadAvailable = false;
yield return new WaitForSeconds(cooldown);
BouncePadAvailable = true;
}
public IEnumerator SuckStarCooldownTimer(float cooldown) //Time between BounceNade throws
{
SuckStarAvailable = false;
yield return new WaitForSeconds(cooldown);
SuckStarAvailable = true;
}
public IEnumerator BouncePadArmTime(float cooldown) //Time it takes for a thrown C4 to be allowed to explode (No BO2 insta-popping c4s).
{
bouncePadArmed = false;
yield return new WaitForSeconds(cooldown);
bouncePadArmed = true;
}
public IEnumerator NadeSpamPreventer(float cooldown) //A 1 second delay on throwing any grenade after a grenade is thrown.
{
cantThrowNades = true;
yield return new WaitForSeconds(cooldown);
cantThrowNades = false;
}
public IEnumerator SpecialC4Cooldown(float cooldown) //A 1 second delay on throwing a C4 after a C4 EXPLODES. (exploding doesn't trigger NadeSpamPreventer.)
{
C4InstantThrowCooldown = true;
yield return new WaitForSeconds(cooldown);
C4InstantThrowCooldown = false;
}
private void OnDrawGizmosSelected() //No idea what this does
{
// Visualize ground check ray in editor
Gizmos.color = Color.yellow;
Gizmos.DrawLine(transform.position, transform.position + Vector3.down * groundCheckDistance);
}
}
Editor is loading...
Leave a Comment