QFramework示例 QFramework源码示例 0.计数器
完整分析 以下顺序是按照源码顺序分析,实际开发时编写顺序可根据自身需要调整
1.定义Model 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public interface ICounterAppModel : IModel { BindableProperty<int > Count { get ; } } public class CounterAppModel : AbstractModel ,ICounterAppModel { public BindableProperty<int > Count { get ; } = new BindableProperty<int >(); protected override void OnInit () { var storage = this .GetUtility<IStorage>(); Count.SetValueWithoutEvent(storage.LoadInt(nameof (Count))); Count.Register(newCount => { storage.SaveInt(nameof (Count),newCount); }); } }
2.定义System 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public interface IAchievementSystem : ISystem { } public class AchievementSystem : AbstractSystem ,IAchievementSystem { protected override void OnInit () { this .GetModel<ICounterAppModel>() .Count .Register(newCount => { if (newCount == 10 ) { Debug.Log("触发 点击达人 成就" ); } else if (newCount == 20 ) { Debug.Log("触发 点击专家 成就" ); } else if (newCount == -10 ) { Debug.Log("触发 点击菜鸟 成就" ); } }); } }
3.定义Utility 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public interface IStorage : IUtility { void SaveInt (string key, int value ) ; int LoadInt (string key, int defaultValue = 0 ) ; } public class Storage : IStorage { public void SaveInt (string key, int value ) { PlayerPrefs.SetInt(key,value ); } public int LoadInt (string key, int defaultValue = 0 ) { return PlayerPrefs.GetInt(key, defaultValue); } }
4.定义Architecture 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class CounterApp : Architecture <CounterApp >{ protected override void Init () { this .RegisterSystem<IAchievementSystem>(new AchievementSystem()); this .RegisterModel<ICounterAppModel>(new CounterAppModel()); this .RegisterUtility<IStorage>(new Storage()); } protected override void ExecuteCommand (ICommand command ) { Debug.Log("Before " + command.GetType().Name + "Execute" ); base .ExecuteCommand(command); Debug.Log("After " + command.GetType().Name + "Execute" ); } protected override TResult ExecuteCommand <TResult >(ICommand<TResult> command ) { Debug.Log("Before " + command.GetType().Name + "Execute" ); var result = base .ExecuteCommand(command); Debug.Log("After " + command.GetType().Name + "Execute" ); return result; } }
5.引入Command 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class IncreaseCountCommand : AbstractCommand { protected override void OnExecute () { var model = this .GetModel<ICounterAppModel>(); model.Count.Value++; } } public class DecreaseCountCommand : AbstractCommand { protected override void OnExecute () { this .GetModel<ICounterAppModel>().Count.Value--; } }
6.编写Controller 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 public class CounterAppController : MonoBehaviour , IController { private Button mBtnAdd; private Button mBtnSub; private Text mCountText; private ICounterAppModel mModel; void Start () { mModel = this .GetModel<ICounterAppModel>(); mBtnAdd = transform.Find("BtnAdd" ).GetComponent<Button>(); mBtnSub = transform.Find("BtnSub" ).GetComponent<Button>(); mCountText = transform.Find("CountText" ).GetComponent<Text>(); mBtnAdd.onClick.AddListener(() => { this .SendCommand<IncreaseCountCommand>(); }); mBtnSub.onClick.AddListener(() => { this .SendCommand(new DecreaseCountCommand()); }); mModel.Count.RegisterWithInitValue(newCount => { UpdateView(); }).UnRegisterWhenGameObjectDestroyed(gameObject); } void UpdateView () { mCountText.text = mModel.Count.ToString(); } public IArchitecture GetArchitecture () { return CounterApp.Interface; } private void OnDestroy () { mModel = null ; } }
完整代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 using UnityEngine;using UnityEngine.UI;namespace QFramework.Example { public interface ICounterAppModel : IModel { BindableProperty<int > Count { get ; } } public class CounterAppModel : AbstractModel ,ICounterAppModel { public BindableProperty<int > Count { get ; } = new BindableProperty<int >(); protected override void OnInit () { var storage = this .GetUtility<IStorage>(); Count.SetValueWithoutEvent(storage.LoadInt(nameof (Count))); Count.Register(newCount => { storage.SaveInt(nameof (Count),newCount); }); } } public interface IAchievementSystem : ISystem { } public class AchievementSystem : AbstractSystem ,IAchievementSystem { protected override void OnInit () { this .GetModel<ICounterAppModel>() .Count .Register(newCount => { if (newCount == 10 ) { Debug.Log("触发 点击达人 成就" ); } else if (newCount == 20 ) { Debug.Log("触发 点击专家 成就" ); } else if (newCount == -10 ) { Debug.Log("触发 点击菜鸟 成就" ); } }); } } public interface IStorage : IUtility { void SaveInt (string key, int value ) ; int LoadInt (string key, int defaultValue = 0 ) ; } public class Storage : IStorage { public void SaveInt (string key, int value ) { PlayerPrefs.SetInt(key,value ); } public int LoadInt (string key, int defaultValue = 0 ) { return PlayerPrefs.GetInt(key, defaultValue); } } public class CounterApp : Architecture <CounterApp > { protected override void Init () { this .RegisterSystem<IAchievementSystem>(new AchievementSystem()); this .RegisterModel<ICounterAppModel>(new CounterAppModel()); this .RegisterUtility<IStorage>(new Storage()); } protected override void ExecuteCommand (ICommand command ) { Debug.Log("Before " + command.GetType().Name + "Execute" ); base .ExecuteCommand(command); Debug.Log("After " + command.GetType().Name + "Execute" ); } protected override TResult ExecuteCommand <TResult >(ICommand<TResult> command ) { Debug.Log("Before " + command.GetType().Name + "Execute" ); var result = base .ExecuteCommand(command); Debug.Log("After " + command.GetType().Name + "Execute" ); return result; } } public class IncreaseCountCommand : AbstractCommand { protected override void OnExecute () { var model = this .GetModel<ICounterAppModel>(); model.Count.Value++; } } public class DecreaseCountCommand : AbstractCommand { protected override void OnExecute () { this .GetModel<ICounterAppModel>().Count.Value--; } } public class CounterAppController : MonoBehaviour , IController { private Button mBtnAdd; private Button mBtnSub; private Text mCountText; private ICounterAppModel mModel; void Start () { mModel = this .GetModel<ICounterAppModel>(); mBtnAdd = transform.Find("BtnAdd" ).GetComponent<Button>(); mBtnSub = transform.Find("BtnSub" ).GetComponent<Button>(); mCountText = transform.Find("CountText" ).GetComponent<Text>(); mBtnAdd.onClick.AddListener(() => { this .SendCommand<IncreaseCountCommand>(); }); mBtnSub.onClick.AddListener(() => { this .SendCommand(new DecreaseCountCommand()); }); mModel.Count.RegisterWithInitValue(newCount => { UpdateView(); }).UnRegisterWhenGameObjectDestroyed(gameObject); } void UpdateView () { mCountText.text = mModel.Count.ToString(); } public IArchitecture GetArchitecture () { return CounterApp.Interface; } private void OnDestroy () { mModel = null ; } } }
1.查询示例
重点分析 定义两个Model 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class StudentModel : AbstractModel { public List<string > StudentNames = new List<string >() { "张三" , "李四" }; protected override void OnInit () { } } public class TeacherModel : AbstractModel { public List<string > TeacherNames = new List<string >() { "王五" , "赵六" }; protected override void OnInit () { } }
Query对象 Query对象查询两个Model,返回结果
1 2 3 4 5 6 7 8 9 10 11 public class SchoolAllPersonCountQuery : AbstractQuery <int >{ protected override int OnDo () { return this .GetModel<StudentModel>().StudentNames.Count + this .GetModel<TeacherModel>().TeacherNames.Count; } }
调用Query 1 mAllPersonCount = this .SendQuery(new SchoolAllPersonCountQuery());
完整代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 using System.Collections.Generic;using UnityEngine;namespace QFramework.Example { public class QueryExampleController : MonoBehaviour ,IController { public class StudentModel : AbstractModel { public List<string > StudentNames = new List<string >() { "张三" , "李四" }; protected override void OnInit () { } } public class TeacherModel : AbstractModel { public List<string > TeacherNames = new List<string >() { "王五" , "赵六" }; protected override void OnInit () { } } public class QueryExampleApp : Architecture <QueryExampleApp > { protected override void Init () { this .RegisterModel(new StudentModel()); this .RegisterModel(new TeacherModel()); } } public class SchoolAllPersonCountQuery : AbstractQuery <int > { protected override int OnDo () { return this .GetModel<StudentModel>().StudentNames.Count + this .GetModel<TeacherModel>().TeacherNames.Count; } } private int mAllPersonCount = 0 ; private void OnGUI () { GUILayout.Label(mAllPersonCount.ToString()); if (GUILayout.Button("查询学校总人数" )) { mAllPersonCount = this .SendQuery(new SchoolAllPersonCountQuery()); } } public IArchitecture GetArchitecture () { return QueryExampleApp.Interface; } } }
2.类型事件系统 1) 事件绑定与自动解绑 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 using UnityEngine;namespace QFramework.Example { public class TypeEventSystemBasicExample : MonoBehaviour { public struct TestEventA { public int Age; } private void Start () { TypeEventSystem.Global.Register<TestEventA>(e => { Debug.Log(e.Age); }).UnRegisterWhenGameObjectDestroyed(gameObject); } private void Update () { if (Input.GetMouseButtonDown(0 )) { TypeEventSystem.Global.Send(new TestEventA() { Age = 18 }); } if (Input.GetMouseButtonDown(1 )) { TypeEventSystem.Global.Send<TestEventA>(); } } } }
2) 继承事件绑定 注意:如果注册时的泛型使用接口/父类,在发送时也需要使用接口/父类,只是传入的参数可以是 实现类/子类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 using UnityEngine;namespace QFramework.Example { public class TypeEventSystemInheritEventExample : MonoBehaviour { public interface IEventA { } public struct EventB : IEventA { } private void Start () { TypeEventSystem.Global.Register<IEventA>(e => { Debug.Log(e.GetType().Name); }).UnRegisterWhenGameObjectDestroyed(gameObject); } private void Update () { if (Input.GetMouseButtonDown(0 )) { TypeEventSystem.Global.Send<IEventA>(new EventB()); TypeEventSystem.Global.Send<EventB>(); } } } }
3) 事件手动解绑 1.调用UnRegister可以手动解绑
2.将注册的事件绑定到UnregisterList,调用UnRegisterAll()即可实现一键手动解绑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 using System.Collections.Generic;using UnityEngine;namespace QFramework.Example { public class TypeEventSystemUnRegisterExample : MonoBehaviour { public struct EventA { } public struct EventB { } private void Start () { TypeEventSystem.Global.Register<EventA>(OnEventA); TypeEventSystem.Global.Register<EventB>(b => { }).UnRegisterWhenGameObjectDestroyed(this ); } void OnEventA (EventA e ) { } private void OnDestroy () { TypeEventSystem.Global.UnRegister<EventA>(OnEventA); } public class NoneMonoScript : IUnRegisterList { public List<IUnRegister> UnregisterList { get ; } = new List<IUnRegister>(); void Start () { TypeEventSystem.Global.Register<EventA>(a => { }).AddToUnregisterList(this ); } void OnDestroy () { this .UnRegisterAll(); } } } }
4) 实现事件接口通过实现接口的方式实现事件绑定。概念冗余,不推荐。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 using System;using System.Collections;using System.Collections.Generic;using UnityEngine;namespace QFramework.Example { public struct InterfaceEventA { } public struct InterfaceEventB { } public class InterfaceEventModeExample : MonoBehaviour , IOnEvent <InterfaceEventA > , IOnEvent <InterfaceEventB > { public void OnEvent (InterfaceEventA e ) { Debug.Log(e.GetType().Name); } public void OnEvent (InterfaceEventB e ) { Debug.Log(e.GetType().Name); } private void Start () { this .RegisterEvent<InterfaceEventA>() .UnRegisterWhenGameObjectDestroyed(gameObject); this .RegisterEvent<InterfaceEventB>(); } private void OnDestroy () { this .UnRegisterEvent<InterfaceEventB>(); } private void Update () { if (Input.GetMouseButtonDown(0 )) { TypeEventSystem.Global.Send<InterfaceEventA>(); TypeEventSystem.Global.Send<InterfaceEventB>(); } } } }
3.简单事件 EasyEvent是类型事件系统的底层实现,本质就是对C# Action委托的扩展
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 using UnityEngine;namespace QFramework.Example { public class EasyEventExample : MonoBehaviour { private EasyEvent mOnMouseLeftClickEvent = new EasyEvent(); private EasyEvent<int > mOnValueChanged = new EasyEvent<int >(); public class EventA : EasyEvent <int ,int > { } private EventA mEventA = new EventA(); private void Start () { mOnMouseLeftClickEvent.Register(() => { Debug.Log("鼠标左键点击" ); }).UnRegisterWhenGameObjectDestroyed(gameObject); mOnValueChanged.Register(value => { Debug.Log($"值变更:{value } " ); }).UnRegisterWhenGameObjectDestroyed(gameObject); mEventA.Register((a, b) => { Debug.Log($"自定义事件:{a} {b} " ); }).UnRegisterWhenGameObjectDestroyed(gameObject); } private void Update () { if (Input.GetMouseButtonDown(0 )) { mOnMouseLeftClickEvent.Trigger(); } if (Input.GetMouseButtonDown(1 )) { mOnValueChanged.Trigger(10 ); } if (Input.GetMouseButtonDown(2 )) { mEventA.Trigger(1 ,2 ); } } } }
4.BindableProperty
绑定属性,实现值修改触发事件的逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 using UnityEngine;namespace QFramework.Example { public class BindablePropertyExample : MonoBehaviour { private BindableProperty<int > mSomeValue = new BindableProperty<int >(0 ); private BindableProperty<string > mName = new BindableProperty<string >("QFramework" ); void Start () { mSomeValue.Register(newValue => { Debug.Log(newValue); }).UnRegisterWhenGameObjectDestroyed(gameObject); mName.RegisterWithInitValue(newName => { Debug.Log(mName); }).UnRegisterWhenGameObjectDestroyed(gameObject); mSomeValue.SetValueWithoutEvent(10 ); } void Update () { if (Input.GetMouseButtonDown(0 )) { mSomeValue.Value++; } } } }
5.IOC容器 提供对象注册到IOC容器的操作
作者认为注册到容器的对象不可被销毁,这样在调用时确保安全性。本人在这做了一些修改,为容器引入Release方法,能够在需要时销毁容器内对象(大型项目可能需要)。此操作风险与收益并存,需要慎用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 using UnityEngine;namespace QFramework.Example { public class IOCContainerExample : MonoBehaviour { public class SomeService { public void Say () { Debug.Log("SomeService Say Hi" ); } } public interface INetworkService { void Connect () ; } public class NetworkService : INetworkService { public void Connect () { Debug.Log("NetworkService Connect Succeed" ); } } private void Start () { var container = new IOCContainer(); container.Register(new SomeService()); container.Register<INetworkService>(new NetworkService()); container.Get<SomeService>().Say(); container.Get<INetworkService>().Connect(); } } }
6.不使用架构如果熟练到一定程度,即使不使用架构约束也能写出架构程度的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 using System;using System.Collections.Generic;using UnityEngine;namespace QFramework.Example { public class ArchitectureInHeartExample : MonoBehaviour { #region Framework public interface ICommand { void Execute () ; } public class BindableProperty <T > { private T mValue = default ; public T Value { get => mValue; set { if (mValue != null && mValue.Equals(value )) return ; mValue = value ; OnValueChanged?.Invoke(mValue); } } public event Action<T> OnValueChanged = _ => { }; } #endregion #region 定义 Model public static class CounterModel { public static BindableProperty<int > Counter = new BindableProperty<int >() { Value = 0 }; } #endregion #region 定义 Command public struct IncreaseCountCommand : ICommand { public void Execute () { CounterModel.Counter.Value++; } } public struct DecreaseCountCommand : ICommand { public void Execute () { CounterModel.Counter.Value--; } } #endregion private void OnGUI () { if (GUILayout.Button("+" )) { new IncreaseCountCommand().Execute(); } GUILayout.Label(CounterModel.Counter.Value.ToString()); if (GUILayout.Button("-" )) { new DecreaseCountCommand().Execute(); } } } }
7-11 实战项目
篇幅有限,详见源码
12.协程&异步&带返回值的命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 using System;using System.Collections;using System.Threading.Tasks;using UnityEngine;namespace QFramework.Example { public class CommandWithResultExample : MonoBehaviour { public class ExampleArchitecture : Architecture <ExampleArchitecture > { protected override void Init () { } } public class SimpleResultCommand : AbstractCommand <string > { protected override string OnExecute () { return "Hello Command With Result" ; } } public class ACoroutineCommand : AbstractCommand <IEnumerator > { protected override IEnumerator OnExecute () { Debug.Log("ACoroutineCommandStart:" + Time.time); yield return new WaitForSeconds (1.0f ) ; Debug.Log("ACoroutineCommandFinish:" + Time.time); } } public class TaskACommand : AbstractCommand <Task <bool >> { protected override async Task<bool > OnExecute () { await Task.Delay(TimeSpan.FromSeconds(2.0f )); return true ; } } void Start () { Debug.Log(ExampleArchitecture.Interface.SendCommand(new SimpleResultCommand())); StartCoroutine(ExampleArchitecture.Interface.SendCommand(new ACoroutineCommand())); SendTaskACommand(); } async void SendTaskACommand () { var result = await ExampleArchitecture.Interface.SendCommand(new TaskACommand()); Debug.Log("ATaskCommandResult:" + result + " time:" + Time.time); } } }
13.Or事件 将一个事件同时注册到多个BIndableProperty或EasyEvent
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 using System;using System.Collections;using System.Collections.Generic;using UnityEngine;namespace QFramework.Example { public class OrEventExample : MonoBehaviour { private BindableProperty<int > mPropertyA = new BindableProperty<int >(10 ); private BindableProperty<int > mPropertyB = new BindableProperty<int >(5 ); private EasyEvent EventA = new EasyEvent(); void Start () { mPropertyA .Or(EventA) .Or(mPropertyB) .Register(() => { Debug.Log("Event Received" ); }).UnRegisterWhenGameObjectDestroyed(gameObject); } private void Update () { if (Input.GetMouseButtonDown(0 )) { mPropertyA.Value++; } if (Input.GetMouseButtonDown(1 )) { mPropertyB.Value++; } if (Input.GetKeyDown(KeyCode.Space)) { EventA.Trigger(); } } } }
14.释放资源 [自定义] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 using System;using System.Collections;using System.Collections.Generic;using UnityEngine;namespace QFramework { public class ModelA : AbstractModel { public int data = 100 ; protected override void OnInit () { Debug.Log("ModelA: OnInit()" ); } protected override void OnRelease () { base .OnRelease(); Debug.Log("ModelA: OnRelease()" ); } } public class ArchitectureA : Architecture <ArchitectureA > { protected override void Init () { Debug.Log("ArchitectureA: Init!" ); RegisterModel(new ModelA()); } } public class ReleaseExample : MonoBehaviour { private void Start () { var modelA = ArchitectureA.Interface.GetModel<ModelA>(); if (modelA != null ) { Debug.Log("Init data: " + modelA.data); modelA.data = 888 ; Debug.Log("Change data: " + modelA.data); } ArchitectureA.Interface.Release(); modelA = ArchitectureA.Interface.GetModel<ModelA>(); if (modelA != null ) { Debug.Log("data: " + modelA.data); } } } }
QFramework 工具集示例 1.ActionKit Start执行时不会阻塞当前代码
源码分析 ActionKit大致工作过程(以Delay举例):
调用Action.Delay函数,传入延迟时间和时间结束的回调函数。底层会从==缓存池内申请==一个Delay对象,做一些初始化工作并绑定上DelayTime
和OnDelayFinish
。最后返回Delay Action。
调用Start函数,传入Monobehaviour对象(以下简称Mono)和整个Action执行结束的回调函数onFinish。函数会在Mono上添加或获取MonoUpdateActionExecutor脚本
。
紧接着调用MonoUpdateActionExecutor
的Execute函数
,以Action作为Key ,onFinish作为Value 生成Key-Value Pair
添加到mPrepareExecutionActions列表
中(这里不直接使用字典,说明重复添加同一事件 是会累加 的)。
在MonoUpdateActionExecutor脚本
的Update里,每帧会判断mPrepareExecutionActions列表
是否有元素,如果有就取出放到mExecutingActions字典
中。(源码中出现重复Key会直接用Value覆盖,但是Key不大可能重复)接着遍历mExecutingActions字典
,执行每个动作的Execute函数
。
Execute函数
本质是一个if-else状态机,分为NotStart、Started、Finished。返回true代表动作执行结束,返回false代表动作未完成。其中Started->Finished的过程通常根据需求决定。下面是状态机大致工作顺序:
调用OnStart,从NotStart->Started,返回false
调用OnExecute,返回false。
直到调用Finish函数,从Started->Finished,立刻调用OnFinish,返回true
调用onFinish回调函数,通知动作结束。调用动作的Deinit函数
,将动作先托管给ActionQueue
(Monobehaviour单例),由ActionQueue将动作==放回缓存池==。(猜测作者是确保执行过程互不交叉,每个阶段做完所有该阶段的任务后再进入下一个阶段)
放回缓存池前还可以调用mResetMethod事件
来初始化对象(本例不需要,因为Delay对象每次取出会做一次完整的初始化)
0) Delay 延迟1s后触发回调函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 using UnityEngine;namespace QFramework.Example { public class DelayExample : MonoBehaviour { void Start () { Debug.Log("Start Time:" + Time.time); ActionKit.Delay(1.0f , () => { Debug.Log("End Time:" + Time.time); }).Start(this ); } } }
Actionkit底层分析(Delay) QFramework.Actionkit底层分析-Delay
详见:Mindmaster
1) Sequence 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 using UnityEngine;namespace QFramework.Example { public class SequenceAndCallback : MonoBehaviour { void Start () { Debug.Log("Sequence Start:" + Time.time); ActionKit.Sequence() .Callback(() => Debug.Log("Delay Start:" + Time.time)) .Delay(1.0f ) .Callback(() => Debug.Log("Delay Finish:" + Time.time)) .Start(this , _ => { Debug.Log("Sequence Finish:" + Time.time); }); } } }
2) DelayFrame 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 using UnityEngine;namespace QFramework.Example { public class DelayFrameExample : MonoBehaviour { void Start () { Debug.Log("Delay Frame Start FrameCount:" + Time.frameCount); ActionKit.DelayFrame(1 , () => { Debug.Log("Delay Frame Finish FrameCount:" + Time.frameCount); }) .Start(this ); ActionKit.Sequence() .DelayFrame(10 ) .Callback(() => Debug.Log("Sequence Delay FrameCount:" + Time.frameCount)) .Start(this ); ActionKit.NextFrame(() => { }).Start(this ); } } }
3) Condition 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 using UnityEngine;namespace QFramework.Example { public class ConditionExample : MonoBehaviour { private void Start () { ActionKit.Sequence() .Callback(() => Debug.Log("Before Condition" )) .Condition(() => Input.GetMouseButtonDown(0 )) .Callback(() => Debug.Log("Mouse Clicked" )) .Start(this ); } } }
4) Repeat 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 using UnityEngine;namespace QFramework.Example { public class RepeatExample : MonoBehaviour { private void Start () { ActionKit.Repeat() .Condition(() => Input.GetMouseButtonDown(0 )) .Callback(() => Debug.Log("Mouse Clicked" )) .Start(this ); ActionKit.Repeat(5 ) .Condition(() => Input.GetMouseButtonDown(1 )) .Callback(() => Debug.Log("Mouse right clicked" )) .Start(this , () => { Debug.Log("Right click finished" ); ActionKit.Repeat(5 ) .Condition(() => Input.GetMouseButtonDown(1 )) .Callback(() => Debug.Log("Mouse right clicked" )) .Start(this , () => { Debug.Log("Right click finished" ); }); }); } } }
5) Parallel 只有全部并行任务完成后才会继续执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 using UnityEngine;namespace QFramework.Example { public class ParallelExample : MonoBehaviour { void Start () { Debug.Log("Parallel Start:" + Time.time); ActionKit.Parallel() .Delay(1.0f , () => { Debug.Log(Time.time); }) .Delay(2.0f , () => { Debug.Log(Time.time); }) .Delay(3.0f , () => { Debug.Log(Time.time); }) .Start(this , () => { Debug.Log("Parallel Finish:" + Time.time); }); } } }
6) 综合 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 using UnityEngine;namespace QFramework.Example { public class ComplexExample : MonoBehaviour { private void Start () { ActionKit.Sequence() .Callback(() => Debug.Log("Sequence Start" )) .Callback(() => Debug.Log("Parallel Start" )) .Parallel(p => { p.Delay(1.0f , () => Debug.Log("Delay 1s Finished" )) .Delay(2.0f , () => Debug.Log("Delay 2s Finished" )); }) .Callback(() => Debug.Log("Parallel Finished" )) .Callback(() => Debug.Log("Check Mouse Clicked" )) .Sequence(s => { s.Condition(() => Input.GetMouseButton(0 )) .Callback(() => Debug.Log("Mouse Clicked" )); }) .Start(this , () => { Debug.Log("Finish" ); }); } } }
7) Custom 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 using UnityEngine;namespace QFramework.Example { public class CustomExample : MonoBehaviour { class SomeData { public int ExecuteCount = 0 ; } private void Start () { ActionKit.Custom(a => { a .OnStart(() => { Debug.Log("OnStart" ); }) .OnExecute(dt => { Debug.Log("OnExecute" ); a.Finish(); }) .OnFinish(() => { Debug.Log("OnFinish" ); }); }).Start(this ); ActionKit.Custom<SomeData>(a => { a .OnStart(() => { a.Data = new SomeData() { ExecuteCount = 0 }; }) .OnExecute(dt => { Debug.Log(a.Data.ExecuteCount); a.Data.ExecuteCount++; if (a.Data.ExecuteCount >= 5 ) { a.Finish(); } }).OnFinish(() => { Debug.Log("Finished" ); }); }) .Start(this ); } } }
Actionkit底层分析(Custom) QFramework.Actionkit底层分析-Custom
详见:Mindmaster
8) Coroutine 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 using System.Collections;using UnityEngine;namespace QFramework.Example { public class CoroutineExample : MonoBehaviour { private void Start () { ActionKit.Coroutine(SomeCoroutine).Start(this ); SomeCoroutine().ToAction().Start(this ); ActionKit.Sequence() .Coroutine(SomeCoroutine) .Start(this ); } IEnumerator SomeCoroutine () { yield return new WaitForSeconds (1.0f ) ; Debug.Log("Hello:" + Time.time); } } }
9) Unity生命周期 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 using UnityEngine;namespace QFramework.Example { public class GlobalMonoEventsExample : MonoBehaviour { void Start () { ActionKit.OnUpdate.Register(() => { if (Time.frameCount % 30 == 0 ) { Debug.Log("Update" ); } }).UnRegisterWhenGameObjectDestroyed(gameObject); ActionKit.OnFixedUpdate.Register(() => { }).UnRegisterWhenGameObjectDestroyed(gameObject); ActionKit.OnLateUpdate.Register(() => { }).UnRegisterWhenGameObjectDestroyed(gameObject); ActionKit.OnGUI.Register(() => { GUILayout.Label("See Example Code" ); GUILayout.Label("请查看示例代码" ); }).UnRegisterWhenGameObjectDestroyed(gameObject); ActionKit.OnApplicationFocus.Register(focus => { Debug.Log("focus:" + focus); }).UnRegisterWhenGameObjectDestroyed(gameObject); ActionKit.OnApplicationPause.Register(pause => { Debug.Log("pause:" + pause); }).UnRegisterWhenGameObjectDestroyed(gameObject); ActionKit.OnApplicationQuit.Register(() => { Debug.Log("quit" ); }).UnRegisterWhenGameObjectDestroyed(gameObject); } } }
10) DOTween直接将DOTween写在Callback里也一样
11) UniRx12) RandomDelay 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 using System.Collections;using System.Collections.Generic;using UnityEngine;namespace QFramework.Example { public class RandomDelayExample : MonoBehaviour { void Start () { ActionKit.Repeat() .Delay(() => Random.Range(0.5f , 2.5f )) .Callback(() => { Debug.Log("Time:" + Time.time); }) .Start(this ); } void Update () { } } }
13) StartGlobal 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 using System.Collections;using System.Collections.Generic;using UnityEngine;namespace QFramework.Example { public class StartGlobalExample : MonoBehaviour { void Start () { var action = ActionKit.Repeat() .Delay(1.0f ) .Callback(() => Debug.Log("wait 1.0f" )) .StartGlobal(); } void Update () { } } }
14) PauseResume 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 using System.Collections;using System.Collections.Generic;using UnityEngine;namespace QFramework.Example { public class PauseResumeExample : MonoBehaviour { IEnumerator Start () { var action = ActionKit.Repeat() .Delay(0.5f ) .Callback(() => Debug.Log(Time.time)) .Start(this ); yield return new WaitForSeconds (3.0f ) ; Debug.Log("Pause" ); action.Pause(); yield return new WaitForSeconds (2.0f ) ; Debug.Log("Restart" ); action.Resume(); } } }
15) Lerp (用DOTween)1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 using System.Collections;using System.Collections.Generic;using UnityEngine;namespace QFramework.Example { public class CubeLerpExample : MonoBehaviour { void Start () { ActionKit.Lerp(0 , 360 , 5.0f , (v) => { this .Rotation(Quaternion.Euler(0 , 0 , v)); }).Start(this ); } void Update () { } } }
16) StopManagement17) LerpWithEase (用DOTween)2.BinablePropertyKit 1) EditorPrefsBoolProperty 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #if UNITY_EDITOR using UnityEditor;namespace QFramework { public class EditorPrefsBoolProperty : BindableProperty <bool > { public EditorPrefsBoolProperty (string key, bool initValue = false ) { mValue = EditorPrefs.GetBool(key, initValue); Register(value => { EditorPrefs.SetBool(key, value ); }); } } } #endif
2) PlayerPrefsBoolProperty 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 using UnityEngine;namespace QFramework { public class PlayerPrefsBooleanProperty : BindableProperty <bool > { public PlayerPrefsBooleanProperty (string saveKey, bool defaultValue = false ) { mValue = PlayerPrefs.GetInt(saveKey, defaultValue ? 1 : 0 ) == 1 ; this .Register(value => PlayerPrefs.SetInt(saveKey, value ? 1 : 0 )); } } }
3) PlayerPrefsFloatProperty 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 using UnityEngine;namespace QFramework { public class PlayerPrefsFloatProperty : BindableProperty <float > { public PlayerPrefsFloatProperty (string saveKey, float defaultValue = 0.0f ) { mValue = PlayerPrefs.GetFloat(saveKey, defaultValue); this .Register(value => PlayerPrefs.SetFloat(saveKey, value )); } } }
3.CodeGenKit 步骤:
使用Alt+V或手动添加ViewController
脚本
为需要绑定的对象添加Bind
脚本(添加Bind脚本的对象会在生成代码时,自动赋值到最近的ViewController生成的脚本中)
也可以为ViewController脚本所在对象添加OtherBinds
脚本,手动赋值
4.EventKit 1) EasyEvent 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 using UnityEngine;namespace QFramework.Example { public class EasyEventExample : MonoBehaviour { private EasyEvent<string > mSomeStringEvent = new EasyEvent<string >(); private void Awake () { mSomeStringEvent.Register(str => { Debug.Log(str); }).UnRegisterWhenGameObjectDestroyed(gameObject); EasyEvents.Register<EasyEvent<int >>(); EasyEvents.Register<MyEvent>(); EasyEvents.Get<EasyEvent<int >>().Register(number => { Debug.Log(number); }).UnRegisterWhenGameObjectDestroyed(gameObject); EasyEvents.Get<MyEvent>().Register((str1,str2)=> { Debug.Log(str1 +":" + str2); }).UnRegisterWhenGameObjectDestroyed(gameObject); } private void Update () { if (Input.GetMouseButtonDown(0 )) { mSomeStringEvent.Trigger("Hello World" ); } if (Input.GetMouseButtonDown(1 )) { EasyEvents.Get<EasyEvent<int >>().Trigger(123 ); } if (Input.GetKeyDown(KeyCode.Space)) { EasyEvents.Get<MyEvent>().Trigger("你好" ,"EasyEvent" ); } } public class MyEvent : EasyEvent <string ,string > { } } }
2) EnumEvent 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 using UnityEngine;namespace QFramework { public class EnumEventExample : MonoBehaviour { #region 事件定义 public enum TestEvent { Start, TestOne, End, } public enum TestEventB { Start = TestEvent.End + 1 , TestB, End, } #endregion 事件定义 void Start () { Debug.Log("End: " + (int ) TestEvent.End + " Start: " + (int ) TestEventB.Start); EnumEventSystem.Global.Register(TestEvent.TestOne, OnEvent); } void OnEvent (int key, params object [] obj ) { switch (key) { case (int ) TestEvent.TestOne: Debug.Log(obj[0 ]); break ; } } private void Update () { if (Input.GetMouseButtonDown(0 )) { EnumEventSystem.Global.Send(TestEvent.TestOne, "Hello World!" ); } } private void OnDestroy () { EnumEventSystem.Global.UnRegister(TestEvent.TestOne, OnEvent); } } }
3) UnityEventTrigger 本质是扩展了GameObject,使用Onxxx即可注册事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 using UnityEngine;namespace QFramework.Example { public class EventTriggerExample : MonoBehaviour { void Start () { GameObject.Find("Ground" ).OnTriggerEnter2DEvent(collider2D1 => { Debug.Log(collider2D1.name + ": entered" ); }).UnRegisterWhenGameObjectDestroyed(gameObject); var uiImage = GameObject.Find("Canvas/Image" ); uiImage.OnPointerDownEvent(data => { Debug.Log("Click" ); }).UnRegisterWhenGameObjectDestroyed(gameObject); } } }
4) StringEvent 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 using UnityEngine;namespace QFramework.Example { public class StringEventSystemExample : MonoBehaviour { void Start () { StringEventSystem.Global.Register("TEST_ONE" , () => { Debug.Log("TEST_ONE" ); }).UnRegisterWhenGameObjectDestroyed(gameObject); StringEventSystem.Global.Register<int >("TEST_TWO" , (count) => { Debug.Log("TEST_TWO:" + count); }).UnRegisterWhenGameObjectDestroyed(gameObject); } private void Update () { if (Input.GetMouseButtonDown(0 )) { StringEventSystem.Global.Send("TEST_ONE" ); StringEventSystem.Global.Send("TEST_TWO" ,10 ); } } } }
对比 TypeEventSystem:
事件体定义简洁
比较适合用于设计框架
支持 struct 获得较好内存性能
使用反射,CPU 性能相对比较差
EasyEvent
方便、易用、开发效率高
CPU 性能、内存性能较好,接近委托
功能有限
比较适合设计通用解决工具,比如通用背包、全局生命周期触发等
StringEventSystem、TypeEventSystem 的底层由 EasyEvent 实现
EnumEventSystem
使用枚举作为事件 id,比较适合和服务端的 protobuf 或带有消息 id 的长链接通信
性能较好
枚举用于定义消息体有维护成本
StringEventSystem
使用字符串作为事件 id,比较适合和其他脚本层通信,比如 Lua、ILRuntime、PlayMaker 等。
性能一般
目前官方推荐使用 TypeEventSystem 和 EasyEvent 这两个工具。
如果要和网络通信则选择用 EnumEventSystem。
如果要和其他脚本层通信选择用 StringEventSystem。
5.FluentAPI详见QFramework API文档
6.FSMKit 1) Chain 相当于用委托实现状态事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 using UnityEngine;namespace QFramework.Example { public class IStateBasicUsageExample : MonoBehaviour { public enum States { A, B } public FSM<States> FSM = new FSM<States>(); void Start () { Application.targetFrameRate = 60 ; FSM.OnStateChanged((previousState, nextState) => { Debug.Log($"{previousState} =>{nextState} " ); }); FSM.State(States.A) .OnCondition(()=>FSM.CurrentStateId == States.B) .OnEnter(() => { Debug.Log("Enter A" ); }) .OnUpdate(() => { if (FSM.FrameCountOfCurrentState % 60 == 0 ) { Debug.Log("Heart beat" ); } }) .OnFixedUpdate(() => { }) .OnGUI(() => { GUILayout.Label("State A" ); if (GUILayout.Button("To State B" )) { FSM.ChangeState(States.B); } }) .OnExit(() => { Debug.Log("Exit A" ); }); FSM.State(States.B) .OnCondition(() => FSM.CurrentStateId == States.A) .OnGUI(() => { GUILayout.Label("State B" ); if (GUILayout.Button("To State A" )) { FSM.ChangeState(States.A); } }); FSM.StartState(States.A); } private void Update () { FSM.Update(); } private void FixedUpdate () { FSM.FixedUpdate(); } private void OnGUI () { FSM.OnGUI(); } private void OnDestroy () { FSM.Clear(); } } }
2) Class 相当于用类继承实现状态事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 using UnityEngine;namespace QFramework.Example { public class IStateClassExample : MonoBehaviour { public enum States { A, B, C } public FSM<States> FSM = new FSM<States>(); public class StateA : AbstractState <States ,IStateClassExample > { public StateA (FSM<States> fsm, IStateClassExample target ) : base (fsm, target ) { } protected override bool OnCondition () { return mFSM.CurrentStateId == States.B; } public override void OnGUI () { GUILayout.Label("State A" ); if (GUILayout.Button("To State B" )) { mFSM.ChangeState(States.B); } } } public class StateB : AbstractState <States ,IStateClassExample > { public StateB (FSM<States> fsm, IStateClassExample target ) : base (fsm, target ) { } protected override bool OnCondition () { return mFSM.CurrentStateId == States.A; } public override void OnGUI () { GUILayout.Label("State B" ); if (GUILayout.Button("To State A" )) { mFSM.ChangeState(States.A); } } } private void Start () { FSM.AddState(States.A, new StateA(FSM, this )); FSM.AddState(States.B, new StateB(FSM, this )); FSM.StartState(States.A); } private void OnGUI () { FSM.OnGUI(); } private void OnDestroy () { FSM.Clear(); } } }
3) 综合 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 using UnityEngine;using UnityEngine.UI;namespace QFramework.Example { public class FSMFrameCountExample : MonoBehaviour { enum States { FadeAlphaIn, FadeAlphaOut, FadeColorBlue, FadeColorRed, Delay, RotateTo } private FSM<States> mFSM = new FSM<States>(); public Image Image; void Start () { Application.targetFrameRate = 60 ; mFSM.State(States.FadeAlphaIn) .OnEnter(() => Image.ColorAlpha(0 )) .OnUpdate(() => { if (mFSM.FrameCountOfCurrentState <= 60 ) { Image.ColorAlpha(Mathf.Lerp(0 , 1 , mFSM.FrameCountOfCurrentState / 60.0f )); } else { mFSM.ChangeState(States.FadeAlphaOut); } }); mFSM.State(States.FadeAlphaOut) .OnUpdate(() => { if (mFSM.FrameCountOfCurrentState <= 60 ) { Image.ColorAlpha(Mathf.Lerp(1 , 0 , mFSM.FrameCountOfCurrentState / 60.0f )); } else { mFSM.ChangeState(States.FadeColorBlue); } }); mFSM.State(States.FadeColorBlue) .OnUpdate(() => { if (mFSM.FrameCountOfCurrentState <= 60 ) { Image.color = Color.Lerp(Color.white, Color.blue, mFSM.FrameCountOfCurrentState / 60.0f ); } else { mFSM.ChangeState(States.FadeColorRed); } }); mFSM.State(States.FadeColorRed) .OnUpdate(() => { if (mFSM.FrameCountOfCurrentState <= 60 ) { Image.color = Color.Lerp(Color.blue, Color.red, mFSM.FrameCountOfCurrentState / 60.0f ); } else { mFSM.ChangeState(States.Delay); } }); mFSM.State(States.Delay) .OnUpdate(() => { if (mFSM.FrameCountOfCurrentState > 60 ) { mFSM.ChangeState(States.RotateTo); } }); mFSM.State(States.RotateTo) .OnUpdate(() => { if (mFSM.FrameCountOfCurrentState <= 60 ) { Image.Rotation(Quaternion.Lerp(Quaternion.identity, Quaternion.Euler(45 , 45 , 45 ), mFSM.FrameCountOfCurrentState / 60.0f )); } }); mFSM.StartState(States.FadeAlphaIn); } void Update () { mFSM.Update(); } private void OnDestroy () { mFSM.Clear(); mFSM = null ; } } }
7.GridKit 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 using UnityEngine;namespace QFramework.Example { public class GridKitExample : MonoBehaviour { void Start () { var grid = new EasyGrid<string >(4 , 4 ); grid.Fill("Empty" ); grid[2 , 3 ] = "@@@ Hello @@@" ; grid.ForEach((x, y, content) => Debug.Log($"({x} ,{y} ):{content} " )); grid.Resize(5 , 5 , (x, y) => "123" ); grid.ForEach((x, y, content) => Debug.Log($"({x} ,{y} ):{content} " )); grid.Clear(); } } }
8.LocalKit 使用步骤:
在QFrameworkData/LocaleKit里可以配置语言类型
为Text组件绑定LocaleText脚本,可以为不同语言设置文本
为任意GameObject绑定LocaleUnityEvent脚本,在切换到该语言时可以触发事件
为交互组件绑定LocaleKitChangeLanguageAction脚本,触发交互时可以切换到任意语言
9.LogKit
10.PoolKit 1) SimpleObjectPool 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 using System.Collections;using System.Collections.Generic;using UnityEngine;namespace QFramework { public class SimpleObjectPoolExample : MonoBehaviour { private SimpleObjectPool<GameObject> mObjectPool; private List<GameObject> cacheList = new List<GameObject>(); void Start () { mObjectPool = new SimpleObjectPool<GameObject>(() => { var gameObj = new GameObject(); gameObj.SetActive(false ); return gameObj; }, gameObj => { gameObj.SetActive(false ); }, 5 ); } void Update () { if (Input.GetMouseButtonDown(0 )) { var obj = mObjectPool.Allocate(); obj.SetActive(true ); } if (Input.GetMouseButtonDown(1 )) { mObjectPool.Clear(o => { Destroy(o); }); } } } }
2) SafeObjectPool 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 using UnityEngine;namespace QFramework { public class CallPool : MonoBehaviour { class Fish { } private void Start () { #region SimpleObjectPool var pool = new SimpleObjectPool<Fish>(() => new Fish(),initCount:50 ); Debug.Log(pool.CurCount); var fish = pool.Allocate(); Debug.Log(pool.CurCount); pool.Recycle(fish); Debug.Log(pool.CurCount); var gameObjPool = new SimpleObjectPool<GameObject>(() => { var gameObj = new GameObject("AGameObject" ); return gameObj; }, (gameObj) => { }); #endregion #region SafeObjectPool SafeObjectPool<Bullet>.Instance.SetFactoryMethod(() => { return new Bullet(); }); SafeObjectPool<Bullet>.Instance.SetObjectFactory(new DefaultObjectFactory<Bullet>()); SafeObjectPool<Bullet>.Instance.Init(50 ,25 ); var bullet = Bullet.Allocate(); Debug.Log(SafeObjectPool<Bullet>.Instance.CurCount); bullet.Recycle2Cache(); Debug.Log(SafeObjectPool<Bullet>.Instance.CurCount); #endregion } class Bullet :IPoolable ,IPoolType { public void OnRecycled () { Debug.Log("回收了" ); } public bool IsRecycled { get ; set ; } public static Bullet Allocate () { return SafeObjectPool<Bullet>.Instance.Allocate(); } public void Recycle2Cache () { SafeObjectPool<Bullet>.Instance.Recycle(this ); } } } }
对比 1.实现方式不同:SimpleObjectPool通过委托定义Allocate和Recycle函数;SafeObjectPool通过实现IPoolable,IPoolType接口来定义
2.容量不同:SimpleObjectPool支持无限扩容,SafeObjctPool有最大值
作者建议:
①适合用于项目开发SimpleObjectPool
②适合用于库级开发,更多限制,要求开发者一开始就想好,更安全的SafeObjectPool
11.SingletonKit 1) Singleton 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 namespace QFramework.Example { using UnityEngine; internal class Class2Singleton : Singleton <Class2Singleton > { private static int mIndex = 0 ; private Class2Singleton () {} public override void OnSingletonInit () { mIndex++; } public void Log (string content ) { Debug.Log("Class2Singleton" + mIndex + ":" + content); } } public class SingletonExample : MonoBehaviour { private void Start () { Class2Singleton.Instance.Log("Hello World!" ); Class2Singleton.Instance.Dispose(); Class2Singleton.Instance.Log("Hello World!" ); } } }
2) MonoSingleton 适合做场景内单例,如关卡蓝图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 namespace QFramework.Example { using System.Collections; using UnityEngine; internal class Class2MonoSingleton : MonoSingleton <Class2MonoSingleton > { public override void OnSingletonInit () { Debug.Log(name + ":" + "OnSingletonInit" ); } private void Awake () { Debug.Log(name + ":" + "Awake" ); } private void Start () { Debug.Log(name + ":" + "Start" ); } protected override void OnDestroy () { base .OnDestroy(); Debug.Log(name + ":" + "OnDestroy" ); } } public class MonoSingletonExample : MonoBehaviour { private IEnumerator Start () { var instance = Class2MonoSingleton.Instance; yield return new WaitForSeconds (3.0f ) ; instance.Dispose(); } } }
3) SingletonProperty 感觉和1) Singleton没区别啊,就是多了一层封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 namespace QFramework.Example { using UnityEngine; internal class Class2SingletonProperty : ISingleton { public static Class2SingletonProperty Instance { get { return SingletonProperty<Class2SingletonProperty>.Instance; } } private Class2SingletonProperty () {} private static int mIndex = 0 ; public void OnSingletonInit () { mIndex++; } public void Dispose () { SingletonProperty<Class2SingletonProperty>.Dispose(); } public void Log (string content ) { Debug.Log("Class2SingletonProperty" + mIndex + ":" + content); } } public class SingletonPropertyExample : MonoBehaviour { void Start () { Class2SingletonProperty.Instance.Log("Hello World!" ); Class2SingletonProperty.Instance.Dispose(); Class2SingletonProperty.Instance.Log("Hello World!" ); } } }
4) MonoSingletonProperty 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 namespace QFramework.Example { using System.Collections; using UnityEngine; internal class Class2MonoSingletonProperty : MonoBehaviour ,ISingleton { public static Class2MonoSingletonProperty Instance { get { return MonoSingletonProperty<Class2MonoSingletonProperty>.Instance; } } public void Dispose () { MonoSingletonProperty<Class2MonoSingletonProperty>.Dispose(); } public void OnSingletonInit () { Debug.Log(name + ":" + "OnSingletonInit" ); } private void Awake () { Debug.Log(name + ":" + "Awake" ); } private void Start () { Debug.Log(name + ":" + "Start" ); } protected void OnDestroy () { Debug.Log(name + ":" + "OnDestroy" ); } } public class MonoSingletonPropertyExample : MonoBehaviour { private IEnumerator Start () { var instance = Class2MonoSingletonProperty.Instance; yield return new WaitForSeconds (3.0f ) ; instance.Dispose(); } } }
5) MonoSingletonPath 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 namespace QFramework.Example { using UnityEngine; [MonoSingletonPath("[Example]/MonoSingletonPath" ) ] internal class ClassUseMonoSingletonPath : MonoSingleton <ClassUseMonoSingletonPath > { } public class MonoSingletonPathExample : MonoBehaviour { private void Start () { var intance = ClassUseMonoSingletonPath.Instance; } } }
6) PersistentMonoSingleton 当场景里包含两个PersistentMonoSingleton,保留先创建的
分析:由于单例生成代码中多了一行FindObjectOfType,因此如果场景存在单例对象就不会重复创建
适用场景:建议切换场景不释放的Mono设为该单例,例如背景音乐等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 using System.Collections;using UnityEngine;using UnityEngine.SceneManagement;namespace QFramework.Example { public class PersistentMonoSingletonExample : MonoBehaviour { IEnumerator Start () { var instance = GameManager.Instance; instance.PrintID(); new GameObject().AddComponent<GameManager>(); instance.PrintID(); yield return new WaitForEndOfFrame () ; Debug.Log(FindObjectsOfType<GameManager>().Length); Debug.Log(instance == FindObjectOfType<GameManager>()); instance.PrintID(); if (SceneManager.GetActiveScene().name == "SceneA" ) SceneManager.LoadScene("SceneB" ); } public class GameManager : PersistentMonoSingleton <GameManager > { public static int gm_id; public int id; protected override void Awake () { base .Awake(); GameManager.gm_id++; this .id = gm_id; } public void PrintID () { Debug.Log("instance id: " + this .id); } } } }
7) ReplaceableMonoSingleton和Persistent相反,保留后创建的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 using System.Collections;using UnityEngine;namespace QFramework.Example { public class ReplaceableMonoSingletonExample : MonoBehaviour { IEnumerator Start () { var instance = GameManager.Instance; new GameObject().AddComponent<GameManager>(); yield return new WaitForEndOfFrame () ; Debug.Log(FindObjectsOfType<GameManager>().Length); Debug.Log(instance != FindObjectOfType<GameManager>()); } public class GameManager : ReplaceableMonoSingleton <GameManager > { } } }
8) PrefabSingletonProperty1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 using System;using UnityEngine;namespace QFramework.Example { public class PrefabSingletonPropertyExample : MonoBehaviour { private void Start () { var prefabA = MyPrefabA.Instance; } private void OnDestroy () { PrefabSingletonProperty<MyPrefabA>.Dispose(); } } }
1 2 3 4 5 6 7 8 9 10 using UnityEngine;namespace QFramework.Example { public class MyPrefabA : MonoBehaviour { public static MyPrefabA Instance => PrefabSingletonProperty<MyPrefabA> .InstanceWithLoader(prefabName => Resources.Load<GameObject>("Prefabs/" + prefabName)); } }
9) ScriptableSingletonProperty1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 using System;using System.Collections;using System.Collections.Generic;using UnityEngine;namespace QFramework.Example { public class ScriptableSingletonPropertyExample : MonoBehaviour { void Start () { MyScriptableA.Instance.ScriptableKey.LogInfo(); } private void OnDestroy () { ScriptableSingletonProperty<MyScriptableA>.Dispose(); } } }
12.TableKit1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 using System;using System.Collections.Generic;using System.Linq;using UnityEngine;namespace QFramework { public class TableKitExample : MonoBehaviour { public class Student { public string Name { get ; set ; } public int Age { get ; set ; } public int Level { get ; set ; } } public class School : Table <Student > { public TableIndex<int , Student> AgeIndex = new TableIndex<int , Student>((student) => student.Age); public TableIndex<int , Student> LevelIndex = new TableIndex<int , Student>((student) => student.Level); protected override void OnAdd (Student item ) { AgeIndex.Add(item); LevelIndex.Add(item); } protected override void OnRemove (Student item ) { AgeIndex.Remove(item); LevelIndex.Remove(item); } protected override void OnClear () { AgeIndex.Clear(); LevelIndex.Clear(); } public override IEnumerator<Student> GetEnumerator () { return AgeIndex.Dictionary.Values.SelectMany(s=>s).GetEnumerator(); } protected override void OnDispose () { AgeIndex.Dispose(); LevelIndex.Dispose(); } } private void Start () { var school = new School(); school.Add(new Student(){Age = 1 ,Level = 2 ,Name = "liangxie" }); school.Add(new Student(){Age = 2 ,Level = 2 ,Name = "ava" }); school.Add(new Student(){Age = 3 ,Level = 2 ,Name = "abc" }); school.Add(new Student(){Age = 3 ,Level = 3 ,Name = "efg" }); foreach (var student in school.LevelIndex.Get(2 ).Where(s=>s.Age < 3 )) { Debug.Log(student.Age + ":" + student.Level + ":" + student.Name); } } } }
UIKit 1) Basic 如何生成面板的操作见QFramework文档
2) InitAndCallAPIS 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 using UnityEngine;namespace QFramework.Example { public class InitAndCallApisExample : MonoBehaviour { private void Start () { ResKit.Init(); UIKit.Root.SetResolution(1920 , 1080 , 1 ); UIKit.OpenPanel<UIBasicPanel>(); ActionKit.Sequence() .Delay(1f ) .Callback(() => UIKit.HidePanel<UIBasicPanel>()) .Delay(1 ) .Callback(() => UIKit.OpenPanel<UIBasicPanel>()) .Delay(1 ) .Callback(() => UIKit.HidePanel<UIBasicPanel>()) .Delay(1 ) .Callback(() => UIKit.ShowPanel<UIBasicPanel>()) .Start(this ); } } }
3) OpenPanelAsync 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 using System.Collections;using UnityEngine;namespace QFramework.Example { public class OpenPanelAsyncExample : MonoBehaviour { IEnumerator Start () { yield return ResKit.InitAsync(); yield return UIKit.OpenPanelAsync<UIBasicPanel>(); } } }
4) Stack
Push相当于缓存打开的页面到Stack,并取消显示
Pop相当于从缓存中取出页面并显示(底层没有非空判断,注意下)
作者版本(个人感觉不易理解)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 using System;using UnityEngine;namespace QFramework.Example { public class UIKitStackExample : MonoBehaviour { private void Start () { ResKit.Init(); UIKit.Stack.Push(UIKit.OpenPanel<UIStackPanel1>()); UIKit.Stack.Push(UIKit.OpenPanel<UIStackPanel2>()); UIKit.Stack.Pop(); ActionKit.Delay(3 , () => { UIKit.Stack.Pop(); }).Start(this ); } } }
修改后版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 using System;using UnityEngine;namespace QFramework.Example { public class UIKitStackExample : MonoBehaviour { private UIStackPanel1 ret; private void Start () { ResKit.Init(); ret = UIKit.OpenPanel<UIStackPanel1>(); } private void Update () { if (Input.GetKeyDown(KeyCode.Q)) { Debug.Log("Q Pressed" ); UIKit.Stack.Push(ret); } if (Input.GetKeyDown(KeyCode.E)) { Debug.Log("E Pressed" ); UIKit.Stack.Pop(); } } } }
5) MultiPanel OpenType分为Single和Multiple
Single:对同一面板,无论调用多少次Open,都只会打开一个
Multiple:对同一面板,每次调用会生成一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 using System;using UnityEngine;namespace QFramework.Example { public class MultiPanelExample : MonoBehaviour { void Start () { ResKit.Init(); } private UIMultiPanel mUIMultiPanel; private int mPageIndex = 0 ; private void OnGUI () { if (GUILayout.Button("打开" )) { mUIMultiPanel = UIKit.OpenPanel<UIMultiPanel>(new UIMultiPanelData() { PageIndex = mPageIndex }, PanelOpenType.Multiple); mPageIndex++; } if (GUILayout.Button("关闭" )) { UIKit.ClosePanel<UIMultiPanel>(); } if (mUIMultiPanel && GUILayout.Button("关闭当前" )) { UIKit.ClosePanel(mUIMultiPanel); mUIMultiPanel = null ; } } } }
6) ResourcesPanelLoader1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 using System;using UnityEngine;namespace QFramework.Example { public class CustomPanelLoaderExample : MonoBehaviour { public class ResourcesPanelLoaderPool : AbstractPanelLoaderPool { public class ResourcesPanelLoader : IPanelLoader { private GameObject mPanelPrefab; public GameObject LoadPanelPrefab (PanelSearchKeys panelSearchKeys ) { mPanelPrefab = Resources.Load<GameObject>(panelSearchKeys.GameObjName); return mPanelPrefab; } public void LoadPanelPrefabAsync (PanelSearchKeys panelSearchKeys, Action<GameObject> onPanelLoad ) { var request = Resources.LoadAsync<GameObject>(panelSearchKeys.GameObjName); request.completed += operation => { onPanelLoad(request.asset as GameObject); }; } public void Unload () { mPanelPrefab = null ; } } protected override IPanelLoader CreatePanelLoader () { return new ResourcesPanelLoader(); } } void Start () { UIKit.Config.PanelLoaderPool = new ResourcesPanelLoaderPool(); } } }
ResKit 1) Basic 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 using UnityEngine;namespace QFramework.Example { public class ResKitExample : MonoBehaviour { private ResLoader mResLoader = ResLoader.Allocate(); private void Start () { ResKit.Init(); var prefab = mResLoader.LoadSync<GameObject>("AssetObj" ); var gameObj = Instantiate(prefab); gameObj.name = "这是使用通过 AssetName 加载的对象" ; prefab = mResLoader.LoadSync<GameObject>("assetobj_prefab" , "AssetObj" ); gameObj = Instantiate(prefab); gameObj.name = "这是使用通过 AssetName 和 AssetBundle 加载的对象" ; } private void OnDestroy () { mResLoader.Recycle2Cache(); mResLoader = null ; } } }
2) AsyncLoad 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 using System.Collections;using UnityEngine;namespace QFramework.Example { public class AsyncLoadExample : MonoBehaviour { IEnumerator Start () { yield return ResKit.InitAsync(); var resLoader = ResLoader.Allocate(); resLoader.Add2Load<GameObject>("AssetObj 1" ,(b, res) => { if (b) { res.Asset.As<GameObject>().Instantiate(); } }); resLoader.Add2Load<GameObject>("assetobj 2_prefab" ,"AssetObj 2" ,(b, res) => { if (b) { res.Asset.As<GameObject>().Instantiate(); } }); resLoader.Add2Load<GameObject>("AssetObj 3" ,(b, res) => { if (b) { res.Asset.As<GameObject>().Instantiate(); } }); resLoader.LoadAsync(() => { ActionKit.Delay(5.0f , () => { resLoader.Recycle2Cache(); }).Start(this ); }); } } }
3) LoadScene 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 using UnityEngine;namespace QFramework.Example { public class LoadSceneExample : MonoBehaviour { private ResLoader mResLoader = null ; void Start () { ResKit.Init(); mResLoader = ResLoader.Allocate(); mResLoader.LoadSceneAsync("SceneRes" , onStartLoading: operation => { Debug.Log(operation.progress); operation.completed += asyncOperation => { Debug.Log(operation.progress); Debug.Log("SceneRes loaded" ); }; }); } private void OnDestroy () { mResLoader.Recycle2Cache(); mResLoader = null ; } } }
4) ResoucesLoad 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 using UnityEngine;using UnityEngine.UI;namespace QFramework.Example { public class LoadResourcesResExample : MonoBehaviour { public RawImage RawImage; private ResLoader mResLoader = ResLoader.Allocate(); private void Start () { RawImage.texture = mResLoader.LoadSync<Texture2D>("resources://TestTexture" ); } private void OnDestroy () { Debug.Log("On Destroy " ); mResLoader.Recycle2Cache(); mResLoader = null ; } } }
5) SpriteAtlas(有卸载问题) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 using System;using System.Collections;using UnityEngine;using UnityEngine.U2D;using UnityEngine.UI;namespace QFramework { public class TestSpriteAtlas : MonoBehaviour { [SerializeField ] private Image mImage; private ResLoader loader; private IEnumerator Start () { loader = ResLoader.Allocate(); ResKit.Init(); var spriteAtlas = loader.LoadSync<SpriteAtlas>("spriteatlas" ); var square = spriteAtlas.GetSprite("shop" ); loader.AddObjectForDestroyWhenRecycle2Cache(square); mImage.sprite = square; yield return new WaitForSeconds (3.0f ) ; Debug.Log("Recycle2Cache" ); loader.Recycle2Cache(); loader = null ; } } }
6) NetImage 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 using UnityEngine;using UnityEngine.UI;namespace QFramework.Example { public class NetImageExample : MonoBehaviour { ResLoader mResLoader = ResLoader.Allocate(); void Start () { var image = transform.Find("Image" ).GetComponent<Image>(); mResLoader.Add2Load<Texture2D>( "http://pic.616pic.com/ys_b_img/00/44/76/IUJ3YQSjx1.jpg" .ToNetImageResName(), (b, res) => { if (b) { var texture = res.Asset as Texture2D; var sprite = Sprite.Create(texture, new Rect(0 , 0 , texture.width, texture.height), Vector2.one * 0.5f ); image.sprite = sprite; mResLoader.AddObjectForDestroyWhenRecycle2Cache(sprite); } }); mResLoader.LoadAsync(); } private void OnDestroy () { mResLoader.Recycle2Cache(); mResLoader = null ; } } }
7) LocalImage 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 namespace QFramework.Example { using System.Collections; using UnityEngine.UI; using UnityEngine; public class ImageLoaderExample : MonoBehaviour { private ResLoader mResLoader = null ; private IEnumerator Start () { ResMgr.Init(); mResLoader = ResLoader.Allocate(); var localImageUrl = "file://" + Application.persistentDataPath + "/Workspaces/lM1wmsLQtfzRQc6fsdEU.jpg" ; mResLoader.Add2Load(localImageUrl.ToLocalImageResName(), delegate (bool b, IRes res) { Debug.LogError(b); if (b) { var texture2D = res.Asset as Texture2D; transform.Find("Image" ).GetComponent<Image>().sprite = Sprite.Create(texture2D, new Rect(0 , 0 , texture2D.width, texture2D.height), Vector2.one * 0.5f ); } }); mResLoader.LoadAsync(); yield return new WaitForSeconds (5.0f ) ; mResLoader.Recycle2Cache(); mResLoader = null ; } } }
8) Custom 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 using UnityEngine;namespace QFramework { public class CustomResExample : MonoBehaviour { public class MyRes : Res { public MyRes (string name ) { mAssetName = name; } public override bool LoadSync () { State = ResState.Ready; return true ; } public override void LoadAsync () { State = ResState.Ready; } protected override void OnReleaseRes () { State = ResState.Waiting; } } public class MyResCreator : IResCreator { public bool Match (ResSearchKeys resSearchKeys ) { return resSearchKeys.AssetName.StartsWith("myres://" ); } public IRes Create (ResSearchKeys resSearchKeys ) { return new MyRes(resSearchKeys.AssetName); } } void Start () { ResFactory.AddResCreator<MyResCreator>(); var resLoader = ResLoader.Allocate(); var resSearchKeys = ResSearchKeys.Allocate("myres://hello_world" ); var myRes = resLoader.LoadResSync(resSearchKeys); resSearchKeys.Recycle2Cache(); Debug.Log(myRes.AssetName); Debug.Log(myRes.State); } } }
9) LoadPrefabByMonoType 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 using System;using System.Collections;using System.Collections.Generic;using UnityEngine;namespace QFramework.Example { public class LoadPrefabByMonoType : MonoBehaviour { private ResLoader mResLoader; private void Awake () { ResKit.Init(); mResLoader = ResLoader.Allocate(); } private void Start () { mResLoader.LoadSync<SquareA>("SquareA" ) .InstantiateWithParent(transform) .LocalIdentity(); mResLoader.Add2Load<SquareA>("SquareA" ,(b, res) => { if (b) { (res.Asset as GameObject).GetComponent<SquareA>() .InstantiateWithParent(transform) .LocalRotation(Quaternion.Euler(new Vector3(0 , 0 , 45 ))) .GetComponent<SpriteRenderer>() .color = Color.blue; } }); mResLoader.LoadAsync(); } private void OnDestroy () { mResLoader.Recycle2Cache(); mResLoader = null ; } } }
AudioKit 1) Basic 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 using UnityEngine;using UnityEngine.UI;namespace QFramework.Example { public class AudioExample : MonoBehaviour { private void Awake () { var btnPlayHome = transform.Find("BtnPlayHome" ).GetComponent<Button>(); var btnPlayGame = transform.Find("BtnPlayGame" ).GetComponent<Button>(); var btnPlaySound = transform.Find("BtnPlaySoundClick" ).GetComponent<Button>(); var btnPlayVoiceA = transform.Find("BtnPlayVoice" ).GetComponent<Button>(); var btnSoundOn = transform.Find("BtnSoundOn" ).GetComponent<Button>(); var btnSoundOff = transform.Find("BtnSoundOff" ).GetComponent<Button>(); var btnMusicOn = transform.Find("BtnMusicOn" ).GetComponent<Button>(); var btnMusicOff = transform.Find("BtnMusicOff" ).GetComponent<Button>(); var btnVoiceOn = transform.Find("BtnVoiceOn" ).GetComponent<Button>(); var btnVoiceOff = transform.Find("BtnVoiceOff" ).GetComponent<Button>(); var btnStopAllSound = transform.Find("BtnStopAllSounds" ).GetComponent<Button>(); var musicVolumeSlider = transform.Find("MusicVolume" ).GetComponent<Slider>(); var voiceVolumeSlider = transform.Find("VoiceVolume" ).GetComponent<Slider>(); var soundVolumeSlider = transform.Find("SoundVolume" ).GetComponent<Slider>(); btnPlayHome.onClick.AddListener(() => { AudioKit.PlayMusic("resources://home_bg" ); }); btnPlayGame.onClick.AddListener(() => { AudioKit.PlayMusic("resources://game_bg" ); }); btnPlaySound.onClick.AddListener(() => { AudioKit.PlaySound("resources://game_bg" ); }); btnPlayVoiceA.onClick.AddListener(() => { AudioKit.PlayVoice("resources://game_bg" ); }); btnSoundOn.onClick.AddListener(() => { AudioKit.Settings.IsSoundOn.Value = true ; }); btnSoundOff.onClick.AddListener(() => { AudioKit.Settings.IsSoundOn.Value = false ; }); btnMusicOn.onClick.AddListener(() => { AudioKit.Settings.IsMusicOn.Value = true ; }); btnMusicOff.onClick.AddListener(() => { AudioKit.Settings.IsMusicOn.Value = false ; }); btnVoiceOn.onClick.AddListener(() => { AudioKit.Settings.IsVoiceOn.Value = true ; }); btnVoiceOff.onClick.AddListener(() => { AudioKit.Settings.IsVoiceOn.Value = false ; }); btnStopAllSound.onClick.AddListener(() => { AudioKit.StopAllSound(); }); AudioKit.Settings.MusicVolume.RegisterWithInitValue(v => musicVolumeSlider.value = v); AudioKit.Settings.VoiceVolume.RegisterWithInitValue(v => voiceVolumeSlider.value = v); AudioKit.Settings.SoundVolume.RegisterWithInitValue(v => soundVolumeSlider.value = v); musicVolumeSlider.onValueChanged.AddListener(v => { AudioKit.Settings.MusicVolume.Value = v; }); voiceVolumeSlider.onValueChanged.AddListener(v => { AudioKit.Settings.VoiceVolume.Value = v; }); soundVolumeSlider.onValueChanged.AddListener(v => { AudioKit.Settings.SoundVolume.Value = v; }); } } }
2) CustomAudioLoader 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 using System;using UnityEngine;namespace QFramework.Example { public class CustomAudioLoaderExample : MonoBehaviour { class ResourcesAudioLoaderPool : AbstractAudioLoaderPool { protected override IAudioLoader CreateLoader () { return new ResourcesAudioLoader(); } } class ResourcesAudioLoader : IAudioLoader { private AudioClip mClip; public AudioClip Clip => mClip; public AudioClip LoadClip (AudioSearchKeys panelSearchKeys ) { mClip = Resources.Load<AudioClip>(panelSearchKeys.AssetName); return mClip; } public void LoadClipAsync (AudioSearchKeys audioSearchKeys, Action<bool ,AudioClip> onLoad ) { var resourceRequest = Resources.LoadAsync<AudioClip>(audioSearchKeys.AssetName); resourceRequest.completed += operation => { var clip = resourceRequest.asset as AudioClip; onLoad(clip, clip); }; } public void Unload () { Resources.UnloadAsset(mClip); } } void Start () { AudioKit.Config.AudioLoaderPool = new ResourcesAudioLoaderPool(); } } }