Initial Commit

This commit is contained in:
Sebastian Cabrera 2021-08-02 05:44:37 -04:00
parent 53eb92e9af
commit 270ab7d11f
15341 changed files with 700234 additions and 0 deletions

View file

@ -0,0 +1,60 @@
using UnityEngine;
namespace Unity.VisualScripting
{
public static class ApplicationVariables
{
public const string assetPath = "ApplicationVariables";
private static VariablesAsset _asset;
public static VariablesAsset asset
{
get
{
if (_asset == null)
{
Load();
}
return _asset;
}
}
public static void Load()
{
_asset = Resources.Load<VariablesAsset>(assetPath) ?? ScriptableObject.CreateInstance<VariablesAsset>();
}
public static VariableDeclarations runtime { get; private set; }
public static VariableDeclarations initial => asset.declarations;
public static VariableDeclarations current => Application.isPlaying ? runtime : initial;
public static void OnEnterEditMode()
{
DestroyRuntimeDeclarations(); // Required because assemblies don't reload on play mode exit
}
public static void OnExitEditMode() { }
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void OnEnterPlayMode()
{
CreateRuntimeDeclarations();
}
internal static void OnExitPlayMode() { }
private static void CreateRuntimeDeclarations()
{
runtime = asset.declarations.CloneViaFakeSerialization();
}
private static void DestroyRuntimeDeclarations()
{
runtime = null;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4d01e1923ad0e4026ac76f84efb86bd4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,7 @@
namespace Unity.VisualScripting
{
public interface IGraphDataWithVariables : IGraphData
{
VariableDeclarations variables { get; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 063c633d54a2c4cc39055f9f31c5824a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,11 @@
using System.Collections.Generic;
namespace Unity.VisualScripting
{
public interface IGraphWithVariables : IGraph
{
VariableDeclarations variables { get; }
IEnumerable<string> GetDynamicVariableNames(VariableKind kind, GraphReference reference);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0c89b45dbb68e46bcb601d11297cf52f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,15 @@
using System;
namespace Unity.VisualScripting
{
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
public sealed class InspectorVariableNameAttribute : Attribute
{
public InspectorVariableNameAttribute(ActionDirection direction)
{
this.direction = direction;
}
public ActionDirection direction { get; private set; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b7ea96f808d5944a5994aab176a2809b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,34 @@
using System;
using UnityEngine;
namespace Unity.VisualScripting
{
public static class ObjectVariables
{
public static VariableDeclarations Declarations(GameObject source, bool autoAddComponent, bool throwOnMissing)
{
Ensure.That(nameof(source)).IsNotNull(source);
var variables = source.GetComponent<Variables>();
if (variables == null && autoAddComponent)
{
variables = source.AddComponent<Variables>();
}
if (variables != null)
{
return variables.declarations;
}
if (throwOnMissing)
{
throw new InvalidOperationException($"Game object '{source.name}' does not have variables.");
}
else
{
return null;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 198f804e1b99b4ddeb98fa9709a9ad43
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,180 @@
using System;
using System.Linq;
using UnityEngine;
using UnityObject = UnityEngine.Object;
namespace Unity.VisualScripting
{
public static class SavedVariables
{
#region Storage
public const string assetPath = "SavedVariables";
public const string playerPrefsKey = "LudiqSavedVariables";
private static VariablesAsset _asset;
public static VariablesAsset asset
{
get
{
if (_asset == null)
{
Load();
}
return _asset;
}
}
public static void Load()
{
_asset = Resources.Load<VariablesAsset>(assetPath) ?? ScriptableObject.CreateInstance<VariablesAsset>();
}
#endregion
#region Lifecycle
public static void OnEnterEditMode()
{
FetchSavedDeclarations();
DestroyMergedDeclarations(); // Required because assemblies don't reload on play mode exit
}
public static void OnExitEditMode()
{
SaveDeclarations(saved);
}
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void OnEnterPlayMode()
{
FetchSavedDeclarations();
MergeInitialAndSavedDeclarations();
VariablesSaver.Instantiate();
}
internal static void OnExitPlayMode()
{
SaveDeclarations(merged);
}
#endregion
#region Declarations
public static VariableDeclarations initial => asset.declarations;
public static VariableDeclarations saved { get; private set; }
public static VariableDeclarations merged { get; private set; }
public static VariableDeclarations current => Application.isPlaying ? merged : initial;
public static void SaveDeclarations(VariableDeclarations declarations)
{
WarnAndNullifyUnityObjectReferences(declarations);
try
{
var data = declarations.Serialize();
if (data.objectReferences.Length != 0)
{
// Hopefully, WarnAndNullify will have prevented this exception,
// but in case an object reference was nested as a member of the
// serialized objects, it wouldn't have caught it, and thus we need
// to abort the save process and inform the user.
throw new InvalidOperationException("Cannot use Unity object variable references in saved variables.");
}
PlayerPrefs.SetString(playerPrefsKey, data.json);
PlayerPrefs.Save();
}
catch (Exception ex)
{
Debug.LogWarning($"Failed to save variables to player prefs: \n{ex}");
}
}
public static void FetchSavedDeclarations()
{
if (PlayerPrefs.HasKey(playerPrefsKey))
{
try
{
saved = (VariableDeclarations)new SerializationData(PlayerPrefs.GetString(playerPrefsKey)).Deserialize();
}
catch (Exception ex)
{
Debug.LogWarning($"Failed to fetch saved variables from player prefs: \n{ex}");
saved = new VariableDeclarations();
}
}
else
{
saved = new VariableDeclarations();
}
}
private static void MergeInitialAndSavedDeclarations()
{
merged = initial.CloneViaFakeSerialization();
WarnAndNullifyUnityObjectReferences(merged);
foreach (var name in saved.Select(vd => vd.name))
{
if (!merged.IsDefined(name))
{
merged[name] = saved[name];
}
else if (merged[name] == null)
{
if (saved[name] == null || saved[name].GetType().IsNullable())
{
merged[name] = saved[name];
}
else
{
Debug.LogWarning($"Cannot convert saved player pref '{name}' to null.\n");
}
}
else
{
if (saved[name].IsConvertibleTo(merged[name].GetType(), true))
{
merged[name] = saved[name];
}
else
{
Debug.LogWarning($"Cannot convert saved player pref '{name}' to expected type ({merged[name].GetType()}).\nReverting to initial value.");
}
}
}
}
private static void DestroyMergedDeclarations()
{
merged = null;
}
private static void WarnAndNullifyUnityObjectReferences(VariableDeclarations declarations)
{
Ensure.That(nameof(declarations)).IsNotNull(declarations);
foreach (var declaration in declarations)
{
if (declaration.value is UnityObject)
{
Debug.LogWarning($"Saved variable '{declaration.name}' refers to a Unity object. This is not supported. Its value will be null.");
declarations[declaration.name] = null;
}
}
}
#endregion
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ef3cfbade00494fc5b1e96a2b78e4650
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,55 @@
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Unity.VisualScripting
{
[Singleton(Name = "Scene Variables", Automatic = true, Persistent = false)]
[RequireComponent(typeof(Variables))]
[DisableAnnotation]
[AddComponentMenu("")]
[IncludeInSettings(false)]
public sealed class SceneVariables : MonoBehaviour, ISingleton
{
public static SceneVariables Instance(Scene scene)
{
return SceneSingleton<SceneVariables>.InstanceIn(scene);
}
public static bool InstantiatedIn(Scene scene)
{
return SceneSingleton<SceneVariables>.InstantiatedIn(scene);
}
public static VariableDeclarations For(Scene? scene)
{
Ensure.That(nameof(scene)).IsNotNull(scene);
return Instance(scene.Value).variables.declarations;
}
private void Awake()
{
SceneSingleton<SceneVariables>.Awake(this);
}
private void OnDestroy()
{
SceneSingleton<SceneVariables>.OnDestroy(this);
}
private Variables _variables;
public Variables variables
{
get
{
if (_variables == null)
{
_variables = gameObject.GetOrAddComponent<Variables>();
}
return _variables;
}
}
}
}

View file

@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 765181c9ef4b24d32a4f7cbd2ef370dc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon:
fileID: 2800000
guid: 85b733ff25d1eee479a6d510562bd0b2
type: 3
userData: ''
assetBundleName: ''
assetBundleVariant: ''
...

View file

@ -0,0 +1,23 @@
using System;
namespace Unity.VisualScripting
{
[SerializationVersion("A")]
public sealed class VariableDeclaration
{
[Obsolete(Serialization.ConstructorWarning)]
public VariableDeclaration() { }
public VariableDeclaration(string name, object value)
{
this.name = name;
this.value = value;
}
[Serialize]
public string name { get; private set; }
[Serialize]
public object value { get; set; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e762a163978c84082924f89ccdd7dbf3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,29 @@
using System.Collections.ObjectModel;
namespace Unity.VisualScripting
{
[SerializationVersion("A")]
public sealed class VariableDeclarationCollection : KeyedCollection<string, VariableDeclaration>, IKeyedCollection<string, VariableDeclaration>
{
protected override string GetKeyForItem(VariableDeclaration item)
{
return item.name;
}
public void EditorRename(VariableDeclaration item, string newName)
{
ChangeItemKey(item, newName);
}
public bool TryGetValue(string key, out VariableDeclaration value)
{
if (Dictionary == null)
{
value = default(VariableDeclaration);
return false;
}
return Dictionary.TryGetValue(key, out value);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 37555ce9c5e6b4629a515670c9f64bef
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,106 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Unity.VisualScripting
{
[SerializationVersion("A")]
public sealed class VariableDeclarations : IEnumerable<VariableDeclaration>, ISpecifiesCloner
{
public VariableDeclarations()
{
collection = new VariableDeclarationCollection();
}
[Serialize, InspectorWide(true)]
private VariableDeclarationCollection collection;
public object this[[InspectorVariableName(ActionDirection.Any)] string variable]
{
get => Get(variable);
set => Set(variable, value);
}
public void Set([InspectorVariableName(ActionDirection.Set)] string variable, object value)
{
if (string.IsNullOrEmpty(variable))
{
// Do nothing: safer for e.g. default event configurations,
// where we don't specify a variable name. If we specified a default,
// we might override a user value inadvertently.
return;
}
if (collection.TryGetValue(variable, out var declaration))
{
declaration.value = value;
}
else
{
collection.Add(new VariableDeclaration(variable, value));
}
}
public object Get([InspectorVariableName(ActionDirection.Get)] string variable)
{
if (string.IsNullOrEmpty(variable))
{
throw new ArgumentException("No variable name specified.", nameof(variable));
}
if (collection.TryGetValue(variable, out var declaration))
{
return declaration.value;
}
throw new InvalidOperationException($"Variable not found: '{variable}'.");
}
public T Get<T>([InspectorVariableName(ActionDirection.Get)] string variable)
{
return (T)Get(variable, typeof(T));
}
public object Get([InspectorVariableName(ActionDirection.Get)] string variable, Type expectedType)
{
return ConversionUtility.Convert(Get(variable), expectedType);
}
public void Clear()
{
collection.Clear();
}
public bool IsDefined([InspectorVariableName(ActionDirection.Any)] string variable)
{
if (string.IsNullOrEmpty(variable))
{
throw new ArgumentException("No variable name specified.", nameof(variable));
}
return collection.Contains(variable);
}
public VariableDeclaration GetDeclaration(string variable)
{
if (collection.TryGetValue(variable, out var declaration))
{
return declaration;
}
throw new InvalidOperationException($"Variable not found: '{variable}'.");
}
public IEnumerator<VariableDeclaration> GetEnumerator()
{
return collection.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)collection).GetEnumerator();
}
ICloner ISpecifiesCloner.cloner => VariableDeclarationsCloner.instance;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 836aceb6bf15047ffb768a3f1b73f56a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,29 @@
using System;
namespace Unity.VisualScripting
{
// Variable declarations are cloned for every graph instantiation, it's worth speeding it up.
public sealed class VariableDeclarationsCloner : Cloner<VariableDeclarations>
{
public static readonly VariableDeclarationsCloner instance = new VariableDeclarationsCloner();
public override bool Handles(Type type)
{
return type == typeof(VariableDeclarations);
}
public override VariableDeclarations ConstructClone(Type type, VariableDeclarations original)
{
return new VariableDeclarations();
}
public override void FillClone(Type type, ref VariableDeclarations clone, VariableDeclarations original, CloningContext context)
{
foreach (var variable in original)
{
clone[variable.name] = variable.value.CloneViaFakeSerialization();
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6689e9a3380e54bd0b495f1c349e25ec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,37 @@
namespace Unity.VisualScripting
{
public enum VariableKind
{
/// <summary>
/// Temporary variables local to the execution flow.
/// </summary>
Flow,
/// <summary>
/// Variables local to the current graph.
/// </summary>
Graph,
/// <summary>
/// Variables shared across the current game object.
/// </summary>
Object,
/// <summary>
/// Variables shared across the current scene.
/// </summary>
Scene,
/// <summary>
/// Variables shared across scenes.
/// These will be reset when the application quits.
/// </summary>
Application,
/// <summary>
/// Variables that persist even after the application quits.
/// Unity object references are not supported.
/// </summary>
Saved
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bbd0ae7801d694a1990062998bcdb7fe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,15 @@
using System;
namespace Unity.VisualScripting
{
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class VariableKindAttribute : Attribute
{
public VariableKindAttribute(VariableKind kind)
{
this.kind = kind;
}
public VariableKind kind { get; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 75ea31011c3384cc1b2a331c903c71e8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,92 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Unity.VisualScripting
{
[AddComponentMenu("Visual Scripting/Variables")]
[DisableAnnotation]
[IncludeInSettings(false)]
public class Variables : LudiqBehaviour, IAotStubbable
{
[Serialize, Inspectable, VariableKind(VariableKind.Object)]
public VariableDeclarations declarations { get; internal set; } = new VariableDeclarations();
public static VariableDeclarations Graph(GraphPointer pointer)
{
Ensure.That(nameof(pointer)).IsNotNull(pointer);
if (pointer.hasData)
{
return GraphInstance(pointer);
}
else
{
return GraphDefinition(pointer);
}
}
public static VariableDeclarations GraphInstance(GraphPointer pointer)
{
return pointer.GetGraphData<IGraphDataWithVariables>().variables;
}
public static VariableDeclarations GraphDefinition(GraphPointer pointer)
{
return GraphDefinition((IGraphWithVariables)pointer.graph);
}
public static VariableDeclarations GraphDefinition(IGraphWithVariables graph)
{
return graph.variables;
}
public static VariableDeclarations Object(GameObject go) => go.GetOrAddComponent<Variables>().declarations;
public static VariableDeclarations Object(Component component) => Object(component.gameObject);
public static VariableDeclarations Scene(Scene? scene) => SceneVariables.For(scene);
public static VariableDeclarations Scene(GameObject go) => Scene(go.scene);
public static VariableDeclarations Scene(Component component) => Scene(component.gameObject);
public static VariableDeclarations ActiveScene => Scene(SceneManager.GetActiveScene());
public static VariableDeclarations Application => ApplicationVariables.current;
public static VariableDeclarations Saved => SavedVariables.current;
public static bool ExistOnObject(GameObject go) => go.GetComponent<Variables>() != null;
public static bool ExistOnObject(Component component) => ExistOnObject(component.gameObject);
public static bool ExistInScene(Scene? scene) => scene != null && SceneVariables.InstantiatedIn(scene.Value);
public static bool ExistInActiveScene => ExistInScene(SceneManager.GetActiveScene());
[ContextMenu("Show Data...")]
protected override void ShowData()
{
base.ShowData();
}
public IEnumerable<object> aotStubs
{
get
{
// Include the constructors for AOT serialization
// https://support.ludiq.io/communities/5/topics/3952-x
foreach (var declaration in declarations)
{
var defaultConstructor = declaration.value?.GetType().GetPublicDefaultConstructor();
if (defaultConstructor != null)
{
yield return defaultConstructor;
}
}
}
}
}
}

View file

@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: e741851cba3ad425c91ecf922cc6b379
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon:
fileID: 2800000
guid: f4241d97facf9bf4881222c2789a4c14
type: 3
userData: ''
assetBundleName: ''
assetBundleVariant: ''
...

View file

@ -0,0 +1,17 @@
using UnityEngine;
namespace Unity.VisualScripting
{
[IncludeInSettings(false)]
public sealed class VariablesAsset : LudiqScriptableObject
{
[Serialize, Inspectable, InspectorWide(true)]
public VariableDeclarations declarations { get; internal set; } = new VariableDeclarations();
[ContextMenu("Show Data...")]
protected override void ShowData()
{
base.ShowData();
}
}
}

View file

@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 1c63e9930078442aa987ca9a1511ae44
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon:
fileID: 2800000
guid: 7e1f811eb716b3844aad1127122172ed
type: 3
userData: ''
assetBundleName: ''
assetBundleVariant: ''
...

View file

@ -0,0 +1,46 @@
using UnityEngine;
namespace Unity.VisualScripting
{
/// <summary>
/// Listens to the OnApplicationQuit on OnApplicationPause
/// hooks to trigger the serialization of saved variables into PlayerPrefs.
/// </summary>
[Singleton(Name = "Variables Saver", Automatic = true, Persistent = true)]
[AddComponentMenu("")]
[DisableAnnotation]
[IncludeInSettings(false)]
public class VariablesSaver : MonoBehaviour, ISingleton
{
private void Awake()
{
Singleton<VariablesSaver>.Awake(this);
}
private void OnDestroy()
{
Singleton<VariablesSaver>.OnDestroy(this);
}
private void OnApplicationQuit()
{
SavedVariables.OnExitPlayMode();
ApplicationVariables.OnExitPlayMode();
}
private void OnApplicationPause(bool isPaused)
{
if (!isPaused)
{
return;
}
SavedVariables.OnExitPlayMode();
ApplicationVariables.OnExitPlayMode();
}
public static VariablesSaver instance => Singleton<VariablesSaver>.instance;
public static void Instantiate() => Singleton<VariablesSaver>.Instantiate();
}
}

View file

@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 559342c7f4b384dca87c44931ae7a8d4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon:
fileID: 2800000
guid: c49720c31a0991d48aa5d1fcf9d6d1a6
type: 3
userData: ''
assetBundleName: ''
assetBundleVariant: ''
...