HATEBIN
>
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.InputSystem; namespace TallyHo { public class CharMoveV : MonoBehaviour { // This script handles moving the character on the Y axis, for jumping and gravity. [HideInInspector]public bool CharacterCanMove = true; [Header("Components")] [HideInInspector] public Rigidbody2D rb2d; private BoxCollider2D col; private GroundedDetection ground; [HideInInspector] public Vector2 velocity; private CharJuice juice; private CharDropDown dropDown; [Header("Jumping Stats")] [SerializeField, Range(2.0f, 5.5f)] [Tooltip("Maximum jump height")] public float jumpHeight = 2.88f; // Literal height the jump should reach. [SerializeField, Range(0.2f, 1.25f)] [Tooltip("How long it takes to reach that height before coming back down")] public float timeToJumpApex = 0.46f; [SerializeField, Range(0.0f, 5.0f)] [Tooltip("Gravity multiplier to apply when going up")] public float upwardMovementMultiplier = 1.0f; [SerializeField, Range(2.0f, 5.5f)] [Tooltip("Max extra jump height when pushing up")] public float upwardMovementMultExtra = 0.7f; [SerializeField, Range(2.0f, 5.5f)] [Tooltip("Default max jump height")] public float upwardMovementMultDefault = 1.0f; [SerializeField, Range(1.0f, 10.0f)] [Tooltip("Gravity multiplier to apply when going down")] public float downwardMovementMultiplier = 1.37f; [SerializeField, Range(0.0f, 1.0f)] [Tooltip("How many times can you jump in the air?")] public int maxAirJumps = 0; [Header("ColliderStats")] // Height and half height etc private Vector2 offsetNorm; private Vector2 heightNorm; private Vector2 offsetHalf; private Vector2 heightHalf; private ContactFilter2D standUpFilter = new ContactFilter2D(); public LayerMask filterEverythingButGroundLayer; [Header("Options")] [Tooltip("Should the character drop when you let go of jump?")] public bool variableJumpHeight = true; [SerializeField, Range(1.0f, 10.0f)] [Tooltip("Gravity multiplier when you let go of jump")] public float jumpCutOff = 1.37f; [SerializeField] [Tooltip("The fastest speed the character can fall")] public float speedLimit = 14.0f; [SerializeField] [Tooltip("Limits Y velocity. Be careful it doesn't limit jump height!")] public float jumpSpeedLimit = 14.0f; [SerializeField, Range(0.0f, 0.3f)] [Tooltip("How long should coyote time last?")] public float coyoteTime = 0.15f; [SerializeField, Range(0.0f, 0.3f)] [Tooltip("How far from ground shoud we cache your jump?")] public float jumpBuffer = 0.15f; [Header("Calculations")] private float jumpSpeed; private float defaultGravityScale; private float gravMultiplier; [Header("Current State")] private bool canJumpAgain = false; private bool desiredJump; private bool desiredButtstomp = false; private float jumpBufferCounter; private float coyoteTimeCounter = 0.0f; private bool pressingJump; [HideInInspector] public bool onGround; private bool currentlyJumping; [HideInInspector] public bool currentlyButtstomping = false; private bool desiredCrouch = false; private bool currentlyCrouched = false; private float directionY; void Awake() {// Find the character's Rigidbody and ground detection and juice scripts. rb2d = GetComponent
(); ground = GetComponent
(); juice = GetComponentInChildren
(); dropDown = GetComponent
(); defaultGravityScale = 1.0f; col = GetComponent
(); offsetNorm = new Vector2(col.offset.x, col.offset.y); heightNorm = new Vector2(col.size.x, col.size.y); offsetHalf = new Vector2(col.offset.x, offsetNorm.y * 0.5f); heightHalf = new Vector2(col.size.x, heightNorm.y * 0.5f); // filterEverythingButGroundLayer.value = ~filterEverythingButGroundLayer.value; standUpFilter.SetLayerMask(filterEverythingButGroundLayer); } public void OnJump(InputAction.CallbackContext context) {// This function is called when one of the jump buttons (like space or the A button) is pressed. if (CharacterCanMove) { if (!currentlyCrouched) { if (!currentlyButtstomping) { if (context.started) { desiredJump = true; pressingJump = true; } if (context.canceled) { pressingJump = false; } } } else { if (context.started) { dropDown.DropDown(); } } } } public void OnUpDown(InputAction.CallbackContext context) { directionY = context.ReadValue
(); } public void OnExtraHighJump(InputAction.CallbackContext context) { if (context.started) { upwardMovementMultiplier = upwardMovementMultExtra; } if (context.canceled) { upwardMovementMultiplier = upwardMovementMultDefault; } } public void OnButtstomp(InputAction.CallbackContext context) { if (currentlyJumping && !currentlyButtstomping && !currentlyCrouched) { desiredButtstomp = true; } } private void FixedUpdate() { // Get velocity from char's rb2d velocity = rb2d.velocity; if (desiredJump) {// Keep trying to do a jump, for as long as desiredJump is true DoAJump(); rb2d.velocity = velocity; // WHY THE FUCK IS THIS HERE?! // Skip gravity calculations this frame, so currentlyJumping doesn't turn off // This makes sure you can't do the coyote time double jump bug return; } if (desiredButtstomp) { DoAButtstomp(); } CalculateGravity(); } void Update() { setPhysics(); // Check if we're on the ground onGround = ground.IsGrounded(); if(jumpBuffer > 0)// Jump buffer allows us to queue up a jump, which will play when we next hit the ground {// Instead of immediately turning off "desire jump", start counting up... All the while, the DoAJump function will be repeatedly fired off if (desiredJump) { jumpBufferCounter += Time.deltaTime; if(jumpBufferCounter > jumpBuffer) {// If time exceeds the jump buffer, turn off "desire jump" (it 'expires') desiredJump = false; jumpBufferCounter = 0; } } } if(!currentlyJumping && !onGround) {// If we're not on the ground and not currently jumping, that means we've stepped off the edge of a platform - so start the coyote timer. coyoteTimeCounter += Time.deltaTime; } else {// Reset it when we touch the ground, or jump coyoteTimeCounter = 0.0f; } if (CharacterCanMove && !currentlyJumping && !currentlyButtstomping && ground.IsGrounded() && directionY < 0) { currentlyCrouched = true; col.size = heightHalf; col.offset = offsetHalf; } else { if (currentlyCrouched) { List
hits = new List
(); bool test = col.Cast(Vector2.up, standUpFilter, hits, heightHalf.y, true) == 0; foreach (var hit in hits) { Debug.Log(hit.transform.name); } if (test) { currentlyCrouched = false; col.size = heightNorm; col.offset = offsetNorm; } } } juice.CrouchAnimation(currentlyCrouched); } private void OnDrawGizmosSelected() { Gizmos.color = Color.magenta; //Gizmos.DrawWireCube() } private void setPhysics() {// Determine the character's gravity scale, using the stats provided. Multiply it by a gravMultiplier, used later float newGravity = (-2 * jumpHeight) / (timeToJumpApex * timeToJumpApex); // https://www.khanacademy.org/science/physics/one-dimensional-motion/acceleration-tutorial/v/acceleration rb2d.gravityScale = (newGravity / Physics2D.gravity.y) * gravMultiplier; // https://docs.unity3d.com/ScriptReference/Physics2D-gravity.html The default is (0, -9.8) } private void CalculateGravity() {// We change the character's gravity based on Y direction if(rb2d.velocity.y > 0.01f) {// If rb2d is going up... if (onGround) {// ...and grounded (eg. moving platform), don't change gravity. gravMultiplier = defaultGravityScale; } else { if (variableJumpHeight) {// If we're using variable jump height... if(pressingJump && currentlyJumping) {// Apply upward multiplier if player is rising and holding jump gravMultiplier = upwardMovementMultiplier; } else {// But apply a special downward multiplier if the player lets go of jump gravMultiplier = jumpCutOff; } } else {gravMultiplier = upwardMovementMultiplier;} } } else if (rb2d.velocity.y < -0.01f) {// Else if rb2d is going down... if (onGround) {// ...and grounded (eg. moving platform), don't change gravity. gravMultiplier = defaultGravityScale; } else {// Otherwise, apply the downward gravity multiplier as char comes back to earth. gravMultiplier = downwardMovementMultiplier; } } else {// Else not moving at all... if (onGround) { currentlyJumping = false; if (currentlyButtstomping) { juice.ButtstompEffects(); currentlyButtstomping = false; } } gravMultiplier = defaultGravityScale; } // Set the charcter's rb2d velocity, clamping the Y var within the bounds of the speed limit to enforce terminal velocity rb2d.velocity = ClampVelocityY(velocity); // NOTE - this might cause issues if character needs to go up faster than it does jumping! } private void DoAJump() { if(onGround || (coyoteTimeCounter > 0.03f && coyoteTimeCounter < coyoteTime || canJumpAgain)) {// Create the jump, provided we are on the ground, in coyoteTime or have a double jump available desiredJump = false; jumpBufferCounter = 0.0f; coyoteTimeCounter = 0.0f; canJumpAgain = (maxAirJumps == 1 && canJumpAgain == false); // If we have double jump on, allow us to jump again (but only once). A weirdly recursive toggle. float jumpSpeedPreAssignment = jumpSpeed; //Determine the power of the jump, based on our gravity and stats - https://www.quora.com/Is-square-root-of-any-number-always-positive jumpSpeed = Mathf.Sqrt(-2f * Physics2D.gravity.y * rb2d.gravityScale * jumpHeight); // Knowing gravity and knowing the distance/height, this is the required velocity/jumpspeed required to achieve that distance. The minus 2f is due to the fact you cannot Square Root a minus value and Physics2D Gravity (Y) is by default -9.8, so we're flipping it. //If we're moving up or down when we jump (such as when doing a double jump), change the jumpSpeed //This will ensure the jump is the exact same strength, no matter your velocity. if (velocity.y > 0.0f) {// If moving UP: Remove upwards velocity from jumpSpeed (remainder will be added to velocity later). If jumpSpeed / velocity is MINUS, set vel at 0. jumpSpeed = Mathf.Max(jumpSpeed - velocity.y, 0.0f); } else if (velocity.y < 0.0f) {// If moving DOWN: Add the value of downward velocity to counteract jumpSpeed += Mathf.Abs(rb2d.velocity.y); } // Apply the new jumpSpeed to the velocity. It will be sent to the Rigidbody in FixedUpdate velocity.y += jumpSpeed; // We COULD clamp jumpSpeed here, but we might as well ClampVelocityY() here and in CalculateGravity(); velocity = ClampVelocityY(velocity);// As we don't CalculateGravity() if we DoAJump(), we need to clamp velocity here. currentlyJumping = true; if(juice != null) juice.JumpEffects(); // Apply the jumping effects on the juice script } if(jumpBuffer == 0) {// If we don't have a jump buffer, then turn off desiredJump immediately after hitting jumping desiredJump = false; } } private void DoAButtstomp() { juice.Buttstomping(); desiredButtstomp = false; currentlyButtstomping = true; jumpBufferCounter = 0; desiredJump = false; rb2d.velocity = velocity = new Vector2(0.0f, -speedLimit); } private Vector2 ClampVelocityY(Vector2 vel) { return new Vector2(vel.x, Mathf.Clamp(vel.y, -speedLimit, jumpSpeedLimit)); } // Enforce height-increase speed and terminal velocity. } }