Search Unity

2D Platformer Controller

Discussion in '2D' started by alKaszL, Nov 22, 2013.

  1. alKaszL

    alKaszL

    Joined:
    Jan 26, 2013
    Posts:
    7
    I've been dabbling with the 4.3 2D tools for the last week trying to get a tight 2D platformer controller working. I have used several methods to move the player around - Addforce(), velocity, and transform. I've found that I can get the "tightest" controls via either transform or velocity changes, but most suggestions including the documentation suggest to only manipulate the rigidbody's movement via addforce.

    I found the character controller in the 3D tools nice for achieving a 2D platformer controller, but there is not one in the 2D tools. What I am looking to achieve is instantaneous movement, and make it so the player doesn't maintain momentum after player input is not present (I made a script that set rigidbody drag higher when there's no input and it worked ok.)

    Has anyone achieved a nice feeling player control via addforce? Should I just stick to manipulating the velocity directly?
     
  2. bryantdrewjones

    bryantdrewjones

    Joined:
    Aug 29, 2011
    Posts:
    147
    This is a nice tool from prime31 that's worth checking out: https://github.com/prime31/CharacterController2D.

    I've had the most success rolling my own movement and collision code. All you really need for a platformer are rectangular bounding boxes and the ability to check if two rectangles are overlapping.
     
  3. elvis-satta

    elvis-satta

    Joined:
    Nov 13, 2013
    Posts:
    8
    Hi, thanks for this! Can you please help me to hack the script to avoid double jump?
    I'm not a coder but i think it will need a simple
    Code (csharp):
    1. StartCoroutine(waitForSeconds());
    somewhere in the PlayerTester.cs - maybe after the..

    Code (csharp):
    1. if( Input.GetKeyDown( KeyCode.UpArrow ) )
    2.         {
    3.             var targetJumpHeight = 2f;
    4.             velocity.y = Mathf.Sqrt( 2f * targetJumpHeight * -gravity );  
    5.             _animator.Play( Animator.StringToHash( "Jump" ) ); 
    6.            
    7.         }
    This could be an opportunity for me to begin to study.. Thanks in advance,
    -elvis
     
  4. bryantdrewjones

    bryantdrewjones

    Joined:
    Aug 29, 2011
    Posts:
    147
    I haven't used that CharacterController2D stuff myself, so I won't be much help. But I think it contains a variable called "isGrounded" that becomes true when your character is touching the ground. To remove double jump, you would only add the jump velocity if the character is currently grounded.
     
  5. elvis-satta

    elvis-satta

    Joined:
    Nov 13, 2013
    Posts:
    8
    Thanks! I think i've found where to make the changes and it actually work :)
    Thanks also to prime31 on twitter for the hint! Here's my modified version
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5.  
    6. public class PlayerTester : MonoBehaviour
    7. {
    8.     // movement config
    9.     public float gravity = -15f;
    10.     public float runSpeed = 8f;
    11.     public float groundDamping = 20f; // how fast do we change direction? higher means faster
    12.     public float inAirDamping = 5f;
    13.     // public float jumpWaitTime = 2.0;
    14.  
    15.     [HideInInspector]
    16.     public float rawMovementDirection = 1;
    17.     //[HideInInspector]
    18.     public float normalizedHorizontalSpeed = 0;
    19.  
    20.     CharacterController2D _controller;
    21.     Animator _animator;
    22.     public RaycastHit2D lastControllerColliderHit;
    23.  
    24.     [HideInInspector]
    25.     public Vector3 velocity;
    26.  
    27.  
    28.     void Awake()
    29.     {
    30.         _animator = GetComponent<Animator>();
    31.         _controller = GetComponent<CharacterController2D>();
    32.         _controller.onControllerCollidedEvent += onControllerCollider;
    33.     }
    34.  
    35.    
    36.     void onControllerCollider( RaycastHit2D hit )
    37.     {
    38.         // bail out on plain old ground hits
    39.         if( hit.normal.y == 1f )
    40.          return;
    41.  
    42.         // logs any collider hits
    43.         //Debug.Log( "flags: " + _controller.collisionState + ", hit.normal: " + hit.normal );
    44.     }
    45.  
    46.  
    47.     void Update()
    48.     {
    49.         // grab our current velocity to use as a base for all calculations
    50.         velocity = _controller.velocity;
    51.  
    52.         if( _controller.isGrounded )
    53.             velocity.y = 0;
    54.  
    55.         if( Input.GetKey( KeyCode.RightArrow ) )
    56.         {
    57.             normalizedHorizontalSpeed = 1;
    58.             if( transform.localScale.x < 0f )
    59.                 transform.localScale = new Vector3( -transform.localScale.x, transform.localScale.y, transform.localScale.z );
    60.  
    61.             if( _controller.isGrounded )
    62.                 _animator.Play( Animator.StringToHash( "Run" ) );
    63.         }
    64.         else if( Input.GetKey( KeyCode.LeftArrow ) )
    65.         {
    66.             normalizedHorizontalSpeed = -1;
    67.             if( transform.localScale.x > 0f )
    68.                 transform.localScale = new Vector3( -transform.localScale.x, transform.localScale.y, transform.localScale.z );
    69.  
    70.             if( _controller.isGrounded )
    71.                 _animator.Play( Animator.StringToHash( "Run" ) );
    72.         }
    73.         else
    74.         {
    75.             normalizedHorizontalSpeed = 0;
    76.  
    77.             if( _controller.isGrounded )
    78.                 _animator.Play( Animator.StringToHash( "Idle" ) );
    79.         }
    80.  
    81.  
    82.         if( Input.GetKeyDown( KeyCode.UpArrow ) )
    83.         {
    84.         //to avoid DOUBLE JUMP
    85.             if( _controller.isGrounded ) {
    86.             var targetJumpHeight = 2f;
    87.             velocity.y = Mathf.Sqrt( 2f * targetJumpHeight * -gravity );  
    88.             _animator.Play( Animator.StringToHash( "Jump" ) ); 
    89.             }
    90.         }
    91.  
    92.  
    93.         // apply horizontal speed smoothing it
    94.         var smoothedMovementFactor = _controller.isGrounded ? groundDamping : inAirDamping; // how fast do we change direction?
    95.         velocity.x = Mathf.Lerp( velocity.x, normalizedHorizontalSpeed * rawMovementDirection * runSpeed, Time.deltaTime * smoothedMovementFactor );
    96.        
    97.         // apply gravity before moving
    98.         velocity.y += gravity * Time.deltaTime;
    99.  
    100.         _controller.move( velocity * Time.deltaTime );
    101.     }
    102.  
    103. }
    104.  
    Thanks again,
    -elvis
     
  6. alKaszL

    alKaszL

    Joined:
    Jan 26, 2013
    Posts:
    7
    Thanks for the replies. Ive got some more tinkering to do :)
     
  7. softwizz

    softwizz

    Joined:
    Mar 12, 2011
    Posts:
    793
    I have been searching for a 2D version of CharacterController for a week.

    Thanks for supplying the solution and many thanks to prime31studios for this.
     
  8. natchoguy

    natchoguy

    Joined:
    Sep 13, 2013
    Posts:
    1
    I think the prime31studios solution is great but I must ask- is there a way for the player to control jump height (like mario or megaman) to make tighter platforming? Can someone please give a few guidelines on how to achieve this.
     
  9. hashtagyolo

    hashtagyolo

    Joined:
    Jul 11, 2014
    Posts:
    3
    I'm looking for the same thing. Need some very tight controls for my platformer, but I have literally no idea where to start and I've been having a nightmare even getting Prime31's script installed and running. I'm getting a different set of errors everytime I try it seems. Very frustrating. I've followed the instruction on how to install script, but for some reason I'm missing something. ANy helps at all, or links to a better tutorial than the unity one.
     
  10. drudiverse

    drudiverse

    Joined:
    May 16, 2013
    Posts:
    218
    You can use the first person controller by placing the camera far away on z axis facing the player, and only moving in xy axes... that's what i did for this level generator:
    https://dl.dropboxusercontent.com/u/114667999/2dfdemo.html

    Indeed you shouldnt use rigidbody.velocity, unless it's for a shadow or folowing object... you can make a transform.position controlled player, and then slave a physicial rigidbody to that player object by making it move onto him using velocity, so the physical object is 1 frame behind on where the player is, but it is controlled by transform position and has am independent physics rigidbody.
     
  11. _Evasi0n

    _Evasi0n

    Joined:
    Apr 13, 2015
    Posts:
    9
    it has a lot of errors
     
  12. mifzer

    mifzer

    Joined:
    Aug 19, 2015
    Posts:
    1
    Thank you :D
     
  13. Jadencandy

    Jadencandy

    Joined:
    Mar 13, 2016
    Posts:
    1
    on line 20 CharactorController2D could not be found as a namespace????
     
unityunity