简体   繁体   中英

Unity2D move object up and back down with smooth transition

(I'm a beginner so please have patience with me).

How it needs to happend:

I have a player that can jump and hit a tile, on collision the tile with move a fixed distance up and come back down with a smooth transition.

在此处输入图像描述

What I have so far: I am detecting the collision now there's only the matter of moving the tile up and down.

The catch

The tiles are suspended in air so basically they either don't have a RigidBody2D or they have one with gravity scale 0

What I've tried

Basically I've tried 2 solutions:

(I'm not limited to these 2, I want to implement the solution that is correct so I am open to other ideas)

  1. I was thinking to simply take the current position, calculate another vector with another position, and lerp to the new position and then lerp back to the initial one.

     Vector2 initialPosition = transform.position; Vector2 targetPosition = new Vector2(initialPosition.x + 4f, initialPosition.y + 4f); print("Initial position: " + initialPosition); print("Target position: " + targetPosition); Vector2.Lerp(initialPosition, targetPosition, 1f);
  2. I tried adding a rigidbody with scale 0

     private void OnCollisionEnter2D(Collision2D collision) { if (collision.gameObject.tag == "Ground") { Jumping = false; anim.SetInteger("State", 0); } // print("Colision layer: " + collision.collider.gameObject.layer); if (collision.collider.gameObject.layer == Mathf.Log(layerMask.value, 2)) { GameObject Tile = collision.gameObject; Rigidbody2D rigidBody = Tile.GetComponent<Rigidbody2D>(); rigidBody.gravityScale = 1; rigidBody.AddForce(new Vector2(0, 10f), ForceMode2D.Force); StartCoroutine(MoveTileWithForce(rigidBody)); } } IEnumerator MoveTileWithForce(Rigidbody2D TileRigidBody) { yield return new WaitForSeconds(1); TileRigidBody.AddForce(new Vector2(0, -5f), ForceMode2D.Force); TileRigidBody.gravityScale = 0; print("END MY COROUTINE, gravityScale: " + TileRigidBody.gravityScale); }

I think the best way you can achieve this is in this simple way:

1

Make sure you have a box collider for your character and a Rigidbody and the same for the block you wanna move. For both of the colliders set on trigger . If you want to have another collider for the player in order to make him touch the ground or hit enemies you can add another box collider , but make sure you have one on trigger on his head

2

Add a script to the block you wanna move and also a tag to the player, for example "player"

3

Inside of this new script check if the block is triggering the player:

void OnTriggerEnter2D(Collision other)
{
if(other.compareTag("player"))
{ StartCoroutine(MovingBlock(0.5f, transform.position, upperPosition));}
|

It will enter inside the if when the player touches the block, it will start a coroutine that I will now write so you will understand the 0.5f and the other variables

IEnumerator MovingBlock(float time, Vector2 startpos, Vector2 endpos)
{
    
            float elapsedTime = 0;

                while (elapsedTime < time)
                {
                    transform.position= Vector2.Lerp(startpos, endpos, (elapsedTime / time));
                    elapsedTime += Time.deltaTime;

                    yield return null;
            }

          elapsedTime = 0f;

            while (elapsedTime < time)
                {
                    transform.position= Vector2.Lerp(endpos, startpos, (elapsedTime / time));
                    elapsedTime += Time.deltaTime;

                    yield return null;
            }
}

Basically, the Coroutine will move the object from startpos to endpos and then back again in the amount of time that you decide (in this case 0.5 seconds). I have used the Vector2 variable upperposition and it's just the height you want to reach with the platform.

If you want, you can also add inside the coroutine some yield return new WaitForSeconds(timeToWait) and make the platform wait in a certain position the amount of seconds you want (timeToWait)

Declare Vector2 initialPosition at the beginning of your class.

On Start() of the tile object you should get the initialPosition = transform.position .

And in the OnCollisionEnter2D(Collision2D collision) you could start a coroutine to bring the tile back down.

So for example:

Player Script:

public class Player : MonoBehaviour
{
    public Rigidbody2D rb;
    //Initialize the rigidbody on the editor.

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            rb.AddForce(new Vector2(0f, 1000f));
        }
    }
}

And the tile script:

public class MyTile : MonoBehaviour
{
  
    Vector2 initialPosition;
    float speed = 2f; //the speed the tile will go down.

    private void Start()
    {
        initialPosition = transform.position;
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        //The amount you want to go up on the Y component, for this example I used 2.
        transform.position = new Vector2(transform.position.x, transform.position.y + 2f);

        StartCoroutine(MoveTileDown());
    }

    IEnumerator MoveTileDown()
    {
        while (transform.position.y > initialPosition.y)
        {
            transform.position = Vector2.Lerp(transform.position, initialPosition, Time.deltaTime * speed);
            yield return null; //Make it run every frame, just like Update()
        }

        StopCoroutine(MoveTileDown());
    }

}

There are a lot of ways you can achieve the same result, this is just one of them.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM