3
votes

I'm trying to make a 2D portal in Unity that doesn't just transport the player to the other portal gameobject. But keeps the players position and movements direction and velocity after going through the portal.

I'm be no means an artist, but for example: enter image description here The player is a ball bouncing around an area, when he does through the portal his velocity is maintained, along with the fact that he entered he center of the portal.

Where here for example: enter image description here If the player enters the bottom half of the portal, he will come out the bottom half of the portal.

When this works, it works great! However, it only works 50% of the time, that 50% can have a bunch of different issues though, sometimes the ball will just not teleport. Sometimes the ball hits the first portal, teleports to the second portal, then back to the first portal, and it does this repeatedly forever. And it experiences these issues seemingly at random.

Here is my Script:

public GameObject otherPortal;
    public PortalController otherPortalScript;
    private BallController ballController;
    public float waitTime = 0.5f;

    [HideInInspector]
    public bool teleporting;

    // Use this for initialization
    void Start () 
    {

    }

    // Update is called once per frame
    void Update () 
    {

    }

    void OnTriggerEnter2D(Collider2D other)
    {
        if (other.gameObject.tag == "Ball")
        {
            ballController = other.GetComponent<BallController>();
            if(!teleporting)
            {
                var offset = other.transform.position - transform.position;
                offset.x = 0;
                other.transform.position = otherPortal.transform.position + offset;
                otherPortalScript.teleporting = true;
                teleporting = true;
                StartCoroutine("Teleport");
            }            
        }
    }

    void OnTriggerExit2D(Collider2D other)
    {
        if (other.gameObject.tag == "Ball")
        {
            teleporting = false;
            otherPortalScript.teleporting = false;
        }

    }

    IEnumerator Teleport()
    {
        yield return new WaitForSeconds(waitTime);
        teleporting = false;
        otherPortalScript.teleporting = false;
        ballController.teleporting = false;
    }
}

The script is attached to both portals, which are both prefabs of the same object. I set both "otherPortal", "otherPortalScript", & "waitTime" in the editor. "waitTime is something I had" to add after the fact to fix another issue I was having where sometimes "teleporting" never got set to false, I believe the the cause of the that problem is the same cause of this problem, making "waitTime" just a bandage for a larger issue. Also, anytime the Portal Script changes a variable in "ballController" such as "ballController.teleporting = false;", it's only there because the ball is add/removing points from a score system, it doesn't at all affect the ball's movement.

1
Have a look at this coding adventures video, he goes through all the bugs he came accross while making portals work. I'm pretty sure he comes accross similar if not the same issues to yourself. - GTBebbo
Because of floating point error, it can help to think of the portal as having thickness, and realize that touching the portal is a distinct state from being fully on either side of it. - Leo Bartkus
@GTBebbo I could be wrong, but after watching the video it appears to only explain the struggles of a visual portal illusion, my portals are 2D and do not need that illusion because they player doesn't need to have a camera looking through the other portal. Did I miss something? - Jhon Piper
@LeoBartkus That is a good point, how would you go about countering it? - Jhon Piper
@JhonPiper If I remember correctly, he does expand the thickness of the portal to deal with the not always teleporting issue as recommended by LeoBartkus. But sorry, I missread the orginal post, didn't realise it was only 2D. - GTBebbo

1 Answers

1
votes

Consider getting rid of the teleporting property of the portals and the ball as well as the waitTime.

Now give the ball a List<PortalController> inUseControllers (note you need to add using System.Collections.Generic). Whenever it collision-enters one portal, check if the list is empty via inUseControllers.Count == 0, and if so, add both involved PortalController's to that list and handle the teleporting movement. When the ball collision-exits a PortalController, remove it from the inUseControllers list; it will thus only be emptied again once the ball leaves every portal zone.

This approach should simplify the code, yet safely protect against accidental back-and-forth circles.