How to remove deleted scenes from build settings - unity3d

After adding scenes to build settings then deleting them, the deleted scenes are still showing up. Problem is its affecting scene count. They all show up as a valid scene even though it says deleted. Highlighting them all and selecting remove selections does nothing.
EditorBuildSettingsScene[] scenes = EditorBuildSettings.scenes;
Debug.Log(scenes.Length);
This log statement returns ALL scenes even the deleted ones.

You could filter out all EditorBuildSettingsScene which have a File.Exists(scenePath) = false like
using System.IO;
using System.Linq;
using UnityEditor;
public static class EditorBuildSettingsTools
{
[MenuItem("CustomBuildTools/RemoveDeletedScenes")]
public static void CleanUpDeletedScenes()
{
var currentScenes = EditorBuildSettings.scenes;
var filteredScenes = currentScenes.Where(ebss => File.Exists(ebss.path)).ToArray();
EditorBuildSettings.scenes = filteredScenes;
}
}
Place this into a folder named Editor so it is excluded from a build e.g. like
Assets/Editor/EditorBuildSettingsTools.cs

The solution of derhug can not seems working. For example, when you delete a folder via script that contain scene and call the method CleanUpDeletedScenes the file scene exist in same frame because it is in cache. I advice of use the method AssetDatabase.LoadAssetAtPath to check if exist scene file:
public static void CleanUpDeletedScenes() {
currentScenes = EditorBuildSettings.scenes;
var filteredScenes = currentScenes.Where(ebss => AssetDatabase.LoadAssetAtPath(ebss.path, typeof(SceneAsset)) != null).ToArray();
}

Related

I'm getting an UnassignedReferenceException when the reference is set. I've seen other questions similar but not exactly the same

