GeeItSomeLaldy

Unity Basic 2D Platformer Script

Nov 23rd, 2014
2,265
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /////////////////////////////////////////////////////////////////////
  2. ///// Simple 2D Platform Controller for Unity
  3. ///// By David McQuillan
  4. ///// 23/11/2014
  5. /////////////////////////////////////////////////////////////////////
  6.  
  7. using System.Collections;
  8. /*  
  9.  * This is a basic, but powerful barebones 2D platform controller for Unity
  10.  *  It allows you to seperate Walls, Roofs and Platforms, and allows the dropping down through said platforms
  11.  *  It does this using Layers and Raycasts. This does not rely on Unity's 2D Physics engine and does not require rigidbodies
  12.  *  All level geometry need 2D coliders on them however. These can be any of the 2D colliders, including Edge Colliders.
  13.  *  Edge Colliders are recommended for Platforms that you intend to drop down from.
  14. */
  15. using UnityEngine;
  16.  
  17. public class Simple2dPlatformController : MonoBehaviour
  18. {
  19.     private bool wasJumpPressed;
  20.     private bool platformDropDown = false;
  21.  
  22.     #region Public Variables, hidden from inspector
  23.     /// <summary>
  24.     /// Other scripts might want to access these at some point
  25.     /// </summary>
  26.     [HideInInspector]
  27.     public bool isGrounded = false;
  28.     [HideInInspector]
  29.     public Vector3 Velocity;
  30.     #endregion
  31.  
  32.     #region Public Inspector Variables
  33.     public bool DigitalKeyboardControls = false;
  34.     public float Gravity = -9.81f;
  35.     public float JumpStrength = 6f;
  36.     public float Acceleration = 10f;
  37.     public float SkidAcceleration = 20f;
  38.     public float ColliderSize = 0.15f;
  39.     public float MaximumHorizontalSpeed = 10f;
  40.     public float MaximumVerticalSpeed = 10f;
  41.     public float platformDropDownCoolDown = 0.2f;
  42.  
  43.     /// <summary>
  44.     /// At minimum you want 2 layers. One for your player and one for your geometry.
  45.     /// To get the most out of this you want 4 layers. One for Walls, One for Roofs, One for Platforms and one for the player.
  46.     /// </summary>
  47.     public LayerMask Walls;
  48.     public LayerMask Roofs;
  49.     public LayerMask Platforms;
  50.     #endregion
  51.  
  52.     /// <summary>
  53.     /// Show a cube in the scene view showing the bounds of the collider.
  54.     /// </summary>
  55.     void OnDrawGizmos()
  56.     {
  57.         Gizmos.DrawWireCube(this.transform.position, Vector3.one * ColliderSize * 2);
  58.     }
  59.     void Update()
  60.     {
  61.         //Player Input
  62.         var horizontalMovement = DigitalKeyboardControls?Input.GetAxisRaw("Horizontal"):Input.GetAxis("Horizontal");
  63.         var verticalMovement = DigitalKeyboardControls?Input.GetAxisRaw("Vertical"):Input.GetAxis("Vertical");
  64.  
  65.         var canJump = GetCanJump();
  66.        
  67.         //Simple Jump, ignored if you're pressing the DOWN key
  68.         if (verticalMovement >= 0 && isGrounded && canJump)  
  69.         {
  70.             isGrounded = false;  
  71.             Velocity.y = JumpStrength;
  72.         }
  73.  
  74.         //Drop down from a platform, using DOWN and Jump
  75.         if (!platformDropDown && verticalMovement < 0 && isGrounded && canJump)
  76.         {
  77.             StartCoroutine(DropDown()); //We use a co-routine for this (explained below)
  78.         }
  79.  
  80.         //Simple Horizontal Movement (Same in the air as on the ground)
  81.         if (horizontalMovement != 0)
  82.         {
  83.             if (Velocity.x == 0 || Mathf.Sign(horizontalMovement) == Mathf.Sign(Velocity.x))
  84.             {
  85.                 Velocity.x += horizontalMovement * Acceleration * Time.deltaTime;
  86.             }
  87.             else //If we're moving in the opposite direction, skid.
  88.             {
  89.                 Velocity.x += horizontalMovement * SkidAcceleration * Time.deltaTime ;
  90.             }
  91.         }
  92.         else if (Velocity.x != 0)
  93.         {
  94.             Velocity.x -= Mathf.Sign(Velocity.x) * Acceleration * Time.deltaTime ;
  95.             Velocity.x = Velocity.x < 0.001f ? (Velocity.x > -0.001f ? 0 : Velocity.x) : Velocity.x; //set to 0 if it's close enough
  96.         }
  97.  
  98.         //Clamp to maximum
  99.         Velocity.x = Mathf.Clamp(Velocity.x, -MaximumHorizontalSpeed, MaximumHorizontalSpeed);
  100.         Velocity.y = Mathf.Clamp(Velocity.y, -MaximumVerticalSpeed, MaximumVerticalSpeed);
  101.     }
  102.      
  103.     /// <summary>
  104.     /// Smooth Jump Button detection
  105.     /// </summary>
  106.     /// <returns>Whether or not the jump button is pressed AND you can jump</returns>
  107.     private bool GetCanJump()
  108.     {
  109.         //Input.GetButtonDown tends to be quite 'sticky' and sometimes doesn't fire.
  110.         //This is a smoother way of doing things
  111.  
  112.         var jumpButtonDown = Input.GetButton("Jump");
  113.  
  114.         //If we have previously pressed the jump button and the jump button has been released
  115.         if (wasJumpPressed && !jumpButtonDown)
  116.         {
  117.             wasJumpPressed = false; //Re-enable jumping
  118.         }
  119.         if (isGrounded && jumpButtonDown && !wasJumpPressed)
  120.         {
  121.             wasJumpPressed = true; //Disable jumping
  122.             return true; //tell the parent that we've jumped
  123.         }
  124.         return false; //Can't Jump, Won't Jump
  125.  
  126.     }
  127.  
  128.     /// <summary>
  129.     /// Drop Down from certain platforms. Call as CoRoutine
  130.     /// </summary>
  131.     /// <returns></returns>
  132.     private IEnumerator DropDown()
  133.     {
  134.         //A Naive WaitForSeconds is used here. You could, perhaps, get the current ground and just remove that from the calculations if you wanted to be smart about it.
  135.         platformDropDown = true; //Activate the dropdown flag
  136.         isGrounded = false; //tell the engine we're not grounded any more
  137.         yield return new WaitForSeconds(platformDropDownCoolDown); //wait x seconds (so that we don't just pop back up onto the platform we're on)
  138.         platformDropDown = false;  //Deactivate the dropdown flag
  139.  
  140.     }
  141.    
  142.     /// <summary>
  143.     /// Our physics loop
  144.     /// </summary>
  145.     void FixedUpdate()
  146.     {
  147.         //Add gravity if we're off the ground
  148.         if (!isGrounded)
  149.         {
  150.             Velocity.y += Gravity * Time.fixedDeltaTime;
  151.         }
  152.  
  153.         //Move the player
  154.         this.transform.position += Velocity * Time.fixedDeltaTime;
  155.  
  156.         //Collision tests!
  157.         UpTest();
  158.         DownTest(!platformDropDown); //Down tests, pass in whether or not we want to do the platform drop-down tests or not
  159.         WallTest();
  160.     }
  161.  
  162.     /// <summary>
  163.     /// Used in Raycast shortcut
  164.     /// </summary>
  165.     RaycastHit2D lastHitResult = new RaycastHit2D();
  166.     /// <summary>
  167.     /// Raycast shortcut
  168.     /// </summary>
  169.     /// <param name="Direction"></param>
  170.     /// <param name="mask"></param>
  171.     /// <returns>True on hit, False on not hit</returns>
  172.     bool Raycast(Vector2 Direction, LayerMask mask)
  173.     {
  174.         lastHitResult = Physics2D.Raycast(this.transform.position, Direction, ColliderSize, mask);
  175.         if (lastHitResult != null && lastHitResult.collider != null) return true;
  176.         return false;
  177.  
  178.     }
  179.     /// <summary>
  180.     /// Test to see if we hit the ceiling
  181.     /// </summary>
  182.     void UpTest()
  183.     {
  184.         if (Velocity.y < 0) return; //Don't bother unless we're moving upwards
  185.         if (Raycast(this.transform.up, Roofs))
  186.         {
  187.             this.transform.position = new Vector3(this.transform.position.x, lastHitResult.point.y - ColliderSize, this.transform.position.z);
  188.             Velocity.y = 0;
  189.         }
  190.     }
  191.     /// <summary>
  192.     /// Test to see if we hit the ground
  193.     /// </summary>
  194.     /// <param name="TestAllColliders">If false, ignores "Platforms" mask to allow dropping down</param>
  195.     void DownTest(bool TestAllColliders = true)
  196.     {
  197.         //Only test if we're moving downwards, or not moving vertically at all
  198.         if ((Velocity.y <= 0) && Raycast(-this.transform.up, TestAllColliders? (Platforms | Walls | Roofs) : (Walls | Roofs)))
  199.         {
  200.             this.transform.position = new Vector3(this.transform.position.x, lastHitResult.point.y + ColliderSize, this.transform.position.z);
  201.             Velocity.y = 0;
  202.             isGrounded = true;
  203.         }
  204.         else //otherwise we're not grounded D:
  205.         {
  206.             isGrounded = false;
  207.         }
  208.     }
  209.     /// <summary>
  210.     /// Test to see if we hit a wall
  211.     /// </summary>
  212.     void WallTest()
  213.     {
  214.         if (Velocity.x < 0 && Raycast(-this.transform.right, Walls)) //Only test Left if we're moving Left
  215.         {
  216.             this.transform.position = new Vector3(lastHitResult.point.x + ColliderSize, this.transform.position.y, this.transform.position.z);
  217.             Velocity.x = 0;
  218.         }
  219.         if (Velocity.x > 0 && Raycast(this.transform.right, Walls)) //Only test Right if we're moving Right
  220.         {
  221.             this.transform.position = new Vector3(lastHitResult.point.x - ColliderSize, this.transform.position.y, this.transform.position.z);
  222.             Velocity.x = 0;
  223.         }
  224.  
  225.     }
  226. }
RAW Paste Data