2024-09-30 00:17:16 +09:00
|
|
|
using System;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
using Object = UnityEngine.Object;
|
|
|
|
|
#if UNITY_EDITOR
|
2024-11-21 01:39:16 +09:00
|
|
|
using System.IO;
|
2024-09-30 00:17:16 +09:00
|
|
|
using UnityEditor;
|
|
|
|
|
using UnityEditor.Build;
|
|
|
|
|
using UnityEditor.Build.Reporting;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
namespace Coffee.UIParticleInternal
|
|
|
|
|
{
|
|
|
|
|
public abstract class PreloadedProjectSettings : ScriptableObject
|
|
|
|
|
#if UNITY_EDITOR
|
|
|
|
|
{
|
2026-06-23 19:04:02 +09:00
|
|
|
[Tooltip("When enabled, this settings asset will be added to PlayerSettings.preloadedAssets in build.\n\n" +
|
|
|
|
|
"When disable, you should load this settings via Resources, AssetBundles or Addressables to use.")]
|
|
|
|
|
[SerializeField]
|
|
|
|
|
[Header("Advanced")]
|
|
|
|
|
[HideInInspector]
|
|
|
|
|
private bool m_PreLoadSettingsInBuild = true;
|
|
|
|
|
|
|
|
|
|
protected static bool s_BuildingPlayer;
|
|
|
|
|
|
2026-06-24 18:57:55 +09:00
|
|
|
private class EditorEvents : AssetPostprocessor, IPreprocessBuildWithReport, IPostprocessBuildWithReport
|
2024-11-21 01:39:16 +09:00
|
|
|
{
|
2026-06-24 18:57:55 +09:00
|
|
|
int IOrderedCallback.callbackOrder => 0;
|
|
|
|
|
|
2024-11-21 01:39:16 +09:00
|
|
|
private static void OnPostprocessAllAssets(string[] _, string[] __, string[] ___, string[] ____)
|
|
|
|
|
{
|
|
|
|
|
Initialize();
|
|
|
|
|
}
|
2024-09-30 00:17:16 +09:00
|
|
|
|
|
|
|
|
void IPreprocessBuildWithReport.OnPreprocessBuild(BuildReport report)
|
|
|
|
|
{
|
2026-06-23 19:04:02 +09:00
|
|
|
AssetDatabase.Refresh();
|
|
|
|
|
Initialize();
|
|
|
|
|
s_BuildingPlayer = true;
|
|
|
|
|
|
|
|
|
|
foreach (var t in TypeCache.GetTypesDerivedFrom(typeof(PreloadedProjectSettings<>)))
|
|
|
|
|
{
|
|
|
|
|
var settings = GetDefaultSettings(t);
|
2026-06-24 18:57:55 +09:00
|
|
|
if (settings == null || settings.m_PreLoadSettingsInBuild) continue;
|
2026-06-23 19:04:02 +09:00
|
|
|
|
|
|
|
|
PlayerSettings.SetPreloadedAssets(
|
|
|
|
|
PlayerSettings.GetPreloadedAssets()
|
2026-06-24 18:57:55 +09:00
|
|
|
.Where(x => x != null && x.GetType() != t)
|
2026-06-23 19:04:02 +09:00
|
|
|
.ToArray());
|
|
|
|
|
|
|
|
|
|
Debug.Log($"[PreloadedProjectSettings] Build started: removed '{settings.name}' " +
|
|
|
|
|
$"({t.Name}) from PreloadedAssets. " +
|
|
|
|
|
$"It will be restored after build completes.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void IPostprocessBuildWithReport.OnPostprocessBuild(BuildReport report)
|
|
|
|
|
{
|
|
|
|
|
s_BuildingPlayer = false;
|
2024-09-30 00:17:16 +09:00
|
|
|
Initialize();
|
|
|
|
|
}
|
2026-06-24 18:57:55 +09:00
|
|
|
|
|
|
|
|
#if UNITY_2019_3_OR_NEWER
|
|
|
|
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
|
|
|
|
private static void OnDomainReload()
|
|
|
|
|
{
|
|
|
|
|
foreach (var t in TypeCache.GetTypesDerivedFrom(typeof(PreloadedProjectSettings<>)))
|
|
|
|
|
{
|
|
|
|
|
var defaultSettings = GetDefaultSettings(t);
|
|
|
|
|
if (defaultSettings != null)
|
|
|
|
|
{
|
|
|
|
|
defaultSettings.OnDomainReload();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2024-09-30 00:17:16 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void Initialize()
|
|
|
|
|
{
|
|
|
|
|
foreach (var t in TypeCache.GetTypesDerivedFrom(typeof(PreloadedProjectSettings<>)))
|
|
|
|
|
{
|
|
|
|
|
var defaultSettings = GetDefaultSettings(t);
|
2026-03-24 17:32:31 +09:00
|
|
|
if (defaultSettings == null)
|
2024-09-30 00:17:16 +09:00
|
|
|
{
|
2026-06-23 19:04:02 +09:00
|
|
|
if (!s_BuildingPlayer)
|
|
|
|
|
{
|
|
|
|
|
// When create a new instance, automatically set it as default settings.
|
|
|
|
|
defaultSettings = CreateInstance(t) as PreloadedProjectSettings;
|
|
|
|
|
SetDefaultSettings(defaultSettings);
|
|
|
|
|
}
|
2024-09-30 00:17:16 +09:00
|
|
|
}
|
|
|
|
|
else if (GetPreloadedSettings(t).Length != 1)
|
|
|
|
|
{
|
2026-06-23 19:04:02 +09:00
|
|
|
if (!s_BuildingPlayer) SetDefaultSettings(defaultSettings);
|
2024-09-30 00:17:16 +09:00
|
|
|
}
|
|
|
|
|
|
2026-03-24 17:32:31 +09:00
|
|
|
if (defaultSettings != null)
|
2024-11-21 01:39:16 +09:00
|
|
|
{
|
|
|
|
|
defaultSettings.OnInitialize();
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-09-30 00:17:16 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected static string GetDefaultName(Type type, bool nicify)
|
|
|
|
|
{
|
2024-11-21 01:39:16 +09:00
|
|
|
var typeName = type.Name;
|
2024-09-30 00:17:16 +09:00
|
|
|
return nicify
|
|
|
|
|
? ObjectNames.NicifyVariableName(typeName)
|
|
|
|
|
: typeName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static Object[] GetPreloadedSettings(Type type)
|
|
|
|
|
{
|
|
|
|
|
return PlayerSettings.GetPreloadedAssets()
|
2026-03-24 17:32:31 +09:00
|
|
|
.Where(x => x != null && x.GetType() == type)
|
2024-09-30 00:17:16 +09:00
|
|
|
.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected static PreloadedProjectSettings GetDefaultSettings(Type type)
|
|
|
|
|
{
|
|
|
|
|
return GetPreloadedSettings(type).FirstOrDefault() as PreloadedProjectSettings
|
2026-06-23 19:04:02 +09:00
|
|
|
?? AssetDatabase.FindAssets($"t:{type.Name}")
|
2024-09-30 00:17:16 +09:00
|
|
|
.Select(AssetDatabase.GUIDToAssetPath)
|
|
|
|
|
.Select(AssetDatabase.LoadAssetAtPath<PreloadedProjectSettings>)
|
2026-03-24 17:32:31 +09:00
|
|
|
.FirstOrDefault(x => x != null && x.GetType() == type);
|
2024-09-30 00:17:16 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected static void SetDefaultSettings(PreloadedProjectSettings asset)
|
|
|
|
|
{
|
2026-03-24 17:32:31 +09:00
|
|
|
if (asset == null) return;
|
2024-11-21 01:39:16 +09:00
|
|
|
|
2024-09-30 00:17:16 +09:00
|
|
|
var type = asset.GetType();
|
|
|
|
|
if (string.IsNullOrEmpty(AssetDatabase.GetAssetPath(asset)))
|
|
|
|
|
{
|
|
|
|
|
if (!AssetDatabase.IsValidFolder("Assets/ProjectSettings"))
|
|
|
|
|
{
|
|
|
|
|
AssetDatabase.CreateFolder("Assets", "ProjectSettings");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var assetPath = $"Assets/ProjectSettings/{GetDefaultName(type, false)}.asset";
|
|
|
|
|
assetPath = AssetDatabase.GenerateUniqueAssetPath(assetPath);
|
2024-11-21 01:39:16 +09:00
|
|
|
if (!File.Exists(assetPath))
|
|
|
|
|
{
|
|
|
|
|
AssetDatabase.CreateAsset(asset, assetPath);
|
|
|
|
|
asset.OnCreateAsset();
|
|
|
|
|
}
|
2024-09-30 00:17:16 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var preloadedAssets = PlayerSettings.GetPreloadedAssets();
|
|
|
|
|
var projectSettings = GetPreloadedSettings(type);
|
|
|
|
|
PlayerSettings.SetPreloadedAssets(preloadedAssets
|
2026-03-24 17:32:31 +09:00
|
|
|
.Where(x => x != null)
|
2024-09-30 00:17:16 +09:00
|
|
|
.Except(projectSettings.Except(new[] { asset }))
|
|
|
|
|
.Append(asset)
|
|
|
|
|
.Distinct()
|
|
|
|
|
.ToArray());
|
|
|
|
|
|
|
|
|
|
AssetDatabase.Refresh();
|
|
|
|
|
}
|
2024-11-21 01:39:16 +09:00
|
|
|
|
|
|
|
|
protected virtual void OnCreateAsset()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual void OnInitialize()
|
|
|
|
|
{
|
|
|
|
|
}
|
2026-06-24 18:57:55 +09:00
|
|
|
|
|
|
|
|
protected virtual void OnDomainReload()
|
|
|
|
|
{
|
|
|
|
|
}
|
2024-09-30 00:17:16 +09:00
|
|
|
}
|
2026-06-23 19:04:02 +09:00
|
|
|
|
|
|
|
|
internal abstract class PreloadedProjectSettingsEditor : Editor
|
|
|
|
|
{
|
|
|
|
|
private SerializedProperty _preLoadSettingsInBuild;
|
|
|
|
|
|
|
|
|
|
protected virtual void OnEnable()
|
|
|
|
|
{
|
|
|
|
|
_preLoadSettingsInBuild = serializedObject.FindProperty("m_PreLoadSettingsInBuild");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected void DrawPreLoadSettingsInBuild(string packageName)
|
|
|
|
|
{
|
|
|
|
|
EditorGUILayout.PropertyField(_preLoadSettingsInBuild);
|
|
|
|
|
if (!_preLoadSettingsInBuild.boolValue)
|
|
|
|
|
{
|
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
|
|
|
|
EditorGUILayout.HelpBox(
|
|
|
|
|
$"{target.GetType().Name} asset will not be built in.\n" +
|
|
|
|
|
$"please load manually from Resources, AssetBundle, or Addressables before using {packageName}.",
|
|
|
|
|
MessageType.Warning);
|
|
|
|
|
if (GUILayout.Button("Ping"))
|
|
|
|
|
{
|
|
|
|
|
EditorGUIUtility.PingObject(target);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-09-30 00:17:16 +09:00
|
|
|
#else
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
public abstract class PreloadedProjectSettings<T> : PreloadedProjectSettings
|
|
|
|
|
where T : PreloadedProjectSettings<T>
|
|
|
|
|
{
|
|
|
|
|
private static T s_Instance;
|
|
|
|
|
|
|
|
|
|
#if UNITY_EDITOR
|
|
|
|
|
private string _jsonText;
|
|
|
|
|
|
2026-03-24 17:32:31 +09:00
|
|
|
public static bool hasInstance => s_Instance != null;
|
2025-01-03 22:58:18 +09:00
|
|
|
|
2024-09-30 00:17:16 +09:00
|
|
|
public static T instance
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2026-03-24 17:32:31 +09:00
|
|
|
if (s_Instance != null) return s_Instance;
|
2024-09-30 00:17:16 +09:00
|
|
|
|
|
|
|
|
s_Instance = GetDefaultSettings(typeof(T)) as T;
|
2026-03-24 17:32:31 +09:00
|
|
|
if (s_Instance != null) return s_Instance;
|
2024-09-30 00:17:16 +09:00
|
|
|
|
|
|
|
|
s_Instance = CreateInstance<T>();
|
2026-03-24 17:32:31 +09:00
|
|
|
if (s_Instance == null)
|
2024-09-30 00:17:16 +09:00
|
|
|
{
|
|
|
|
|
s_Instance = null;
|
|
|
|
|
return s_Instance;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-23 19:04:02 +09:00
|
|
|
if (!s_BuildingPlayer) SetDefaultSettings(s_Instance);
|
2024-09-30 00:17:16 +09:00
|
|
|
return s_Instance;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnPlayModeStateChanged(PlayModeStateChange state)
|
|
|
|
|
{
|
2026-06-24 18:57:55 +09:00
|
|
|
if (this == null) return;
|
2026-06-08 13:27:27 +09:00
|
|
|
|
2024-09-30 00:17:16 +09:00
|
|
|
switch (state)
|
|
|
|
|
{
|
|
|
|
|
case PlayModeStateChange.ExitingEditMode:
|
|
|
|
|
_jsonText = EditorJsonUtility.ToJson(this);
|
|
|
|
|
break;
|
|
|
|
|
case PlayModeStateChange.ExitingPlayMode:
|
|
|
|
|
if (_jsonText != null)
|
|
|
|
|
{
|
|
|
|
|
EditorJsonUtility.FromJsonOverwrite(_jsonText, this);
|
|
|
|
|
_jsonText = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-06-24 18:57:55 +09:00
|
|
|
|
|
|
|
|
protected override void OnDomainReload()
|
|
|
|
|
{
|
|
|
|
|
s_Instance = null;
|
|
|
|
|
}
|
2024-09-30 00:17:16 +09:00
|
|
|
#else
|
2026-06-24 18:57:55 +09:00
|
|
|
public static T instance => s_Instance != null ? s_Instance : s_Instance = CreateInstance<T>();
|
2024-09-30 00:17:16 +09:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// This function is called when the object becomes enabled and active.
|
|
|
|
|
/// </summary>
|
|
|
|
|
protected virtual void OnEnable()
|
|
|
|
|
{
|
|
|
|
|
#if UNITY_EDITOR
|
2026-03-24 17:32:31 +09:00
|
|
|
var isDefaultSettings = s_Instance == null || s_Instance == this || GetDefaultSettings(typeof(T)) == this;
|
2024-09-30 00:17:16 +09:00
|
|
|
if (!isDefaultSettings)
|
|
|
|
|
{
|
|
|
|
|
DestroyImmediate(this, true);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-24 18:57:55 +09:00
|
|
|
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
|
2024-09-30 00:17:16 +09:00
|
|
|
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
2026-06-23 19:04:02 +09:00
|
|
|
#else
|
2026-06-24 18:57:55 +09:00
|
|
|
if (s_Instance != null && s_Instance != this)
|
2026-06-23 19:04:02 +09:00
|
|
|
{
|
|
|
|
|
Destroy(s_Instance);
|
|
|
|
|
}
|
2024-09-30 00:17:16 +09:00
|
|
|
#endif
|
|
|
|
|
s_Instance = this as T;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// This function is called when the behaviour becomes disabled.
|
|
|
|
|
/// </summary>
|
|
|
|
|
protected virtual void OnDisable()
|
|
|
|
|
{
|
|
|
|
|
#if UNITY_EDITOR
|
|
|
|
|
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
|
|
|
|
|
#endif
|
|
|
|
|
if (s_Instance != this) return;
|
|
|
|
|
|
|
|
|
|
s_Instance = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if UNITY_EDITOR
|
|
|
|
|
protected sealed class PreloadedProjectSettingsProvider : SettingsProvider
|
|
|
|
|
{
|
|
|
|
|
private Editor _editor;
|
|
|
|
|
private PreloadedProjectSettings<T> _target;
|
|
|
|
|
|
|
|
|
|
public PreloadedProjectSettingsProvider(string path) : base(path, SettingsScope.Project)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void OnGUI(string searchContext)
|
|
|
|
|
{
|
2026-03-24 17:32:31 +09:00
|
|
|
if (_target == null)
|
2024-09-30 00:17:16 +09:00
|
|
|
{
|
2026-06-24 18:57:55 +09:00
|
|
|
if (_editor != null)
|
2024-09-30 00:17:16 +09:00
|
|
|
{
|
|
|
|
|
DestroyImmediate(_editor);
|
|
|
|
|
_editor = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_target = instance;
|
|
|
|
|
_editor = Editor.CreateEditor(_target);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_editor.OnInspectorGUI();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|