Unity - unable to list all scenes in a project - c#

In my app, I wanted to get a list of certain scenes and their build indices at start up of a particular scene. The probability is that none of the scenes has yet been loaded.
To do this I tried SceneManager.GetSceneByName() - I have the names of the relevant scenes. Nothing came back.
Looking at the docs it seemed this should work. However looking at the similar GetSceneByIndex() that does state it only works for loaded scenes, and I am assuming - thought it does not say so - the same applies to GetSceneByName().
So, I have not managed to find a way of listing all the available, but maybe not yet loaded, scenes. So far I have hard coded the relevant build indicies but I do not like that.
is there a way of doing this?

In general it makes sense that you can not get any reference to a Scene that is not loaded yet. Even using SceneManager.GetSceneByBuildIndex states
This method will return a valid Scene if a Scene has been added to the build settings at the given build index AND the Scene is loaded. If it has not been loaded yet the SceneManager cannot return a valid Scene.
What you can get however are at least all scene paths/names by using SceneUtility.GetScenePathByBuildIndex by iterating over SceneManager.sceneCountInBuildSettings
int sceneCount = SceneManager.sceneCountInBuildSettings;
string[] scenes = new string[sceneCount];
for( int i = 0; i < sceneCount; i++ )
{
scenes[i] = SceneUtility.GetScenePathByBuildIndex(i);
}
These would be the full scene paths. If you rather only want the scene name itself (but possible duplicates) you could probably instead use
scenes[i] = Path.GetFileNameWithoutExtension(SceneUtility.GetScenePathByBuildIndex(i));

Related

Optimizing GameObject unique ID assigner Unity

Recently I was working on the door system for my multiplayer game, and I had a problem with door ID (that specifies which door to interact with). I wanted to avoid assigning IDs manually, due to my game has a lot of doors, so I have found a solution - instead of unreliable FindObjectsOfType, I have build following scene looper:
void IterateThroughScene(Scene scene)
{
SortedList<string, NetworkedObject> sortedObjects = new SortedList<string, NetworkedObject>();
//List<CrossSceneAudioSyncListener> audioListeners = new List<CrossSceneAudioSyncListener>();
foreach (GameObject sceneRootObject in scene.GetRootGameObjects())
{
foreach (NetworkedObject netObj in sceneRootObject.GetComponentsInChildren<NetworkedObject>())
{
Vector3 position = netObj.transform.position;
string transformHash = position.x.ToString("0.00") + position.y.ToString("0.00") + position.z.ToString("0.00");
transformHash = Hashing.MD5(transformHash);
sortedObjects.Add(transformHash, netObj);
//CrossSceneAudioSyncListener audioListener = netObj.GetComponent<CrossSceneAudioSyncListener>();
//if(audioListener != null) audioListeners.Add(audioListener);
}
}
//CrossSceneAudioSyncInstance.LoadObjects(audioListeners);
Dictionary<ushort, NetworkedObject> preparedObjects = new Dictionary<ushort, NetworkedObject>();
ushort lastId = _ids[scene.name];
foreach (var part in sortedObjects)
{
NetworkedObject preparedObject = part.Value;
preparedObject.GetObjectData().id = lastId;
preparedObjects[lastId] = preparedObject;
lastId++;
}
_ids[scene.name] = lastId;
_registeredObjects[scene.name] = preparedObjects;
}
Here I list step-by-step what this method is doing:
SortedList is instantiated to hold objects
2 foreach's are getting all objects in the scene
here script generates MD5 of position and adds it to SortedList
After that, Dictionary instance is created to hold final data
Next foreach is done, to loop through SortedList and assign NetworkedObject IDs in a reliable way.
This was doing a great job in small/medium scenes, but I started having serious problems with big scenes - I have a good PC & itaration takes 12 seconds, and my game's target are medium PCs.
So, Do you have any idea how can I optimize this algorithm?
As I do not know the internals of FindObjectsOfType it is hard to compare the method to your snippet. You did however mention that order matters, and as FindObjectsOfType is not guaranteed to return objects in any particular order, it would not work for your use case.
To optimize the snippet you would need to prune your search in some way. As you are looking at all root gameObjects, you can mark each root object with the number of doors that it has as children, and keep a counter for each root. That way, once you reach the number needed sent from the children, it can stop searching that root object.
However, as it seems you would just like to assign ordered ids to objects in a scene for referencing, I would advise to use an editor tool to handle the assignment. The code would be near identical but with the advantage of not needing to care about overhead during runtime as it is computed outside of the game. When implementing any editor tool that alters your scene, make sure to mark the scene as dirty to assure Unity will save the changes.

Removing excess objects from a dynamically changing list of objects

