6

I'm writing a 2D Platformer in Unity 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:

public class MovingPlatform : MonoBehaviour
{
private float useSpeed;
public float directionSpeed = 9.0f;
float origY;
public float distance = 10.0f;

// Use this for initialization
void Start () 
{
    origY = transform.position.y;
    useSpeed = -directionSpeed;
}

// Update is called once per frame
void Update ()
{
    if(origY - transform.position.y > distance)
    {
        useSpeed = directionSpeed; //flip direction
    }
    else if(origY - transform.position.y < -distance)
    {
        useSpeed = -directionSpeed; //flip direction
    }
    transform.Translate(0,useSpeed*Time.deltaTime,0);
}

AND here is the code for the Player's movement (in Update):

CharacterController controller = GetComponent<CharacterController>();
    float rotation = Input.GetAxis("Horizontal");
    if(controller.isGrounded)
    {
        moveDirection.Set(rotation, 0, 0); //moveDirection = new Vector3(rotation, 0, 0);
        moveDirection = transform.TransformDirection(moveDirection);

        //running code
        if(Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) //check if shift is held
        { running = true; }
        else
        { running = false; }

        moveDirection *= running ? runningSpeed : walkingSpeed; //set speed

        //jump code
        if(Input.GetButtonDown("Jump"))
        {
            //moveDirection.y = jumpHeight;
            jump ();
        }
    }
    moveDirection.y -= gravity * Time.deltaTime;
    controller.Move(moveDirection * Time.deltaTime);

EDIT: I think it might have to do with how I'm defining the player and the platform, but I've tried different combinations. If the platform is a trigger (on its collider), the player goes through it consistently. If not, I can't use OnTrigger functions. I have a rigidbody attached to both the player and the platform, but it doesn't seem to affect anything. When the player DOES get on the platform in some setups, he jitters and often just ends up falling through.

muttley91
  • 12,278
  • 33
  • 106
  • 160
  • I've never used Unity3D, but is it possible to do something like `moveDirection += theplatform.moveDirection` when the character is on a platform? – mbeckish Mar 26 '13 at 17:59
  • I've tried that, but I seem to run into issues with the OnTrigger and OnCollision functions, which would be responsible for the "when player is on the platform" part of that. I think it has to do with how I'm defining my objects, but I'm not sure of the proper way to define them such that this can work. – muttley91 Mar 26 '13 at 18:02
  • Also, you'll need to ensure that the character's moveDirection.y is never less than your platform's moveDirection.y, or else you would fall through (unless the Unity3D framework handles that for you automatically?) – mbeckish Mar 26 '13 at 18:03
  • Unfortunately it seems Unity won't do it on its own, so I'm trying to fight with it to get the player to stay on. I'll be incorporating your advice in addition to the 2 solutions presented and hopefully one of them works! – muttley91 Mar 26 '13 at 23:25

5 Answers5

2

What you need seems to be a second collider on the platform. The main collider has isTrigger = false to ensure that your character controller is working. The second one runs with isTrigger = true and its only function is to detect that the player is kind of attached to the platform when OnTriggerEnter is called and leaves the platform on OnTriggerExit.

As you cannot have 2 colliders of the same type (I guess you need box colliders), build an empty child under your platform game object and assign the box collider to it. I usually take a special physics material called ActionMaterial just to have my stuff clean. If you are using layers and have tweaked collision matrix ensure that your collision is handled by physX.

Kay
  • 12,918
  • 4
  • 55
  • 77
  • Could I use two different colliders (one box, one capsule) on the same object for the same effect, rather than have 2 objects and have them both need the move script? Also, I'm not using layers and have no tweaked collision matrix. – muttley91 Mar 26 '13 at 21:50
  • Different colliders are allowed. But bear in mind that a capsule collider is far more expensive. So if have a cube shape, you are better off using the child object solution. No layers, no collision matrix manipulation => no increased complexity ;) You only need that stuff when you are running into performance problems. I am developing for iOS so performance is always an issue for me – Kay Mar 26 '13 at 21:57
  • Ah fair enough! So to be clear, you mean having two game objects in the scene? How do you specifically make one object a child of another? – muttley91 Mar 26 '13 at 22:16
  • I'm still working with this solution. The child platform needs to use the OnTriggerEnter and OnTriggerExit functions, how might I write those? – muttley91 Mar 27 '13 at 00:04
  • In hierarchy view drag the child object to the parent. Programmatically via `gameObject.transform.parent`. Just `void OnTriggerEnter (Collider collider) {` – Kay Mar 27 '13 at 07:46
  • This method isn't working because OnTriggerExit never seems to be called for some reason... – muttley91 Mar 27 '13 at 15:33
  • Maybe http://stackoverflow.com/questions/14326535/how-to-collide-objects-with-high-speed-in-unity/14387936#14387936 or some other answers about colliders help – Kay Mar 27 '13 at 16:17
1

This is not the best way to do it, but you could perform a RayCast from the player to the moving platform. If the RayCast hits "MovingPlatform" you can check the difference on the Y axis to determine if the player is close enough to it to be considered "OnThePlatform." Then you can just adjust the players position as normal.

An example would be:

private bool isOnMovingPlatform;
private float offsetToKeepPlayerAbovePlatform = 2.2f;
private float min = 0.2f;
private float max = 1.2f;

private void Update()
{
RaycastHit hit;
if(Physics.Raycast (player.position, player.TransformDirection(Vector3.down), out hit))
{
    if(hit.transform.name.Contains("MovingPlatform"))
    {
        Transform movingPlatform  = hit.collider.transform;

        if(movingPlatform.position.y - player.position.y <= min && movingPlatform.position.y - player.position.y >= max)
        {
            isOnMovingPlatform = true;
        }
        else
        {
            isOnMovingPlatform = false;
        }
    }

    if(isOnMovingPlatform)
    {
        player.position = new Vector3(hit.transform.x, hit.transform.y + offsetToKeepPlayerAbovePlatform, 0);
    }
}
}
Nestor Ledon
  • 1,925
  • 2
  • 23
  • 36
  • Just curious as I'm about to try this, the min, max, and offset, do they relate to the size of the platform at all? – muttley91 Mar 26 '13 at 21:47
  • Not necessarily. Min and max are just ranges so you know where the player is relevant to the platform. If the players Y value is 5 above max for example, it's clear he is not on the platform and vise versa. Change the ranges until you get a good pair of floats. In theory they could derive from the height of the platform but they don't have to. – Nestor Ledon Mar 26 '13 at 22:05
0

This Unity Answers page might help.

  • It's indeed pretty good. But you should post it as comment or provide more info. Answers containing just a link will be killed on SO (I just lost some) – Kay Mar 27 '13 at 16:14
0

You could also simply make the player gameobjects transform parent equal to the transform of the platform. This allows the player to still keep moving while they're on it. As for detection a Raycast is a bit slow but gets the job done.

0

We solved the problem by using a SliderJoint2d and setting the gravity scale of the character to zero while on the moving platform. Here is the link with the full explanation:

http://spacelizardstudio.com/devblog/index.php/2016/03/02/unity-a-guide-to-moving-platforms/

  • You should add relevant information from the link so that the answer is still useful and relevant even if the link dies. – drneel Mar 02 '16 at 16:06