BASIC TELEPORTING QUEST: ‘Teleport Player Random’ - Solutions

Feel free to share your solutions, ideas and creations below. If you get stuck, you can find some ideas here for completing this challenge.

1 Like

I created a new array:

[SerializeField] Transform[] teleportTargets;

And I modified my TeleportPlayer method:

void TeleportPlayer()
{
    var randomIndex = Random.Range(0, teleportTargets.Length);
    var teleportTarget = teleportTargets[randomIndex];
    player.transform.position = teleportTarget.position;
}

I set the teleportTargets in the Inspector.

4 Likes

This was my solution below, I think less lines makes for slightly cleaner code.

[SerializeField] Transform[] teleporters;
    void TeleportPlayerRandom()
    {
        Transform teleporter = teleporters[Random.Range(0,teleporters.Length)];
        player.transform.position = teleporter.transform.position;
    }
1 Like

We can use fewer!

    void TeleportPlayer()
    {
        player.transform.position = teleportTargets[Random.Range(0, teleportTargets.Length)].position;
    }
1 Like

Doesn’t Array.Length return the total amount of values?
Couldn’t then targets[index] potentially return a NullPointerException or an ArrayIndexOutOfBoundsException?

Not sure if that is correct but here is my solution:

if (teleportTargets == null && teleportTargets.Length > 0) return;
TeleportPlayer(teleportTargets[Random.Range(0, teleportTargets.Length - 1)].position);

It depends if it is a design issue if it’s not been set up properly.
You could put out your own meaningful error or warning if so.
If it doesnt matter then great.

I believe you do have a bug in that code though…

I see the bug now @EddieRocks :sweat_smile: Weird how the my character still teleports to a random location :face_with_raised_eyebrow:

if (teleportTargets == null && teleportTargets.Length > 0) return;
// Should be
if (teleportTargets == null || !teleportTargets.Length > 0) return;

But say that we have an array of 5 targets, the teleportTargets.Length returns 5.
The array position is n-1 for the last element in the array which means that if Random.Range(0, teleportTargets.Length) returns a 5 then I assume we would get an IndexOutOfRangeException?

Whoops - Ok you had two bugs then as I hadn’t spotted that one!!

If you have a teleport with say 2 options I think you will find it never goes to the second/last one.

(Im assuming the code is still…

teleportTargets[Random.Range(0, teleportTargets.Length - 1)]

)
{Ints and floats work differently in Random.Range}

Double negative kinda things are hard to read so I’d swap this around || if = 0

I’ve tried explaining what I mean in code here: https://dotnetfiddle.net/jf13EL

Not sure what you mean by Ints and floats working differently in Random.Range, but when I try to access array[array.Length] I get an exception. Could be that Unity has some inbuilt error handling that automatically resolves issues such as these, I’ll have to try that out when I’m at home :sweat_smile:

I also agree that the !teleportTargets.Length > 0 smells a bit bad, will definitely update that to || teleportTargets.Length == 0. Thanks for the tip :smiley:

If you look up Unity’s Random.Range you’ll find that Floats are inclusive of the end range and Ints are not so you need to use teleportTargets.Length to make sure you get the last teleport.
Test each way with 2 teleports and one wont ever get to the 2nd one…

2 Likes
    void TeleportPlayerRandom()
    {
            player.transform.position = teleportTarget[Random.Range(0, teleportTarget.Length)].position;
    }
2 Likes

I am getting the list of possible locations each teleport, maybe this is too heavy on load ?

    private void TeleportPlayerRandom()
    {
        player.transform.position = GetListOfSpawnLocations();
        Debug.Log("Teleporting to "+ transform.position);
    }

The randomTeleportPosition is cached anyway, so i could use those in a seperate call, with the Random.Range, or leave it as is?

   private Vector3 GetListOfSpawnLocations()
    {
        foreach (var t in teleportPrefab)
        {
            teleportTargets.Add(t);
        }

        teleportIndex = Random.Range(0, teleportTargets.Count);
        return randomTeleportPosition = teleportTargets[teleportIndex].position;
    }
1 Like

This one was a bit of Struggle but I definitely needed this!!!

public class Teleport : MonoBehaviour
{
    [SerializeField] GameObject[] teleportions;
    [SerializeField] GameObject teleportSpot;
    [SerializeField] int randomTeleport;

    [SerializeField] Transform teleportTarget;
    [SerializeField] GameObject player;
    [SerializeField] Light areaLight;
    [SerializeField] Light mainWorldLight;

    [SerializeField] float worldlightBlinkTime = 3;
    [SerializeField] bool hasTriggered = false;

    void Start() 
    {
        // CHALLENGE TIP: Make sure all relevant lights are turned off until you need them on
        areaLight.enabled = false;
        mainWorldLight.enabled = false;
        // because, you know, that would look cool.
        StartCoroutine(BlinkWorldLight());
    }

    void OnTriggerEnter(Collider other) 
    {
        // Challenge 2: TeleportPlayer();
        TeleportPlayer(other);
        // Challenge 3: DeactivateObject();
        // Challenge 4: IlluminateArea();
        // Challenge 5: StartCoroutine ("BlinkWorldLight");
        // Challenge 6: TeleportPlayerRandom();
    }

    void TeleportPlayer(Collider other)
    {
        if(player || hasTriggered == false)
        {
            TeleportPlayerRandom();
            //player.transform.position = teleportTarget.position;
            IlluminateArea();
            StartCoroutine(BlinkWorldLight());
            DeactivateObject();
        }
 }
    void TeleportPlayerRandom()
    {
        randomTeleport = Random.Range(0,teleportions.Length);
        teleportSpot = teleportions[randomTeleport];
        player.transform.position = teleportSpot.transform.position; 
    }

I didn’t want to have to drag and drop each individual teleport target in the inspector, so I took a different route with this one.

[SerializeField] Transform teleportTargetParent;
Transform[] teleportTargets;

All the teleport targets are nested under an empty parent in the inspector. These are then iterated through in the Awake method to fill the private teleportTargets array:

void Awake() 
{
    int numTeleportTargets = teleportTargetParent.childCount;
    teleportTargets = new Transform[numTeleportTargets];
    for (int i = 0; i < numTeleportTargets; i++)
    {
        teleportTargets[i] = teleportTargetParent.GetChild(i).transform;
    }
}

Then the teleportPlayerRandom method takes one of the teleport targets from the array at random:

void TeleportPlayerRandom()
{
    if (teleportTargets.Length <= 0)
    {
        Debug.LogError("ERROR: No teleport targets set in inspector.");
        return;
    }
    int teleportIndex = Random.Range(0, teleportTargets.Length);
    player.gameObject.transform.position = teleportTargets[teleportIndex].position;
}

I’ve done it this way so that when you create new teleport targets, you only have to drag them under the appropriate object in the hierarchy for them to work (no need to drag and drop each target onto the teleport script as a SerializedField).

image

I know this code is longer, but I felt it makes the design process of placing these targets faster and easier.

I also chose to write my code like this:

int teleportIndex = Random.Range(0, teleportTargets.Length);
player.gameObject.transform.position = teleportTargets[teleportIndex].position;

rather than like this:

player.gameObject.transform.position = teleportTargets[Random.Range(0, teleportTargets.Length)].position;

because I think having a variable called teleportIndex makes the code easier to read, plus it avoids having that really long line of code.

2 Likes

Privacy & Terms