Strange是一个unity3d中用于控制反转的第三方框架,控制反转(IOC-Inversion of Control)思想是类间解耦的一个重要方法,对于我来说,任何解耦技术都值得去学习。什么是IOC?这里有详细解答。IOC框架已经在企业级开发和其他非游戏软件的开发中成为了主流,并且可以说已经非常成熟。我觉得它可以帮助游戏开发变得更加容易测试,更好的进行协作开发。我非常想尝试它看看到底可以在游戏开发过程中起到多大的帮助程度。
Strange使用起来真的像他的名字一样,非常”奇怪”。我发现它对于初学者来说,使用起来真的非常”闹心”,比如你想试着去写一个”Hello World”都非常不容易。这里是StrangeIOC框架的说明页面,但是这上面并没有一个真正意义上的”新手引导”来帮助我们了解Strange的工作机制,这就是你现在看到现在这篇文章的意义-用StrangeIOC框架写一个HelloWorld。
一些提醒:
- 在阅读本篇文章之前,最好先去上面提到的官方说明页面了解一下Strange框架的架构(看看它的每个部分的功能以及怎么整合到一块工作的)。
- 这篇文档使用的是signal(消息)而非event(事件)(因为相比event我更喜欢signal)
- 我不会把文档中的Unity项目提供出来,因为我希望大家自己动手去做,这样肯定会学到更多:)
- 这个Hello World示例只是简单的提供注入绑定(injection binding)、命令绑定(command binding)、调解绑定(mediation binding)的示例。
Signal
建立一个空Unity项目,下载并且解压Strange框架到Assets文件夹中,我们只需要框架的脚本,把”examples”和”.doc”文件夹去除,在Unity的的结构应该是这样的:
Assets
StrangeIoC
scripts
在Assets文件夹下创建”Game”文件夹,即用来创建Hello World示例的文件夹。文件夹的的结构应该是这样的:
Assets
Game
Scenes
Scripts在Scripts文件夹下新建名为HelloWorldSignals.cs的c#脚本,这个类将包含所有用到的signal,让我们coding起来:
1 2 3 4 5 6 7 8 9 |
using System;
using strange.extensions.signal.impl;
namespace Game {
public class StartSignal : Signal {}
} |
在Strange中,这个signal的概念非常像观察者模式(observer pattern)中的事件(events)。在这里,它以命名类的方式实现了继承Strange的Signal类.别急,我们马上会看到怎么去使用它。
Strange采用”Contexts”的概念来识别不同的问题域或者子模块。在实际的游戏项目中,你可以有多个”Contexts”,比如游戏逻辑、资源、持久层、统计分析、社交模块等等。我们在这个实例中只用了一个”Context”。
一个预构建的context在Strange中称为MVCSContext,MVCSContext默认使用event机制,我们来创建另外一种context父类,改造成使用signal机制,我们其他的context要继承这个SignalContext。
在Scripts下创建名为SignalContext.cs的脚本:
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 |
using System;
using UnityEngine;
using strange.extensions.context.impl; using strange.extensions.command.api; using strange.extensions.command.impl; using strange.extensions.signal.impl;
namespace Game { public class SignalContext : MVCSContext {
/** * Constructor */ public SignalContext (MonoBehaviour contextView) : base(contextView) { }
protected override void addCoreComponents() { base.addCoreComponents();
// bind signal command binder injectionBinder.Unbind<ICommandBinder>(); injectionBinder.Bind<ICommandBinder>().To<SignalCommandBinder>().ToSingleton(); }
public override void Launch() { base.Launch(); Signal startSignal = injectionBinder.GetInstance<StartSignal>(); startSignal.Dispatch(); }
} } |
在”Scripts”文件夹下创建一个新文件夹”Controller”,到这里有了一点MVC模式的特征。Strange作者建议我们应该以指令类(Command Class)的形式实现各个Controller接口,这个文件夹将包含所有的Command类,现在我们创建一个在StartSignal指令调用时执行的指令。在Controller文件夹下创建名为HelloWorldStartCommand.cs的类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
using System;
using UnityEngine;
using strange.extensions.context.api; using strange.extensions.command.impl;
namespace Game { public class HelloWorldStartCommand : Command {
public override void Execute() { // perform all game start setup here Debug.Log("Hello World"); }
} } |
现在我们为这个HelloWorld示例创建一个自定义的context类HelloWorldContext.cs:
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 |
[/color] using System;
using UnityEngine;
using strange.extensions.context.impl;
namespace Game { public class HelloWorldContext : SignalContext {
/** * Constructor */ public HelloWorldContext(MonoBehaviour contextView) : base(contextView) { }
protected override void mapBindings() { base.mapBindings();
// we bind a command to StartSignal since it is invoked by SignalContext (the parent class) on Launch() commandBinder.Bind<StartSignal>().To<HelloWorldStartCommand>().Once(); }
} } |
在这里,我们把StartSignal类绑定(bind)给了HelloWorldStartCommand类。这样在StartSignal的实例被调用时,HelloWorldStartCommand会进行实例化(instantiated)和执行(executed),注意在我们的示例中StartSignal信号会在SignalContext.Launch()方法中调用发出。
最后一步就是创建一个MonoBehaviour来在Unity中管理context,在Scripts文件夹下创建HelloWorldBootstrap.cs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
using System;
using UnityEngine;
using strange.extensions.context.impl;
namespace Game { public class HelloWorldBootstrap : ContextView {
void Awake() { this.context = new HelloWorldContext(this); }
} } |
用于在Unity中管理Strange context的接口类通常命名为“xxxBootstrap”,当然这只是一个建议,如果你乐意你可以随意起名字。这里唯一需要注意的是继承Strange框架的ContextView类的类需要是一个MonoBehaviour,我们在Awake()里分配了一个我们自定义好的context实例给继承的变量”context”。
创建一个空场景命名为”HelloStrange”,创建一个EmptyObject命名为Bootstrap,把我们之前创建的HelloWorldBootstrap add上来。可以跑一下这个场景,之前程序正确的话,你应该看到控制台的”Hello World”输出了。
更多unity2018的功能介绍请到paws3d学习中心查找。链接https://www.paws3d.com/learn/,也可以加入unity学习讨论群935714213
近期更有资深开发人士直播分享unity开发经验,详情请进入官网或加入QQ群了解