I am currently working on a small project in Unity to test out some new features, primarly the new Input System and i have some trouble wrapping my head around lists. My goal with this project is to make a script that creates a small cursor for each player to use and move around with using their respective controllers.
Currently, i am creating and storing objects within a list during runtime whenever my computer detects a new Gamepad Controller. What i am having issues with is finding a method that removes excess objects that are not inside a list, i.e. when the list gets reduced in size which in this case is when a player disconnects their Gamepad Controller.
I have had similar issues in past projects whenever i had to add and remove items from a list while trying to destroy said items in the scene.
I have tried searching for such an answer on trying to achieve this effect on multiple websites (including this one) but with no prevail.
This is my current code that i am using at this moment to add players whenever a new device is added.
public GameObject cursorPrefab;
public int amountOfPlayers = 4;
List<GameObject> currentPlayers = new List<GameObject>();
private void DynamicADP(){
if(Gamepad.all.Count > amountOfPlayers) return;
for(int i = 0; i < Gamepad.all.Count; i++){
if(currentPlayers[i] != null) continue;
Cursor obj = Instantiate(cursorPrefab).GetComponent<Cursor>();
obj.PlayerDevice = Gamepad.all[i];
currentPlayer.Add(obj.gameObject);
}
}
Edit: While Kevins solution didnt quite solve the problem, it did however help me broaden my perspective on the matter which finally lead into a solution for what i desire with this piece of code! So i will accept Kevin's answer for it! Thanks Kevin!
Here is what i added, its very simple.
private void DynamicADP(){
//Previous logic from the code snipper above
if(currentPlayers.Count > Gamepad.all.Count){
Destroy(currentPlayers.Count[currentPlayers.Count - 1]);
currentPlayers.RemoveAt(currentPlayers.Count - 1);
//Whenever there is a difference in size on both lists, remove the last
//object from both in-scene and from currentPlayers list.
}
}
}
Reverse the loop to remove items from a list.
private void DynamicADP(){
if(Gamepad.all.Count > amountOfPlayers) return;
for(int i = Gamepad.all.Count-1; i >=0 ; i--){
if (somelogic) Gamepad.all.RemoveAt(i);
}
}
List is not a UnityEngine class, if you seek for List methods on unity, you'll never find. But in Microsft API documentation you can.
I never used list in Unity, but I think this way should work:
currentPlayers.RemoveAt(removed_controller_index);
See more of Lists on Microsft docs:
https://learn.microsoft.com/pt-br/dotnet/api/system.collections.generic.list-1?view=netframework-4.7.2

Scene not changing when the game is exported, even though it does in the engine

Using unity for a small video game, changing levels works in the engine completely fine, but when I export it to .exe it just doesn't work. The game itself works fine in .exe, only level changing doesn't work
public KeyCode nextLevelKey;
if(Input.GetKeyDown(nextLevelKey) && gameOver == true)
{
SceneManager.LoadScene(nextLevel.name);
Debug.Log("loaded next level");
}
Any ideas on why it doesn't work when exported?
I have included every scene in the right order when making the build so it's not that.
Ok so i took a look at unity answers and found this:
https://answers.unity.com/questions/139598/setting-variable-as-type-quotscenequot.html
and also this:
https://answers.unity.com/questions/205742/adding-scene-to-build.html
Editor Way:
What people have tried in 2nd link i guess is with custom editor scripts !That you can keep your Object nextLevel field also add string nextLevelName;
And then using OnValidate() or some other Editor event you can set nextLevelName value whenever nextLevel 's value changes and use SceneManager.Load(nextLevelName) instead.
Kinda Easier Way : Try converting that field to string it's a pain to update scene names twice (or maybe more) but fixes things anyways.
To Smooth things a bit you can try sth like this , though I'm not sure what you're trying to achieve with having nextLevel as variable but you can try:
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex +1)
Or you can have a Scenes class that provides a way of dealing with your nextLevel problem. Maybe with tokens? Maybe sth else.
public class Scenes
{
public static string[,] scenes = new string[,]{
{"first_level", "scene-1-name"},
{"level_2", "scene-2-name"},
{"level_3", "factory-level"},
{"level_4", "scene-4-name"},
{"level_5", "jungle"},
};
public static string GetSceneNameByToken(string token)
{
for (var i = 0; i < scenes.GetLength(0); i++)
if (scenes[i, 0] == token)
return scenes[i, 1];
return null;
}
}
I Personally like the 2nd approach cos first one is still kinda unreliable imo (specially if you were to change unity version in middle of project or for next projects) maybe that's cos I've not worked with Editor Events much idk.
Ok, so I figured it out. Instead of making the nextLevel variable an Object I made it a string, and then in the object (that the code for changing levels is attached to) I entered the level name.

C# / Unity - Need help debugging my Chess program - trouble creating instance of class within another instance of the same class

