Initial Commit

This commit is contained in:
2026-03-16 14:38:46 +02:00
commit b8f7327a21
2327 changed files with 253610 additions and 0 deletions
@@ -0,0 +1,30 @@
namespace Zenject
{
// Derive from this class, add [InitializeOnLoad], and then call Install
// in a static constructor to add some editor time bindings
// For example:
//
// [InitializeOnLoad]
// public class FooInstaller : EditorStaticInstaller<FooInstaller>
// {
// static FooInstaller()
// {
// Install();
// }
//
// public override void InstallBindings()
// {
// Container.BindInstance("hello world");
// }
// }
//
public abstract class EditorStaticInstaller<T> : InstallerBase
where T : EditorStaticInstaller<T>
{
public static void Install()
{
StaticContext.Container.Instantiate<T>().InstallBindings();
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: fcb2e11e0ed56de48b0ba9b5e3ae10c9
timeCreated: 1486079412
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: bd8d602c69b71714babee52a2d454aed
folderAsset: yes
timeCreated: 1461708046
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,160 @@
using System;
using ModestTree;
using UnityEditor;
using UnityEngine;
namespace Zenject
{
public abstract class ZenjectEditorWindow : EditorWindow
{
[Inject]
[NonSerialized]
Kernel _kernel;
[Inject]
[NonSerialized]
GuiRenderableManager _guiRenderableManager;
[NonSerialized]
DiContainer _container;
[NonSerialized]
Exception _fatalError;
[NonSerialized]
GUIStyle _errorTextStyle;
GUIStyle ErrorTextStyle
{
get
{
if (_errorTextStyle == null)
{
_errorTextStyle = new GUIStyle(GUI.skin.label);
_errorTextStyle.fontSize = 18;
_errorTextStyle.normal.textColor = Color.red;
_errorTextStyle.wordWrap = true;
_errorTextStyle.alignment = TextAnchor.MiddleCenter;
}
return _errorTextStyle;
}
}
protected DiContainer Container
{
get { return _container; }
}
public virtual void OnEnable()
{
if (_fatalError != null)
{
return;
}
Initialize();
}
protected virtual void Initialize()
{
Assert.IsNull(_container);
_container = new DiContainer(new[] { StaticContext.Container });
// Make sure we don't create any game objects since editor windows don't have a scene
_container.AssertOnNewGameObjects = true;
ZenjectManagersInstaller.Install(_container);
_container.Bind<Kernel>().AsSingle();
_container.Bind<GuiRenderableManager>().AsSingle();
_container.BindInstance(this);
InstallBindings();
_container.QueueForInject(this);
_container.ResolveRoots();
_kernel.Initialize();
}
public virtual void OnDisable()
{
if (_fatalError != null)
{
return;
}
_kernel.Dispose();
}
public virtual void Update()
{
if (_fatalError != null)
{
return;
}
try
{
_kernel.Tick();
}
catch (Exception e)
{
Log.ErrorException(e);
_fatalError = e;
}
// We might also consider only calling Repaint when changes occur
Repaint();
}
public virtual void OnGUI()
{
if (_fatalError != null)
{
var labelWidth = 600;
var labelHeight = 200;
GUI.Label(new Rect(Screen.width / 2 - labelWidth / 2, Screen.height / 3 - labelHeight / 2, labelWidth, labelHeight), "Unrecoverable error occurred! \nSee log for details.", ErrorTextStyle);
var buttonWidth = 100;
var buttonHeight = 50;
var offset = new Vector2(0, 100);
if (GUI.Button(new Rect(Screen.width / 2 - buttonWidth / 2 + offset.x, Screen.height / 3 - buttonHeight / 2 + offset.y, buttonWidth, buttonHeight), "Reload"))
{
ExecuteFullReload();
}
}
else
{
try
{
if (_guiRenderableManager != null)
{
_guiRenderableManager.OnGui();
}
}
catch (Exception e)
{
Log.ErrorException(e);
_fatalError = e;
}
}
}
protected virtual void ExecuteFullReload()
{
_kernel = null;
_guiRenderableManager = null;
_container = null;
_fatalError = null;
Initialize();
}
public abstract void InstallBindings();
}
}
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: bc25e694ccedbed4893d980ee61d1c8f
timeCreated: 1527961729
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: fbb1477b2e261944dad77cce5626aab0
folderAsset: yes
timeCreated: 1461708046
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,49 @@
#if !ODIN_INSPECTOR
namespace Zenject
{
[NoReflectionBaking]
public class ContextEditor : UnityInspectorListEditor
{
protected override string[] PropertyNames
{
get
{
return new string[]
{
"_scriptableObjectInstallers",
"_monoInstallers",
"_installerPrefabs",
};
}
}
protected override string[] PropertyDisplayNames
{
get
{
return new string[]
{
"Scriptable Object Installers",
"Mono Installers",
"Prefab Installers",
};
}
}
protected override string[] PropertyDescriptions
{
get
{
return new string[]
{
"Drag any assets in your Project that implement ScriptableObjectInstaller here",
"Drag any MonoInstallers that you have added to your Scene Hierarchy here.",
"Drag any prefabs that contain a MonoInstaller on them here",
};
}
}
}
}
#endif
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 23155ecdf203bf24480fd49763b73677
timeCreated: 1461708049
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,29 @@
#if !ODIN_INSPECTOR
using UnityEditor;
namespace Zenject
{
[CustomEditor(typeof(GameObjectContext))]
[NoReflectionBaking]
public class GameObjectContextEditor : RunnableContextEditor
{
SerializedProperty _kernel;
public override void OnEnable()
{
base.OnEnable();
_kernel = serializedObject.FindProperty("_kernel");
}
protected override void OnGui()
{
base.OnGui();
EditorGUILayout.PropertyField(_kernel);
}
}
}
#endif
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b0873c763efd1e94fb3a56ff80843cf1
timeCreated: 1461708052
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,38 @@
#if !ODIN_INSPECTOR
using UnityEditor;
namespace Zenject
{
[CustomEditor(typeof(ProjectContext))]
[NoReflectionBaking]
public class ProjectContextEditor : ContextEditor
{
SerializedProperty _settingsProperty;
SerializedProperty _editorReflectionBakingCoverageModeProperty;
SerializedProperty _buildsReflectionBakingCoverageModeProperty;
SerializedProperty _parentNewObjectsUnderContextProperty;
public override void OnEnable()
{
base.OnEnable();
_settingsProperty = serializedObject.FindProperty("_settings");
_editorReflectionBakingCoverageModeProperty = serializedObject.FindProperty("_editorReflectionBakingCoverageMode");
_buildsReflectionBakingCoverageModeProperty = serializedObject.FindProperty("_buildsReflectionBakingCoverageMode");
_parentNewObjectsUnderContextProperty = serializedObject.FindProperty("_parentNewObjectsUnderContext");
}
protected override void OnGui()
{
base.OnGui();
EditorGUILayout.PropertyField(_settingsProperty, true);
EditorGUILayout.PropertyField(_editorReflectionBakingCoverageModeProperty, true);
EditorGUILayout.PropertyField(_buildsReflectionBakingCoverageModeProperty, true);
EditorGUILayout.PropertyField(_parentNewObjectsUnderContextProperty);
}
}
}
#endif
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b5ad40b612e67574aad508d053e6965b
timeCreated: 1461708053
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,29 @@
#if !ODIN_INSPECTOR
using UnityEditor;
namespace Zenject
{
[NoReflectionBaking]
public class RunnableContextEditor : ContextEditor
{
SerializedProperty _autoRun;
public override void OnEnable()
{
base.OnEnable();
_autoRun = serializedObject.FindProperty("_autoRun");
}
protected override void OnGui()
{
base.OnGui();
EditorGUILayout.PropertyField(_autoRun);
}
}
}
#endif
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 02bed9738f9c4323ac05524465473dee
timeCreated: 1494728675
@@ -0,0 +1,37 @@
#if !ODIN_INSPECTOR
using UnityEditor;
namespace Zenject
{
[CanEditMultipleObjects]
[CustomEditor(typeof(SceneContext))]
[NoReflectionBaking]
public class SceneContextEditor : RunnableContextEditor
{
SerializedProperty _contractNameProperty;
SerializedProperty _parentNamesProperty;
SerializedProperty _parentNewObjectsUnderSceneContextProperty;
public override void OnEnable()
{
base.OnEnable();
_contractNameProperty = serializedObject.FindProperty("_contractNames");
_parentNamesProperty = serializedObject.FindProperty("_parentContractNames");
_parentNewObjectsUnderSceneContextProperty = serializedObject.FindProperty("_parentNewObjectsUnderSceneContext");
}
protected override void OnGui()
{
base.OnGui();
EditorGUILayout.PropertyField(_contractNameProperty, true);
EditorGUILayout.PropertyField(_parentNamesProperty, true);
EditorGUILayout.PropertyField(_parentNewObjectsUnderSceneContextProperty);
}
}
}
#endif
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c04ae1d59f53f514f96e284ba43122f7
timeCreated: 1461708053
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,79 @@
#if !ODIN_INSPECTOR
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
using ModestTree;
namespace Zenject
{
[CustomEditor(typeof(SceneDecoratorContext))]
[NoReflectionBaking]
public class SceneDecoratorContextEditor : ContextEditor
{
SerializedProperty _decoratedContractNameProperty;
protected override string[] PropertyNames
{
get
{
return base.PropertyNames.Concat(new string[]
{
"_lateInstallers",
"_lateInstallerPrefabs",
"_lateScriptableObjectInstallers"
})
.ToArray();
}
}
protected override string[] PropertyDisplayNames
{
get
{
return base.PropertyDisplayNames.Concat(new string[]
{
"Late Installers",
"Late Prefab Installers",
"Late Scriptable Object Installers"
})
.ToArray();
}
}
protected override string[] PropertyDescriptions
{
get
{
return base.PropertyDescriptions.Concat(new string[]
{
"Drag any MonoInstallers that you have added to your Scene Hierarchy here. They'll be installed after the target installs its bindings",
"Drag any prefabs that contain a MonoInstaller on them here. They'll be installed after the target installs its bindings",
"Drag any assets in your Project that implement ScriptableObjectInstaller here. They'll be installed after the target installs its bindings"
})
.ToArray();
}
}
public override void OnEnable()
{
base.OnEnable();
_decoratedContractNameProperty = serializedObject.FindProperty("_decoratedContractName");
}
protected override void OnGui()
{
base.OnGui();
EditorGUILayout.PropertyField(_decoratedContractNameProperty);
}
}
}
#endif
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d2b9289e80031104295e10acf518d75a
timeCreated: 1461708053
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ModestTree;
namespace Zenject
{
// Responsibilities:
// - Output a file specifying the full object graph for a given root dependency
// - This file uses the DOT language with can be fed into GraphViz to generate an image
// - http://www.graphviz.org/
public static class ObjectGraphVisualizer
{
public static void OutputObjectGraphToFile(
DiContainer container, string outputPath,
IEnumerable<Type> externalIgnoreTypes, IEnumerable<Type> contractTypes)
{
// Output the entire object graph to file
var graph = CalculateObjectGraph(container, contractTypes);
var ignoreTypes = new List<Type>
{
typeof(DiContainer),
typeof(InitializableManager)
};
ignoreTypes.AddRange(externalIgnoreTypes);
var resultStr = "digraph { \n";
resultStr += "rankdir=LR;\n";
foreach (var entry in graph)
{
if (ShouldIgnoreType(entry.Key, ignoreTypes))
{
continue;
}
foreach (var dependencyType in entry.Value)
{
if (ShouldIgnoreType(dependencyType, ignoreTypes))
{
continue;
}
resultStr += GetFormattedTypeName(entry.Key) + " -> " + GetFormattedTypeName(dependencyType) + "; \n";
}
}
resultStr += " }";
File.WriteAllText(outputPath, resultStr);
}
static bool ShouldIgnoreType(Type type, List<Type> ignoreTypes)
{
return ignoreTypes.Contains(type);
}
static Dictionary<Type, List<Type>> CalculateObjectGraph(
DiContainer container, IEnumerable<Type> contracts)
{
var map = new Dictionary<Type, List<Type>>();
foreach (var contractType in contracts)
{
var depends = GetDependencies(container, contractType);
if (depends.Any())
{
map.Add(contractType, depends);
}
}
return map;
}
static List<Type> GetDependencies(
DiContainer container, Type type)
{
var dependencies = new List<Type>();
foreach (var contractType in container.GetDependencyContracts(type))
{
List<Type> dependTypes;
if (contractType.FullName.StartsWith("System.Collections.Generic.List"))
{
var subTypes = contractType.GenericArguments();
Assert.IsEqual(subTypes.Length, 1);
var subType = subTypes[0];
dependTypes = container.ResolveTypeAll(subType);
}
else
{
dependTypes = container.ResolveTypeAll(contractType);
Assert.That(dependTypes.Count <= 1);
}
foreach (var dependType in dependTypes)
{
dependencies.Add(dependType);
}
}
return dependencies;
}
static string GetFormattedTypeName(Type type)
{
var str = type.PrettyName();
// GraphViz does not read names with <, >, or . characters so replace them
str = str.Replace(">", "_");
str = str.Replace("<", "_");
str = str.Replace(".", "_");
return str;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 29f47f2a06418244e8fcbe27db1a0eea
timeCreated: 1461708049
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 8b93c2560e2014a4893f387eb7690dbd
folderAsset: yes
timeCreated: 1520777708
licenseType: Free
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Zenject.Internal
{
public class DefaultSceneContractConfig : ScriptableObject
{
public const string ResourcePath = "ZenjectDefaultSceneContractConfig";
public List<ContractInfo> DefaultContracts;
[Serializable]
public class ContractInfo
{
public string ContractName;
public SceneAsset Scene;
}
}
}
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 8620c058a9173b84a97d72ed5e94dbd7
timeCreated: 1520778887
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,326 @@
using System;
using System.Collections.Generic;
using ModestTree;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Zenject.Internal
{
[InitializeOnLoad]
public static class SceneParentAutomaticLoader
{
static SceneParentAutomaticLoader()
{
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
}
static void OnPlayModeStateChanged(PlayModeStateChange state)
{
if (state == PlayModeStateChange.ExitingEditMode)
{
try
{
ValidateMultiSceneSetupAndLoadDefaultSceneParents();
}
catch (Exception e)
{
EditorApplication.isPlaying = false;
throw new ZenjectException(
"Failure occurred when attempting to load default scene parent contracts!", e);
}
}
else if (state == PlayModeStateChange.EnteredEditMode)
{
// It would be cool to restore the initial scene set up here but in order to do this
// we would have to make sure that the user saves the scene before running which
// would be too annoying, so just leave any changes we've made alone
}
}
public static void ValidateMultiSceneSetupAndLoadDefaultSceneParents()
{
var defaultContractsMap = LoadDefaultContractsMap();
// NOTE: Even if configs is empty we still want to do the below logic to validate the
// multi scene setup
var sceneInfos = GetLoadedZenjectSceneInfos();
var contractMap = GetCurrentSceneContractsMap(sceneInfos);
foreach (var sceneInfo in sceneInfos)
{
ProcessScene(sceneInfo, contractMap, defaultContractsMap);
}
}
static Dictionary<string, LoadedSceneInfo> GetCurrentSceneContractsMap(
List<LoadedSceneInfo> sceneInfos)
{
var contractMap = new Dictionary<string, LoadedSceneInfo>();
foreach (var info in sceneInfos)
{
AddToContractMap(contractMap, info);
}
return contractMap;
}
static void ProcessScene(
LoadedSceneInfo sceneInfo,
Dictionary<string, LoadedSceneInfo> contractMap,
Dictionary<string, string> defaultContractsMap)
{
if (sceneInfo.SceneContext != null)
{
Assert.IsNull(sceneInfo.DecoratorContext);
ProcessSceneParents(sceneInfo, contractMap, defaultContractsMap);
}
else
{
Assert.IsNotNull(sceneInfo.DecoratorContext);
ProcessSceneDecorators(sceneInfo, contractMap, defaultContractsMap);
}
}
static void ProcessSceneDecorators(
LoadedSceneInfo sceneInfo,
Dictionary<string, LoadedSceneInfo> contractMap,
Dictionary<string, string> defaultContractsMap)
{
var decoratedContractName = sceneInfo.DecoratorContext.DecoratedContractName;
LoadedSceneInfo decoratedSceneInfo;
if (contractMap.TryGetValue(decoratedContractName, out decoratedSceneInfo))
{
ValidateDecoratedSceneMatch(sceneInfo, decoratedSceneInfo);
return;
}
decoratedSceneInfo = LoadDefaultSceneForContract(
sceneInfo, decoratedContractName, defaultContractsMap);
EditorSceneManager.MoveSceneAfter(decoratedSceneInfo.Scene, sceneInfo.Scene);
ValidateDecoratedSceneMatch(sceneInfo, decoratedSceneInfo);
ProcessScene(decoratedSceneInfo, contractMap, defaultContractsMap);
}
static void ProcessSceneParents(
LoadedSceneInfo sceneInfo,
Dictionary<string, LoadedSceneInfo> contractMap,
Dictionary<string, string> defaultContractsMap)
{
foreach (var parentContractName in sceneInfo.SceneContext.ParentContractNames)
{
LoadedSceneInfo parentInfo;
if (contractMap.TryGetValue(parentContractName, out parentInfo))
{
ValidateParentChildMatch(parentInfo, sceneInfo);
continue;
}
parentInfo = LoadDefaultSceneForContract(sceneInfo, parentContractName, defaultContractsMap);
AddToContractMap(contractMap, parentInfo);
EditorSceneManager.MoveSceneBefore(parentInfo.Scene, sceneInfo.Scene);
ValidateParentChildMatch(parentInfo, sceneInfo);
ProcessScene(parentInfo, contractMap, defaultContractsMap);
}
}
static LoadedSceneInfo LoadDefaultSceneForContract(
LoadedSceneInfo sceneInfo, string contractName, Dictionary<string, string> defaultContractsMap)
{
string scenePath;
if (!defaultContractsMap.TryGetValue(contractName, out scenePath))
{
throw Assert.CreateException(
"Could not fill contract '{0}' for scene '{1}'. No scenes with that contract name are loaded, and could not find a match in any default scene contract configs to auto load one either."
.Fmt(contractName, sceneInfo.Scene.name));
}
Scene scene;
try
{
scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Additive);
}
catch (Exception e)
{
throw new ZenjectException(
"Error while attempting to load contracts for scene '{0}'".Fmt(sceneInfo.Scene.name), e);
}
return CreateLoadedSceneInfo(scene);
}
static void ValidateDecoratedSceneMatch(
LoadedSceneInfo decoratorInfo, LoadedSceneInfo decoratedInfo)
{
var decoratorIndex = GetSceneIndex(decoratorInfo.Scene);
var decoratedIndex = GetSceneIndex(decoratedInfo.Scene);
var activeIndex = GetSceneIndex(EditorSceneManager.GetActiveScene());
Assert.That(decoratorIndex < decoratedIndex,
"Decorator scene '{0}' must be loaded before decorated scene '{1}'. Please drag the decorator scene to be placed above the other scene in the scene hierarchy.",
decoratorInfo.Scene.name, decoratedInfo.Scene.name);
if (activeIndex > decoratorIndex)
{
EditorSceneManager.SetActiveScene(decoratorInfo.Scene);
}
}
static void ValidateParentChildMatch(
LoadedSceneInfo parentSceneInfo, LoadedSceneInfo sceneInfo)
{
var parentIndex = GetSceneIndex(parentSceneInfo.Scene);
var childIndex = GetSceneIndex(sceneInfo.Scene);
var activeIndex = GetSceneIndex(EditorSceneManager.GetActiveScene());
Assert.That(parentIndex < childIndex,
"Parent scene '{0}' must be loaded before child scene '{1}'. Please drag it to be placed above its child in the scene hierarchy.", parentSceneInfo.Scene.name, sceneInfo.Scene.name);
if (activeIndex > parentIndex)
{
EditorSceneManager.SetActiveScene(parentSceneInfo.Scene);
}
}
static int GetSceneIndex(Scene scene)
{
for (int i = 0; i < EditorSceneManager.sceneCount; i++)
{
if (EditorSceneManager.GetSceneAt(i) == scene)
{
return i;
}
}
throw Assert.CreateException();
}
static Dictionary<string, string> LoadDefaultContractsMap()
{
var configs = Resources.LoadAll<DefaultSceneContractConfig>(DefaultSceneContractConfig.ResourcePath);
var map = new Dictionary<string, string>();
foreach (var config in configs)
{
foreach (var info in config.DefaultContracts)
{
if (info.ContractName.Trim().IsEmpty())
{
Log.Warn("Found empty contract name in default scene contract config at path '{0}'", AssetDatabase.GetAssetPath(config));
continue;
}
Assert.That(!map.ContainsKey(info.ContractName),
"Found duplicate contract '{0}' in default scene contract config at '{1}'! Default contract already specified", info.ContractName, AssetDatabase.GetAssetPath(config));
map.Add(info.ContractName, AssetDatabase.GetAssetPath(info.Scene));
}
}
return map;
}
static LoadedSceneInfo CreateLoadedSceneInfo(Scene scene)
{
var info = TryCreateLoadedSceneInfo(scene);
Assert.IsNotNull(info, "Expected scene '{0}' to be a zenject scene", scene.name);
return info;
}
static LoadedSceneInfo TryCreateLoadedSceneInfo(Scene scene)
{
var sceneContext = ZenUnityEditorUtil.TryGetSceneContextForScene(scene);
var decoratorContext = ZenUnityEditorUtil.TryGetDecoratorContextForScene(scene);
if (sceneContext == null && decoratorContext == null)
{
return null;
}
var info = new LoadedSceneInfo
{
Scene = scene
};
if (sceneContext != null)
{
Assert.IsNull(decoratorContext,
"Found both SceneContext and SceneDecoratorContext in scene '{0}'", scene.name);
info.SceneContext = sceneContext;
}
else
{
Assert.IsNotNull(decoratorContext);
info.DecoratorContext = decoratorContext;
}
return info;
}
static List<LoadedSceneInfo> GetLoadedZenjectSceneInfos()
{
var result = new List<LoadedSceneInfo>();
for (int i = 0; i < EditorSceneManager.sceneCount; i++)
{
var scene = EditorSceneManager.GetSceneAt(i);
var info = TryCreateLoadedSceneInfo(scene);
if (info != null)
{
result.Add(info);
}
}
return result;
}
static void AddToContractMap(
Dictionary<string, LoadedSceneInfo> contractMap, LoadedSceneInfo info)
{
if (info.SceneContext == null)
{
return;
}
foreach (var contractName in info.SceneContext.ContractNames)
{
LoadedSceneInfo currentInfo;
if (contractMap.TryGetValue(contractName, out currentInfo))
{
throw Assert.CreateException(
"Found multiple scene contracts with name '{0}'. Scene '{1}' and scene '{2}'",
contractName, currentInfo.Scene.name, info.Scene.name);
}
contractMap.Add(contractName, info);
}
}
public class LoadedSceneInfo
{
public SceneContext SceneContext;
public SceneDecoratorContext DecoratorContext;
public Scene Scene;
}
}
}
@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: c9f09e0752f64214ba3413d7fdc47736
timeCreated: 1520771371
licenseType: Free
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,93 @@
using System.Collections.Generic;
using System.Linq;
using ModestTree;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace Zenject
{
public abstract class UnityInspectorListEditor : Editor
{
List<ReorderableList> _installersLists;
List<SerializedProperty> _installersProperties;
protected abstract string[] PropertyDisplayNames
{
get;
}
protected abstract string[] PropertyNames
{
get;
}
protected abstract string[] PropertyDescriptions
{
get;
}
public virtual void OnEnable()
{
_installersProperties = new List<SerializedProperty>();
_installersLists = new List<ReorderableList>();
var descriptions = PropertyDescriptions;
var names = PropertyNames;
var displayNames = PropertyDisplayNames;
Assert.IsEqual(descriptions.Length, names.Length);
var infos = Enumerable.Range(0, names.Length).Select(i => new { Name = names[i], DisplayName = displayNames[i], Description = descriptions[i] }).ToList();
foreach (var info in infos)
{
var installersProperty = serializedObject.FindProperty(info.Name);
_installersProperties.Add(installersProperty);
ReorderableList installersList = new ReorderableList(serializedObject, installersProperty, true, true, true, true);
_installersLists.Add(installersList);
var closedName = info.DisplayName;
var closedDesc = info.Description;
installersList.drawHeaderCallback += rect =>
{
GUI.Label(rect,
new GUIContent(closedName, closedDesc));
};
installersList.drawElementCallback += (rect, index, active, focused) =>
{
rect.width -= 40;
rect.x += 20;
EditorGUI.PropertyField(rect, installersProperty.GetArrayElementAtIndex(index), GUIContent.none, true);
};
}
}
public sealed override void OnInspectorGUI()
{
serializedObject.Update();
OnGui();
serializedObject.ApplyModifiedProperties();
}
protected virtual void OnGui()
{
if (Application.isPlaying)
{
GUI.enabled = false;
}
foreach (var list in _installersLists)
{
list.DoLayoutList();
}
GUI.enabled = true;
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 06d16f2f9107265428d41710db4cbb14
timeCreated: 1461708048
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,349 @@
#if !NOT_UNITY3D
using System.IO;
using ModestTree;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
namespace Zenject.Internal
{
public static class ZenMenuItems
{
[MenuItem("Edit/Zenject/Validate Current Scenes #&v")]
public static void ValidateCurrentScene()
{
ValidateCurrentSceneInternal();
}
[MenuItem("Edit/Zenject/Validate Then Run #&r")]
public static void ValidateCurrentSceneThenRun()
{
if (ValidateCurrentSceneInternal())
{
EditorApplication.isPlaying = true;
}
}
[MenuItem("Edit/Zenject/Help...")]
public static void OpenDocumentation()
{
Application.OpenURL("https://github.com/svermeulen/zenject");
}
[MenuItem("GameObject/Zenject/Scene Context", false, 9)]
public static void CreateSceneContext(MenuCommand menuCommand)
{
var root = new GameObject("SceneContext").AddComponent<SceneContext>();
Selection.activeGameObject = root.gameObject;
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
}
[MenuItem("GameObject/Zenject/Decorator Context", false, 9)]
public static void CreateDecoratorContext(MenuCommand menuCommand)
{
var root = new GameObject("DecoratorContext").AddComponent<SceneDecoratorContext>();
Selection.activeGameObject = root.gameObject;
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
}
[MenuItem("GameObject/Zenject/Game Object Context", false, 9)]
public static void CreateGameObjectContext(MenuCommand menuCommand)
{
var root = new GameObject("GameObjectContext").AddComponent<GameObjectContext>();
Selection.activeGameObject = root.gameObject;
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
}
[MenuItem("Edit/Zenject/Create Project Context")]
public static void CreateProjectContextInDefaultLocation()
{
var fullDirPath = Path.Combine(Application.dataPath, "Resources");
if (!Directory.Exists(fullDirPath))
{
Directory.CreateDirectory(fullDirPath);
}
CreateProjectContextInternal("Assets/Resources");
}
[MenuItem("Assets/Create/Zenject/Default Scene Contract Config", false, 80)]
public static void CreateDefaultSceneContractConfig()
{
var folderPath = ZenUnityEditorUtil.GetCurrentDirectoryAssetPathFromSelection();
if (!folderPath.EndsWith("/Resources"))
{
EditorUtility.DisplayDialog("Error",
"ZenjectDefaultSceneContractConfig objects must be placed directly underneath a folder named 'Resources'. Please try again.", "Ok");
return;
}
var config = ScriptableObject.CreateInstance<DefaultSceneContractConfig>();
ZenUnityEditorUtil.SaveScriptableObjectAsset(
Path.Combine(folderPath, DefaultSceneContractConfig.ResourcePath + ".asset"), config);
}
[MenuItem("Assets/Create/Zenject/Scriptable Object Installer", false, 1)]
public static void CreateScriptableObjectInstaller()
{
AddCSharpClassTemplate("Scriptable Object Installer", "UntitledInstaller",
"using UnityEngine;"
+ "\nusing Zenject;"
+ "\n"
+ "\n[CreateAssetMenu(fileName = \"CLASS_NAME\", menuName = \"Installers/CLASS_NAME\")]"
+ "\npublic class CLASS_NAME : ScriptableObjectInstaller<CLASS_NAME>"
+ "\n{"
+ "\n public override void InstallBindings()"
+ "\n {"
+ "\n }"
+ "\n}");
}
[MenuItem("Assets/Create/Zenject/Mono Installer", false, 1)]
public static void CreateMonoInstaller()
{
AddCSharpClassTemplate("Mono Installer", "UntitledInstaller",
"using UnityEngine;"
+ "\nusing Zenject;"
+ "\n"
+ "\npublic class CLASS_NAME : MonoInstaller"
+ "\n{"
+ "\n public override void InstallBindings()"
+ "\n {"
+ "\n }"
+ "\n}");
}
[MenuItem("Assets/Create/Zenject/Installer", false, 1)]
public static void CreateInstaller()
{
AddCSharpClassTemplate("Installer", "UntitledInstaller",
"using UnityEngine;"
+ "\nusing Zenject;"
+ "\n"
+ "\npublic class CLASS_NAME : Installer<CLASS_NAME>"
+ "\n{"
+ "\n public override void InstallBindings()"
+ "\n {"
+ "\n }"
+ "\n}");
}
[MenuItem("Assets/Create/Zenject/Editor Window", false, 20)]
public static void CreateEditorWindow()
{
AddCSharpClassTemplate("Editor Window", "UntitledEditorWindow",
"using UnityEngine;"
+ "\nusing UnityEditor;"
+ "\nusing Zenject;"
+ "\n"
+ "\npublic class CLASS_NAME : ZenjectEditorWindow"
+ "\n{"
+ "\n [MenuItem(\"Window/CLASS_NAME\")]"
+ "\n public static CLASS_NAME GetOrCreateWindow()"
+ "\n {"
+ "\n var window = EditorWindow.GetWindow<CLASS_NAME>();"
+ "\n window.titleContent = new GUIContent(\"CLASS_NAME\");"
+ "\n return window;"
+ "\n }"
+ "\n"
+ "\n public override void InstallBindings()"
+ "\n {"
+ "\n // TODO"
+ "\n }"
+ "\n}");
}
[MenuItem("Assets/Create/Zenject/Project Context", false, 40)]
public static void CreateProjectContext()
{
var absoluteDir = ZenUnityEditorUtil.TryGetSelectedFolderPathInProjectsTab();
if (absoluteDir == null)
{
EditorUtility.DisplayDialog("Error",
"Could not find directory to place the '{0}.prefab' asset. Please try again by right clicking in the desired folder within the projects pane."
.Fmt(ProjectContext.ProjectContextResourcePath), "Ok");
return;
}
var parentFolderName = Path.GetFileName(absoluteDir);
if (parentFolderName != "Resources")
{
EditorUtility.DisplayDialog("Error",
"'{0}.prefab' must be placed inside a directory named 'Resources'. Please try again by right clicking within the Project pane in a valid Resources folder."
.Fmt(ProjectContext.ProjectContextResourcePath), "Ok");
return;
}
CreateProjectContextInternal(absoluteDir);
}
static void CreateProjectContextInternal(string absoluteDir)
{
var assetPath = ZenUnityEditorUtil.ConvertFullAbsolutePathToAssetPath(absoluteDir);
var prefabPath = (Path.Combine(assetPath, ProjectContext.ProjectContextResourcePath) + ".prefab").Replace("\\", "/");
var gameObject = new GameObject();
try
{
gameObject.AddComponent<ProjectContext>();
#if UNITY_2018_3_OR_NEWER
var prefabObj = PrefabUtility.SaveAsPrefabAsset(gameObject, prefabPath);
#else
var prefabObj = PrefabUtility.ReplacePrefab(gameObject, PrefabUtility.CreateEmptyPrefab(prefabPath));
#endif
Selection.activeObject = prefabObj;
}
finally
{
GameObject.DestroyImmediate(gameObject);
}
Debug.Log("Created new ProjectContext at '{0}'".Fmt(prefabPath));
}
public static string AddCSharpClassTemplate(
string friendlyName, string defaultFileName, string templateStr)
{
return AddCSharpClassTemplate(
friendlyName, defaultFileName, templateStr, ZenUnityEditorUtil.GetCurrentDirectoryAssetPathFromSelection());
}
public static string AddCSharpClassTemplate(
string friendlyName, string defaultFileName,
string templateStr, string folderPath)
{
var absolutePath = EditorUtility.SaveFilePanel(
"Choose name for " + friendlyName,
folderPath,
defaultFileName + ".cs",
"cs");
if (absolutePath == "")
{
// Dialog was cancelled
return null;
}
if (!absolutePath.ToLower().EndsWith(".cs"))
{
absolutePath += ".cs";
}
var className = Path.GetFileNameWithoutExtension(absolutePath);
File.WriteAllText(absolutePath, templateStr.Replace("CLASS_NAME", className));
AssetDatabase.Refresh();
var assetPath = ZenUnityEditorUtil.ConvertFullAbsolutePathToAssetPath(absolutePath);
EditorUtility.FocusProjectWindow();
Selection.activeObject = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(assetPath);
return assetPath;
}
[MenuItem("Edit/Zenject/Validate All Active Scenes")]
public static void ValidateAllActiveScenes()
{
ZenUnityEditorUtil.SaveThenRunPreserveSceneSetup(() =>
{
var numValidated = ZenUnityEditorUtil.ValidateAllActiveScenes();
Log.Info("Validated all '{0}' active scenes successfully", numValidated);
});
}
static bool ValidateCurrentSceneInternal()
{
return ZenUnityEditorUtil.SaveThenRunPreserveSceneSetup(() =>
{
SceneParentAutomaticLoader.ValidateMultiSceneSetupAndLoadDefaultSceneParents();
ZenUnityEditorUtil.ValidateCurrentSceneSetup();
Log.Info("All scenes validated successfully");
});
}
[MenuItem("Assets/Create/Zenject/Unit Test", false, 60)]
public static void CreateUnitTest()
{
AddCSharpClassTemplate("Unit Test", "UntitledUnitTest",
"using Zenject;"
+ "\nusing NUnit.Framework;"
+ "\n"
+ "\n[TestFixture]"
+ "\npublic class CLASS_NAME : ZenjectUnitTestFixture"
+ "\n{"
+ "\n [Test]"
+ "\n public void RunTest1()"
+ "\n {"
+ "\n // TODO"
+ "\n }"
+ "\n}");
}
[MenuItem("Assets/Create/Zenject/Integration Test", false, 60)]
public static void CreateIntegrationTest()
{
AddCSharpClassTemplate("Integration Test", "UntitledIntegrationTest",
"using Zenject;"
+ "\nusing System.Collections;"
+ "\nusing UnityEngine.TestTools;"
+ "\n"
+ "\npublic class CLASS_NAME : ZenjectIntegrationTestFixture"
+ "\n{"
+ "\n [UnityTest]"
+ "\n public IEnumerator RunTest1()"
+ "\n {"
+ "\n // Setup initial state by creating game objects from scratch, loading prefabs/scenes, etc"
+ "\n"
+ "\n PreInstall();"
+ "\n"
+ "\n // Call Container.Bind methods"
+ "\n"
+ "\n PostInstall();"
+ "\n"
+ "\n // Add test assertions for expected state"
+ "\n // Using Container.Resolve or [Inject] fields"
+ "\n yield break;"
+ "\n }"
+ "\n}");
}
[MenuItem("Assets/Create/Zenject/Scene Test", false, 60)]
public static void CreateSceneTest()
{
AddCSharpClassTemplate("Scene Test Fixture", "UntitledSceneTest",
"using Zenject;"
+ "\nusing System.Collections;"
+ "\nusing UnityEngine;"
+ "\nusing UnityEngine.TestTools;"
+ "\n"
+ "\npublic class CLASS_NAME : SceneTestFixture"
+ "\n{"
+ "\n [UnityTest]"
+ "\n public IEnumerator TestScene()"
+ "\n {"
+ "\n yield return LoadScene(\"InsertSceneNameHere\");"
+ "\n"
+ "\n // TODO: Add assertions here now that the scene has started"
+ "\n // Or you can just uncomment to simply wait some time to make sure the scene plays without errors"
+ "\n //yield return new WaitForSeconds(1.0f);"
+ "\n"
+ "\n // Note that you can use SceneContainer.Resolve to look up objects that you need for assertions"
+ "\n }"
+ "\n}");
}
}
}
#endif
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d45338750ad0b4f4b90ed09091927b46
timeCreated: 1461708053
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,352 @@
#if !NOT_UNITY3D
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ModestTree;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Zenject.Internal
{
public static class ZenUnityEditorUtil
{
// Returns true if succeeds without errors
public static bool SaveThenRunPreserveSceneSetup(Action action)
{
if (EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo())
{
var originalSceneSetup = EditorSceneManager.GetSceneManagerSetup();
try
{
action();
return true;
}
catch (Exception e)
{
Log.ErrorException(e);
return false;
}
finally
{
EditorSceneManager.RestoreSceneManagerSetup(originalSceneSetup);
}
}
return false;
}
// Feel free to call this method from an editor script, or a unit test, etc.
// An exception will be thrown if any validation errors are encountered
public static void ValidateCurrentSceneSetup()
{
bool encounteredError = false;
Application.LogCallback logCallback = (condition, stackTrace, type) =>
{
if (type == LogType.Error || type == LogType.Assert
|| type == LogType.Exception)
{
encounteredError = true;
}
};
Application.logMessageReceived += logCallback;
try
{
Assert.That(!ProjectContext.HasInstance);
ProjectContext.ValidateOnNextRun = true;
foreach (var sceneContext in GetAllSceneContexts())
{
sceneContext.Validate();
}
}
catch (Exception e)
{
Log.ErrorException(e);
encounteredError = true;
}
finally
{
Application.logMessageReceived -= logCallback;
}
if (encounteredError)
{
throw new ZenjectException("Zenject Validation Failed! See errors below for details.");
}
}
// NOTE: An exception will be thrown if any validation errors are encountered
// Returns the number of scenes that successfully validated
public static int ValidateAllActiveScenes()
{
var activeScenePaths = EditorBuildSettings.scenes.Where(x => x.enabled)
.Select(x => x.path).ToList();
foreach (var scenePath in activeScenePaths)
{
EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single);
ValidateCurrentSceneSetup();
}
return activeScenePaths.Count;
}
// Don't use this
public static void RunCurrentSceneSetup()
{
Assert.That(!ProjectContext.HasInstance);
foreach (var sceneContext in GetAllSceneContexts())
{
try
{
sceneContext.Run();
}
catch (Exception e)
{
// Add a bit more context
throw new ZenjectException(
"Scene '{0}' Failed To Start!".Fmt(sceneContext.gameObject.scene.name), e);
}
}
}
public static SceneContext GetSceneContextForScene(Scene scene)
{
var sceneContext = TryGetSceneContextForScene(scene);
Assert.IsNotNull(sceneContext,
"Could not find scene context for scene '{0}'", scene.name);
return sceneContext;
}
public static SceneContext TryGetSceneContextForScene(Scene scene)
{
if (!scene.isLoaded)
{
return null;
}
var sceneContexts = scene.GetRootGameObjects()
.SelectMany(x => x.GetComponentsInChildren<SceneContext>()).ToList();
if (sceneContexts.IsEmpty())
{
return null;
}
Assert.That(sceneContexts.Count == 1,
"Found multiple SceneContexts in scene '{0}'. Expected a maximum of one.", scene.name);
return sceneContexts[0];
}
public static SceneDecoratorContext GetDecoratorContextForScene(Scene scene)
{
var decoratorContext = TryGetDecoratorContextForScene(scene);
Assert.IsNotNull(decoratorContext,
"Could not find decorator context for scene '{0}'", scene.name);
return decoratorContext;
}
public static SceneDecoratorContext TryGetDecoratorContextForScene(Scene scene)
{
if (!scene.isLoaded)
{
return null;
}
var decoratorContexts = scene.GetRootGameObjects()
.SelectMany(x => x.GetComponentsInChildren<SceneDecoratorContext>()).ToList();
if (decoratorContexts.IsEmpty())
{
return null;
}
Assert.That(decoratorContexts.Count == 1,
"Found multiple DecoratorContexts in scene '{0}'. Expected a maximum of one.", scene.name);
return decoratorContexts[0];
}
static IEnumerable<SceneContext> GetAllSceneContexts()
{
var decoratedSceneNames = new List<string>();
for (int i = 0; i < EditorSceneManager.sceneCount; i++)
{
var scene = EditorSceneManager.GetSceneAt(i);
var sceneContext = TryGetSceneContextForScene(scene);
var decoratorContext = TryGetDecoratorContextForScene(scene);
if (sceneContext != null)
{
Assert.That(decoratorContext == null,
"Found both SceneDecoratorContext and SceneContext in the same scene '{0}'. This is not allowed", scene.name);
decoratedSceneNames.RemoveAll(x => sceneContext.ContractNames.Contains(x));
yield return sceneContext;
}
else if (decoratorContext != null)
{
Assert.That(!string.IsNullOrEmpty(decoratorContext.DecoratedContractName),
"Missing Decorated Contract Name on SceneDecoratorContext in scene '{0}'", scene.name);
decoratedSceneNames.Add(decoratorContext.DecoratedContractName);
}
}
Assert.That(decoratedSceneNames.IsEmpty(),
"Found decorator scenes without a corresponding scene to decorator. Missing scene contracts: {0}", decoratedSceneNames.Join(", "));
}
public static string ConvertAssetPathToAbsolutePath(string assetPath)
{
return Path.Combine(
Path.Combine(Path.GetFullPath(Application.dataPath), ".."), assetPath);
}
public static string ConvertFullAbsolutePathToAssetPath(string fullPath)
{
fullPath = Path.GetFullPath(fullPath);
var assetFolderFullPath = Path.GetFullPath(Application.dataPath);
if (fullPath.Length == assetFolderFullPath.Length)
{
Assert.IsEqual(fullPath, assetFolderFullPath);
return "Assets";
}
var assetPath = fullPath.Remove(0, assetFolderFullPath.Length + 1).Replace("\\", "/");
return "Assets/" + assetPath;
}
public static string GetCurrentDirectoryAssetPathFromSelection()
{
return ConvertFullAbsolutePathToAssetPath(
GetCurrentDirectoryAbsolutePathFromSelection());
}
public static string GetCurrentDirectoryAbsolutePathFromSelection()
{
var folderPath = TryGetSelectedFolderPathInProjectsTab();
if (folderPath != null)
{
return folderPath;
}
var filePath = TryGetSelectedFilePathInProjectsTab();
if (filePath != null)
{
return Path.GetDirectoryName(filePath);
}
return Application.dataPath;
}
public static string TryGetSelectedFilePathInProjectsTab()
{
return GetSelectedFilePathsInProjectsTab().OnlyOrDefault();
}
public static List<string> GetSelectedFilePathsInProjectsTab()
{
return GetSelectedPathsInProjectsTab()
.Where(x => File.Exists(x)).ToList();
}
public static List<string> GetSelectedAssetPathsInProjectsTab()
{
var paths = new List<string>();
UnityEngine.Object[] selectedAssets = Selection.GetFiltered(
typeof(UnityEngine.Object), SelectionMode.Assets);
foreach (var item in selectedAssets)
{
var assetPath = AssetDatabase.GetAssetPath(item);
if (!string.IsNullOrEmpty(assetPath))
{
paths.Add(assetPath);
}
}
return paths;
}
public static List<string> GetSelectedPathsInProjectsTab()
{
var paths = new List<string>();
UnityEngine.Object[] selectedAssets = Selection.GetFiltered(
typeof(UnityEngine.Object), SelectionMode.Assets);
foreach (var item in selectedAssets)
{
var relativePath = AssetDatabase.GetAssetPath(item);
if (!string.IsNullOrEmpty(relativePath))
{
var fullPath = Path.GetFullPath(Path.Combine(
Application.dataPath, Path.Combine("..", relativePath)));
paths.Add(fullPath);
}
}
return paths;
}
// Taken from http://wiki.unity3d.com/index.php?title=CreateScriptableObjectAsset
public static void SaveScriptableObjectAsset(
string path, ScriptableObject asset)
{
Assert.That(path.EndsWith(".asset"));
string assetPathAndName = AssetDatabase.GenerateUniqueAssetPath(path);
AssetDatabase.CreateAsset(asset, assetPathAndName);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
EditorUtility.FocusProjectWindow();
Selection.activeObject = asset;
}
// Note that the path is relative to the Assets folder
public static List<string> GetSelectedFolderPathsInProjectsTab()
{
return GetSelectedPathsInProjectsTab()
.Where(x => Directory.Exists(x)).ToList();
}
// Returns the best guess directory in projects pane
// Useful when adding to Assets -> Create context menu
// Returns null if it can't find one
// Note that the path is relative to the Assets folder for use in AssetDatabase.GenerateUniqueAssetPath etc.
public static string TryGetSelectedFolderPathInProjectsTab()
{
return GetSelectedFolderPathsInProjectsTab().OnlyOrDefault();
}
}
}
#endif
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: f798e3a2f0079b840804c0516d265f03
timeCreated: 1461710838
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,10 @@
{
"name": "Zenject-Editor",
"references": [
"Zenject"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": []
}
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: d0acddb179989574c8355991596bd3e6
timeCreated: 1531030222
licenseType: Free
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: