Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve player movement #356

Merged
merged 6 commits into from
Dec 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions Assets/Prefabs/Input/Player.prefab
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,19 @@ MonoBehaviour:
serializedVersion: 2
m_Bits: 65335
lookSpeed: 3
maxVelocity: 10
groundDrag: 6
airDrag: 1
maxVelocityBeforeExtraDamping: 10
extraDamping: 25
strafeForceGrounded: 60
strafeForceCrouched: 30
strafeForceInAir: 16
drag: 6
airDrag: 2
strafeForceInAir: 30
jumpForce: 10
leapForce: 12.5
jumpTimeout: 0.5
leapTimeout: 0.875
leapTimeout: 0.25
dashHeightMultiplier: 0.5
dashForwardMultiplier: 1
dashDamping: 0.5
crouchedHeightOffset: 0.8
airThreshold: 0.4
slopeAngleThreshold: 50
Expand Down Expand Up @@ -145,8 +148,8 @@ Rigidbody:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 71015680234268067}
serializedVersion: 4
m_Mass: 10
m_Drag: 4
m_Mass: 5
m_Drag: 0
m_AngularDrag: 0.05
m_CenterOfMass: {x: 0, y: 0, z: 0}
m_InertiaTensor: {x: 1, y: 1, z: 1}
Expand Down
131 changes: 87 additions & 44 deletions Assets/Scripts/Control&Input/PlayerMovement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,22 @@ public class PlayerMovement : MonoBehaviour
[SerializeField]
private float lookSpeed = 3;

[Header("Drag")]
[SerializeField]
private float maxVelocity = 10;
private float groundDrag = 6f;

private float strafeForce;
[SerializeField]
private float airDrag = 2f;

[SerializeField]
private float maxVelocityBeforeExtraDamping = 20f;

[SerializeField]
private float extraDamping = 25f;

private float dragForce = 0;

[Header("Strafe")]
[SerializeField]
private float strafeForceGrounded = 60;

Expand All @@ -37,33 +48,36 @@ public class PlayerMovement : MonoBehaviour
[SerializeField]
private float strafeForceInAir = 16;

private float strafeForce;

[Header("Jumping")]
[SerializeField]
private float drag = 6f;
private float jumpForce = 10;

[SerializeField]
private float airDrag = 2f;
private float leapForce = 12.5f;

[SerializeField]
private float jumpForce = 10;
private float leapTimeout = 0.25f;

[SerializeField]
private float leapForce = 12.5f;
private float dashHeightMultiplier = 0.5f;

[SerializeField]
private float jumpTimeout = 0.5f;
private float dashForwardMultiplier = 1.5f;

[SerializeField]
private float leapTimeout = 0.875f;
private float dashDamping = 4f;

private const float MarsGravity = 3.72076f;
[SerializeField]
private float minDashVelocity = 8f;

private bool canJump = true;
private bool isDashing = false;

[Header("State")]
[SerializeField]
private float crouchedHeightOffset = 0.3f;

private float localCameraHeight;

[SerializeField]
private float airThreshold = 0.4f;

Expand All @@ -76,8 +90,15 @@ public class PlayerMovement : MonoBehaviour
[SerializeField]
private Animator animator;

private const float MarsGravity = 3.7f;

private float localCameraHeight;

private Vector2 aimAngle = Vector2.zero;

private delegate void MovementEvent();
private MovementEvent onLanding;