I'm creating a Chess program. I've decided to make it so that, when a piece is selected, all of its possible legal moves are displayed. In order for that to work, I have to have a mechanism to predict the outcomes of moves (because you can't make a legal move which puts your own king in check, for example). This will also be useful when I start working on computer opponents, since the AI will need to be able to see several turns in advance in order to make intelligent decisions.
To this end, I've made it so that, at the start of each turn, all possible moves are calculated for the player's pieces, and then each of those possible moves is simulated to see whether or not it leaves the King in check. If so, that option is removed. I have a main class called BoardManager which controls the visuals and contains the actual chess board layout (in a ChessBoardSim), and I have a class ChessBoardSim, whose objects each contain one possible board state. ChessBoardSim can create other instances of ChessBoardSim, allowing the state of the board any number of turns in advance to be simulated.
I have a bug which I've been struggling with for over 5 hours, which appeared after I tried to restructure a lot of the code. I'm at my wit's end, and I just need a fresh pair of eyes. The issue is making it so that pieces aren't being removed from their old positions when moved, and the locations of some of the pieces on the board seem to be shifted two tiles up. I believe, based on the debugging, that the problem is appearing in CalculateAllMovementOptions() in the ChessBoardSim class - for some reason, when a ChessBoardSim has a ChessBoardSim child and the CalculateAllMovementOptions() function is called in the child, it changes the data in the parent.
I've included the full code below, and I'll describe the specific issue as best I can.
The flow of the problem is:
1: private void Start() in BoardManager is called.
2: The program draws and populates the board properly. A ChessBoardSim (called ChessPieces) is created to contain the data of the current board state.
3: StartTurn() is called in BoardManager, in order to begin the game.
4: StartTurn() calls ChessPieces.CalculateAllMovementOptions(). The intended function of CalculateAllMovementOptions() is to get back an array of Lists. Each List in the array contains the legal moves of one of the player's pieces. However, this actually seems to be changing the actual board data, and I can't work out why.
When a player tries to move a piece, the game breaks because the pieces aren't where they're supposed to be and the software can't cope with it. The piece which is moved doesn't remove itself from its old location (despite my best efforts), and this causes there to be two instances of that object, leading to an indexoutofrange exception when a piece of the code tries to access the 17th element of an array with 16 elements.
I would really appreciate it if anyone could help me, I feel like I've wasted an entire day on this already and I'm sure I'm missing something simple.
The link to my code is https://github.com/FC123321/Chess
Array.Clone doesn't create a deep copy of an array, so when you call boardLayout.Clone() in SimulateBoard (and again in the ChessBoardSim constructor), you are copying the references in boardLayout to the new array. This means that the pieces in the new array are the same object in the old one.
This means that when you're in MovePiece in SimulateBoard and you call piece.SetPosition and such on members of that copied array, you're setting the position of the pieces in the source of the copy as well.
Instead of using boardLayout.Clone(), you need to loop through the whole source array, and do newBoardLayout[x,y] = new ChessPiece(); and then copy over the values to the new ChessPiece. Alternatively, you could create a new ChessPiece constructor that takes another chess piece and copies the values there:
// ChessPiece copy constructor
public ChessPiece(ChessPiece other) {
this.Position = new int[2] { other.Position[0], other.Position[1]};
this.isWhite = other.isWhite;
this.movementType = other.movementType;
this.hasMoved = other.hasMoved;
this.turnDoubleMoved = other.turnDobleMoved;
}
// instead of boardLayout.Clone() in the ChessBoardSim constructor:
for (int x=0 ; x<8;x++){
for(int y=0; y<8;y++){
if (boardLayout[x,y] != null)
this.boardLayout[x,y] = new ChessPiece(boardLayout[x,y]);
}
}
// In SimulateBoard, take out the redundant Clone call
ChessBoardSim simBoard = new ChessBoardSim(boardLayout, turnNumber);

Unity 5 C# - How to Change Scenes and Import all Resources from Last Scene

I'm not sure how to switch scenes and bring all of my resources with me. I do understand that upon load of new scene, the previous scene gets destroyed on load. I've explored some with DontDestroyOnLoad() but had no luck with what I'm trying to do.
I tried to make my player controller a prefab and simply put him into the next scene; however, I returned a heck of a lot of errors because of the many scripts I have. Mostly stuff like checkpoint, HP bars, and even the weapon.
What I need to know is how to import everything into the next scene. How do I do this without having to recode or even re-make all of the stuff that I need?
You're looking for LoadSceneMode.Additive. This is the second parameter of the LoadScene method and will load the new scene into the current one.
If you need to import every object from the previous scene, then what's the point in creating a new one?
What you could do is saving the objects positions into a file and then loading that file back on the next scene or try (again) with DontDestroyOnLoad();
I recommend to check the documentation of Unity about this function.
If you want an individual object not to be destroyed on the scene change then on the void Awake() function of Unity make a DontDestroyOnLoad(this.gameObject);
Could you provide more information? I ask because it seems from your description that
DontDestoryOnLoad
should accomplish what you want but you say it does not. As long as the object holding the components whose states you want to save is persisted into the next scene using that method, then all of those components' states should be persisted as well. Please elaborate and we can possibly provide a better answer. As for how to use it to save the state of every game object:
GameObject[] allObjects = UnityEngine.Object.FindObjectsOfType<GameObject>();
foreach(GameObject go in allObjects) {
if (go.activeInHierarchy) { /* and any other logic you want. Maybe like !isTerrain */
Object.DontDestroyOnLoad(go);
}
}
For the above code, I ripped it from https://answers.unity.com/questions/329395/how-to-get-all-gameobjects-in-scene.html

Categories

Resources