├── README.md ├── ServiceLocator └── Locator.cs ├── Singleton └── Singleton.cs └── StateMachine ├── StateMachine.asmdef ├── StateMachine.asmdef.meta ├── StateMachine.cs └── StateMachine.cs.meta /README.md: -------------------------------------------------------------------------------- 1 | # unity-csharp-design-patterns 2 | 3 | UnityC#上で使えるデザインパターンのコードです。 4 | Youtube動画シリーズ『UnityC#で学ぶデザインパターン』で解説しています。 5 | 再生リストは[こちら](https://www.youtube.com/playlist?list=PLKDiJl9C6HD6Orb7gjcGKGRfyPyK1TeZh) 6 | 7 | こちらのリポジトリに格納されているコードはパブリックドメインです(ライセンスを主張しません)。 8 | -------------------------------------------------------------------------------- /ServiceLocator/Locator.cs: -------------------------------------------------------------------------------- 1 |  2 | public static class Locator where T : class 3 | { 4 | public static T I { get; private set; } 5 | 6 | public static bool IsValid() => I != null; 7 | 8 | public static void Bind(T instance) 9 | { 10 | I = instance; 11 | } 12 | 13 | public static void Unbind(T instance) 14 | { 15 | if (I == instance) 16 | { 17 | I = null; 18 | } 19 | } 20 | 21 | public static void Clear() 22 | { 23 | I = null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Singleton/Singleton.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public class Singleton : MonoBehaviour where T : Singleton 4 | { 5 | protected virtual bool DestroyTargetGameObject => false; 6 | 7 | public static T I { get; private set; } = null; 8 | 9 | /// 10 | /// Singletonが有効か 11 | /// 12 | public static bool IsValid() => I != null; 13 | 14 | private void Awake() 15 | { 16 | if (I == null) 17 | { 18 | I = this as T; 19 | I.Init(); 20 | return; 21 | } 22 | if (DestroyTargetGameObject) 23 | { 24 | Destroy(gameObject); 25 | } 26 | else 27 | { 28 | Destroy(this); 29 | } 30 | } 31 | 32 | /// 33 | /// 派生クラス用のAwake 34 | /// 35 | protected virtual void Init() 36 | { 37 | } 38 | 39 | private void OnDestroy() 40 | { 41 | if (I == this) 42 | { 43 | I = null; 44 | } 45 | OnRelease(); 46 | } 47 | 48 | /// 49 | /// 派生クラス用のOnDestroy 50 | /// 51 | protected virtual void OnRelease() 52 | { 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /StateMachine/StateMachine.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "StateMachine" 3 | } 4 | -------------------------------------------------------------------------------- /StateMachine/StateMachine.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: df08efdd0bd344f2085f22d4f36c7cd2 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /StateMachine/StateMachine.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | /// 4 | /// ステートマシン 5 | /// 6 | public class StateMachine 7 | { 8 | /// 9 | /// ステートを表すクラス 10 | /// 11 | public abstract class State 12 | { 13 | /// 14 | /// このステートを管理しているステートマシン 15 | /// 16 | protected StateMachine StateMachine => stateMachine; 17 | internal StateMachine stateMachine; 18 | /// 19 | /// 遷移の一覧 20 | /// 21 | internal Dictionary transitions = new Dictionary(); 22 | /// 23 | /// このステートのオーナー 24 | /// 25 | protected TOwner Owner => stateMachine.Owner; 26 | 27 | /// 28 | /// ステート開始 29 | /// 30 | internal void Enter(State prevState) 31 | { 32 | OnEnter(prevState); 33 | } 34 | /// 35 | /// ステートを開始した時に呼ばれる 36 | /// 37 | protected virtual void OnEnter(State prevState) { } 38 | 39 | /// 40 | /// ステート更新 41 | /// 42 | internal void Update() 43 | { 44 | OnUpdate(); 45 | } 46 | /// 47 | /// 毎フレーム呼ばれる 48 | /// 49 | protected virtual void OnUpdate() { } 50 | 51 | /// 52 | /// ステート終了 53 | /// 54 | internal void Exit(State nextState) 55 | { 56 | OnExit(nextState); 57 | } 58 | /// 59 | /// ステートを終了した時に呼ばれる 60 | /// 61 | protected virtual void OnExit(State nextState) { } 62 | } 63 | 64 | /// 65 | /// どのステートからでも特定のステートへ遷移できるようにするための仮想ステート 66 | /// 67 | public sealed class AnyState : State { } 68 | 69 | /// 70 | /// このステートマシンのオーナー 71 | /// 72 | public TOwner Owner { get; } 73 | /// 74 | /// 現在のステート 75 | /// 76 | public State CurrentState { get; private set; } 77 | 78 | // ステートリスト 79 | private LinkedList states = new LinkedList(); 80 | 81 | /// 82 | /// ステートマシンを初期化する 83 | /// 84 | /// ステートマシンのオーナー 85 | public StateMachine(TOwner owner) 86 | { 87 | Owner = owner; 88 | } 89 | 90 | /// 91 | /// ステートを追加する(ジェネリック版) 92 | /// 93 | public T Add() where T : State, new() 94 | { 95 | var state = new T(); 96 | state.stateMachine = this; 97 | states.AddLast(state); 98 | return state; 99 | } 100 | 101 | /// 102 | /// 特定のステートを取得、なければ生成する 103 | /// 104 | public T GetOrAddState() where T : State, new() 105 | { 106 | foreach (var state in states) 107 | { 108 | if (state is T result) 109 | { 110 | return result; 111 | } 112 | } 113 | return Add(); 114 | } 115 | 116 | /// 117 | /// 遷移を定義する 118 | /// 119 | /// イベントID 120 | public void AddTransition(int eventId) 121 | where TFrom : State, new() 122 | where TTo : State, new() 123 | { 124 | var from = GetOrAddState(); 125 | if (from.transitions.ContainsKey(eventId)) 126 | { 127 | // 同じイベントIDの遷移を定義済 128 | throw new System.ArgumentException( 129 | $"ステート'{nameof(TFrom)}'に対してイベントID'{eventId.ToString()}'の遷移は定義済です"); 130 | } 131 | 132 | var to = GetOrAddState(); 133 | from.transitions.Add(eventId, to); 134 | } 135 | 136 | /// 137 | /// どのステートからでも特定のステートへ遷移できるイベントを追加する 138 | /// 139 | /// イベントID 140 | public void AddAnyTransition(int eventId) where TTo : State, new() 141 | { 142 | AddTransition(eventId); 143 | } 144 | 145 | /// 146 | /// ステートマシンの実行を開始する(ジェネリック版) 147 | /// 148 | public void Start() where TFirst : State, new() 149 | { 150 | Start(GetOrAddState()); 151 | } 152 | 153 | /// 154 | /// ステートマシンの実行を開始する 155 | /// 156 | /// 起動時のステート 157 | /// パラメータ 158 | public void Start(State firstState) 159 | { 160 | CurrentState = firstState; 161 | CurrentState.Enter(null); 162 | } 163 | 164 | /// 165 | /// ステートを更新する 166 | /// 167 | public void Update() 168 | { 169 | CurrentState.Update(); 170 | } 171 | 172 | /// 173 | /// イベントを発行する 174 | /// 175 | /// イベントID 176 | public void Dispatch(int eventId) 177 | { 178 | State to; 179 | if (!CurrentState.transitions.TryGetValue(eventId, out to)) 180 | { 181 | if (!GetOrAddState().transitions.TryGetValue(eventId, out to)) 182 | { 183 | // イベントに対応する遷移が見つからなかった 184 | return; 185 | } 186 | } 187 | Change(to); 188 | } 189 | 190 | /// 191 | /// ステートを変更する 192 | /// 193 | /// 遷移先のステート 194 | private void Change(State nextState) 195 | { 196 | CurrentState.Exit(nextState); 197 | nextState.Enter(CurrentState); 198 | CurrentState = nextState; 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /StateMachine/StateMachine.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6d8ba095820274f33b04b0e551d5a327 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | --------------------------------------------------------------------------------