void Start()
{
body = GetComponent<Rigidbody>();
Expand All @@ -93,67 +114,78 @@ public void SetPlayerInput(InputManager player)
{
inputManager = player;
inputManager.onSelect += OnJump;
inputManager.onCrouchPerformed += SetCrouch;
inputManager.onCrouchCanceled += SetCrouch;
inputManager.onCrouchPerformed += OnCrouch;
inputManager.onCrouchCanceled += OnCrouch;
localCameraHeight = inputManager.transform.localPosition.y;
}

private void OnJump(InputAction.CallbackContext ctx)
{
if (!(canJump && state == PlayerState.GROUNDED))
if (!(state == PlayerState.GROUNDED))
return;

// Leap jump
// Leap/dash jump
if (animator.GetBool("Crouching"))
{
body.AddForce(Vector3.up * leapForce, ForceMode.VelocityChange);
body.AddForce(Vector3.up * leapForce * (isDashing ? dashHeightMultiplier : 1f), ForceMode.VelocityChange);
Vector3 forwardDirection = new Vector3(inputManager.transform.forward.x, 0, inputManager.transform.forward.z);
body.AddForce(forwardDirection * leapForce, ForceMode.VelocityChange);
body.AddForce(forwardDirection * leapForce * (isDashing ? dashForwardMultiplier : 1f), ForceMode.VelocityChange);
animator.SetTrigger("Leap");
StartCoroutine(JumpTimeout(leapTimeout));
onLanding += EnableDash;
return;
}

// Normal jump
body.AddForce(Vector3.up * jumpForce, ForceMode.VelocityChange);
StartCoroutine(JumpTimeout(jumpTimeout));
}

private void SetCrouch(InputAction.CallbackContext ctx)
private void OnCrouch(InputAction.CallbackContext ctx)
{
if (LeanTween.isTweening(inputManager.gameObject))
{
LeanTween.cancel(inputManager.gameObject);
inputManager.transform.localPosition = new Vector3(inputManager.transform.localPosition.x, localCameraHeight, inputManager.transform.localPosition.z);
}


if (ctx.performed)
{
if (IsInAir())
{
onLanding += StartCrouch;
return;
animator.SetBool("Crouching", true);
strafeForce = strafeForceCrouched;
inputManager.gameObject.LeanMoveLocalY(localCameraHeight - crouchedHeightOffset, 0.2f);
}
StartCrouch();
}

if (ctx.canceled)
{
animator.SetBool("Crouching", false);
strafeForce = strafeForceGrounded;
inputManager.gameObject.LeanMoveLocalY(localCameraHeight, 0.2f);
isDashing = false;
onLanding -= StartCrouch;
}

}

private void StartCrouch()
{
animator.SetBool("Crouching", true);
strafeForce = strafeForceCrouched;
inputManager.gameObject.LeanMoveLocalY(localCameraHeight - crouchedHeightOffset, 0.2f);
}

private void EnableDash()
{
StartCoroutine(JumpTimeout(leapTimeout));
}

private IEnumerator JumpTimeout(float time)
{
canJump = false;
yield return new WaitForSeconds(time);
canJump = true;
isDashing = IsInAir();
onLanding -= EnableDash;
}


private bool IsInAir()
{
// Cast a box to detect (partial) ground. See OnDrawGizmos for what I think is the extent of the box cast.
Expand All @@ -175,8 +207,6 @@ private Vector3 GroundNormal()
return Vector3.up;
}



private void UpdatePosition(Vector3 input)
{
// Modify input to addforce with relation to current rotation.
Expand All @@ -185,22 +215,23 @@ private void UpdatePosition(Vector3 input)
{
case PlayerState.IN_AIR:
{
// Strafe slightly with less drag.
body.drag = airDrag;
body.AddForce(input * strafeForceInAir, ForceMode.VelocityChange);
body.AddForce(Vector3.down * MarsGravity, ForceMode.Acceleration);
if (!IsInAir()) state = PlayerState.GROUNDED;
dragForce = airDrag;
body.AddForce(input * strafeForceInAir * Time.deltaTime, ForceMode.VelocityChange);
body.AddForce(Vector3.down * MarsGravity * Time.deltaTime, ForceMode.Acceleration);
if (IsInAir())
break;
state = PlayerState.GROUNDED;
onLanding?.Invoke();
break;
}
case PlayerState.GROUNDED:
{
// Strafe normally with heavy drag.
body.drag = drag;
dragForce = groundDrag;
// Walk along ground normal (adjusted if on heavy slope).
var groundNormal = GroundNormal();
var direction = Vector3.ProjectOnPlane(input, groundNormal);
body.AddForce(direction * strafeForce, ForceMode.VelocityChange);
body.AddForce(direction * strafeForce, ForceMode.Impulse);
body.AddForce(direction * strafeForce * Time.deltaTime, ForceMode.VelocityChange);
body.AddForce(direction * strafeForce * Time.deltaTime, ForceMode.Impulse);
if (IsInAir()) state = PlayerState.IN_AIR;
break;
}
Expand All @@ -210,6 +241,8 @@ private void UpdatePosition(Vector3 input)
break;
}
}
var yDrag = body.velocity.y < 0 ? 0f : body.velocity.y;
body.AddForce(-dragForce * body.mass * new Vector3(body.velocity.x, yDrag, body.velocity.z), ForceMode.Force);
Fueredoriku marked this conversation as resolved.
Show resolved Hide resolved
}

private void UpdateRotation()
Expand All @@ -226,8 +259,8 @@ private void UpdateRotation()

private void UpdateAnimatorParameters()
{
animator.SetFloat("Forward", Vector3.Dot(body.velocity, transform.forward) / maxVelocity);
animator.SetFloat("Right", Vector3.Dot(body.velocity, transform.right) / maxVelocity);
animator.SetFloat("Forward", Vector3.Dot(body.velocity, transform.forward) / maxVelocityBeforeExtraDamping);
animator.SetFloat("Right", Vector3.Dot(body.velocity, transform.right) / maxVelocityBeforeExtraDamping);
}

void OnDrawGizmos()
Expand All @@ -241,11 +274,21 @@ void OnDrawGizmos()
void FixedUpdate()
{
var positionInput = new Vector3(inputManager.moveInput.x, 0, inputManager.moveInput.y);
UpdatePosition(positionInput * Time.deltaTime);
UpdatePosition(positionInput);
// Limit velocity when not grounded
if (state == PlayerState.GROUNDED)
return;
body.velocity = new Vector3(Mathf.Clamp(body.velocity.x, -maxVelocity, maxVelocity), body.velocity.y, Mathf.Clamp(body.velocity.z, -maxVelocity, maxVelocity));

if (isDashing)
{
var directionalForces = new Vector3(body.velocity.x, 0, body.velocity.z);
if (directionalForces.magnitude < minDashVelocity)
isDashing = false;
}
// Add extra drag when player velocity is too high
var maxVelocityReached = Mathf.Abs(body.velocity.x) > maxVelocityBeforeExtraDamping || Mathf.Abs(body.velocity.z) > maxVelocityBeforeExtraDamping;
if (maxVelocityReached)
body.AddForce(-(isDashing ? dashDamping : extraDamping) * body.mass * new Vector3(body.velocity.x, 0, body.velocity.z), ForceMode.Force);
}

void Update()
Expand Down