Multiple Door Spawns in One Scene, Unity 2D

Hello Unity Community,

Let me start with this: I know there are other answers out there, and I have been trying to use them for help. After two weeks of trying to use other solutions, I realize that I need a personal discussion for guided help because I am still confused with some logistics… For reference, I am using Unity 4.6 and I am scripting in C#.

Ok. So the problem here is that I am trying to create a 2D game that has scenes with multiple doors. The major map areas will have 4-8 doorways to smaller scenes such as rooms, and the rooms will have a door that returns the player to the major map areas. This concept breaks down into two major functions:

  1. Doors in scenes must contain some form of tagging system:
  • Each door should theoretically have a tag variable which can be referenced on a player script (such as an public int doorID#, or possibly a static int?).
  • Each door can set the reference on the player so that the player carries the variable to the new scene, which would in turn be used to find the door in the new scene with a matching variable number. (i.e. public int doorID = 0; + an OnTriggerEnter() for the door object = setting a public int playerDoorID to 0, which will stay set when the new scene loads, since the player does not need to be destroyed).
  1. Doors in scenes must also contain some form of spawning system:
  • Each door object in a scene inherently has a transform.position location which can be targeted. This position will need to correspond with the player when the scene loads in such a way that the player can be loaded into different locations based on the variable ID# from the first function. Thus, if you entered the third door in a large scene, when returning to this large scene you will be loaded at the third door again.

//EDIT: I had more written here, but it seems that my computer did not load it properly when I submitted the post. The question I am trying to ask about this process is written in my first response down below…

Im not sure as to what the question is, but it seems like you are trying to store information from a previous scene so that when you return, you can resume back from where you left off. With this you can use something such as the playerprefs class.

If you only need to remember which door you entered so you can find it in another scene, than Im not sure as to what you need since you already explained how to do it :wink:
If you dont or cant use the built in unity tag system…
Create a class named DoorTag and place it on all doors. In this class you can have a public string variable that you can type in the inspector. The doors that corrospond to eachother get the same string name. You can get more advance and create a static list of doortags and choose your tag from that list.
When the player touches the door, assign the tag to the player. In the new scene, have a method somewhere on awake to search for the players current doortag and to teleport the player to the doors position. Maybe each door can have a child object called spawnPoint which can be set anywhere near the door for the player to teleport to.
When returning back to the oroginal scene, find the proper door again and teleport.
Since you basically already said this in your question, Im not really sure what it is you need.

Thank you for the response!
I will clarify:

The question that I have comes down to issues with the spawning mechanism in C# scripting. I understand the doorTagging very well, but my tags are set up as integer variables. When I load a new scene, I do not know how to make my script find the door transform location based on only the integer variable
(since doorTagID# = gameobject.transform.position creates an error due to many obvious reasons)

What would be most beneficial to me is if someone could assist in explaining the proper coding necessary to bridge the disconnect that I am having between having the player locate the correct doorID# during OnLevelWasLoaded() and the scripting necessary to actually spit the player out in that objects location.

In the most basic sense, can anyone describe a C# script that handles specifically the spawning aspect of this process when the only information that you have in the player’s OnLevelWasLoaded is the playerDoorID#???

P.S. I don’t know why this happened, but when I reread through my initial post I just discovered that the bottom 2 paragraphs apparently didn’t make it… thats why the question element is a little absent ha!

I think what you are looking for is FindObjectsOfType

Lets say you have this as your id class component that you put on your doors.
Click for code

using UnityEngine;

public class DoorId : MonoBehaviour
{
    public int id;
}

When your scene loads, you can use this to find the door you desire.
Click for code

using System;
using UnityEngine;
using System.Linq;

public static partial class ExtGameObject
{
    public static GameObject FindDoorWithID(int id)
    {
        return FindDoorsWithID(id)[0];
    }

    public static GameObject[] FindDoorsWithID(int id)
    {
        return GameObject.FindObjectsOfType<DoorId>().Where(x => x.id == id).Select(x => x.gameObject).ToArray();
    }
}

If you want to know what that linq query is doing, its basically something like this

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

public static partial class ExtGameObject
{
    public static GameObject[] FindDoorsWithID(int id)
    {
        List<GameObject> doors = new List<GameObject>();
        DoorId[] doorIds = GameObject.FindObjectsOfType<DoorId>();

        foreach(DoorId door in doorIds)
        {
            if(door.id == id) doors.Add(door.gameObject);
        }

        return doors.ToArray();
    }
}

You would use it like this to get all the doors with the id of 1
Click for code

GameObject[] doors = ExtGameObject.FindDoorsWithID(1);

You then choose which door you want if there were multiple, or if you are confident you will just want the first door in the returned gameobject array, you can do

GameObject door = ExtGameObject.FindDoorWithID(1);

After you have the door you need, you can decide where to teleport the player based on possibly a child object of the door acting only as the spawn point near the door.

The code wasn’t tested, but should work :wink:

I like the look of how this is set up, and this is similar to some things that I have tried. Finding the door number and getting that to be carried around on the player are things that I am beginning to understand fairly well.

The trouble I still have is the step after what your code covered in your previous comment. I don’t understand how to use the information gathered from finding the right door through an ID# int variable and have it connect me to the transform location. I also don’t completely understand how to utilize the numerical values of the game objects in the array once they are there… though I am not sure if this would be relevant.

Once I can connect those things all I think I need is another variable of some sort that triggers a method on the player to spawn at the connected transform location… But with spawning I am not even certain that the spawn scripting would be built onto the player… would the spawning method actually be placed within the Door script? Or does it make the most sense that the player has the spawning method (this seems the most logical).

In the end… where do you go from where you started to achieve the actual spawning at the correct locations???

Thank you for all of you help, and I am sorry to be a nuisance,

Alex

I guess it would be something like this.
Click for code

    void OnLevelWasLoaded(int level)
    {
        GameObject door = ExtGameObject.FindDoorWithID(doorId);

        //transform.position = door.gameObject.transform.position; //use this if you just want to go to the doors transform and ignore the bottom code.
        foreach(Transform child in door.transform)
        {
            if(child.tag == "DoorSpawnPoint")
            {
                transform.position = child.position;
            }
        }
    }

If you want to have a child object representing the spawn point of the door (so the player doesnt spawn right ontop of the door) then make sure to tag it something. In my code I have it tagged “DoorSpawnPoint” but you can do anything.
If you just want to go right ontop of the door, then just use the code I left in the comments.

All you would need to do is have a variable that holds the door Id while you go from scene to scene so this method could access it (in this case the variable name would be doorId).

Since you will most likely only have 2 doors with the same Id, and those 2 doors will not be in the same scene, meaning each scene will never have duplicate door Ids, we can just return the first door we find with the Id so we don’t have to worry about any arrays.

I am very intrigued by the conversation we have had thus far, thank you for speaking with me and trying to help.

So far, I have been playing around with a combination of some of the things we have discussed and some of what I have already created… I am having some strange results. Currently, I have it so that the doors are sending me to the right scenes, and they are spawning me in front of the door now instead of where the level spawn is… but the detection for the correct door seems to be off, and the player is getting created anew each time I load the scene, so if I go through a door there are two of me, and then if I go back, there are three of me, and so on hahah…

I don’t know what is going on presently, but I need to fiddle around with it a bit more and see if I can get it all sorted out the way that it should be working… If I can’t make it happen and I need some more assistance I will come back and post some updates on here in the next couple of days…

in the mean time, can you elaborate on what you mean about not using the array system? I don’t know why you wouldn’t still need the array if there won’t be doors of conflicting ID#'s?

There is the method FindDoorsWithId and FindDoorWithId
FindDoorsWIthId returns an array of doors with the id you provided. FindDoorWithId returns only 1 door with the id you provided, even if there were multiple doors with the id. It is find for us to just use the FindDoorWithId, ignoreing the array of doors and just returning 1 door, since you are most likely setting things up in a way so that there will only be 1 door with the id “3” (or something) in your scene. When you move to another scene, there can be a door with the id “3” as well, but its a separate scene.

For your debugging…
1 - Make sure you have no doors in the same scene with the same id, and make sure the doors you want to teleport to eachother have the same id.
2 - When you go to a scene and see multiple of your players, is at least one of them at the correct door? Are you at door “0”? You might be respawning your character through some method even though you are DontDestroyOnLoad your player. And since you are respawning a player, it might be making you see and control that player, and since that players doorId may be at a default of “0”, that is the door you teleport to.

Alright, it is finally my day off and now I am going to sit down and try and grind out fixing this issue once and for all. Before I begin fiddling with what I have, I want to show you where I am in this process and see if there is anything you may be able to point out.

To begin, there are 3 scripts: DoorManager, DoorPortal, & Player.

The Door Manager is a parent game object with the array coding you described earlier. To this end I still don’t understand quite yet how to avoid using the array as a part of this process, but here is what the script looks like:


The Door Portal Script functions as the location where spawning occurs, and as the position where the player will be tagged with new information to be carried to the new level:
2156489--142431--Screen Shot 2015-06-12 at 10.38.47 AM.png

Finally, we have the player, which for this description has been simplified to show only the parts relevant to the door stuff I have added to the player script:
2156489--142432--Screen Shot 2015-06-12 at 10.15.42 AM.png

It seems to me that things are so much closer to working than this was before I started this conversation… but there are definitely still some buggy aspects that I do not technically understand. A good example of bugginess is that: It seems as if having more than one door in a scene is throwing the scripts off, as in one door seems to work great, but 2 or 3 messes everything up.

Also I still have no idea about the multiple players thing, I am still working on that.

Is seeing any of this helpful to elaborating on where I am going wrong?

perhaps you can upload your scene here (just drag and drop it onto your reply) and I can take a look at it. Or you can send a private message with it in case you don’t want others seeing it.

Go to assets, export package so that its exported as a unity package.

If you don’t want to send your whole project than maybe create a sample scene of the problem and send that.

If it would be most helpful to solving the problem I can personally send you what I have so far… But if a game surfaces in the future with my assets I will seppuku just so you know :wink:

1 Like

Kk the problems were…

-In regards to the duplicating player…
You start the scene off with a player already inside, and you are doing dontdestroyonload.
Each time you enter a scene, you werent creating a new player, there was already a player there + the one you didnt destroyonload.
This can be fixed by using something known as the singleton.
Basically on start, we check if there is a player set in a static variable, and if not, we assign ourselves to that static variable.
If there was already a player in that variable, we then just destroy ourselves.

For example…
Click for code

    public static GameObject myPlayer;

    public void Awake()
    {
        CheckAndSavePlayerInstance();
    }

    void CheckAndSavePlayerInstance()
    {
        if(myPlayer == null)
        {
            myPlayer = gameObject;
        }else{
            Destroy(gameObject);
        }
    }

Put that in your player script and your problem should be fixed.

-In regards to the doors not going to the right place…
You had a static variable “DoorSpawnID” which at start you were assigning with whatever was in “DoorSpawnNum”.
Since all doors have this script, and a static variable is only 1 variable, each door was fighting for who puts in their value into that static variable.
To fix this, simply just dont use the static variable. Instead just do…
Click for code

    public void OnTriggerEnter2D(Collider2D other)
    {
        Player.newDoorSpawnID = DoorSpawnNum;
    }

Hope all is good now! :wink:

Ok, everything is so very close to being perfect on my end… but there is still one thing not coming together with the singleton solution. When I reload into a scene I have already been inside of, the singleton aspect of the script is always destroying one of the clones. This is destruction of the clone is great, but the problem is that my character controller is not taking hold onto the clone that survives the destruction.

When I start the level, enter the door and return back to the first level, this error is thrown:

MissingReferenceException: The object of type ‘Transform’ has been destroyed but you are still trying to access it. Your script should either check if it is null or you should not destroy the object.

Now I thought that with the singleton we were already checking if the object was null…

Is this a simple issue that is taking place with the singleton, or is this embedded deeper in one of my other scripts?

Maybe you set it up wrong (or your issue is with something else), since on my end I had everything working fine. I was able to go back and forth many many times. I could just hold down the up key and it would spam me back and forth lol.
Send over your script.

I remember disabling the cameras script because of that spazzing bug it gave me when you sent your files over. Is it the camera that is causing problems? I deleted your files so I cant check, but I can redownload if needed.

I guess an ez fix would to have all things that were referencing the player, to now just reference the static gameobject of the player. Not sure if that is a good way to do things though.

Well I think I typed everything in correctly… but maybe I am just blind, I wouldn’t put it past me…

Either way, I have attached the player script as it exists after adding the singleton stuff…

Anything that stands out?

2157024–142494–Player.cs (2.38 KB)

Turns out it was the camera script trying to reference the player. By having it reference the static player now, it all now works!

Hazah! Hazah for completion!

1 Like