A game about forced loneliness, made by TACStudios
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using UnityEngine; 5 6namespace Unity.VisualScripting 7{ 8 public abstract class State : GraphElement<StateGraph>, IState 9 { 10 public class Data : IGraphElementData 11 { 12 public bool isActive; 13 14 public bool hasEntered; 15 } 16 17 public class DebugData : IStateDebugData 18 { 19 public int lastEnterFrame { get; set; } 20 21 public float lastExitTime { get; set; } 22 23 public Exception runtimeException { get; set; } 24 } 25 26 public IGraphElementData CreateData() 27 { 28 return new Data(); 29 } 30 31 public IGraphElementDebugData CreateDebugData() 32 { 33 return new DebugData(); 34 } 35 36 [Serialize] 37 public bool isStart { get; set; } 38 39 [DoNotSerialize] 40 public virtual bool canBeSource => true; 41 42 [DoNotSerialize] 43 public virtual bool canBeDestination => true; 44 45 public override void BeforeRemove() 46 { 47 base.BeforeRemove(); 48 49 Disconnect(); 50 } 51 52 public override void Instantiate(GraphReference instance) 53 { 54 base.Instantiate(instance); 55 56 var data = instance.GetElementData<Data>(this); 57 58 if (this is IGraphEventListener listener && data.isActive) 59 { 60 listener.StartListening(instance); 61 } 62 else if (isStart && !data.hasEntered && graph.IsListening(instance)) 63 { 64 using (var flow = Flow.New(instance)) 65 { 66 OnEnter(flow, StateEnterReason.Start); 67 } 68 } 69 } 70 71 public override void Uninstantiate(GraphReference instance) 72 { 73 if (this is IGraphEventListener listener) 74 { 75 listener.StopListening(instance); 76 } 77 78 base.Uninstantiate(instance); 79 } 80 81 #region Poutine 82 83 protected void CopyFrom(State source) 84 { 85 base.CopyFrom(source); 86 87 isStart = source.isStart; 88 width = source.width; 89 } 90 91 #endregion 92 93 #region Transitions 94 95 public IEnumerable<IStateTransition> outgoingTransitions => graph?.transitions.WithSource(this) ?? Enumerable.Empty<IStateTransition>(); 96 97 public IEnumerable<IStateTransition> incomingTransitions => graph?.transitions.WithDestination(this) ?? Enumerable.Empty<IStateTransition>(); 98 99 protected List<IStateTransition> outgoingTransitionsNoAlloc => graph?.transitions.WithSourceNoAlloc(this) ?? Empty<IStateTransition>.list; 100 101 public IEnumerable<IStateTransition> transitions => LinqUtility.Concat<IStateTransition>(outgoingTransitions, incomingTransitions); 102 103 public void Disconnect() 104 { 105 foreach (var transition in transitions.ToArray()) 106 { 107 graph.transitions.Remove(transition); 108 } 109 } 110 111 #endregion 112 113 #region Lifecycle 114 115 public virtual void OnEnter(Flow flow, StateEnterReason reason) 116 { 117 var data = flow.stack.GetElementData<Data>(this); 118 119 if (data.isActive) // Prevent re-entry from Any State 120 { 121 return; 122 } 123 124 data.isActive = true; 125 126 data.hasEntered = true; 127 128 foreach (var transition in outgoingTransitionsNoAlloc) 129 { 130 // Start listening for the transition's events 131 // before entering the state in case OnEnterState 132 // actually instantly triggers a transition via event 133 // http://support.ludiq.io/topics/261-event-timing-issue/ 134 (transition as IGraphEventListener)?.StartListening(flow.stack); 135 } 136 137 if (flow.enableDebug) 138 { 139 var editorData = flow.stack.GetElementDebugData<DebugData>(this); 140 141 editorData.lastEnterFrame = EditorTimeBinding.frame; 142 } 143 144 OnEnterImplementation(flow); 145 146 foreach (var transition in outgoingTransitionsNoAlloc) 147 { 148 try 149 { 150 transition.OnEnter(flow); 151 } 152 catch (Exception ex) 153 { 154 transition.HandleException(flow.stack, ex); 155 throw; 156 } 157 } 158 } 159 160 public virtual void OnExit(Flow flow, StateExitReason reason) 161 { 162 var data = flow.stack.GetElementData<Data>(this); 163 164 if (!data.isActive) 165 { 166 return; 167 } 168 169 OnExitImplementation(flow); 170 171 data.isActive = false; 172 173 if (flow.enableDebug) 174 { 175 var editorData = flow.stack.GetElementDebugData<DebugData>(this); 176 177 editorData.lastExitTime = EditorTimeBinding.time; 178 } 179 180 foreach (var transition in outgoingTransitionsNoAlloc) 181 { 182 try 183 { 184 transition.OnExit(flow); 185 } 186 catch (Exception ex) 187 { 188 transition.HandleException(flow.stack, ex); 189 throw; 190 } 191 } 192 } 193 194 protected virtual void OnEnterImplementation(Flow flow) { } 195 196 protected virtual void UpdateImplementation(Flow flow) { } 197 198 protected virtual void FixedUpdateImplementation(Flow flow) { } 199 200 protected virtual void LateUpdateImplementation(Flow flow) { } 201 202 protected virtual void OnExitImplementation(Flow flow) { } 203 204 public virtual void OnBranchTo(Flow flow, IState destination) { } 205 206 #endregion 207 208 #region Widget 209 210 public const float DefaultWidth = 170; 211 212 [Serialize] 213 public Vector2 position { get; set; } 214 215 [Serialize] 216 public float width { get; set; } = DefaultWidth; 217 218 #endregion 219 220 #region Analytics 221 222 public override AnalyticsIdentifier GetAnalyticsIdentifier() 223 { 224 var aid = new AnalyticsIdentifier 225 { 226 Identifier = GetType().FullName, 227 Namespace = GetType().Namespace, 228 }; 229 aid.Hashcode = aid.Identifier.GetHashCode(); 230 return aid; 231 } 232 233 #endregion 234 } 235}