using Player.Interactions; using UnityEngine; using Player.Information; namespace Player.Movement { public class PlayerPhysics : MonoBehaviour { // measured in meters per second squared [SerializeField] private Vector3 gravity; [SerializeField] private float walkingForce; [SerializeField] private float maxWalkSpeed; [SerializeField] private float maxRunSpeed; // measured in N applied when space is pressed [SerializeField] private float jumpForce; [SerializeField] private float defaultAirControl; // the minimum change in speed which will result in damage being taken [SerializeField] private float speedDamageThreshold; // the ratio of speed change to damage taken [SerializeField] private float speedToDamage; // measured in seconds [SerializeField] private float coyoteTime; [SerializeField] private float jumpBuffer; // current air control ratio experienced by the player private float airControl; private float coyoteTimeCounter; // reference to GrappleHook script private GrappleHook grappleHook; // reference to JetPack script private JetPack jetPack; private float jumpBufferCounter; // current maximum input speed of the player private float maxSpeed; // referendce to PlayerStats script private PlayerStats playerStats; // metrics for ground detection //private float playerHeight; //private Vector3 boxDim; // speed of the player in the previous frame private float prevFrameSpeed; private Rigidbody rb; public void Reset() { prevFrameSpeed = 0f; rb.velocity = Vector3.zero; grappleHook.ReleaseGrapple(); jetPack.ResetThrust(); } // Start is called before the first frame update private void Start() { rb = gameObject.GetComponent(); // ground detection constants //playerHeight = (transform.localScale.y / 2f); //Vector3 boxDim = transform.localScale; //boxDim = new Vector3(boxDim.x - 0.5f, boxDim.y + 0.5f, boxDim.z - 0.5f); maxSpeed = maxWalkSpeed; airControl = defaultAirControl; grappleHook = gameObject.GetComponent(); jetPack = gameObject.GetComponent(); playerStats = gameObject.GetComponent(); prevFrameSpeed = 0f; } // Update is called once per frame private void Update() { var grounded = isGrounded(); // coyote time manager if (grounded) coyoteTimeCounter = coyoteTime; else coyoteTimeCounter -= Time.deltaTime; // jump buffer manager if (Input.GetButtonDown("Jump") ) jumpBufferCounter = jumpBuffer; else jumpBufferCounter -= Time.deltaTime; //Debug.Log("Coyote: " + coyoteTimeCounter + ". Buffer: " + jumpBufferCounter); // if jump key is pressed if (coyoteTimeCounter > 0f && jumpBufferCounter > 0f) { Debug.Log("jump"); // apply an upward force to the player rb.AddForce(new Vector3(0f, jumpForce, 0f)); coyoteTimeCounter = 0f; jumpBufferCounter = 0f; } // change current max speed depending on sprinting or not if (Input.GetKey(KeyCode.LeftShift) && grounded ) maxSpeed = maxRunSpeed; else maxSpeed = maxWalkSpeed; } private void FixedUpdate() { // get the input values for player movement var movement = GetMoveInputs(); // calculate the velocity change of the player based on input movement = movement.normalized * walkingForce; // scale movement force if not on ground if (!isGrounded()) movement *= airControl; var relativeMove = transform.right * movement.x + transform.forward * movement.z; var flatCurVelocity = Vector3.ProjectOnPlane(rb.velocity, transform.up); // calculate the velocity the player would have when adding input velocity to it var nextVelocity = flatCurVelocity + relativeMove * Time.deltaTime; // check if the player's next velocity has a magnitude above walking speed if (nextVelocity.magnitude > maxSpeed) { // if the player is currently moving below their maxSpeed and trying to accelerate up to it if (flatCurVelocity.magnitude < maxSpeed) // scale the player's next velocity to be equal to their maxSpeed nextVelocity = nextVelocity.normalized * maxSpeed; else // else, scale the player's next velocity to equal their current speed nextVelocity = nextVelocity.normalized * flatCurVelocity.magnitude; } // set the player's current velocity to their next velocity in the xz-plane rb.velocity = new Vector3(nextVelocity.x, rb.velocity.y, nextVelocity.z); // apply the force of a potential grapple hook to the player rb.AddForce(grappleHook.PullForce(transform.position)); // apply the force of a potential jetpack to the player rb.AddForce(jetPack.ThrustForce()); // apply the force of gravity to the player rb.AddForce(gravity, ForceMode.Acceleration); // calculate the change in player speed this frame var deltaSpeed = Mathf.Abs(rb.velocity.magnitude - prevFrameSpeed); // apply force damage to player depending on change in speed if (deltaSpeed > speedDamageThreshold) //Debug.Log(deltaSpeed - speedDamageThreshold); playerStats.ChangeHealth(-speedToDamage * (deltaSpeed - speedDamageThreshold)); // update speed record for next frame prevFrameSpeed = rb.velocity.magnitude; //Debug.Log(isGrounded()); } private void OnTriggerEnter(Collider other) { var tp = other.gameObject.GetComponent(); if (tp != null && tp.lethalToEnter) playerStats.Respawn(); } public Vector3 GetMoveInputs() { var xzMove = Vector3.zero; if (Input.GetKey(KeyCode.W)) xzMove.z += 1f; if (Input.GetKey(KeyCode.S)) xzMove.z += -1f; if (Input.GetKey(KeyCode.D)) xzMove.x += 1f; if (Input.GetKey(KeyCode.A)) xzMove.x += -1f; return xzMove; } public bool isGrounded() { // Raycast using ray //return Physics.Raycast(transform.position, -Vector3.up, playerHeight + 0.1f); // Raycast using box //Vector3 boxCenter = transform.position - new Vector3(0f, playerHeight, 0f); //return Physics.CheckBox(boxCenter, boxDim); // Raycast using box hardcoded var boxCenter = transform.position - new Vector3(0f, 0.5f, 0f); return Physics.CheckBox(boxCenter, new Vector3(0.25f, 0.52f, 0.25f)); } } }