本章建立一个最小可运行结构:Game 入口、Value/DI、一个自定义服务、事件和取消令牌。UI 有独立专题。
using IFramework;
public sealed class AppGame : Game, IInjectAble
{
[Inject] private IClockService clock;
protected override void Startup()
{
// ValueService 必须先注册,后续服务才能使用注入。
this.UseValues();
this.Use<IClockService>(new ClockService());
this.EnterService<IClockService>();
this.Values().Inject(this);
clock.StartClock();
}
protected override void OnQuit()
{
Log.L("Game quit");
}
}将 AppGame 挂到启动场景中的 GameObject。Game.Awake() 会创建服务容器并调用 Startup()。
using IFramework;
public interface IClockService : IService
{
void StartClock();
}
public sealed class ClockService : ServiceBase, IClockService
{
public void StartClock()
{
Log.L("Clock started");
}
protected override void OnUse(IServiceCollection services)
{
// 注册进容器时执行。适合保存依赖、绑定基础回调。
}
protected override void OnEnter(IServiceCollection services)
{
// EnterService<IClockService>() 时执行。
}
protected override void OnQuit(IServiceCollection services)
{
// Game.Quit() 时按服务注册逆序执行。
}
}服务使用流程是:
Use -> Enter -> 业务运行 -> Quit
Use 负责注册,Enter 负责进入。不是每个服务都必须单独 Enter;内置扩展会根据自身设计提供 EnterMvc、EnterState<T> 等入口。
只有实现 IInjectAble 的对象会被 IValueService.Inject 处理。
public sealed class PlayerController : IInjectAble
{
[Inject] private IClockService clock;
[Inject("secondary")] private IClockService secondaryClock;
}注册命名实例:
this.Use<IClockService>(new ClockService());
this.Use<IClockService>(new ClockService(), "secondary");
var controller = new PlayerController();
this.Values().Inject(controller);注入规则:
- 字段必须带
[Inject]。 - 字段不能是
readonly。 - 字段类型不能是值类型。
- 目标对象必须实现
IInjectAble。 - 名称必须和注册名称一致;默认名称是空字符串。
定义消息:
public sealed class GoldChanged : IEventArgs
{
public int value;
}订阅并绑定生命周期:
public sealed class GoldPresenter
{
public void Enable()
{
Events.Subscribe<GoldChanged>(new GoldHandler()).AddTo(this);
}
public void Disable()
{
this.ClearDisposable();
}
}
public sealed class GoldHandler : IEventHandler<GoldChanged>
{
public void OnEvent(GoldChanged message)
{
Log.L("Gold: {0}", message.value);
}
}发布:
Events.Publish(new GoldChanged { value = 100 });也可以使用字符串通道:
IDisposable subscription = Events.Subscribe("player.ready", _ => Log.L("ready"));
Events.Publish("player.ready", null);
subscription.Dispose();async AsyncTask WaitForSceneReady()
{
await Events.Wait("scene.ready");
Log.L("Scene is ready");
}
void CompleteSceneLoading()
{
Events.Notify("scene.ready");
}同一个 message 同时只能存在一个 Wait。重复等待会记录错误并返回 null。
携带数据:
async AsyncTask WaitForConfig()
{
var config = await Events.Wait<ConfigReady>();
Log.L("Version: {0}", config.version);
}
Events.Notify(new ConfigReady { version = "1.0" });async AsyncTask RunFlow(CancellationToken token)
{
await AsyncTask.NextFrame(token);
await AsyncTask.Delay(0.5f, token);
await AsyncTask.Sequence(token,
() => LoadConfig(),
() => LoadPlayer(),
() => EnterLobby());
}取消:
private CancellationTokenSource source;
void Begin()
{
source = new CancellationTokenSource();
RunFlow(source.Token).Coroutine();
}
void Stop()
{
source.Cancel();
}CancellationTokenSource.Dispose() 在本框架中等价于触发取消。它可以通过 AddTo(owner) 绑定 owner 生命周期:
var source = new CancellationTokenSource().AddTo(this);var list = StaticPool.Get<List<int>>();
list.Clear();
try
{
list.Add(1);
list.Add(2);
}
finally
{
list.Clear();
StaticPool.Set(list);
}数组:
var buffer = StaticPool.GetArray<object>(32);
try
{
// 使用 buffer
}
finally
{
// 归还时框架会清理包含引用的元素。
StaticPool.Set(buffer);
}不要重复归还同一个对象或数组。Pool API 主要面向 Unity 主线程。
主动退出:
Game.Current.Quit();Quit() 幂等,执行:
Game.OnQuit()。- 服务按注册逆序
OnQuit()。 Game上通过AddTo(game)绑定的IDisposable全部释放。
GameObject 销毁时也会自动调用 Quit()。