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 = 75f;
///
/// Nothing like a jump that keeps going. Larger numbers = longer jumps. Must be the extra breakfast beans.
///
public float jumpFuelSeconds = 0.3f;
///
/// 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
///
/// Must be greater than jumpFuelSeconds
public float leapWindowSeconds = 0.2f;
///
/// 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;
// Sanity warning
if (leapWindowSeconds >= jumpFuelSeconds)
Debug.LogError("Leap window is larger than jump fuel, leaping will not be possible");
}
// 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 have a horizontal input, and be within the leap window as dictated by remaining fuel seconds
if (horizontalInput != 0 && currentJumpFuel > jumpFuelSeconds - leapWindowSeconds)
{
// 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));
// }
//}