Portals: Infinite Loop Problem

Hey all,

I’ve been thinking about adding some simple portals to my game. And by “simple”, I mean the Super Meat Boy kind where an object that touches portal A is immediately and entirely moved to portal B-- as opposed to what I’d call “scary complicated wizardry” portals a seen in Portal where objects can exist half-way through each portal.

Anyways, the problem is that if an object touches portal A and is immediately moved to portal B, portal B will send it right back to A until Unity crashes. I haven’t done it, but I expect that would be the case. Also, I’ve read a couple of Unity Answers where people have tried to do this, encountered these results, and then received advice that wouldn’t work for me (like making the portals one-way, or offsetting the exit).

My first thought was that objects that can be teleported need to have a common interface so that they can be marked as “canTeleport = false” when entering and “canTeleport = true” when exiting (via OnTrigerExit2D). But that seems a little too hacky. I’m not completely against those kinds of solutions, but I’d like to find something better this time, if possible.

So the second idea was to give each portal a “canTeleport” property. When an object touches portal A, portal A would first do portalB.canTeleport = false, and then move the player. Portal B would be set to “canTeleport = true” again when the object triggers OnTriggerExit2D. There would need to be a few more things to handle to make sure that the object exiting portal B is the same one that was moved there from portal A, and also probably preventing portal A from teleporting anything else because if a second object exited portal B, the first object would be kicked back to A again. This is getting a little confusing. I hope I haven’t lost you. This solution seems like it could work, but it would be kind of limiting in that portals could be effectively closed as long as an object is in the way. I don’t really want that.

My final solution (yikes) was to make each portal system keep a list of objects that have passed through. OnTriggerEnter2D checks to see if the object is on the list. If it is, don’t teleport it. If it isn’t, add it to the list, then teleport it. OnTriggerExit2D finds the exiting object on the list and removes it. It would be possible for teleporting objects to be destroyed before they exit a portal, leaving a null pointer on the list. I was thinking that every x teleports, the teleporter system checks for nulls and removes them from the list.

What do you think? Particularly of my last idea there? I know it’s hard to say whether something is too expensive without knowing what my game is, but for all I know it’s “obviously” not a good idea. And, of course, if there are better ideas, I’m all ears.

Thanks for reading.

  • Rob
1 Like

Its a bit fiddly. You could detect when they enter the trigger, then you warp them, but then upon doing so it will probably register another ‘enter’ event on the second portal. … you will probably have to script it yourself so that the two portals ‘speak to each other’ ie set a flag saying like ‘just came from the other portal’ and if so, ignore the enter event. Once they EXIT, then you clear the flag which allows the portal to function.

1 Like

Like nearly everything else in game dev this is just a matter of managing state.

If applied to the portals themselves you have states like Waiting/Ready (to do something), Sending (something teleported from here) and Receiving (something teleported to here).

And really if you don’t want some kind of teleport fx on the sending portal then you can further reduce down by eliminating the Sending state and only having Waiting or Ready state and Receiving state.

When player (or whatever) touches a portal… if portal is in Ready state the player is transported and the other Portal goes into Receiving state. This Receiving state is just a bit of code… can show an animation if you like… and in any case has a timer on it and the state simply counts down the timer. This little delay gives the player time to move away from the portal. When timer expires state changes back to Ready. If player is still touching portal at that time sure they beam back to other one.

You can also just put a flag on the object itself… the player… but then you are tracking the same basic thing in multiple places (assuming players and enemies can all portal).

If you want to ensure player cannot stay still and beam back and forth repeatedly just make it so when they are beamed over to other portal they are pushed away from it enough to not trigger portal again unless they move.

Thanks for the input, guys. I found a simple solution that seems to work. I’ve only played with it a little bit, but so far the only concern is the uber high speed that objects reach when falling infinitely when the two portals are positioned above one another. What do you think?

using UnityEngine;
using System.Collections;

public class PortalScript : MonoBehaviour
{
    public PortalScript connectedPortal;
    public bool receiving = false;
    private Vector3 exitPosition;
    public GameObject teleportedObject;

    // Use this for initialization
    void Start ()
    {
        exitPosition = connectedPortal.transform.position;
    }

    void OnTriggerEnter2D(Collider2D col)
    {
        if (!receiving)
        {
            if (col.GetComponent<PlayerController> ())
            {
                PlayerController p = col.GetComponent<PlayerController> ();

                if (p.IsAlive())
                {
                    connectedPortal.receiving = true;
                    connectedPortal.teleportedObject = col.gameObject;
                    col.transform.position = exitPosition;
                }
            }
            else if (col.GetComponent<BombScript> ())
            {
                if (!col.GetComponent<Rigidbody2D> ().isKinematic)
                {
                    connectedPortal.receiving = true;
                    connectedPortal.teleportedObject = col.gameObject;
                    col.transform.position = exitPosition;
                }
            }
               
        }
        else
        {
            if (teleportedObject && teleportedObject.GetInstanceID() == col.gameObject.GetInstanceID())
                receiving = false;

        }
    }
}

I’ve been running into a similar problem, that’s while completely different has solution that might help. Have a “Taking an Action” style flag. So on your on trigger event, use a simple ‘if (!currentlyTeleporting)’. Then for your teleport, when the character enters the teleport, you set the action flag to true. Lastly you have an event that turns the flag off, this could be another trigger or just a timer.

I use a singleton DontDestroyOnLoad GameManager object instance to handle these global flags.

I know I am commenting several years after this was posted, but this thread was a huge help OP. I implemented the second solution that you had discussed in your first post and it is working perfectly for what I need. Portals are awesome, thank you!

1 Like

Please note that you can simply click the like button so that you’re not necroing the thread for everyone.

Thanks.

2 Likes