Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stay on Moving Platforms

Tags:

c#

unity3d

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.

like image 502
muttley91 Avatar asked Nov 03 '22 01:11

muttley91


2 Answers

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.

like image 55
Kay Avatar answered Nov 15 '22 05:11

Kay


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);
    }
}
}
like image 35
Nestor Ledon Avatar answered Nov 15 '22 06:11

Nestor Ledon