Aucune description
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using UnityEngine;
  6. namespace Unity.VisualScripting
  7. {
  8. [SerializationVersion("A")]
  9. public abstract class Unit : GraphElement<FlowGraph>, IUnit
  10. {
  11. public class DebugData : IUnitDebugData
  12. {
  13. public int lastInvokeFrame { get; set; }
  14. public float lastInvokeTime { get; set; }
  15. public Exception runtimeException { get; set; }
  16. }
  17. protected Unit() : base()
  18. {
  19. controlInputs = new UnitPortCollection<ControlInput>(this);
  20. controlOutputs = new UnitPortCollection<ControlOutput>(this);
  21. valueInputs = new UnitPortCollection<ValueInput>(this);
  22. valueOutputs = new UnitPortCollection<ValueOutput>(this);
  23. invalidInputs = new UnitPortCollection<InvalidInput>(this);
  24. invalidOutputs = new UnitPortCollection<InvalidOutput>(this);
  25. relations = new ConnectionCollection<IUnitRelation, IUnitPort, IUnitPort>();
  26. defaultValues = new Dictionary<string, object>();
  27. }
  28. public virtual IGraphElementDebugData CreateDebugData()
  29. {
  30. return new DebugData();
  31. }
  32. public override void AfterAdd()
  33. {
  34. // Important to define before notifying instances
  35. Define();
  36. base.AfterAdd();
  37. }
  38. public override void BeforeRemove()
  39. {
  40. base.BeforeRemove();
  41. Disconnect();
  42. }
  43. public override void Instantiate(GraphReference instance)
  44. {
  45. base.Instantiate(instance);
  46. if (this is IGraphEventListener listener && XGraphEventListener.IsHierarchyListening(instance))
  47. {
  48. listener.StartListening(instance);
  49. }
  50. }
  51. public override void Uninstantiate(GraphReference instance)
  52. {
  53. if (this is IGraphEventListener listener)
  54. {
  55. listener.StopListening(instance);
  56. }
  57. base.Uninstantiate(instance);
  58. }
  59. #region Poutine
  60. protected void CopyFrom(Unit source)
  61. {
  62. base.CopyFrom(source);
  63. defaultValues = source.defaultValues;
  64. }
  65. #endregion
  66. #region Definition
  67. [DoNotSerialize]
  68. public virtual bool canDefine => true;
  69. [DoNotSerialize]
  70. public bool failedToDefine => definitionException != null;
  71. [DoNotSerialize]
  72. public bool isDefined { get; private set; }
  73. protected abstract void Definition();
  74. protected virtual void AfterDefine() { }
  75. protected virtual void BeforeUndefine() { }
  76. private void Undefine()
  77. {
  78. // Because a node is always undefined on definition,
  79. // even if it wasn't defined before, we make sure the user
  80. // code for undefinition can safely presume it was defined.
  81. if (isDefined)
  82. {
  83. BeforeUndefine();
  84. }
  85. Disconnect();
  86. defaultValues.Clear();
  87. controlInputs.Clear();
  88. controlOutputs.Clear();
  89. valueInputs.Clear();
  90. valueOutputs.Clear();
  91. invalidInputs.Clear();
  92. invalidOutputs.Clear();
  93. relations.Clear();
  94. isDefined = false;
  95. }
  96. public void EnsureDefined()
  97. {
  98. if (!isDefined)
  99. {
  100. Define();
  101. }
  102. }
  103. public void Define()
  104. {
  105. var preservation = UnitPreservation.Preserve(this);
  106. // A node needs to undefine even if it wasn't defined,
  107. // because there might be invalid ports and connections
  108. // that we need to clear to avoid duplicates on definition.
  109. Undefine();
  110. if (canDefine)
  111. {
  112. try
  113. {
  114. Definition();
  115. isDefined = true;
  116. definitionException = null;
  117. AfterDefine();
  118. }
  119. catch (Exception ex)
  120. {
  121. Undefine();
  122. definitionException = ex;
  123. Debug.LogWarning($"Failed to define {this}:\n{ex}");
  124. }
  125. }
  126. preservation.RestoreTo(this);
  127. }
  128. public void RemoveUnconnectedInvalidPorts()
  129. {
  130. foreach (var unconnectedInvalidInput in invalidInputs.Where(p => !p.hasAnyConnection).ToArray())
  131. {
  132. invalidInputs.Remove(unconnectedInvalidInput);
  133. }
  134. foreach (var unconnectedInvalidOutput in invalidOutputs.Where(p => !p.hasAnyConnection).ToArray())
  135. {
  136. invalidOutputs.Remove(unconnectedInvalidOutput);
  137. }
  138. }
  139. #endregion
  140. #region Ports
  141. [DoNotSerialize]
  142. public IUnitPortCollection<ControlInput> controlInputs { get; }
  143. [DoNotSerialize]
  144. public IUnitPortCollection<ControlOutput> controlOutputs { get; }
  145. [DoNotSerialize]
  146. public IUnitPortCollection<ValueInput> valueInputs { get; }
  147. [DoNotSerialize]
  148. public IUnitPortCollection<ValueOutput> valueOutputs { get; }
  149. [DoNotSerialize]
  150. public IUnitPortCollection<InvalidInput> invalidInputs { get; }
  151. [DoNotSerialize]
  152. public IUnitPortCollection<InvalidOutput> invalidOutputs { get; }
  153. [DoNotSerialize]
  154. public IEnumerable<IUnitInputPort> inputs => LinqUtility.Concat<IUnitInputPort>(controlInputs, valueInputs, invalidInputs);
  155. [DoNotSerialize]
  156. public IEnumerable<IUnitOutputPort> outputs => LinqUtility.Concat<IUnitOutputPort>(controlOutputs, valueOutputs, invalidOutputs);
  157. [DoNotSerialize]
  158. public IEnumerable<IUnitInputPort> validInputs => LinqUtility.Concat<IUnitInputPort>(controlInputs, valueInputs);
  159. [DoNotSerialize]
  160. public IEnumerable<IUnitOutputPort> validOutputs => LinqUtility.Concat<IUnitOutputPort>(controlOutputs, valueOutputs);
  161. [DoNotSerialize]
  162. public IEnumerable<IUnitPort> ports => LinqUtility.Concat<IUnitPort>(inputs, outputs);
  163. [DoNotSerialize]
  164. public IEnumerable<IUnitPort> invalidPorts => LinqUtility.Concat<IUnitPort>(invalidInputs, invalidOutputs);
  165. [DoNotSerialize]
  166. public IEnumerable<IUnitPort> validPorts => LinqUtility.Concat<IUnitPort>(validInputs, validOutputs);
  167. public event Action onPortsChanged;
  168. public void PortsChanged()
  169. {
  170. onPortsChanged?.Invoke();
  171. }
  172. #endregion
  173. #region Default Values
  174. [Serialize]
  175. public Dictionary<string, object> defaultValues { get; private set; }
  176. #endregion
  177. #region Connections
  178. [DoNotSerialize]
  179. public IConnectionCollection<IUnitRelation, IUnitPort, IUnitPort> relations { get; private set; }
  180. [DoNotSerialize]
  181. public IEnumerable<IUnitConnection> connections => ports.SelectMany(p => p.connections);
  182. public void Disconnect()
  183. {
  184. // Can't use a foreach because invalid ports may get removed as they disconnect
  185. while (ports.Any(p => p.hasAnyConnection))
  186. {
  187. ports.First(p => p.hasAnyConnection).Disconnect();
  188. }
  189. }
  190. #endregion
  191. #region Analysis
  192. [DoNotSerialize]
  193. public virtual bool isControlRoot { get; protected set; } = false;
  194. #endregion
  195. #region Helpers
  196. protected void EnsureUniqueInput(string key)
  197. {
  198. if (controlInputs.Contains(key) || valueInputs.Contains(key) || invalidInputs.Contains(key))
  199. {
  200. throw new ArgumentException($"Duplicate input for '{key}' in {GetType()}.");
  201. }
  202. }
  203. protected void EnsureUniqueOutput(string key)
  204. {
  205. if (controlOutputs.Contains(key) || valueOutputs.Contains(key) || invalidOutputs.Contains(key))
  206. {
  207. throw new ArgumentException($"Duplicate output for '{key}' in {GetType()}.");
  208. }
  209. }
  210. protected ControlInput ControlInput(string key, Func<Flow, ControlOutput> action)
  211. {
  212. EnsureUniqueInput(key);
  213. var port = new ControlInput(key, action);
  214. controlInputs.Add(port);
  215. return port;
  216. }
  217. protected ControlInput ControlInputCoroutine(string key, Func<Flow, IEnumerator> coroutineAction)
  218. {
  219. EnsureUniqueInput(key);
  220. var port = new ControlInput(key, coroutineAction);
  221. controlInputs.Add(port);
  222. return port;
  223. }
  224. protected ControlInput ControlInputCoroutine(string key, Func<Flow, ControlOutput> action, Func<Flow, IEnumerator> coroutineAction)
  225. {
  226. EnsureUniqueInput(key);
  227. var port = new ControlInput(key, action, coroutineAction);
  228. controlInputs.Add(port);
  229. return port;
  230. }
  231. protected ControlOutput ControlOutput(string key)
  232. {
  233. EnsureUniqueOutput(key);
  234. var port = new ControlOutput(key);
  235. controlOutputs.Add(port);
  236. return port;
  237. }
  238. protected ValueInput ValueInput(Type type, string key)
  239. {
  240. EnsureUniqueInput(key);
  241. var port = new ValueInput(key, type);
  242. valueInputs.Add(port);
  243. return port;
  244. }
  245. protected ValueInput ValueInput<T>(string key)
  246. {
  247. return ValueInput(typeof(T), key);
  248. }
  249. protected ValueInput ValueInput<T>(string key, T @default)
  250. {
  251. var port = ValueInput<T>(key);
  252. port.SetDefaultValue(@default);
  253. return port;
  254. }
  255. protected ValueOutput ValueOutput(Type type, string key)
  256. {
  257. EnsureUniqueOutput(key);
  258. var port = new ValueOutput(key, type);
  259. valueOutputs.Add(port);
  260. return port;
  261. }
  262. protected ValueOutput ValueOutput(Type type, string key, Func<Flow, object> getValue)
  263. {
  264. EnsureUniqueOutput(key);
  265. var port = new ValueOutput(key, type, getValue);
  266. valueOutputs.Add(port);
  267. return port;
  268. }
  269. protected ValueOutput ValueOutput<T>(string key)
  270. {
  271. return ValueOutput(typeof(T), key);
  272. }
  273. protected ValueOutput ValueOutput<T>(string key, Func<Flow, T> getValue)
  274. {
  275. return ValueOutput(typeof(T), key, (recursion) => getValue(recursion));
  276. }
  277. private void Relation(IUnitPort source, IUnitPort destination)
  278. {
  279. relations.Add(new UnitRelation(source, destination));
  280. }
  281. /// <summary>
  282. /// Triggering the destination may fetch the source value.
  283. /// </summary>
  284. protected void Requirement(ValueInput source, ControlInput destination)
  285. {
  286. Relation(source, destination);
  287. }
  288. /// <summary>
  289. /// Getting the value of the destination may fetch the value of the source.
  290. /// </summary>
  291. protected void Requirement(ValueInput source, ValueOutput destination)
  292. {
  293. Relation(source, destination);
  294. }
  295. /// <summary>
  296. /// Triggering the source may assign the destination value on the flow.
  297. /// </summary>
  298. protected void Assignment(ControlInput source, ValueOutput destination)
  299. {
  300. Relation(source, destination);
  301. }
  302. /// <summary>
  303. /// Triggering the source may trigger the destination.
  304. /// </summary>
  305. protected void Succession(ControlInput source, ControlOutput destination)
  306. {
  307. Relation(source, destination);
  308. }
  309. #endregion
  310. #region Widget
  311. [Serialize]
  312. public Vector2 position { get; set; }
  313. [DoNotSerialize]
  314. public Exception definitionException { get; protected set; }
  315. #endregion
  316. #region Analytics
  317. public override AnalyticsIdentifier GetAnalyticsIdentifier()
  318. {
  319. var aid = new AnalyticsIdentifier
  320. {
  321. Identifier = GetType().FullName,
  322. Namespace = GetType().Namespace,
  323. };
  324. aid.Hashcode = aid.Identifier.GetHashCode();
  325. return aid;
  326. }
  327. #endregion
  328. }
  329. }