2D Portal with Physics

Hello everyone, I’ve been working on a portal system that retains the player’s physics when teleporting. For example, if the player comes falling down into one portal, they’ll come flying out the other.
This is what I go so far, but it’s buggy. I used a coroutine to have more control over the parameters surrounding the teleport, but the physics is messed up, as if the player is weighted down.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Portal : MonoBehaviour
{

    public Portal otherPortal;

    [SerializeField] Transform destination;
    [SerializeField] float distance = 0.4f;
    [SerializeField] float secondsBeforeTeleport;

    Rigidbody2D playerRB;
    Vector2 entryVelocity; // Store the velocity when entering the portal

    private void OnTriggerEnter2D(Collider2D player)
    {
        playerRB = player.GetComponent<Rigidbody2D>();

        if (player.CompareTag("Player"))
        {
            if (Vector2.Distance(player.transform.position, transform.position) > distance)
            {
                entryVelocity = playerRB.velocity; // Store the velocity upon entering the portal
                StartCoroutine(TeleportDelay(player, secondsBeforeTeleport));
            }
        }
    }

    IEnumerator TeleportDelay(Collider2D player, float seconds)
    {
        yield return null;
        yield return new WaitForSeconds(seconds);

        player.transform.position = destination.position;

        // Calculate the negative entry velocity
        Vector2 negativeEntryVelocity = -entryVelocity;

        // Check if the player is moving upward or downward
        float verticalVelocity = Mathf.Sign(entryVelocity.y);

        // If moving upward, launch downward from the destination portal, and vice versa
        playerRB.velocity = new Vector2(negativeEntryVelocity.x, verticalVelocity * Mathf.Abs(negativeEntryVelocity.y));

        yield return new WaitForSeconds(seconds);
    }

    private void OnTriggerExit2D(Collider2D player)
    {
        playerRB = player.GetComponent<Rigidbody2D>();

        if (player.CompareTag("Player"))
        {
            // Apply the stored entry velocity when exiting the second portal
            playerRB.velocity = entryVelocity;
        }
    }

}

That means it is… time to start debugging!

By debugging you can find out exactly what your program is doing so you can fix it.

Use the above techniques to get the information you need in order to reason about what the problem is.

You can also use Debug.Log(...); statements to find out if any of your code is even running. Don’t assume it is.

Once you understand what the problem is, you may begin to reason about a solution to the problem.

Given this:

I would investigate some of the numerics going into your velocity reinjection stuff.

1 Like

Thank you! Your input is much appreciated. At first glance, does the logic seem okay at least?

I’ve long since learned that glancing at logic is a fool’s errand, especially as I don’t know exactly what you expect, and especially since everything in Unity is tied to the current state of the scene at any given instant.

Attach the debugger. Get in that habit early and your coding skills will suddenly take a sharp trajectory upwards in terms of your comprehension and understanding of what you are doing.

2 Likes

The logic doesn’t make sense. Entering a trigger you grab the current velocity then start a coroutine which waits for some time. In the meantime, maybe before the coroutine starts doing work, you might get a trigger exit. This is fragile.

Also, you’re doing fragile stuff like storing the trigger exit Rigidbody before you’ve even testing if it’s is the player.

The whole thing looks fragile with a “portal” knowing so much about the player etc.

2 Likes