Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Moving Platforms (2D platformer)

Discussion in 'Scripting' started by junkrar, Mar 26, 2013.

  1. junkrar

    junkrar

    Joined:
    Mar 18, 2013
    Posts:
    17
    I'm writing a 2D Platformer and I'm trying to get the player to stay on a moving platform. I've done searching and tinkering for a day or two now, and I'm not having any luck.

    Basically, I've been told to try to keep the character moving with the platform when they are touching.
    Firstly, if I use anything related to OnTriggerEnter(), the player goes right through the platform. If I do OnCollisionEnter() (with a CharacterController on the player and a BoxCollider on the platform), nothing happens at all. These are two things I've found suggested most. The other is parenting the player with the platform, but this apparently causes "problems" (frequently stated, never explained).

    So, how can I get the player to stay on the moving platform? Here is the code for the moving platform:

    Code (csharp):
    1. public class MovingPlatform : MonoBehaviour
    2. {
    3.     private float useSpeed;
    4.     public float directionSpeed = 9.0f;
    5.     float origY;
    6.     public float distance = 10.0f;
    7.  
    8.     // Use this for initialization
    9.     void Start ()
    10.     {
    11.         origY = transform.position.y;
    12.         useSpeed = -directionSpeed;
    13.     }
    14.    
    15.     // Update is called once per frame
    16.     void Update ()
    17.     {
    18.         if(origY - transform.position.y > distance)
    19.         {
    20.             useSpeed = directionSpeed; //flip direction
    21.         }
    22.         else if(origY - transform.position.y < -distance)
    23.         {
    24.             useSpeed = -directionSpeed; //flip direction
    25.         }
    26.         transform.Translate(0,useSpeed*Time.deltaTime,0);
    27.     }
    28.  
    AND here is the code for the Player's movement (in Update):

    Code (csharp):
    1. CharacterController controller = GetComponent<CharacterController>();
    2.         float rotation = Input.GetAxis("Horizontal");
    3.         if(controller.isGrounded)
    4.         {
    5.             moveDirection.Set(rotation, 0, 0); //moveDirection = new Vector3(rotation, 0, 0);
    6.             moveDirection = transform.TransformDirection(moveDirection);
    7.            
    8.             //running code
    9.             if(Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) //check if shift is held
    10.             { running = true; }
    11.             else
    12.             { running = false; }
    13.            
    14.             moveDirection *= running ? runningSpeed : walkingSpeed; //set speed
    15.            
    16.             //jump code
    17.             if(Input.GetButtonDown("Jump"))
    18.             {
    19.                 //moveDirection.y = jumpHeight;
    20.                 jump ();
    21.             }
    22.         }
    23.         moveDirection.y -= gravity * Time.deltaTime;
    24.         controller.Move(moveDirection * Time.deltaTime);
     
  2. ColossalDuck

    ColossalDuck

    Joined:
    Jun 6, 2009
    Posts:
    3,246
    Make the player a child of the platform when its moving, when the play wants to move or jump, stop the player from being its child. This should make it work dandy.
     
  3. FuzzANDJosh

    FuzzANDJosh

    Joined:
    Aug 23, 2012
    Posts:
    3
    Or if the above idea doesn't work out you could make a ghost platform that is a child of the normal platform to handle movement. That way the normal platform will stop you from falling, and the ghost platform can handle the player movement with OnTriggerEnter().

    Assuming that randomly falling through the platform is the only obstacle down that path... lol
     
  4. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
  5. KholdStare2399

    KholdStare2399

    Joined:
    Jun 29, 2014
    Posts:
    3
    Here is the complete character movement code I used that contains support for moving platforms. It is not perfect but it works pretty well.
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. public class PlayerCharacter : MonoBehaviour
    7. {
    8.     public float speed = 1.0f;
    9.     public string axisName = "Horizontal";
    10.     private Animator anim;
    11.     public string jumpButton = "Fire1";
    12.     public float jumpPower = 10.0f;
    13.     public float minJumpDelay = 0.5f;
    14.     public Transform[] groundChecks;
    15.     private float jumpTime = 0.0f;
    16.     private Transform currentPlatform = null;
    17.     private Vector3 lastPlatformPosition = Vector3.zero;
    18.     private Vector3 currentPlatformDelta = Vector3.zero;
    19.     public float groundRadius = 0.2f;
    20.     public LayerMask whatIsGround;
    21.     bool doubleJump = false;
    22.  
    23.     // Use this for initialization
    24.     void Start ()
    25.     {
    26.         anim = gameObject.GetComponent<Animator>();
    27.     }
    28.    
    29.     // Update is called once per frame
    30.     void Update ()
    31.     {
    32.         //Left and right movement
    33.         anim.SetFloat("Speed", Input.GetAxis(axisName));
    34.         /*if(Input.GetAxis(axisName) < -0.1)
    35.         {
    36.             Vector3 theScale = transform.localScale;
    37.             theScale.x *= -1;
    38.         transform.localScale = theScale;
    39.         }
    40.         else if(Input.GetAxis(axisName) > 0.1)
    41.         {
    42.             Vector3 theScale = transform.localScale;
    43.             theScale.x *= 1;
    44.             transform.localScale = theScale;
    45.         }*/
    46.         transform.position += transform.right*Input.GetAxis(axisName)*speed*Time.deltaTime;
    47.  
    48.         //Jump logic
    49.         bool grounded = false;
    50.         foreach(Transform groundCheck in groundChecks)
    51.         {
    52.             grounded |= Physics2D.OverlapCircle (groundCheck.position, groundRadius, whatIsGround);
    53.         }
    54.         anim.SetBool("Grounded", grounded);
    55.         if(jumpTime > 0)
    56.         {
    57.             jumpTime -= Time.deltaTime;
    58.         }
    59.         if(Input.GetButtonDown(jumpButton) && anim.GetBool("Grounded"))
    60.         {
    61.             anim.SetBool("Jump",true);
    62.             rigidbody2D.AddForce(transform.up*jumpPower);
    63.             jumpTime = minJumpDelay;
    64.         }
    65.         if(anim.GetBool("Grounded") && jumpTime <= 0)
    66.         {
    67.             anim.SetBool("Jump",false);
    68.         }
    69.  
    70.  
    71.  
    72.         //if (anim.GetBool("Grounded")) {
    73.                         //Moving platform logic
    74.                         //Check what platform we are on
    75.                         List<Transform> platforms = new List<Transform> ();
    76.                         bool onSamePlatform = false;
    77.                         foreach (Transform groundCheck in groundChecks) {
    78.                                 RaycastHit2D hit = Physics2D.Linecast (transform.position, groundCheck.position, 1 << LayerMask.NameToLayer ("Ground"));
    79.                                 if (hit.transform != null) {
    80.                                         platforms.Add (hit.transform);
    81.                                         if (currentPlatform == hit.transform) {
    82.                                                 onSamePlatform = true;
    83.                                         }
    84.                                 }
    85.                         }
    86.                
    87.  
    88.                         if (!onSamePlatform) {
    89.                                 foreach (Transform platform in platforms) {
    90.                                         currentPlatform = platform;
    91.                                         lastPlatformPosition = currentPlatform.position;
    92.                                 }
    93.                         }
    94.  
    95.                         if (currentPlatform != null) {
    96.                                 //Determine how far platform has moved
    97.                                 currentPlatformDelta = currentPlatform.position - lastPlatformPosition;
    98.  
    99.                                 lastPlatformPosition = currentPlatform.position;
    100.                         }
    101.                 //}
    102.     }
    103.  
    104.     void LateUpdate()
    105.     {
    106.         if (anim.GetBool ("Grounded")) {
    107.                         if (currentPlatform != null) {
    108.                                 //Move with the platform
    109.                                 transform.position += currentPlatformDelta;
    110.                         }
    111.                 }
    112.     }
    113. }
    114.  
    115.  
     
    Last edited: Jul 8, 2014
  6. paulxthompson

    paulxthompson

    Joined:
    Aug 17, 2015
    Posts:
    6
    Hi all, and thanks KholdStare2399 for getting me thinking along the right lines. I found my character was sliding about a lot on the platform, because the platforms position was being transformed and the character was moving with velocity and it seemed that this was resulting in them moving either too fast or slowly when stood still.

    I ended up having to give the platform a Rigidbody2D (isKinematic = true) and move it using velocity in order to make the player move smoothly with it. The character can also climb on moving vertical platforms.

    Lots of code from fixed update removed for clarity but hopefully this will be enough to get the idea of what i'm doing?

    Anyway, please do let me know if this makes no sense for any reason, and I hope it helps someone.

    Code (CSharp):
    1.  
    2.  
    3. void FixedUpdate ()
    4. {
    5.  
    6. grounded = Physics2D.OverlapCircle (groundCheck.position, groundRadius, whatIsGround);
    7. stucktowall = Physics2D.OverlapCircle (wallCheck.position, groundRadius, whatIsGround);
    8. moveH = Input.GetAxis ("Horizontal");
    9.  
    10. /* lots of code removed that was basically cribbed from the getting started tutorials about jumping and such */
    11.  
    12. if (grounded)
    13. {
    14. // if we're on the ground, get the platform direction from the floor...
    15. platformDirection = GetCurrentPlatformDirection (groundCheck);
    16. theBody.velocity = newVector2 ((moveH * maxSpeed) + platformDirection.x, theBody.velocity.y) ;
    17. }
    18. else if (stucktowall)
    19. {
    20. // get it from the wall we're meant to be stuck to
    21. platformDirection = GetCurrentPlatformDirection (wallCheck);
    22. // we slide down the wall we're stuck to slower than a fall...
    23. theBody.velocity = newVector2 (0, ( platformDirection.y + theBody.velocity.y) / 2);
    24. }
    25. else
    26. {
    27. // basically the physics will be dealing with it, but again,
    28. // code not shown for other stuff not related to the platform moving.
    29. }
    30. /* lots of code removed */
    31. }
    32.  
    33.    Vector2 GetCurrentPlatformDirection(Transform checkWith)
    34.     {
    35.         RaycastHit2D hit = Physics2D.Linecast (transform.position, checkWith.position, 1 << LayerMask.NameToLayer ("Ground"));
    36.         if (hit.transform != null)
    37.         {
    38.             Rigidbody2D cp = hit.transform.GetComponent<Rigidbody2D> ();
    39.             if(cp!=null)
    40.             {
    41.                 return cp.velocity;
    42.             }
    43.         }
    44.         return new Vector2(0,0);
    45.     }
    46.  
    With apologies for formatting.
     
  7. spacelizardstudio

    spacelizardstudio

    Joined:
    Jun 26, 2015
    Posts:
    6
  8. MisterSkitz

    MisterSkitz

    Joined:
    Sep 2, 2015
    Posts:
    833
    [SerializeField]
    private Vector3 speedVar;

    private void OnCollisionEnter(Collision collision)
    {
    if(collision.GameObject.tag == "Player"){
    platformMoving = true;
    collision.collider.transform.SetParent(transform);
    }

    private void OnCollisionExit(Collision collision)
    {

    platformMoving = false;
    collision.collider.transform.SetParent(null);

    }

    private void FixedUpdate()
    {
    if(platformMoving){
    transform.position += (speedVar * Time.deltaTime);

    }
    }



    Try that and see if it works for you. That fixedupdate is the bread and butter!
    I thought I'd hate learning the serialized field but it comes in extremely handy especially with fixedupdate's calculation functionality.