using System.Collections.Generic;
using System.ComponentModel;
using UnityEngine;
using UnityEngine.SceneManagement;
using Component = UnityEngine.Component;
namespace Unity.VisualScripting
{
/// <summary>
/// Component that handles the variables that you can use in the Script Graphs of your project.
/// </summary>
/// <remarks>
/// <para>This component is automatically added to a GameObject when a <see cref="ScriptMachine"/> is
/// added, or when <see cref="SceneVariables"/> (Script) are created using the “Visual Scripting Scene Variables”
/// menu in the Hierarchy.</para>
///
/// <para>The <see cref="Variables"/> component manages the object variables for any GameObject
/// that you add it to. You can access these variables from the Object Variables section of the Blackboard
/// of any <see cref="ScriptMachine"/> associated with the GameObject.</para>
///
/// <para>The <see cref="Variables"/> component also manages the scene variables for any “VisualScripting SceneVariables”
/// GameObject that you add it to. You can access these scene variables from the Scene Variables
/// section of the Blackboard of any <see cref="ScriptMachine"/> you add to this same scene.
/// </para>
///
/// <para><b>Note</b>: It's best not to add a <see cref="ScriptMachine"/> component to a GameObject that also
/// includes the <see cref="SceneVariables"/> (Script). If you do this, the Blackboard displays Scene
/// Variables that are also present in Object Variables and this can cause confusion.</para>
///
/// <para>For more information on how to interact with Variables, refer to the <a href="../manual/vs-variables.html">User Manual</a>.</para>
/// </remarks>
/// <example>
/// <para>The following example shows how to programmatically change the value of a variable used in a Visual Scripting graph.
/// Every time you press the Space key, we double the value of the velocity variable.
/// <b>Note</b>: You can try this example in a Script Graph.</para>
///
/// <code source="../../../DocCodeExamples/VariableExamples.cs" region="PlayerController" title="PlayerController"/>
/// </example>
[AddComponentMenu("Visual Scripting/Variables")]
[DisableAnnotation]
[IncludeInSettings(false)]
[VisualScriptingHelpURL(typeof(Variables))]
public class Variables : LudiqBehaviour, IAotStubbable
{
/// <summary>
/// Retrieves a collection of the variables set in the Variables component.
/// </summary>
[Serialize, Inspectable]
public VariableDeclarations declarations { get; internal set; } =
new VariableDeclarations() { Kind = VariableKind.Object };
/// <summary>
/// Retrieves a collection of graph variables for a given graph.
/// </summary>
/// <param name="pointer">The reference to a graph.</param>
/// <returns>If the graph is instantiated, returns the graph variables of that instantiated graph.
/// Otherwise, returns the graph variables from the definition of the given graph (i.e.: from the graph asset definition)</returns>
/// <exception cref="System.ArgumentNullException">
/// If <paramref name="pointer" /> is <c>null</c>.
/// </exception>
public static VariableDeclarations Graph(GraphPointer pointer)
{
Ensure.That(nameof(pointer)).IsNotNull(pointer);
if (pointer.hasData)
{
return GraphInstance(pointer);
}
else
{
return GraphDefinition(pointer);
}
}
/// <summary>
/// Retrieves a collection of graph variables of an instantiated graph.
/// </summary>
/// <param name="pointer">The reference to a graph.</param>
/// <returns>A collection of graph variables of an instantiated graph.</returns>
/// <exception cref="Unity.VisualScripting.GraphPointerException">
/// If the graph data cannot be read. Which probably means that the graph is not instantiated.
/// </exception>
public static VariableDeclarations GraphInstance(GraphPointer pointer)
{
return pointer.GetGraphData<IGraphDataWithVariables>().variables;
}
/// <summary>
/// Retrieves a collection of graph variables from the definition of a graph.
/// </summary>
/// <param name="pointer">The reference to a graph.</param>
/// <returns>A collection of graph variables of a given graph.</returns>
public static VariableDeclarations GraphDefinition(GraphPointer pointer)
{
return GraphDefinition((IGraphWithVariables)pointer.graph);
}
/// <summary>
/// Retrieves a collection of graph variables from the definition of a graph.
/// </summary>
/// <param name="graph">The reference of a graph</param>
/// <returns>A collection of graph variables of a given graph.</returns>
public static VariableDeclarations GraphDefinition(IGraphWithVariables graph)
{
return graph.variables;
}
/// <summary>
/// Retrieves a collection of the object variables of a given <see cref="GameObject"/>.
/// </summary>
/// <param name="go">The <see cref="GameObject"/> whose object variables will be returned.</param>
/// <returns>A collection of the object variables contained in the Variables component of the <see cref="GameObject"/> that was passed as a parameter.</returns>
/// <remarks>If the <see cref="GameObject"/> doesn't have a Variables component, it is supplied with one by default and the returned collection is empty.</remarks>
public static VariableDeclarations Object(GameObject go) => go.GetOrAddComponent<Variables>().declarations;
/// <summary>
/// Retrieves a collection of the object variables of a given <see cref="GameObject"/>.
/// </summary>
/// <param name="component">The <see cref="Component"/> whose <see cref="GameObject"/>'s object variables are returned.</param>
/// <returns>A collection of object variables contained in the Variables component of the <see cref="Component"/>'s <see cref="GameObject"/>.</returns>
/// <remarks>If the GameObject does not have a Variables component, a Variables component is added to the <see cref="GameObject"/> and the returned collection is empty</remarks>
public static VariableDeclarations Object(Component component) => Object(component.gameObject);
/// <summary>
/// Retrieves a collection of scene variables for a given <see cref="UnityEngine.SceneManagement.Scene">Scene</see>.
/// </summary>
/// <param name="scene">The <see cref="UnityEngine.SceneManagement.Scene">Scene</see> whose scene variables are returned.</param>
/// <returns>A collection of scene variables contained in the Variables component associated with the <see cref="SceneVariables"/> (Script).</returns>
/// <exception cref="System.ArgumentNullException">
/// If <paramref name="scene" /> is <c>null</c>.
/// </exception>
public static VariableDeclarations Scene(Scene? scene) => SceneVariables.For(scene);
/// <summary>
/// Retrieves a collection of scene variables for a given <see cref="UnityEngine.SceneManagement.Scene">Scene</see>.
/// </summary>
/// <param name="go">A <see cref="GameObject"/> whose <see cref="UnityEngine.SceneManagement.Scene">Scene</see> will be accessed to get its variables.</param>
/// <returns>A collection of scene variables contained in the Variables component associated with the <see cref="SceneVariables"/> (Script).</returns>
/// <exception cref="System.ArgumentNullException">
/// If the `go.scene` is <c>null</c>.
/// </exception>
public static VariableDeclarations Scene(GameObject go) => Scene(go.scene);
/// <summary>
/// Retrieves a collection of scene variables for a given <see cref="UnityEngine.SceneManagement.Scene">Scene</see>.
/// </summary>
/// <param name="component">A <see cref="Component"/> whose <see cref="GameObject"/>'s <see cref="UnityEngine.SceneManagement.Scene">Scene</see>'s scene variables will be returned.</param>
/// <returns>A collection of scene variables contained in the Variables component associated with the <see cref="SceneVariables"/> (Script).</returns>
/// <exception cref="System.ArgumentNullException">
/// If the `component.go` or `component.go.scene` is <c>null</c>.
/// </exception>
public static VariableDeclarations Scene(Component component) => Scene(component.gameObject);
/// <summary>
/// Retrieve a collection of the scene variables of the active scene.
/// </summary>
public static VariableDeclarations ActiveScene => Scene(SceneManager.GetActiveScene());
/// <summary>
/// Retrieve a collection of the application variables.
/// </summary>
public static VariableDeclarations Application => ApplicationVariables.current;
/// <summary>
/// Retrieve a collection of the saved variables.
/// </summary>
public static VariableDeclarations Saved => SavedVariables.current;
/// <summary>
/// Check if a Variables component exists on the <see cref="GameObject"/> passed as a parameter.
/// </summary>
/// <param name="go">The <see cref="GameObject"/> we want to check for the Variables component.</param>
/// <returns>True if the <see cref="GameObject"/> has a Variables component. Otherwise, returns false.</returns>
public static bool ExistOnObject(GameObject go) => go.GetComponent<Variables>() != null;
/// <summary>
/// Check if a Variables component exists on the <see cref="Component"/>'s <see cref="GameObject"/> passed as a parameter.
/// </summary>
/// <param name="component">A <see cref="Component"/> for which we want to know if the <see cref="GameObject"/> has a Variables component.</param>
/// <returns>True if the <see cref="Component"/>'s <see cref="GameObject"/> has a Variables component. Otherwise, returns false.</returns>
public static bool ExistOnObject(Component component) => ExistOnObject(component.gameObject);
/// <summary>
/// Check if there is a SceneVariables component instantiated to find out if the scene contains scene variables.
/// </summary>
/// <param name="scene">A <see cref="UnityEngine.SceneManagement.Scene">Scene</see> we want to check for scene variables.</param>
/// <returns>True if the <see cref="UnityEngine.SceneManagement.Scene">Scene</see> is not null and contains scene variables. Otherwise, returns false.</returns>
public static bool ExistInScene(Scene? scene) => scene != null && SceneVariables.InstantiatedIn(scene.Value);
/// <summary>
/// Check if there is a SceneVariables component instantiated to find out if the active scene contains scene variables.
/// </summary>
/// <remarks>Returns true if the active scene contains scene variables. Otherwise, returns false.</remarks>
public static bool ExistInActiveScene => ExistInScene(SceneManager.GetActiveScene());
[ContextMenu("Show Data...")]
protected override void ShowData()
{
base.ShowData();
}
/// <summary>
/// Don't use this method. It is for Unity Visual Scripting internal usage only.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public IEnumerable<object> GetAotStubs(HashSet<object> visited)
{
// Include the constructors for AOT serialization
// https://support.ludiq.io/communities/5/topics/3952-x
foreach (var declaration in declarations)
{
var type = declaration.value?.GetType();
if (type == null)
continue;
// UVSB-2576: When the type is or inherits from AudioMixer (eg: AudioMixerController inherits from AudioMixer), its constructor shouldn't be added to AotStubs:
// - AudioMixer is a singleton that refers to a specific asset and shouldn't be instantiated.
// - AudioMixerController has a public constructor but is an Editor type and shouldn't be part of AotStubs.
const string audioMixerTypeName = "UnityEngine.Audio.AudioMixer";
const string audioControllerTypeName = "UnityEditor.Audio.AudioMixerController";
if (!string.IsNullOrEmpty(type.FullName) && (type.FullName.Contains(audioMixerTypeName) || type.FullName.Contains(audioControllerTypeName)))
continue;
var defaultConstructor = type.GetPublicDefaultConstructor();
if (defaultConstructor != null)
{
yield return defaultConstructor;
}
}
}
}
}