private bool AttemptReground() { int maxSize = 5; bool prevQueriesHitTriggers = Physics2D.queriesHitTriggers; Physics2D.queriesHitTriggers = false; RaycastHit2D[] hits = new RaycastHit2D[maxSize]; int numHits; if ((numHits = capCol.Cast(Vector2.down, hits, stepMax)) > 0) { Physics2D.queriesHitTriggers = prevQueriesHitTriggers; if (numHits > maxSize) { numHits = maxSize; } float distance = stepMax; foreach (RaycastHit2D hit in hits) { if (hit.distance < distance && hit.distance != 0) { distance = hit.distance; } } Vector2 slopeNormal = Vector2.zero; foreach (RaycastHit2D hit in hits) { if (hit.distance > distance) { break; } if (CheckGroundableSlope(hit.normal)) { slopeNormal += hit.normal; } } slopeNormal.Normalize(); if (slopeNormal != Vector2.zero) { HandleReground(mover.Move(distance * Vector2.down), slopeFromNormal(slopeNormal)); return(true); } else { return(false); } } else { return(false); } }
// Update is called once per frame void Update() { if (!isAlive) { return; } if (playerFeetCollider != null && playerLowerBodyCollider) { // Suprisingly this is the simplest way I could find to tell if we are on the ground // We cast the foot collider down and then catch up to 10 things it hits // If one of them have a tag of ground, then we know we are on or at least very close to the ground castFeetCollisions = new RaycastHit2D[ARRAY_LENGTH]; playerFeetCollider.Cast(Vector2.down, castFeetCollisions, .05f); castLowerBodyCollisions = new RaycastHit2D[ARRAY_LENGTH]; playerLowerBodyCollider.Cast(Vector2.down, castLowerBodyCollisions, .05f); for (int i = 0; i < ARRAY_LENGTH; i++) { if ((castFeetCollisions[i].collider != null && castFeetCollisions[i].collider.tag == "Ground") || (castLowerBodyCollisions[i].collider != null && castLowerBodyCollisions[i].collider.tag == "Ground")) { isOnTheGround = true; break; } else { isOnTheGround = false; } } } // Handle Horizontal Movement float xMovement = Input.GetAxisRaw("Horizontal"); if (xMovement > .2f) { if (currentAction != Actions.Walking) { playerAnimator.SetBool("IsWalking", true); currentAction = Actions.Walking; } if (rb2d.velocity.x < MAX_VELOCITY) { rb2d.AddForce(Vector2.right * xMovement * Time.deltaTime * MOVEMENT_SPEED); } if (playerRenderer.flipX) { playerRenderer.flipX = false; } } else if (xMovement < -.2f) { if (currentAction != Actions.Walking) { playerAnimator.SetBool("IsWalking", true); currentAction = Actions.Walking; } if (rb2d.velocity.x > -MAX_VELOCITY) { rb2d.AddForce(Vector2.right * xMovement * Time.deltaTime * MOVEMENT_SPEED); } if (!playerRenderer.flipX) { playerRenderer.flipX = true; } } else { if (currentAction != Actions.Standing) { playerAnimator.SetBool("IsWalking", false); currentAction = Actions.Standing; } if (isOnTheGround) // Stop the player from sliding on the ground { rb2d.velocity = Vector2.up * rb2d.velocity.y; // We still should hold onto vertical velocity } } // Handle Vertical Movement float jumpMovement = Input.GetAxis("Jump"); timeSinceLastJump += Time.deltaTime; if (jumpMovement > .5 && isOnTheGround && timeSinceLastJump > JUMP_COOLDOWN) { // Erase any negative vertical velocity, but keep the horizontal velocity if (rb2d.velocity.y < 0) { rb2d.velocity = Vector2.right * rb2d.velocity.x; } rb2d.AddForce(Vector2.up * JUMP_FORCE); isOnTheGround = false; timeSinceLastJump = 0; } }
protected void UpdateGroundDetection(float deltaTime) { // Ground Check WasGrounded = IsGrounded; FSMController.Fsm.GetFsmBool("isGroundedFSM").Value = IsGrounded = false; Vector2 dir = Vector2.down; RaycastHit2D[] hits = new RaycastHit2D[4]; capsuleCollider.Cast(dir, hits, _skinWidth); for (int i = 0; i < hits.Length; i++) { if (hits[i].collider == null) { break; } var go = hits[i].collider.attachedRigidbody ? hits[i].collider.attachedRigidbody.gameObject : hits[i].collider.gameObject; if (go != this.gameObject) { var point = hits[i].point; var norm = hits[i].normal; var dot = Vector2.Dot(Vector2.up, norm); var angle = Mathf.Rad2Deg * Mathf.Acos(dot); CurrentSlopeAngle = angle; if (angle < _slopeLimit) { Debug.DrawLine(point, point + norm, Color.cyan, 0.5f); FSMController.Fsm.GetFsmBool("isGroundedFSM").Value = IsGrounded = true; CurrentGroundNormal = norm; break; } else { Debug.DrawLine(point, point + norm, Color.magenta, 0.5f); } } } if (WasGrounded != IsGrounded) { if (IsGrounded) { OnBecomeGrounded(); } else { OnBecomeUngrounded(); } } // Ceiling Check WasOnCeiling = IsOnCeiling; IsOnCeiling = false; dir = Vector2.up; hits = new RaycastHit2D[4]; capsuleCollider.Cast(dir, hits, _skinWidth); for (int i = 0; i < hits.Length; i++) { if (hits[i].collider == null) { break; } var go = hits[i].collider.attachedRigidbody ? hits[i].collider.attachedRigidbody.gameObject : hits[i].collider.gameObject; if (go != this.gameObject) { var dot = Vector2.Dot(Vector2.down, hits[i].normal); // Angle against ceiling downward var angle = Mathf.Rad2Deg * Mathf.Acos(dot); if (angle < _ceilingSlopeLimit) { IsOnCeiling = true; break; } } } }