3
\$\begingroup\$

I'm trying to create a two-way teleport system in which players can move into a teleport gameobject, and is then teleported to another linked teleport gameobject.

How I was planning to have it work was very simple. On the teleport gameobject's script, it would have a public property to the other teleport's transform in which to teleport the player to. In OnTriggerEnter, I simply set the entering's collider position to that of the linked teleport.

void OnTriggerEnter(Collider other) {
    other.transform.position = DestinationPoint.position;
}

However, when the player's position is changed, it triggers the OnTriggerEnter of the destination teleport, and thus the player gets stuck bouncing back and forth between the two points.

What's a good way to prevent this? Some less than ideal solutions I came up with is to offset the position that player teleports too slightly so that he doesn't cause OnTriggerEnter to be called at the destination portal, but I'd like to avoid that.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Not sure about you, but I'd want more details in the OnTriggerEnter event such as where the player came from. If they came from a teleporter to trigger the event, simply skip firing off another teleport. \$\endgroup\$
    – Mythics
    Nov 28 '14 at 15:40
  • \$\begingroup\$ What's less-than-ideal about offseting his position? \$\endgroup\$
    – ashes999
    Nov 28 '14 at 20:55
  • 1
    \$\begingroup\$ @ashes999 What if a portal is placed near a wall or any other collision layer? Could you work around that? Possibly, but can get ugly quickly once you start considering all the edge cases you need to cover. \$\endgroup\$
    – Cooper
    Dec 14 '14 at 15:07
  • \$\begingroup\$ If you make the player "immune" from teleporting while inside a teleporter pad and for a short time after OnTriggerExit, this would be a non-issue. This solution has a bonus that it reduces the chance of accidentally teleporting back when exiting a teleporter pad at an oblique angle. Edit: I see now this is basically the same solution you came up with \$\endgroup\$
    – bcrist
    Dec 14 '14 at 15:46
3
\$\begingroup\$

How I ended up solving this is by delegating the act of teleporting to a TeleportEntityCommand. When an entity enters a teleporter in the scene, it fires an event to the command notifying it of a teleport request, sending it the gameobject that wishes to teleport.

TeleportEntityCommand maintains a List<GameObject> of objects being teleported. When a request comes in, the gameobject is added to the list, is teleported, and then after a short cool down time, is removed from the List. While a gameobject is in the List, any requests to teleport from any of the other teleporters for this gameobject will be ignored briefly.

The abbreviated solution is as follows.

public class TeleporterView : MonoBehaviour {
    // Assume this dispatches to TeleportEntityCommand.Execute()
    private Signal TeleportEntitySignal;

    public Transform Destination;

    void OnTriggerEnter(Collider other) {
        TeleportEntitySignal.Dispatch(other.gameObject, Destination.position);
    }
}

public class TeleportEntityCommand : Command {
    private List<GameObject> _gosToTeleport = new List<GameObject>();        

    [Inject]
    public GameObject GameObject { get; set; }

    [Inject]
    public Vector3 Destination { get; set; }

    public override void Execute() {
        if (!_gosToTeleport.Contains(GameObject)) {
            _gosToTeleport.Add(GameObject);
            RoutineRunner.StartCoroutine(TeleportUpdate());
        }

        private IEnumerator TeleportUpdate() {
            // Teleport the object, but then wait n seconds
            // to block teleporting the object again when it
            // hits the destination portal's collider.
            GameObject.transform.position = Destination;
            yield return new WaitForSeconds(1f);
            _gosToTeleport.Remove(GameObject);
        }
    }
}
\$\endgroup\$
0
\$\begingroup\$

Personally i would leave this feature in as it would be to funny to let go :). On a serious note.

coudl you not use an objects state? such as a player object give it HasTeleported property as a boolean.

So simply say

If(!hasTeleported)
{
    //beam me up scotty
    hasteleported = true;
}

then after so many seconds or whatever set HasTeleported back to false, basically a "debuff"

\$\endgroup\$
3
  • \$\begingroup\$ hasteleported could probably also be set to false, in the OnTriggerExit event. \$\endgroup\$ Nov 28 '14 at 20:56
  • 2
    \$\begingroup\$ I'd recommend storing this data on each teleporter pad instead of on the teleported object. That way you can easily teleport a variety of objects if you like, not just players. In this case each teleporter would keep a list of ignored objects. When one teleporter is told to receive a given object, it adds the object to its list. Once that object leaves its trigger volume, it removes the object from its list. This can handle multiple concurrent uses of the same set of teleport pads (though if that's not needed, a simple cooldown on the pads suffices). \$\endgroup\$
    – DMGregory
    Nov 28 '14 at 21:10
  • \$\begingroup\$ @DMGregory this is exactly how I ended up implementing it. I'll post my answer soon. \$\endgroup\$
    – Cooper
    Nov 28 '14 at 21:15
0
\$\begingroup\$

The teleporters obviously need to have a reference to each other in order to send to each other. You could use that reference to temporarily deactivate the destination.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.