https://github.com/Bardent/Rigidbody2D-Slopes-Unity/blob/master/Assets/Scripts/PlayerController.cs
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
public class PlayerController : MonoBehaviour | |
{ | |
[SerializeField] | |
private float movementSpeed; | |
[SerializeField] | |
private float groundCheckRadius; | |
[SerializeField] | |
private float jumpForce; | |
[SerializeField] | |
private float slopeCheckDistance; | |
[SerializeField] | |
private float maxSlopeAngle; | |
[SerializeField] | |
private Transform groundCheck; | |
[SerializeField] | |
private LayerMask whatIsGround; | |
[SerializeField] | |
private PhysicsMaterial2D noFriction; | |
[SerializeField] | |
private PhysicsMaterial2D fullFriction; | |
private float xInput; | |
private float slopeDownAngle; | |
private float slopeSideAngle; | |
private float lastSlopeAngle; | |
private int facingDirection = 1; | |
private bool isGrounded; | |
private bool isOnSlope; | |
private bool isJumping; | |
private bool canWalkOnSlope; | |
private bool canJump; | |
private Vector2 newVelocity; | |
private Vector2 newForce; | |
private Vector2 capsuleColliderSize; | |
private Vector2 slopeNormalPerp; | |
private Rigidbody2D rb; | |
private CapsuleCollider2D cc; | |
private void Start() | |
{ | |
rb = GetComponent<Rigidbody2D>(); | |
cc = GetComponent<CapsuleCollider2D>(); | |
capsuleColliderSize = cc.size; | |
} | |
private void Update() | |
{ | |
CheckInput(); | |
} | |
private void FixedUpdate() | |
{ | |
CheckGround(); | |
SlopeCheck(); | |
ApplyMovement(); | |
} | |
private void CheckInput() | |
{ | |
xInput = Input.GetAxisRaw("Horizontal"); | |
if (xInput == 1 && facingDirection == -1) | |
{ | |
Flip(); | |
} | |
else if (xInput == -1 && facingDirection == 1) | |
{ | |
Flip(); | |
} | |
if (Input.GetButtonDown("Jump")) | |
{ | |
Jump(); | |
} | |
} | |
private void CheckGround() | |
{ | |
isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround); | |
if(rb.velocity.y <= 0.0f) | |
{ | |
isJumping = false; | |
} | |
if(isGrounded && !isJumping && slopeDownAngle <= maxSlopeAngle) | |
{ | |
canJump = true; | |
} | |
} | |
private void SlopeCheck() | |
{ | |
Vector2 checkPos = transform.position - (Vector3)(new Vector2(0.0f, capsuleColliderSize.y / 2)); | |
SlopeCheckHorizontal(checkPos); | |
SlopeCheckVertical(checkPos); | |
} | |
private void SlopeCheckHorizontal(Vector2 checkPos) | |
{ | |
RaycastHit2D slopeHitFront = Physics2D.Raycast(checkPos, transform.right, slopeCheckDistance, whatIsGround); | |
RaycastHit2D slopeHitBack = Physics2D.Raycast(checkPos, -transform.right, slopeCheckDistance, whatIsGround); | |
if (slopeHitFront) | |
{ | |
isOnSlope = true; | |
slopeSideAngle = Vector2.Angle(slopeHitFront.normal, Vector2.up); | |
} | |
else if (slopeHitBack) | |
{ | |
isOnSlope = true; | |
slopeSideAngle = Vector2.Angle(slopeHitBack.normal, Vector2.up); | |
} | |
else | |
{ | |
slopeSideAngle = 0.0f; | |
isOnSlope = false; | |
} | |
} | |
private void SlopeCheckVertical(Vector2 checkPos) | |
{ | |
RaycastHit2D hit = Physics2D.Raycast(checkPos, Vector2.down, slopeCheckDistance, whatIsGround); | |
if (hit) | |
{ | |
slopeNormalPerp = Vector2.Perpendicular(hit.normal).normalized; | |
slopeDownAngle = Vector2.Angle(hit.normal, Vector2.up); | |
if(slopeDownAngle != lastSlopeAngle) | |
{ | |
isOnSlope = true; | |
} | |
lastSlopeAngle = slopeDownAngle; | |
Debug.DrawRay(hit.point, slopeNormalPerp, Color.blue); | |
Debug.DrawRay(hit.point, hit.normal, Color.green); | |
} | |
if (slopeDownAngle > maxSlopeAngle || slopeSideAngle > maxSlopeAngle) | |
{ | |
canWalkOnSlope = false; | |
} | |
else | |
{ | |
canWalkOnSlope = true; | |
} | |
if (isOnSlope && canWalkOnSlope && xInput == 0.0f) | |
{ | |
rb.sharedMaterial = fullFriction; | |
} | |
else | |
{ | |
rb.sharedMaterial = noFriction; | |
} | |
} | |
private void Jump() | |
{ | |
if (canJump) | |
{ | |
canJump = false; | |
isJumping = true; | |
newVelocity.Set(0.0f, 0.0f); | |
rb.velocity = newVelocity; | |
newForce.Set(0.0f, jumpForce); | |
rb.AddForce(newForce, ForceMode2D.Impulse); | |
} | |
} | |
private void ApplyMovement() | |
{ | |
if (isGrounded && !isOnSlope && !isJumping) //if not on slope | |
{ | |
Debug.Log("This one"); | |
newVelocity.Set(movementSpeed * xInput, 0.0f); | |
rb.velocity = newVelocity; | |
} | |
else if (isGrounded && isOnSlope && canWalkOnSlope && !isJumping) //If on slope | |
{ | |
newVelocity.Set(movementSpeed * slopeNormalPerp.x * -xInput, movementSpeed * slopeNormalPerp.y * -xInput); | |
rb.velocity = newVelocity; | |
} | |
else if (!isGrounded) //If in air | |
{ | |
newVelocity.Set(movementSpeed * xInput, rb.velocity.y); | |
rb.velocity = newVelocity; | |
} | |
} | |
private void Flip() | |
{ | |
facingDirection *= -1; | |
transform.Rotate(0.0f, 180.0f, 0.0f); | |
} | |
private void OnDrawGizmos() | |
{ | |
Gizmos.DrawWireSphere(groundCheck.position, groundCheckRadius); | |
} | |
} | |
https://www.youtube.com/watch?v=QPiZSTEuZnw