using UnityEngine; public class PlayerController : MonoBehaviour { /// /// How hard do we strut? /// public float walkForce = 50f; /// /// Strutting faster than this would just look silly. /// public float maxWalkSpeed = 5f; /// /// Larger numbers put kangaroo's to shame. /// public float jumpForce = 400f; /// /// Nothing like a jump that keeps going. Larger numbers = longer jumps. Must be the extra breakfast beans. /// public float jumpFuelSeconds = 1f; /// /// How hard do we leap if the player held both jump and a direction key /// public float leapForce = 200f; /// /// How long do we wait for a jump to become a leap in a direction /// public float leapWindowSeconds = 0.5f; /// /// Larger numbers = more repulsion from giving the wall a kiss. /// public float wallRebound = 300f; private Rigidbody2D rb; private bool jumping = false; private bool wallHit = false; // Start is called before the first frame update void Start() { rb = GetComponent(); // Set up the things. currentJumpFuel = jumpFuelSeconds; currentLeapWindowSeconds = leapWindowSeconds; } // FixedUpdate is called once per physics frame void FixedUpdate() { // If we are rebounding off a wall, we need to wait until the player has stopped moving. if (isRebounding()) return; // We are still concussed by the feindish wall. Keep waiting until we've come around again. // Its dangerous to go alone, we will need this... var input = Input.GetAxisRaw("Horizontal"); // Player says Jump, we ask how high. if (Input.GetKey("space")) jump(input); // Prevent bending the laws of physics like a 90's Tony Hawk if (jumping) return; // We're already in the air, what more did you want?!?! // We're not rebounding off a wall or jumping for the stars, so best Dr Livesey impression in the specified direction. walk(input); } private void OnCollisionEnter2D(Collision2D collision) { // Ahhhh! its touching me!!!! what is it?! // This assumes you have configured the tags to match switch (collision.transform.tag) { // Remember a polymorph can become anything you must maintain absolute vigal.. ITS THE WALL!!! case "LeftWall": hitWall(1); break; // rebound off the wall case "RightWall": hitWall(-1); break; // If we hit a floor, allow us to jump again case "Floor": resetJump(); break; // If you are seeing the below message, please configure your tags / collidable gameobjects or update this switch. default: Debug.Log("AHHH NATURE! ITS ALL OVER ME GET IT OFF!!!"); break; } } private bool isRebounding() { if (!wallHit) return false; // We're not hitting a wall, what are you doing in here? Go away! if (rb.velocity.sqrMagnitude > 0) return true; // Yep we hit a wall and are still reeling from the concussion. Best have a rest. // We are no longer concussed by rapid unplanned wall encounter, ready to go again! wallHit = false; // Stop thining we hit a wall. return false; } private float currentJumpFuel; private float currentLeapWindowSeconds; private void jump(float horizontalInput) { /* * Warning super nerd stuff about 'requirements' * In here there be "Clients" */ // There are lots of ways leaping can be implemented, this does the following: // Given: The player is jumping AND there is still jump fuel remaining // When: The player keeps holding the jump key // Expect: Jump force is continually added to the character // Given: The player is jumping AND there is no more jump fuel remaining // When: The player keeps holding the jump key // Expect: No more jump force is added // Given: There is no more jump fuel remaining // When: The character touches the ground // Expect: Jump fuel is immediately fully replenished // Given: The player character is not moving. // When: The player presses jump and a directional key. // Expect: The character will jump and leap at the same time in the specified direction. // Given: The player character is not moving. // When: The player presses OR holds jump. // When: The player THEN presses a directional key WITHIN the time allowed to make a jump a leap. // Expect: The player character to jump and leap at the same time in the specified direction. // Given: The player character is already jumping but still has jump fuel left. // When: The player keeps holding jump. // Expect: The jump force to keep being added. // Given: The player character is already jumping and has already lept in a direction. // When: The player continues jumping AND The player presses or holds a directional button. // Expect: The character just continues the jump but does not do a new leap because a leap already happened. // Given: The player is walking at any speed. // When: The player jumps and leaps. // Expect: The leap has a predictable momentum not influenced by an existing horizontal speed. /* * End of super nerd stuff. */ // Ok we need to let the player control how high they jump by how long they're holding the jump key for. // Think of this more like a really rubbish jetpack than a jump :) // Jake: "Whats that?" if (currentJumpFuel <= 0) // Elwood: "We're outta gas..." return; // No more fuel so we can't jump till more fuel is added. // BURN_BABY_BURN--MASTER_IGNITION_ROUTINE.agc // This is a tad complex, the player can both jump and leap in a horizontal direction. // Further complicated by they can only leap if they pressed space before a direction key. // And can only add leaping force once per jump not continually // So we need to build up the force vector out of multiple components and conditions. var xVector = 0f; // Step 1. The player may be trying to leap or still just holding the direction key after leaping // There's a grey period where we may have hit jump but still should accept it becoming a leap. // So we must be within the leap window, not leaping, and have a horizontal input left or right if (currentLeapWindowSeconds > 0 && currentJumpFuel == jumpFuelSeconds && horizontalInput != 0) { Debug.Log("LEAPPPPPPPP"); // Cancel the existing horizontal momentum to make leaping more predictable // Maintain any Y velocity to prevent WonkaJump Jankyness due to pressing jump and then a direction toward the tail end of the leap window // If you want the player to make longer leaps by running up to them first, comment the below line out rb.velocity = new Vector2(0, rb.velocity.y); // Leap in the direction of input xVector = leapForce * Mathf.Sign(horizontalInput); // The robinsons are all tucked in we are ready to fly. } // Step 2. If the player is leaping that gets applied for 1 frame but they can still keep jumping // We already know there is current jump fuel, we are being asked to jump and any horizontal force for leaping has already been done. // Putting the pedal to the metal here goes... rb.AddForce(new Vector2(xVector, 1 * jumpForce)); // Reduce the fuel so we can't jump forever. currentJumpFuel -= Time.deltaTime; // Reduce the remaining window to make this jump into a leap. Doesn't matter if leap window is already < 0, assignment is cheaper than if and assignment currentLeapWindowSeconds -= Time.deltaTime; } private void hitWall(int bounceOffInDirection) { Debug.Log("hitwall"); wallHit = true; rb.AddForce(new Vector2(Mathf.Sign(bounceOffInDirection) * wallRebound, 0)); } /// /// This completely resets the movement side effects. Gets called after a player has lept / jumped and reached the ground again. /// /// I'll take your brain to another dimension... Pay close attetion! No seriously this method is the thing making the rest of it work, pay closer attention. /// SOLID principles they said, it'll be fun they said... Oh no I can't avoid side effects without turning this into the plane of Oblivion and now I've gone crosseyed. private void resetJump() { Debug.Log("reset"); // Force an immediate stop when we touch the ground. // Note: We are setting velocity directly here deliberately, however this could also be achieved by cranking up the linear drag for a slightly smoother stop, you'd just have to faff about resetting the drag after a stop. rb.velocity = Vector2.zero; // Allow movement again wallHit = false; // Allow jumping again currentJumpFuel = jumpFuelSeconds; // Allow leaping again currentLeapWindowSeconds = leapWindowSeconds; } private void walk(float inputDirection) { // Check we're not in the middle of a jump if (currentJumpFuel == jumpFuelSeconds) if (Mathf.Abs(rb.velocity.x) < maxWalkSpeed) // The word ROM and DEATH should mean the same thing to you... rb.AddForce(new Vector2(inputDirection * walkForce, 0)); } } // OLD FIXED UPDATE ROUTINE FROM PREVIOUS EXAMPLE. Friends don't let friends work without source control! //// FixedUpdate is called once per physics frame //void FixedUpdate() //{ // /* // * JUMPING // */ // // Check if we are on the ground by drawing a box around the centre of the player character // var grounded = Physics2D.OverlapBox(transform.position, Vector2.one * groundedCollisionArea, 0, floorMask); // // If we are jumping and have reached the ground and the player isn't holding space anymore, allow jumping again. // // Note this would allow buggy super jumping if the player can hammer space fast enough... // if (jumped && grounded && !Input.GetKey("space")) // { // jumped = false; // } // // If we are on the ground and aren't in the middle of a jump. Do a jump. // if (grounded && !jumped && Input.GetKey("space")) // { // Debug.Log("JUMP"); // rb.AddForce(Vector2.up * jumpForce); // // Prevent jumping until we have hit the ground again // jumped = true; // } // /* // * WALKING // */ // var input = Input.GetAxisRaw("Horizontal"); // // Check if the input is in the opposite direction to the player's current direction of travel. // // This makes changing direction always happen when grounded // if (grounded && Mathf.Sign(input) != Mathf.Sign(rb.velocity.x)) // { // rb.AddForce(new Vector2(walkForce * input, 0)); // } // // Only apply more movement speed when the player is below a max speed // else if (input != 0f && Mathf.Abs(rb.velocity.x) < maxWalkSpeed) // { // rb.AddForce(new Vector2(walkForce * input, 0)); // } //}