- Home /
How do you make a one-way slope?
I am trying to help a friend who is making an action game. My friend is making an action game based on Sebastian Lague's Platformer source. He's lost his way trying to figure out how to make a one-way slope like in Super Mario World, and this has stalled his development. I'll send you the following source code and a file that depicts what I want to do, and I hope you'll let me know.
Player.cs :
using UnityEngine;
using System.Collections;
[RequireComponent (typeof (Controller2D))]
public class Player : MonoBehaviour {
[Header("Basic Settings")]
public float maxJumpHeight = 4;
public float minJumpHeight = 1;
public float timeToJumpApex = .4f;
float accelerationTimeAirborne = .2f;
float accelerationTimeGrounded = .1f;
float moveSpeed = 6;
[Header("Movement Settings")]
public bool fastAirStraff;
public float maxRunX;
public float maxWalkX;
public float walkAcc;
public float runAcc;
public float skidPower;
public float releaseDeAcc;
public float airStrafeBorder;
public float airStrafeFast;
[Header("Wall Jump Settings")]
public Vector2 wallJumpClimb;
public Vector2 wallJumpOff;
public Vector2 wallLeap;
public float wallSlideSpeedMax = 3;
public float wallStickTime = .25f;
float timeToWallUnstick;
[Header("Gravity Settings")]
float gravity;
float maxJumpVelocity;
float minJumpVelocity;
Vector3 velocity;
float velocityXSmoothing;
[Header("Player status")]
public bool isGrounded;
Controller2D controller;
Vector2 directionalInput;
bool wallSliding;
int wallDirX;
void Start() {
controller = GetComponent<Controller2D> ();
gravity = -(2 * maxJumpHeight) / Mathf.Pow (timeToJumpApex, 2);
maxJumpVelocity = Mathf.Abs(gravity) * timeToJumpApex;
minJumpVelocity = Mathf.Sqrt (2 * Mathf.Abs (gravity) * minJumpHeight);
}
public bool Right, Left, Sprint;
void Update() {
Right = Input.GetAxisRaw("Horizontal") > 0;
Left = Input.GetAxisRaw("Horizontal") < 0;
Sprint = Input.GetButton("Sprint");
if (controller.collisions.below)
{
isGrounded = true;
}
else
{
isGrounded = false;
}
#region Basic Settings
CalculateVelocity ();
HandleWallSliding ();
controller.Move (velocity * Time.deltaTime, directionalInput);
if (controller.collisions.above || controller.collisions.below) {
if (controller.collisions.slidingDownMaxSlope) {
velocity.y += controller.collisions.slopeNormal.y * -gravity * Time.deltaTime;
} else {
velocity.y = 0;
}
}
#endregion
}
public void SetDirectionalInput (Vector2 input) {
directionalInput = input;
}
void FixedUpdate()
{
CustomMovement();
if(controller.collisions.left || controller.collisions.right)
{
velocity.x = 0;
if(Left || Right)
{
CustomMovement();
}
}
}
void CustomMovement()
{
bool moving = false;
bool skidding = false;
if (Right)
{
if (!isGrounded)
{
if (velocity.x >= 0)
{
if (velocity.x >= airStrafeBorder)
{
velocity.x += runAcc;
}
else
{
velocity.x += walkAcc;
}
}
else if (velocity.x < 0)
{
if (-velocity.x >= airStrafeBorder)
{
velocity.x += runAcc;
}
else
{
if (fastAirStraff)
{
velocity.x += releaseDeAcc;
}
velocity.x += walkAcc;
}
}
}
else
{
moving = true;
if(velocity.x >= 0)
{
if (Sprint)
{
velocity.x += runAcc;
}
else velocity.x += walkAcc;
}else if(velocity.x < 0)
{
velocity.x += skidPower;
skidding = true;
}
}
}
if (Left)
{
if (!isGrounded)
{
if(velocity.x <= 0)
{
if(-velocity.x >= airStrafeBorder)
{
velocity.x -= runAcc;
}
else
{
velocity.x -= walkAcc;
}
}else if(velocity.x > 0)
{
if(velocity.x >= airStrafeBorder)
{
velocity.x -= runAcc;
}
else
{
if (fastAirStraff)
{
velocity.x -= releaseDeAcc;
}
velocity.x -= walkAcc;
}
}
}
else
{
moving = true;
if(velocity.x <= 0)
{
if (Sprint)
{
velocity.x -= runAcc;
}
else velocity.x -= walkAcc;
}else if(velocity.x > 0)
{
velocity.x -= skidPower;
skidding = true;
}
}
}
if(!moving && isGrounded)
{
if(velocity.x > 0)
{
velocity.x -= releaseDeAcc;
if (velocity.x < 0) velocity.x = 0;
}
else
{
velocity.x += releaseDeAcc;
if (velocity.x > 0) velocity.x = 0;
}
}
float maxSpeed = Sprint ? maxRunX : maxWalkX;
if(velocity.x > maxSpeed)
{
velocity.x = maxSpeed;
}else if(velocity.x < -maxSpeed)
{
velocity.x = -maxSpeed;
}
}
public void OnJumpInputDown() {
if (wallSliding) {
if (wallDirX == directionalInput.x) {
velocity.x = -wallDirX * wallJumpClimb.x;
velocity.y = wallJumpClimb.y;
}
else if (directionalInput.x == 0) {
velocity.x = -wallDirX * wallJumpOff.x;
velocity.y = wallJumpOff.y;
}
else {
velocity.x = -wallDirX * wallLeap.x;
velocity.y = wallLeap.y;
}
}
if (controller.collisions.below) {
if (controller.collisions.slidingDownMaxSlope) {
if (directionalInput.x != -Mathf.Sign (controller.collisions.slopeNormal.x)) { // not jumping against max slope
velocity.y = maxJumpVelocity * controller.collisions.slopeNormal.y;
velocity.x = maxJumpVelocity * controller.collisions.slopeNormal.x;
}
} else {
velocity.y = maxJumpVelocity;
}
}
}
public void OnJumpInputUp() {
if (velocity.y > minJumpVelocity) {
velocity.y = minJumpVelocity;
}
}
void HandleWallSliding() {
wallDirX = (controller.collisions.left) ? -1 : 1;
wallSliding = false;
if ((controller.collisions.left || controller.collisions.right) && !controller.collisions.below && velocity.y < 0) {
wallSliding = true;
if (velocity.y < -wallSlideSpeedMax) {
velocity.y = -wallSlideSpeedMax;
}
if (timeToWallUnstick > 0) {
velocityXSmoothing = 0;
velocity.x = 0;
if (directionalInput.x != wallDirX && directionalInput.x != 0) {
timeToWallUnstick -= Time.deltaTime;
}
else {
timeToWallUnstick = wallStickTime;
}
}
else {
timeToWallUnstick = wallStickTime;
}
}
}
void CalculateVelocity() {
velocity.x = Mathf.SmoothDamp (velocity.x, velocity.x, ref velocityXSmoothing, (controller.collisions.below)?accelerationTimeGrounded:accelerationTimeAirborne);
velocity.y += gravity * Time.deltaTime;
}
}
Controller2D.cs :
using UnityEngine;
using System.Collections;
public class Controller2D : RaycastController {
public float maxSlopeAngle = 80;
public CollisionInfo collisions;
[HideInInspector]
public Vector2 playerInput;
public override void Start() {
base.Start ();
collisions.faceDir = 1;
}
public void Move(Vector2 moveAmount, bool standingOnPlatform) {
Move (moveAmount, Vector2.zero, standingOnPlatform);
}
public void Move(Vector2 moveAmount, Vector2 input, bool standingOnPlatform = false) {
UpdateRaycastOrigins ();
collisions.Reset ();
collisions.moveAmountOld = moveAmount;
playerInput = input;
if (moveAmount.y < 0) {
DescendSlope(ref moveAmount);
}
if (moveAmount.x != 0) {
collisions.faceDir = (int)Mathf.Sign(moveAmount.x);
}
HorizontalCollisions (ref moveAmount);
if (moveAmount.y != 0) {
VerticalCollisions (ref moveAmount);
}
transform.Translate (moveAmount);
if (standingOnPlatform) {
collisions.below = true;
}
}
void HorizontalCollisions(ref Vector2 moveAmount) {
float directionX = collisions.faceDir;
float rayLength = Mathf.Abs (moveAmount.x) + skinWidth;
if (Mathf.Abs(moveAmount.x) < skinWidth) {
rayLength = 2*skinWidth;
}
for (int i = 0; i < horizontalRayCount; i ++) {
Vector2 rayOrigin = (directionX == -1)?raycastOrigins.bottomLeft:raycastOrigins.bottomRight;
rayOrigin += Vector2.up * (horizontalRaySpacing * i);
RaycastHit2D hit = Physics2D.Raycast(rayOrigin, Vector2.right * directionX, rayLength, collisionMask);
Debug.DrawRay(rayOrigin, Vector2.right * directionX,Color.red);
if (hit) {
if (hit.distance == 0) {
continue;
}
float slopeAngle = Vector2.Angle(hit.normal, Vector2.up);
if (i == 0 && slopeAngle <= maxSlopeAngle) {
if (collisions.descendingSlope) {
collisions.descendingSlope = false;
moveAmount = collisions.moveAmountOld;
}
float distanceToSlopeStart = 0;
if (slopeAngle != collisions.slopeAngleOld) {
distanceToSlopeStart = hit.distance-skinWidth;
moveAmount.x -= distanceToSlopeStart * directionX;
}
ClimbSlope(ref moveAmount, slopeAngle, hit.normal);
moveAmount.x += distanceToSlopeStart * directionX;
}
if (!collisions.climbingSlope || slopeAngle > maxSlopeAngle) {
moveAmount.x = (hit.distance - skinWidth) * directionX;
rayLength = hit.distance;
if (collisions.climbingSlope) {
moveAmount.y = Mathf.Tan(collisions.slopeAngle * Mathf.Deg2Rad) * Mathf.Abs(moveAmount.x);
}
collisions.left = directionX == -1;
collisions.right = directionX == 1;
}
}
}
}
void VerticalCollisions(ref Vector2 moveAmount) {
float directionY = Mathf.Sign (moveAmount.y);
float rayLength = Mathf.Abs (moveAmount.y) + skinWidth;
for (int i = 0; i < verticalRayCount; i ++) {
Vector2 rayOrigin = (directionY == -1)?raycastOrigins.bottomLeft:raycastOrigins.topLeft;
rayOrigin += Vector2.right * (verticalRaySpacing * i + moveAmount.x);
RaycastHit2D hit = Physics2D.Raycast(rayOrigin, Vector2.up * directionY, rayLength, collisionMask);
Debug.DrawRay(rayOrigin, Vector2.up * directionY,Color.red);
if (hit) {
if (hit.collider.tag == "Through") {
if (directionY == 1 || hit.distance == 0) {
continue;
}
if (collisions.fallingThroughPlatform) {
continue;
}
if (playerInput.y == -1) {
collisions.fallingThroughPlatform = true;
Invoke("ResetFallingThroughPlatform",.5f);
continue;
}
}
moveAmount.y = (hit.distance - skinWidth) * directionY;
rayLength = hit.distance;
if (collisions.climbingSlope) {
moveAmount.x = moveAmount.y / Mathf.Tan(collisions.slopeAngle * Mathf.Deg2Rad) * Mathf.Sign(moveAmount.x);
}
collisions.below = directionY == -1;
collisions.above = directionY == 1;
}
}
if (collisions.climbingSlope) {
float directionX = Mathf.Sign(moveAmount.x);
rayLength = Mathf.Abs(moveAmount.x) + skinWidth;
Vector2 rayOrigin = ((directionX == -1)?raycastOrigins.bottomLeft:raycastOrigins.bottomRight) + Vector2.up * moveAmount.y;
RaycastHit2D hit = Physics2D.Raycast(rayOrigin,Vector2.right * directionX,rayLength,collisionMask);
if (hit) {
float slopeAngle = Vector2.Angle(hit.normal,Vector2.up);
if (slopeAngle != collisions.slopeAngle) {
moveAmount.x = (hit.distance - skinWidth) * directionX;
collisions.slopeAngle = slopeAngle;
collisions.slopeNormal = hit.normal;
}
}
}
}
void ClimbSlope(ref Vector2 moveAmount, float slopeAngle, Vector2 slopeNormal) {
float moveDistance = Mathf.Abs (moveAmount.x);
float climbmoveAmountY = Mathf.Sin (slopeAngle * Mathf.Deg2Rad) * moveDistance;
if (moveAmount.y <= climbmoveAmountY) {
moveAmount.y = climbmoveAmountY;
moveAmount.x = Mathf.Cos (slopeAngle * Mathf.Deg2Rad) * moveDistance * Mathf.Sign (moveAmount.x);
collisions.below = true;
collisions.climbingSlope = true;
collisions.slopeAngle = slopeAngle;
collisions.slopeNormal = slopeNormal;
}
}
void DescendSlope(ref Vector2 moveAmount) {
RaycastHit2D maxSlopeHitLeft = Physics2D.Raycast (raycastOrigins.bottomLeft, Vector2.down, Mathf.Abs (moveAmount.y) + skinWidth, collisionMask);
RaycastHit2D maxSlopeHitRight = Physics2D.Raycast (raycastOrigins.bottomRight, Vector2.down, Mathf.Abs (moveAmount.y) + skinWidth, collisionMask);
if (maxSlopeHitLeft ^ maxSlopeHitRight) {
SlideDownMaxSlope (maxSlopeHitLeft, ref moveAmount);
SlideDownMaxSlope (maxSlopeHitRight, ref moveAmount);
}
if (!collisions.slidingDownMaxSlope) {
float directionX = Mathf.Sign (moveAmount.x);
Vector2 rayOrigin = (directionX == -1) ? raycastOrigins.bottomRight : raycastOrigins.bottomLeft;
RaycastHit2D hit = Physics2D.Raycast (rayOrigin, -Vector2.up, Mathf.Infinity, collisionMask);
if (hit) {
float slopeAngle = Vector2.Angle (hit.normal, Vector2.up);
if (slopeAngle != 0 && slopeAngle <= maxSlopeAngle) {
if (Mathf.Sign (hit.normal.x) == directionX) {
if (hit.distance - skinWidth <= Mathf.Tan (slopeAngle * Mathf.Deg2Rad) * Mathf.Abs (moveAmount.x)) {
float moveDistance = Mathf.Abs (moveAmount.x);
float descendmoveAmountY = Mathf.Sin (slopeAngle * Mathf.Deg2Rad) * moveDistance;
moveAmount.x = Mathf.Cos (slopeAngle * Mathf.Deg2Rad) * moveDistance * Mathf.Sign (moveAmount.x);
moveAmount.y -= descendmoveAmountY;
collisions.slopeAngle = slopeAngle;
collisions.descendingSlope = true;
collisions.below = true;
collisions.slopeNormal = hit.normal;
}
}
}
}
}
}
void SlideDownMaxSlope(RaycastHit2D hit, ref Vector2 moveAmount) {
if (hit) {
float slopeAngle = Vector2.Angle(hit.normal, Vector2.up);
if (slopeAngle > maxSlopeAngle) {
moveAmount.x = Mathf.Sign(hit.normal.x) * (Mathf.Abs (moveAmount.y) - hit.distance) / Mathf.Tan (slopeAngle * Mathf.Deg2Rad);
collisions.slopeAngle = slopeAngle;
collisions.slidingDownMaxSlope = true;
collisions.slopeNormal = hit.normal;
}
}
}
void ResetFallingThroughPlatform() {
collisions.fallingThroughPlatform = false;
}
public struct CollisionInfo {
public bool above, below;
public bool left, right;
public bool climbingSlope;
public bool descendingSlope;
public bool slidingDownMaxSlope;
public float slopeAngle, slopeAngleOld;
public Vector2 slopeNormal;
public Vector2 moveAmountOld;
public int faceDir;
public bool fallingThroughPlatform;
public void Reset() {
above = below = false;
left = right = false;
climbingSlope = false;
descendingSlope = false;
slidingDownMaxSlope = false;
slopeNormal = Vector2.zero;
slopeAngleOld = slopeAngle;
slopeAngle = 0;
}
}
}
PS: The first part of the two programs I just mentioned is difficult to read. I apologize for that.
Your answer
Follow this Question
Related Questions
How do i Flip a 2d game object that is facing towards the cursor 0 Answers
How to change the movement speed of a character 4 Answers
Trying to make the camera follow the player but stop at the edge. 1 Answer
How would I make my gameobject align to the ground 2d 2 Answers
2D Collisions Not Working 2 Answers