As the title says i'm getting the UnassignedReferenceException error for a variable already set. Using ScriptableObjects im working on an inventory system(partially from scratch) and im trying to access the EquipmentUi in another class using a GameObject to hold the prefab containing the Character script. There is no issue with this working to access the Character script as shown in the picture because i can access the name. However, when i try and access the EquipmentUI of that character it gives the error. This isnt the last part i need access to but i have figured out that the UI is the part i cant access, i need the script held in it(which has worked before in another class).
The variable is already assigned, there is no other object in my scene with the same script attached, and the code can access other parts of the script i want access to which is why i made a new post after seeing the other posts and not seeing one that had quite the same issue.
using System.Collections;
using System.Collections.Generic;
using UnitEngine;
[CreateAssetMenu(fileName = "New Character Equipment", menuName = "Inventory/Character/CharacterEquipment)]
public class CharacterEquipmentObject : ScriptableObject
{
[SerializeField]
protected Player user;
[SerializeField]
private GameObject equipmentUser; // Only used to get the user because SOs cant getObject<>
[SerializeField]
public EquipmentObject[] equipment = new EquipmentObject[8];
[SerializeField]
EquipmentDisplay equipmentUI;
//private string[] SlotList = new string[8]{"Helmet", "Shoulders", "Chest", "MainHand", "OffHand", "Ring", "Legs", "Feet"}; may need later
public void start() // must be called in display
{
user = equipmentUser.GetComponent<Player>(); // set the user to be used in other classes\
Debug.Log("user in CEO: " + user.characterName); // this displays fine
Debug.Log("user " + user.characterName + "'s equipment: " + user.EquipmentUI.name); // This is what the editor says is empty when the slot has it assigned
equipmentUI = user.EquipmentUI.GetComponentInChildren<EquipmentDisplay>();
Debug.Log("equipmentUI: " + equipmentUI.name);
}
Thanks for any help in advance.
Player class:
public class Player : Character
{
[SerializeField]
protected Character[] companions = new Character[3];
[SerializeField]
GameObject InventoryUI;
public GameObject EquipmentUI;
bool isSelling = false;
public string characterName;
protected override void Start()
{
InventoryUI.SetActive(false);
EquipmentUI.SetActive(false);
Debug.Log("Player Equipment UI: " + EquipmentUI.GetComponentInChildren<EquipmentDisplay>().name);
base.Start();
}
private void OnTriggerEnter(Collider other)
{
var item = other.GetComponent<InteractItem>();
if(item)
{
characterInventory.addItem(item._item, 1);
useItem(item._item);
}
Destroy(other.gameObject);
}
private void Update()
{
if(Input.GetKeyDown(KeyCode.I))
{
InventoryUI.SetActive(!InventoryUI.activeSelf);
}
if(Input.GetKeyDown(KeyCode.C))
{
EquipmentUI.SetActive(!EquipmentUI.activeSelf);
if(EquipmentUI.activeSelf == true)
{
EquipmentUI.GetComponentInChildren<EquipmentDisplay>().updateEquipmentSlots();
}
}
}
Thanks for all the help on here but i figured out it is a ScriptableObjects issue as i went in and changed the way it is run, even putting the function to edit the users InventoryObject into the player itself it still gave the same issue.
In the player function Update() that does not have the InventoryObject(ScriptableObject) the update runs fine but as soon as the player function EmptySlot() is called from the InventoryObject it sends the same error.
Not entirely sure why this is but SOs are unable to access the GameObject that holds the script for the inventory display in them whatsoever. If anyone has a reason as to why this is I would love to know, but i am going to move on from this particular part and change how it works.
You might be experiencing a bug I have been facing where the element variable info does not update to show variable changes.
To force it to update, cause an error in your code. Switch to unity, when it shows you the error go back and fix it, and see if the variable value changes then.

Multi-scene launching of build, as is, from Unity, how?

Multiscene editing in Unity, blessed be it, permits the launching (via Editor Play mode) of the current scenes, in their current hierarchical state.
However, building and running the project doesn't recognise the current scene setup in the editor, and starts with whatever is set in the Build Settings.
Is there some way to make builds aware of the current editor state of Multi-scene editing hierarchy, and build and run that setup?
1. Getting the editor scenes into the build settings
First for collecting the settings you can use an editor script using
EditorSceneManager.GetSceneManagerSetup to receive the current setup of scenes in the editor
I assume you want only loaded Scenes so make a list of only scenes with isLoaded = true
EditorBuildSettings.scenes to add those scenes to the build settings
1.a. Update on MenuItem click
I made it an extra button in the menu since you might not want to have it always automatically.
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.SceneManagement;
public static class UpdateBuildSettigns
{
[MenuItem("Example/UpdateBuildSettings")]
public static void UpdateSettings()
{
// get current editor setup
SceneSetup[] editorScenes = EditorSceneManager.GetSceneManagerSetup();
// filter list e.g. get only scenes with isActive true
var activeEditorScenes = editorScenes.Where(scene => scene.isLoaded);
// set those scenes as the buildsettings
List<EditorBuildSettingsScene> editorBuildSettingsScenes = new List<EditorBuildSettingsScene>();
foreach (var sceneAsset in activeEditorScenes)
{
string scenePath = sceneAsset.path;
// ignore unsaved scenes
if (!string.IsNullOrEmpty(scenePath)) continue;
editorBuildSettingsScenes.Add(new EditorBuildSettingsScene(scenePath, true));
}
// Set the Build Settings window Scene list
EditorBuildSettings.scenes = editorBuildSettingsScenes.ToArray();
}
}
Updating on menu button
1.b. Update automaticly on (un)loading scenes
If you want it happening automatically you could also add the call as callback to EditorSceneManager.sceneOpened and EditorSceneManager.sceneClosed using InitializeOnLoad and a static constructor to get the callbacks added after recompile or opening the UnityEditor like
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
[InitializeOnLoad]
public static class UpdateBuildSettigns
{
// ofcourse you still can also call it via menu item
[MenuItem("Example/UpdateBuildSettings")]
public static void UpdateSettings()
{
//...
}
static UpdateBuildSettigns()
{
// it is always save to remove callbacks even if they are not there
// makes sure they are always only added once
//
// this is a static constructor so actually there should be no
// callbacks yet ... but .. you never know ;)
EditorSceneManager.sceneOpened -= OnSceneLoaded;
EditorSceneManager.sceneClosed -= OnSceneUnloaded;
EditorSceneManager.sceneOpened += OnSceneLoaded;
EditorSceneManager.sceneClosed += OnSceneUnloaded;
}
private static void OnSceneUnloaded(Scene current)
{
UpdateSettings();
}
private static void OnSceneLoaded(Scene current, OpenSceneMode mode)
{
UpdateSettings();
}
}
Using automatic update
1.c. Enable/Disable automatic updates
If you want more control you can also add extra menu entries for enabling and disabling the automatic updates like
// flag to check if auto-updates are currently enabled
private static bool isEnabled;
// disable the "EnableAutoUpdate" button if already enabled
[MenuItem("Example/EnableAutoUpdate", true)]
private static bool CanEnable()
{
return !isEnabled;
}
// disable the "DisableAutoUpdate" button if already disabled
[MenuItem("Example/DisableAutoUpdate", true)]
private static bool CanDisable()
{
return isEnabled;
}
// add callbacks
[MenuItem("Example/EnableAutoUpdate")]
private static void EnableAutoUpdate()
{
// it is always save to remove callbacks even if they are not there
// makes sure they are always only added once
EditorSceneManager.sceneOpened -= OnSceneLoaded;
EditorSceneManager.sceneClosed -= OnSceneUnloaded;
EditorSceneManager.sceneOpened += OnSceneLoaded;
EditorSceneManager.sceneClosed += OnSceneUnloaded;
isEnabled = true;
}
// remove callbacks
[MenuItem("Example/DisableAutoUpdate")]
private static void DisableAutoUpdate()
{
EditorSceneManager.sceneOpened -= OnSceneLoaded;
EditorSceneManager.sceneClosed -= OnSceneUnloaded;
isEnabled = false;
}
Note since this uses the UnityEditor namespace you should either place this script in an Editor folder or use proper pre-processors like
#if UNITY_EDITOR
// above code here
#endif
2. Loading all scenes from the build settings
Than later when running the app in the first scene there should be a script responsible for loading all those scenes. Something like e.g.
// making it a component to make sure it is inside of one scene
public class SceneLoader : MonoBehaviour
{
private void Start()
{
var thisScene = SceneManager.GetActiveScene();
// load all scenes
for(int i = 0; i < SceneManager.sceneCountInBuildSettings; i++)
{
// skip if is current scene since we don't want it twice
if(thisScene.buildIndex == i) continue;
// Skip if scene is already loaded
if(SceneManager.GetSceneByBuildIndex(i).IsValid()) continue;
SceneManager.LoadScene(i, LoadSceneMode.Additive);
// or depending on your usecase
SceneManager.LoadSceneAsync(i, LoadSceneMode.Additive);
}
}
}
refs:
SceneManager.sceneCountInBuildSettings
Scene.buildIndex
SceneManager.GetSceneByBuildIndex
SceneManager.LoadScene
SceneManager.LoadSceneAsync
What I would do is attach some sort of a script to the launching scene in unity that would then trigger the loading of the rest of the required scenes after the game has started. That would require some fiddling to start properly (e.g detect the fact that the scenes are not already loaded before trying to load them).
I might extend the answer with a code snippet to achieve the result if you need it.
For now you could take a look at the docs here:
https://docs.unity3d.com/ScriptReference/SceneManagement.SceneManager.GetSceneByName.html https://docs.unity3d.com/ScriptReference/SceneManagement.SceneManager.LoadSceneAsync.html
The basic idea would be:
Get the necessary scenes using SceneManager.GetSceneByName and
filter out all the scenes that are already loaded.
For the scenes that are not loaded yet, call LoadSceneAsync and
attach some sort of
the coroutine to check the loading progress.
When all of the scenes are loaded, run a callback so that rest of
the game knows that the scenes are loaded and it is good to run the
rest of necessary actions which rely on those scenes being loaded.
If you want to preserve the current hierarchy (a set of scenes that are opened in the editor) when building, then it might be achievable with BuildPipeline:
https://docs.unity3d.com/Manual/BuildPlayerPipeline.html
There is a way to make a build with a programmatically-accessible list of scenes:
// Get filename.
string path = EditorUtility.SaveFolderPanel("Choose Location of Built Game", "", "");
string[] levels = new string[] {"Assets/Scene1.unity", "Assets/Scene2.unity"}; // You'd have to assemble this list yourself.
// Build player.
BuildPipeline.BuildPlayer(levels, path + "/BuiltGame.exe", BuildTarget.StandaloneWindows, BuildOptions.None);
(which you can determine based on the currently loaded scenes when running your build). This wouldn't be a standard (Cmd + b) build though, but pretty close.

UNITY An object reference is required to access non-static member `Outline.OutlineMode'

I'm trying to create an outline when you are near it, but i'm getting all the time the same error.
void Update () {
if (Input.GetKeyDown(KeyCode.E)){
var outline = gameObject.AddComponent<Outline>();
outline.OutlineMode = Outline.Mode.OutlineAll;
outline.OutlineColor = Color.yellow;
outline.OutlineWidth = 5f;
}
}
void OnTriggerStay(Collider other) {
if (Outline.OutlineMode == Outline.Mode.OutlineAll) {
Debug.Log("test");
}
}
If i press E it works, and if i change it to ontriggerstay works too, but im trying that it only applies one time, because im getting some errors if its on. I have to say that im using an asset, called quick outline
Srry for my very bad english and explanation and thank you
add the outline to your object in Awake() then set it to disabled.
then enable it in OnTriggerEnter() and disable it in OnTriggerExit()
this will keep you from making multiple copies, and it will only be active when you are in range of your trigger

How to access gameobject present in another scene [duplicate]

This question already has answers here:
How to pass data (and references) between scenes in Unity
(6 answers)
Closed 4 years ago.
I am creating a simple number guessing game in unity3d.
I want to open a new scene on button click and change the text of a text element present in the loaded scene from the current scene.
I have been able to open new scene on button click but how can i access the text element in other scene so that i can change its text from the current scene.
This is what i have so far but it obviously throws NullReferenceException because i can't access the text element in another scene from current scene.
SceneManager.LoadScene("End Scene");
gameResultText.text = "You Won!!"; //<-------this line throws the exception
gameResultText.color = Color.green;
Better solution I came up with:
Make a script that sets a static string variable. This script must be in your Game scene and will hold the result.
public class ResultTextScript : MonoBehaviour
{
public static string ResultText;
void EndGame(){
if (won){ //if won game
ResultText = "You won!"
}
else //if lost game
{
ResultText = "You lost, try again another time!"
}
//Change scene with SceneManager.LoadScene("");
}
}
Put this script on your result text in the end scene. This script will retrieve the result and display it.
Using UnityEngine.UI;
public class EndGameDisplayResult{
Text text;
OnEnable(){
Text.text = ResultTextScript.ResultText
}
}
This way, it will set the text as soon as the new scene is loaded.
Previous/alternative method:
If you already have the scene open, one option would be to add a script to the "You won!" text which holds a static variable with a reference to itself.
So like this.
public class ResultTextScript : MonoBehaviour
{
public static ResultTextScript Instance;
void Awake(){
if (Instance == null)
Instance = this;
}
}
Then you can call the reference to that GameObject from anywhere in the other scripts, including between scenes.
Like this ResultTextScript.Instance
Note though that you cannot call the reference in the Awake method, as that is where the variable is initialized, you can use it after the Awake methods have been called though.
Basically
Add the ResultTextScript to your Text object in the 'End Scene'
Open the 'End Scene' from the 'Game Scene', for example with your SceneManager approach as you already do.
Ensure that the End Scene has loaded, then say in the script you wish to change the text gameObject go = ResultTextScript.Instance.gameObject
I do not believe there is a way to modify the context or objects of a scene that is not currently open.
public class GameResults {
public static string finalText = "";
}
In your function where you are loading the scene, right before you call load scene you can access that text like so:
GameResults.finalText = "You Win!";
or
GameResults.finalText = "You Lose!";
load your scene, and on your text object give it a script like this:
using UnityEngine;
using UnityEngine.UI;
public class ResultTextScript : MonoBehaviour
{
public Text textResults;
void Start() {
textResults = getComponent<Text>();
if(textResults != null) {
textResults.text = GameResults.finalText;
}
}
}
There are other things you can use as well is, storing the game results in PlayerPrefs and loading the string or int you stored in PlayerPrefs preferences at the start of your end scene. This will help you avoid creating an unnecessary class or static variable.
So Like before you can do:
PlayerPrefs.SetString("GameResults", "You Win!");
or
PlayerPrefs.SetString("GameResults", "You Lose!");
load your scene, and on your text object give it a script like this:
using UnityEngine;
using UnityEngine.UI;
public class ResultTextScript : MonoBehaviour
{
public Text textResults;
void Start() {
textResults = getComponent<Text>();
if(textResults != null) {
textResults.text = PlayerPrefs.GetString("GameResults", "");
}
}
}

Unity Editor Can't Edit Multiple Objects

I want to build a custom inspector for one of my classes... and well... I thought I would start simple ... and I still can't get it to draw the basic inspector:
My editor script is:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(AbilityBluePrint))]
[CanEditMultipleObjects]
public class AbilityBluePrintEditor : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
// Show default inspector property editor
DrawDefaultInspector();
}
}
And the class I want to edit is:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
[CreateAssetMenu(fileName = "New Ability BluePrint", menuName = "Ability BluePrint")]
public class AbilityBluePrint : ScriptableObject {
public AbilityName abilityName;
public Characteristic[] characteritics;
public Effect[] effects;
public float coolDown;
public Sprite icon;
public string description;
}
Any suggestions, on how to solve the "multi-object editing not supported" message I get instead of my beautiful custom editor ??
You need to use Serialized properties if like to use Multiple object edition.
[CustomEditor(typeof(AbilityBluePrint))]
[CanEditMultipleObjects]
public class AbilityBluePrintEditor : Editor
{
var AbilityName : SerializedProperty;
function OnEnable ()
{
// Setup the SerializedProperties
AbilityName = serializedObject.FindProperty ("Ability");
}
function OnInspectorGUI()
{
// Update the serializedProperty - always do this in the beginning of OnInspectorGUI.
serializedObject.Update ();
...
This is not explained in the documentation and I think it is quite important.
Apart from the option of using Serialized Properties, if you are not able to use those, in case you have your own items, not using the automatically managed Serialized Properties, then you have to use "targets" variable instead of "target".
I noted that if you select different types of objects with no shared script, it doesn't show shared properties, so we don't need to check if the targets are all the same type, it is always one or more. Then you do whatever you want to do by hand with each of them, inside a foreach loop.
Here's a working example of the contents of OnInspectorGUI method inside an Editor class with a simple checkbox that is changed across several scripts. Hope it helps.
var myScript = (UnityTerrainWrapper)target;
var allSelectedScripts = targets;
EditorGUI.BeginChangeCheck();
var value = GUILayout.Toggle(myScript.ShowNativeTerrain, "Draw Unity Terrain");
if (EditorGUI.EndChangeCheck())
{
foreach (var script in allSelectedScripts)
((UnityTerrainWrapper)script).ShowNativeTerrain = value;
SceneView.RepaintAll();
}
DrawDefaultInspector();