Snippets

Alfish Referencing SceneAsset by GUID at runtime in Unity

You are viewing an old version of this snippet. View the current version.
Revised by Alfish f290a0b
// Public Domain. NO WARRANTIES. License: https://opensource.org/licenses/0BSD

using UnityEngine;
using UnityEditor;

[CustomPropertyDrawer(typeof(SceneReference))]
public class SceneReferenceDrawer : PropertyDrawer
{
  public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
    if (!property.NextVisible(true)) return;
    // could use 128-bit type to serialize, like rectIntValue; but it would be less legible in text assets
    var oldGuid = property.stringValue;
    var oldPath = AssetDatabase.GUIDToAssetPath(oldGuid);
    var oldObj = AssetDatabase.LoadAssetAtPath<SceneAsset>(oldPath);
    var newObj = EditorGUI.ObjectField(position, label, oldObj, typeof(SceneAsset), false) as SceneAsset;
    if (newObj == oldObj) return;
    if (newObj == null)
      property.stringValue = "";
    else if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(newObj, out string newGuid, out long _))
      property.stringValue = newGuid;
  }
}
// Public Domain. NO WARRANTIES. License: https://opensource.org/licenses/0BSD

using System;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEngine;

class SceneReferencesBuildProcessor : IPreprocessBuildWithReport, IPostprocessBuildWithReport
{
  const string resourcePath = "Assets/_autoBuild_SceneIndicesByGuid.asset";
  public int callbackOrder => 0;
  public void OnPreprocessBuild(BuildReport report) {
    var sceneReferences = ScriptableObject.CreateInstance<SceneReferences>();
    AssetDatabase.CreateAsset(sceneReferences, resourcePath);
    var preloaded = PlayerSettings.GetPreloadedAssets();
    var n = preloaded.Length;
    Array.Resize(ref preloaded, n + 1);
    preloaded[n] = sceneReferences;
    PlayerSettings.SetPreloadedAssets(preloaded);
  }
  public void OnPostprocessBuild(BuildReport report) {
    var preloaded = PlayerSettings.GetPreloadedAssets();
    var i = Array.FindLastIndex(preloaded, o => o is SceneReferences);
    if (i >= 0) {
      var n = preloaded.Length - 1;
      var newPreloaded = new UnityEngine.Object[n];
      Array.Copy(preloaded, newPreloaded, i);
      Array.Copy(preloaded, i + 1, newPreloaded, i, n - i);
      PlayerSettings.SetPreloadedAssets(newPreloaded);
    }
    AssetDatabase.DeleteAsset(resourcePath);
    AssetDatabase.SaveAssets();
  }
}
// Public Domain. NO WARRANTIES. License: https://opensource.org/licenses/0BSD

using System;
using System.Collections.Generic;
using UnityEngine;
using Guid = System.String; // could use 128-bit int type to serialize, but it would be less legible in text assets

///<summary>SceneReferences contains the GUID to build index mapping, used to reference scenes at runtime.</summary>
///<remarks>This is auto-generated on build as a preloaded asset.</remarks>
public class SceneReferences : ScriptableObject
{
  ///<summary>The singleton instance, automatically preloaded at runtime.</summary>
  public static SceneReferences instance => asset
#if UNITY_EDITOR
  ?? (asset = ScriptableObject.CreateInstance<SceneReferences>())
#endif
  ; static SceneReferences asset;
  ///<summary>The GUID for each scene asset, indexed by its build index in the array.</summary>
  public Guid[] guids => sceneGuids; [SerializeField] Guid[] sceneGuids;
  Dictionary<Guid, int> cache;
  private void Awake() {
#if UNITY_EDITOR
    sceneGuids = Array.ConvertAll(UnityEditor.EditorBuildSettings.scenes, s => s.guid.ToString());
#endif
    if (sceneGuids == null) sceneGuids = new Guid[0];
    int n = sceneGuids.Length;
    cache = new Dictionary<Guid, int>(n);
    for (int i = 0; i < n; i++)
      cache.Add(sceneGuids[i], i);
    asset = this;
  }
  ///<summary>The build index of a scene by GUID. It can be used to load it or to obtain scene info.</summary>
  ///<param name="sceneGuid">The GUID of the scene to look up.</param>
  ///<remarks>All mappings are cached on a dictionary when the asset is preloaded at startup.</remarks>
  public int SceneIndex(Guid sceneGuid) => cache.TryGetValue(sceneGuid, out int i) ? i : -1;
}

///<summary>SceneReference is used to reference a Scene by its guid at runtime.</summary>
[Serializable]
public struct SceneReference
{
  ///<summary>The GUID that uniquely identifies this scene asset, used to serialize scene references reliably.</summary>
  ///<remarks>Even if you move/rename the scene asset, GUID references stay valid.</remarks>
  public Guid guid => sceneGuid; [SerializeField] Guid sceneGuid;
  ///<summary>Create a reference to a scene using its GUID.</summary>
  ///<param name="guid">The GUID of the scene, found in its .scene.meta file, or obtained from AssetDatabase.</param>
  public SceneReference(Guid guid) => this.sceneGuid = guid;
  ///<summary>The build index of this scene, which can be used to load it or to obtain scene info.</summary>
#if UNITY_EDITOR
  public int sceneIndex => this.sceneGuid switch {
    var sceneGuid => Array.FindIndex(UnityEditor.EditorBuildSettings.scenes, s => s.guid.ToString() == sceneGuid)
  };
#else
  public int sceneIndex => SceneReferences.instance.SceneIndex(sceneGuid);
#endif
}
HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.