2017-03-01 09:19:25 chepy 阅读数 946
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

KSFramework是一个Unity 5 Asset Bundle开发框架和工具集,专注于运行时热重载,使用了SLua作为脚本引擎。
https://github.com/mr-kelly/KSFramework

KSFramework是一个整合KEngine、SLua和一些开发组件组成的全功能Unity 5开发框架,适合有一定规模的团队使用。

热重载是KSFramework的开发重点——在不重启游戏的前提下,重载代码、配置表可立刻看到修改效果,最大限度的提升开发、调试的速度,并且在运营阶段方便的进行产品热更新。

看看Demo!

双击打开Assets/Game.unity场景,点击播放。

开始Game.unity后的日志输出

这时候KSFramework的默认Demo开始,做了这些事情:

  • 基础模块的启动
  • Lua模块的启动
  • 尝试读取并打印Excel表格GameConfig.xlsx内容
  • 加载UI界面Login
  • 执行UI界面Login的Lua脚本
  • Lua脚本绑定UI控件、设置UI控件
  • Lua脚本读取并打印Excel表格GameConfig.xlsx内容

总而言之,这个Demo囊括了KSFramework中的几个核心模块的使用:

  • KEngine中的Setting模块的使用
  • KEngine中的UI的资源加载
  • SLua脚本引擎与UI的联合

接下来,将模仿这个Demo,创建/加载一个新的UI界面、创建/读取一个新的配置表格。

尝试做一个公告界面Billboard

接下来,我们将创建一个UI公告牌(Billboard),使用Lua脚本,并从配置表读取公告内容。

创建UI资源

创建New Scene

KEngine-UI-Create UI创建UI布局

点击Create UI后,默认随机名字,把UI名字修改为Billboard

修改UI名字为Billboard,UI界面右边带有黄色UI标识

编辑一下UI场景,一个背景Image,两个Label

保存一下场景,保存到Assets/BundleEditing/UI/Billboard.unity

导出——打包AssetBundle,快捷键Ctrl+Alt+E

加载UI界面

好了,Billboard界面创建好了,也导出成了AssetBundle。
接下来,我们通过代码打开界面。

编辑Assets/Code/Game.cs

在OnFinishInitModules函数的末端,加上这样的一句:

// 开始加载我们的公告界面!
UIModule.Instance.OpenWindow("Billboard");

完成。 打开场景Assets/Game.unity,点击播放按钮:

我们的UI通过AssetBundle打开了,弹出提示找不到UI Lua脚本,接下来我们创建Lua脚本吧

[我们的UI通过AssetBundle打开了,弹出提示找不到UI Lua脚本,接下来我们创建Lua脚本吧。

创建Lua脚本

在目录Product/Lua/UI中新建一个lua文件

写一段Lua代码:UIBillboard的执行逻辑

local UIBase = import("KSFramework/UIBase")

local UIBillboard = {}
extends(UIBillboard, UIBase)

function UIBillboard:OnInit(controller)
    self.Controller = controller
    self.TitleLabel = self:GetUIText('Title')
    self.ContentLabel = self:GetUIText('Content')
end

function UIBillboard:OnOpen()
    self.TitleLabel.text = "This is a title"
    self.ContentLabel.text = "Here is content!"
end

return UIBillboard

这段lua中,创建了一个Table叫UIBillboard,这个table必须有OnInit(controller)函数。它通过代码设置了UI中的文字。

好了,接下来,我们要为策划准备配置表了。

创建配置表

打开Product/SettingSource目录,复制一份StringsTable.xlsx,并改名叫Billboard.xlsx吧

复制一份StringsTable.xlsx

用Excel打开我们新的Billboard.xlsx,编辑我们的公告。我们大概定一下需求,我们假设写入3条公告,每次打开公告随机显示其中一条。每个公告,我们给它一个英文ID,一列中文标题,一列中文内容。

Excel表修改如下:

增加公告内容

回到Unity,监测到Excel变动。点击OK。

上一步监测到变动,只编译Excel表,手动执行一些重新编译,并生成配置表代码

这时候,打开AppSettings.cs代码文件,我们可以发现,已经生成名叫BillboardSettings的类了

因为我们要在Lua使用BillboardSettings读取配置表,这里需要重新生成一下SLua的静态代码

接下来修改Lua代码,随机读取一条公告,并设置Content、Title

运行播放Game.unity,我们的公告界面完成了

公告界面完成了。我们创建了一个UI、写了C#和Lua代码加载它、然后创建了一张配置表,再从Lua读取配置表,设置UI的显示。

玩玩热重载

热重载Lua

接着我们刚才运行的Game.unity。 我们尝试一下对Lua热重载:在不重启游戏的情况,快速重载Lua代码,方便程序调试。

菜单KSFramework->UI->Reload+ReOpen:热重载Lua脚本,并重新打开目前处在打开状态的UI界面

我们可以从菜单执行热重载并重新打开UI界面,或者使用快捷键Ctrl+Alt+Shift+R。
由于我们的Lua脚本中,每次执行随机获取一条公告。因此可以看到公告内容在不停的变动着。

热重载的实现,主要依赖于每个lua脚本的最后一行——return Table;C#层在执行完Lua脚本后,会把这个返回的Table缓存起来,每次需要使用table时,直接从缓存拿;而热重载,实际就是把这个缓存table删掉,这样当需要用到这个table时,就会重新执行Lua文件,来获取Table,这样就达到了热重载得目的。

热重载Excel表格

我们保持运行刚刚的Game.unity,不要停止掉。这时候我们去修改Excel表格。

修改Excel表格

保存后,回到Unity,提示表格有改动。

发现表格有变动,点击OK编译表

从菜单中心一下重载配置表格吧

Ctrl+Alt+Shift+R刷新Lua

重载Lua,我们的新修改的配置表内容生效了。

至此,我们的Lua和配置表的改动,都可以在不重启、不重新转菊花的情况下快速修改。


KEngine策划指南:配置表格的编辑与编译

KEngine资源的打包、加载、调试监控

2016-09-10 11:21:30 u013108312 阅读数 2671
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

原文路径:http://blog.csdn.net/u013108312/article/details/52493799
新建C#脚本文件:CreateResini,这里没有继承MonoBehaviour

using UnityEngine;
using System.Collections;
using UnityEditor;
using System.IO;
using System.Collections.Generic;

public class CreateResini  
{
    [MenuItem("TestMenu/CreateResIni")]
    public static void Createini()
    {

        Dictionary<string, string> dic = new Dictionary<string, string>();
        string pathRes = Application.dataPath +"/Resources/";
        string pathIni = pathRes + "/res.txt";
        if (File.Exists(pathIni))
        {
            File.Delete(pathIni);
        }

        CreateResInfo(pathRes, ref dic);
        List<string> list = new List<string>();
        foreach(KeyValuePair<string,string> keyValue in dic)
        {
            list.Add(keyValue.Key +"="+keyValue.Value);
        }
        File.WriteAllLines(pathRes +"/res.txt",list.ToArray());
        Log.Debug("生成完毕 ");
        AssetDatabase.Refresh();
    }

    public static void CreateResInfo(string path,ref Dictionary<string,string>dic)
    {
        DirectoryInfo dir = new DirectoryInfo(path);
        if (!dir.Exists)
        {
            return;
        }
        FileInfo[] files = dir.GetFiles();
        for (int i = 0; i < files.Length;i++ )
        {

            FileInfo info = files[i];
            if (!(info.Name.IndexOf(".meta",0) > 0))
            {

                string pathdir = info.FullName.Replace("\\","/")
                    .Replace((Application.dataPath + "/Resources/"), "")
                    .Replace(info.Name, "").TrimEnd('/');
                string fileName = Path.GetFileNameWithoutExtension(info.Name);
                Debug.Log("fileName =" + fileName);
                if (!dic.ContainsKey(info.Name))
                {
                    dic.Add(fileName, pathdir);
                }
                else
                {
                    Log.Error("存在相同的资源名称 名称为:" + info.Name + "/path1=" + dic[info.Name] + "/ path2 =" + pathdir);
                }
            }
        }
        DirectoryInfo[] dirs = dir.GetDirectories();
        if (dirs.Length > 0)
        {
            for (int i = 0; i < dirs.Length;i++ )
            {
                string tempPath = Path.Combine(path, dirs[i].Name);
                CreateResInfo(tempPath, ref dic);
            }
        }
    }
}

原文路径:http://blog.csdn.net/u013108312/article/details/52493799

2016-08-29 18:10:52 u013108312 阅读数 10419
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

原文链接:http://blog.csdn.net/u013108312/article/details/52355425
新建一个文件夹:UIMgr
新建2个C#脚本:BaseUI.cs UIMgr.cs
里面的UIMgr.cs继承了EventNode。EventNode在前面有讲,这里重新给出一下。
IResLoadListener.cs接口,资源加载回调

支付宝捐赠

打赏红包

using UnityEngine;
using System.Collections;

public class BaseUI : MonoBehaviour 
{
    
    /// <summary>
    /// 当前界面名称
    /// </summary>
    [HideInInspector]
    public string UIName;

    private Transform mTransform;
    public Transform CacheTransform
    {
        get
        {
            if (mTransform == null) mTransform = this.transform;
            return mTransform;
        }
    }

    private GameObject mGo;
    public GameObject CacheGameObject
    {
        get
        {
            if (mGo == null) mGo = this.gameObject;
            return mGo;
        }
    }

    /// <summary>
    /// 显示当前UI
    /// </summary>
    /// <param name="param">附加参数</param>
    public void Show(object param = null)
    {
        CacheGameObject.SetActive(true);
    }

    /// <summary>
    /// 隐藏当前界面
    /// </summary>
    public void Hide()
    {
        CacheGameObject.SetActive(false);
    }

   

	/// <summary>
	/// 绑定脚本并且激活游戏物体会调用的方法
	/// </summary>
	void Awake() 
	{
        OnAwake();
	}

    /// <summary>
    /// 初始化UI主要用于寻找组件等
    /// </summary>
    public void UIInit()
    {
        OnInit();
    }

    /// <summary>
    /// 显示当前界面
    /// </summary>
    /// <param name="param">附加参数</param>
    protected virtual void OnShow(object param){ }

    /// <summary>
    /// 隐藏当前界面
    /// </summary>
    protected virtual void OnHide() { }

    /// <summary>
    /// 初始化当前界面
    /// </summary>
    protected virtual void OnInit() { }

    protected virtual void OnAwake() { }

    /// <summary>
    /// 删除当前UI 
    /// </summary>
    protected virtual void OnDestroy() { }
}

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class EventNode : MonoBehaviour
{
    /// <summary>
    /// 节点优先级
    /// </summary>
    public int EventNodePriority { set; get; }

    /// <summary>
    /// 所有消息集合
    /// </summary>
    private Dictionary<int, List<IEventListener>> mListeners = new Dictionary<int, List<IEventListener>>();

    /// <summary>
    /// 消息节点
    /// </summary>
    private List<EventNode> mNodeList = new List<EventNode>();

    /// <summary>
    /// 挂载一个消息节点到当前节点上
    /// </summary>
    /// <param name="node">消息节点</param>
    /// <returns>如果当前节点里面已经包含要添加的这个节点那么返回false</returns>
    public bool AttachEventNode(EventNode node)
    {
        if (node == null)
        {
            return false;
        }

        if (mNodeList.Contains(node))
        {
            return false;
        }
        int pos = 0;
        for (int i = 0; i < mNodeList.Count;i++ )
        {
            if (node.EventNodePriority > mNodeList[i].EventNodePriority)
            {
                break;
            }
            pos++;
        }

        mNodeList.Insert(pos,node);
        return true;
    }

    /// <summary>
    /// 卸载一个消息节点
    /// </summary>
    /// <param name="node">消息节点</param>
    /// <returns>如果节点不存在那么返回false</returns>
    public bool DetachEventNode(EventNode node)
    {
        if (!mNodeList.Contains(node))
        {
            return false;
        }
        mNodeList.Remove(node);
        return true;
    }

    /// <summary>
    /// 挂载一个消息监听器到当前的消息节点
    /// </summary>
    /// <param name="key">消息ID</param>
    /// <param name="listener">消息监听器</param>
    /// <returns>当前消息节点已经挂载了这个消息监听器那么返回false</returns>
    public bool AttachEventListener(int key,IEventListener listener)
    {
        if (listener == null)
        {
            return false;
        }
        if (!mListeners.ContainsKey(key))
        {
            mListeners.Add(key,new List<IEventListener>() { listener });
            return true;
        }
        if (mListeners[key].Contains(listener))
        {
            return false;
        }
        int pos = 0;
        for (int i = 0;i< mListeners[key].Count;i++ )
        {
            if (listener.EventPriority() > mListeners[key][i].EventPriority())
            {
                break;
            }
            pos++;
        }
        mListeners[key].Insert(pos,listener);
        return true;
    }

    /// <summary>
    /// 卸载一个消息节点
    /// </summary>
    /// <returns>如果当前消息节点不存在那么返回false</returns>
    public bool DetachEventListener(int key,IEventListener listener)
    {
       if (mListeners.ContainsKey(key) && mListeners[key].Contains(listener))
       {
           mListeners[key].Remove(listener);
           return true;
       }
       return false;
    }

    public void SendEvent(int key,object param1 = null,object param2 = null)
    {
        DispatchEvent(key, param1, param2);
    }

    /// <summary>
    /// 派发消息到子消息节点以及自己节点下的监听器上
    /// </summary>
    /// <param name="key">消息ID</param>
    /// <param name="param1"></param>
    /// <param name="param2"></param>
    /// <returns>如果中断消息返回true</returns>
    private bool DispatchEvent(int key,object param1,object param2)
    {
        for (int i = 0; i < mNodeList.Count;i++ )
        {
            if (mNodeList[i].DispatchEvent(key, param1, param2)) return true;
        }
        return TriggerEvent(key, param1, param2);
    }

  
    /// <summary>
    /// 消息触发
    /// </summary>
    /// <param name="key">消息id</param>
    /// <param name="param1"></param>
    /// <param name="param2"></param>
    /// <returns>是否中断</returns>
    private bool TriggerEvent(int key,object param1,object param2)
    {
        if (!this.gameObject.activeSelf || !this.gameObject.activeInHierarchy || !this.enabled)
        {
            return false;
        }

        if (!mListeners.ContainsKey(key))
        {
            return false;
        }
        List<IEventListener> listeners = mListeners[key];
        for (int i = 0; i < listeners.Count; i++)
        {
            if (listeners[i].HandleEvent(key, param1, param2)) return true;
        }
        return false;
    }

   void OnApplicationQuit()
    {
        mListeners.Clear();
        mNodeList.Clear();
    }

}

新建接口 IResLoadListener .cs

using UnityEngine;
using System.Collections;

/// <summary>
/// 资源加载回调
/// </summary>
public interface IResLoadListener  
{
    void Finish(object asset);

    void Failure();
}

新建一个类UIMgr .cs继承EventNode

using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;

public class UIMgr : EventNode 
{
    private static UIMgr mInstance;
    public static UIMgr Instance
    {
        get
        {
            return mInstance;
        }
    }

    /// <summary>
    /// 所有UI
    /// </summary>
    private Dictionary<string, BaseUI> mDicUI = new Dictionary<string, BaseUI>();

    /// <summary>
    /// 添加一个UI
    /// </summary>
    /// <param name="ui"></param>
    public void AddUI(BaseUI ui)
    {
        if (ui != null)
        {
            mDicUI[ui.UIName] = ui;
            
        }
        
    }

    /// <summary>
    /// 移除一个UI
    /// </summary>
    /// <param name="ui"></param>
    public void RemoveUI(BaseUI ui)
    {
        if (ui != null && mDicUI.ContainsKey(ui.UIName))
        {
            mDicUI.Remove(ui.UIName);
        }
    }

    /// <summary>
    /// 所有命令集合
    /// </summary>
    public List<Command> cmdList = new List<Command>();

    internal Transform UIROOT = null;
	void Awake() 
	{
        UIROOT = this.transform.FindChild("UIRoot");
        mInstance = this;
        DontDestroyOnLoad(this.gameObject);
	}

    #region 创建UI

    /// <summary>
    /// 创建UI
    /// </summary>
    /// <param name="uiName">UI名称</param>
    /// <param name="type">要绑定的脚本</param>
    /// <param name="listener">创建完成的回调</param>
    public void CreateUI(string uiName, Type type, ILoadUIListener listener)
    {
        cmdList.Add(Command.CreateCmd(type,uiName,listener));
    }

    /// <summary>
    /// 创建UI的实体部分
    /// </summary>
    /// <param name="cmd">命令</param>
    private void _Create(Command cmd)
    {
        BaseUI ui= null;
        mDicUI.TryGetValue(cmd.uiName, out ui);
        if (ui != null)
        {
            if (cmd.listener != null) cmd.listener.FiniSh(ui);
        }
        else
        {
            ResMgr.Instance.Load(cmd.uiName,new LoadResFinish(cmd));
        }
    }

    #endregion

    #region 显示UI

    /// <summary>
    /// 显示一个UI界面  如果不存在就创建
    /// </summary>
    /// <param name="uiName">ui名称</param>
    /// <param name="type">要绑定的脚本</param>
    /// <param name="listener">如果界面不存在则会有界面加载完成后的回调</param>
    /// <param name="param">要传入的参数</param>
    public void ShowUI(string uiName, Type type, ILoadUIListener listener,object param = null,bool createCanCall = false)
    {
        BaseUI ui = null;
        mDicUI.TryGetValue(uiName,out ui);
        if (ui == null)
        {
            cmdList.Add(Command.CreateAndShowCmd(uiName,type,listener, param, createCanCall));
        }
        else
        {
            cmdList.Add(Command.ShowCmd(uiName, listener, param, createCanCall));
        }
        
        
    }

    /// <summary>
    /// 显示一个界面
    /// </summary>
    /// <param name="cmd"></param>
    private void _ShowUI(Command cmd)
    {

        BaseUI ui = null;
        mDicUI.TryGetValue(cmd.uiName, out ui);
        if (ui != null)
        {
            if (cmd.listener != null)
            {
                cmd.listener.FiniSh(ui);
            }
            ui.Show();
            
        }
    }

   
    #endregion

    #region  隐藏UI

    /// <summary>
    /// 隐藏这个UI
    /// </summary>
    /// <param name="uiName"></param>
    public void HideUI(string uiName)
    {
        
        cmdList.Add(Command.HideCmd(uiName));
    }


    private void _HideUI(Command cmd)
    {
        Debug.Log("_HideUI " + cmd.uiName);
        BaseUI ui = null;
        mDicUI.TryGetValue(cmd.uiName, out ui);
        if (ui != null)
        {
            ui.Hide();
        }
    }
#endregion

    #region  删除UI

    /// <summary>
    /// 删除UI
    /// </summary>
    /// <param name="uiName">UI名称</param>
    public void DestroyUI(string uiName)
    {
        cmdList.Add(Command.DestroyCmd(uiName));
    }

    private void _DestroyUI(Command cmd)
    {
        BaseUI ui = null;
        mDicUI.TryGetValue(cmd.uiName, out ui);
        if (ui != null)
        {
            mDicUI.Remove(ui.UIName);
            Destroy(ui.CacheGameObject);
        }
    }

    #endregion

	// Update is called every frame, if the MonoBehaviour is enabled.
	void Update() 
	{

        if (cmdList.Count > 0)
        {
            Command tempCmd = null;
            tempCmd = cmdList[0];
            if (tempCmd == null)
            {
                cmdList.RemoveAt(0);
            }
            else
            {
                switch(tempCmd.cmdType)
                {
                    case Command.CmdType.CreateAndShow:
                        _Create(tempCmd);
                        break;
                    case Command.CmdType.Create:
                        _Create(tempCmd);
                        break;
                    case Command.CmdType.Destroy:
                        _DestroyUI(tempCmd);
                        break;
                    case Command.CmdType.Hide:
                        
                        _HideUI(tempCmd);
                        break;
                    case Command.CmdType.Show:
                        _ShowUI(tempCmd);
                        break;
                }
                cmdList.RemoveAt(0);
            }
        }
	}

    /// <summary>
    /// UI资源加载完成的回调
    /// </summary>
    public class LoadResFinish : IResLoadListener
    {
        /// <summary>
        /// 命令
        /// </summary>
        public Command cmd;
        public LoadResFinish(Command _cmd)
        {
            cmd = _cmd;
        }

        public void Finish(object asset)
        {

            if (cmd == null)
            {
                return;
            }
            GameObject go = Instantiate<GameObject>(asset as GameObject);
            go.SetActive(false);
            BaseUI ui =  go.AddComponent(cmd.type) as BaseUI;
            ui.UIInit();
            ui.UIName = cmd.uiName;
            go.gameObject.name = ui.UIName;
            ui.CacheTransform.SetParent(UIMgr.Instance.UIROOT, false);
            UIMgr.Instance.AddUI(ui);
            if (cmd.cmdType == Command.CmdType.CreateAndShow)
            {
                UIMgr.Instance.ShowUI(cmd.uiName, cmd.type, cmd.listener);
            }
            else if (cmd.createCanCall && cmd.listener != null)
            {
                cmd.listener.FiniSh(ui);
            }
        }

        public void Failure()
        {
            if (cmd.createCanCall && cmd.listener != null)
            {
                cmd.listener.Failure();
            }
        }
    }

    /// <summary>
    /// 界面加载回调
    /// </summary>
    public interface ILoadUIListener
    {
        void FiniSh(BaseUI ui);
        void Failure();
    }

    /// <summary>
    /// 操作UI命令集
    /// </summary>
    public class Command
    {
        /// <summary>
        /// 命令类型
        /// </summary>
        public enum CmdType
        {
            /// <summary>
            /// 创建
            /// </summary>
            CreateAndShow,
            /// <summary>
            /// 创建
            /// </summary>
            Create,
            /// <summary>
            /// 显示或者刷新
            /// </summary>
            Show,
            /// <summary>
            /// 隐藏
            /// </summary>
            Hide,
            /// <summary>
            /// 删除
            /// </summary>
            Destroy,
        }

        /// <summary>
        /// UI名称
        /// </summary>
        public string uiName;

        /// <summary>
        /// 要绑定的脚本
        /// </summary>
        public Type type;

        /// <summary>
        /// 加载完成之后的回调
        /// </summary>
        public ILoadUIListener listener;

        /// <summary>
        /// 要传入的数据
        /// </summary>
        public object param;

        /// <summary>
        /// 命令类型
        /// </summary>
        public CmdType cmdType;

        /// <summary>
        /// 创建时候需要回调
        /// </summary>
        public bool createCanCall = true;

        /// <summary>
        /// 获取一个显示的命令
        /// </summary>
        /// <param name="_uiName">UI名称</param>
        /// <param name="_param">要传入的参数</param>
        public static Command CreateAndShowCmd(string uiName, Type type, ILoadUIListener listener, object param , bool createCanCall)
        {
            Command cmd = new Command(CmdType.CreateAndShow, uiName, type);
            cmd.createCanCall = createCanCall;
            cmd.listener = listener;
            cmd.type = type;
            cmd.param = param;
            return cmd;
        }
        /// <summary>
        /// 获取一个显示的命令
        /// </summary>
        /// <param name="_uiName">UI名称</param>
        /// <param name="_param">要传入的参数</param>
        public static Command ShowCmd(string _uiName,ILoadUIListener listener ,object _param, bool _createCanCall)
        {
            Command cmd = new Command(CmdType.Show, _uiName, _param);
            cmd.createCanCall = _createCanCall;
            cmd.listener = listener;
            return cmd;
        }


        /// <summary>
        /// 获取一个创建的命令
        /// </summary>
        /// <param name="_type">要绑定的脚本</param>
        /// <param name="_listener">加载完成之后的回调</param>
        public static Command CreateCmd(Type _type,string _uiName, ILoadUIListener _listener)
        {
            return new Command(CmdType.Create, _uiName, _type, _listener);
        }

        /// <summary>
        /// 获取一个隐藏命令
        /// </summary>
        /// <param name="_uiName">要隐藏的UI名称</param>
        /// <returns></returns>
        public static Command HideCmd(string _uiName)
        {
            return new Command(CmdType.Hide, _uiName,null);
        }

        /// <summary>
        /// 获取一个删除的命令
        /// </summary>
        /// <param name="_uiName">UI名称</param>
        /// <returns></returns>
        public static Command DestroyCmd(string _uiName)
        {
            return new Command(CmdType.Destroy, _uiName, null);
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="_cmdType">命令类型</param>
        /// <param name="_uiName">UI名称</param>
        /// <param name="_param">要传入的参数</param>
        public Command(CmdType _cmdType, string _uiName, object _param)
        {
            uiName = _uiName;
            cmdType = _cmdType;
            param = _param;
        }

        /// <summary>
        /// 构造
        /// </summary>
        /// <param name="_cmdType">命令类型</param>
        /// <param name="_type">要绑定的脚本</param>
        /// <param name="_listener">加载完成之后的回调</param>
        public Command(CmdType _cmdType,string _uiName,Type _type,ILoadUIListener _listener)
        {
            cmdType = _cmdType;
            type = _type;
            listener = _listener;
            uiName = _uiName;
        }
    }
}

支付宝捐赠

打赏红包

原文链接:http://blog.csdn.net/u013108312/article/details/52355425

2016-08-29 12:12:35 u013108312 阅读数 6578
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

原文链接:http://blog.csdn.net/u013108312/article/details/52351850
这里写图片描述
新建文件夹:ResMgr。接着新建三个C#脚本。代码如下:
IResLoadListener.cs
AssetInfo.cs
ResMgr.cs

using UnityEngine;
using System.Collections;

/// <summary>
/// 资源加载回调
/// </summary>
public interface IResLoadListener  
{
    void Finish(object asset);

    void Failure();
}
using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

/// <summary>
/// 资源信息
/// </summary>
public class AssetInfo
{
    /// <summary>
    /// 资源
    /// </summary>
    public object asset;

    /// <summary>
    /// 是否常驻内存
    /// </summary>
    public bool isKeepInMemory;

    /// <summary>
    /// 资源堆栈数量
    /// </summary>
    public int stackCount = 0;
}

/// <summary>
/// 资源加载信息
/// </summary>
public class RequestInfo
{
    /// <summary>
    /// 资源反馈信息
    /// </summary>
    public ResourceRequest request;

    /// <summary>
    /// 是否常驻内存
    /// </summary>
    public bool isKeepInMemory;

    /// <summary>
    /// 加载完成之后的回调
    /// </summary>
    public List<IResLoadListener> linsteners;

    public void AddListener(IResLoadListener listener)
    {
        if (linsteners == null)
        {
            linsteners = new List<IResLoadListener>() { listener };
        }
        else
        {
            if (!linsteners.Contains(listener))
            {
                linsteners.Add(listener);
            }
        }
    }

    /// <summary>
    /// 资源名称
    /// </summary>
    public string assetName;

    public string assetFullName
    {
        get
        {
            return ResMgr.Instance.GetFileFullName(assetName);
        }
    }

    /// <summary>
    /// 资源类型
    /// </summary>
    public Type type;

    /// <summary>
    /// 资源是否加载完成
    /// </summary>
    public bool IsDone
    {
        get
        {
            return (request != null && request.isDone);
        }
    }

    /// <summary>
    /// 加载到的资源
    /// </summary>
    public object Asset
    {
        get
        {
            return request != null ? request.asset : null;
        }
    }

    public void LoadAsync()
    {
        request = Resources.LoadAsync(assetFullName, type);
    }
}
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.IO;

public class ResMgr : EventNode,IEventListener 
{
    private Dictionary<string, string> mAssetPathDic = new Dictionary<string, string>();
    public string GetFileFullName(string assetName)
    {
        if (mAssetPathDic.Count == 0)
        {
            UnityEngine.TextAsset tex = Resources.Load<TextAsset>("res");
            StringReader sr = new StringReader(tex.text);
            string fileName = sr.ReadLine();
            while (fileName != null)
            {
                Debug.Log("fileName =" + fileName);
                string [] ss = fileName.Split('=');
                mAssetPathDic.Add(ss[0], ss[1]);
                fileName = sr.ReadLine();
            }
        }
        return assetName = mAssetPathDic[assetName] + "/" + assetName; 
    }
    /// <summary>
    /// 所有资源字典
    /// </summary>
    private Dictionary<string, AssetInfo> mDicAaaet = new Dictionary<string, AssetInfo>();

    /// <summary>
    /// CPU 个数
    /// </summary>
    private int mProcessorCount = 0;

    private static ResMgr mInstance;
    public static ResMgr Instance
    {
        get
        {
            return mInstance;
        }
    }



    // Awake is called when the script instance is being loaded.
    void Awake()
    {
        mInstance = this;
        DontDestroyOnLoad(this.gameObject);
        AttachEventListener(EventDef.ResLoadFinish, this);
        mProcessorCount = SystemInfo.processorCount > 0 && SystemInfo.processorCount <= 8 ? SystemInfo.processorCount : 1;
    }

    void OnDestroy()
    {
        if (Instance != null)
        {
            Instance.DetachEventListener(EventDef.ResLoadFinish, this);
        }
    }

    /// <summary>
    /// 正在加载的列表
    /// </summary>
    public List<RequestInfo> mInLoads = new List<RequestInfo>();

    /// <summary>
    /// 等待加载的列表
    /// </summary>
    public Queue<RequestInfo> mWaitting = new Queue<RequestInfo>();

    /// <summary>
    /// 资源加载堆栈
    /// </summary>
    public Stack<List<string>> mAssetStack = new Stack<List<string>>();

    #region 加载资源
    public void Load(string assetName, IResLoadListener listener, Type type = null, bool isKeepInMemory = false, bool isAsync = true)
    {
        if (mDicAaaet.ContainsKey(assetName))
        {
            listener.Finish(mDicAaaet[assetName]);
            return;
        }
        if (isAsync)
        {
            LoadAsync(assetName, listener,isKeepInMemory,type);
        }
    }
    #endregion

    #region 异步Res加载
    private void LoadAsync(string assetName, IResLoadListener listener,bool isKeepInMemory,Type type)
    {
        for (int i = 0; i < mInLoads.Count; i++)
        {
            if (mInLoads[i].assetName == assetName)
            {
                mInLoads[i].AddListener(listener);
                return;
            }
        }

        foreach(RequestInfo info in mWaitting)
        {
            if (info.assetName == assetName)
            {
                info.AddListener(listener);
                return;
            }
        }

        RequestInfo requestInfo = new RequestInfo();
        requestInfo.assetName = assetName;
        requestInfo.AddListener(listener);
        requestInfo.isKeepInMemory = isKeepInMemory;
        requestInfo.type = type == null ? typeof(GameObject) : type;
        mWaitting.Enqueue(requestInfo);
    }
 #endregion

    #region 资源处理

        /// <summary>
        /// 从资源字典中取得一个资源
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <returns></returns>
        public AssetInfo GetAsset(string assetName)
        {
            AssetInfo info = null;
            mDicAaaet.TryGetValue(assetName,out info);
            return info;
        }

        /// <summary>
        /// 释放一个资源
        /// </summary>
        /// <param name="assetName">资源名称</param>
        public void ReleaseAsset(string assetName)
        {
            AssetInfo info = null;
            mDicAaaet.TryGetValue(assetName, out info);

            if (info != null && !info.isKeepInMemory)
            {
                mDicAaaet.Remove(assetName);
            }
        }

        /// <summary>
        /// 修改资源是否常驻内存
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="IsKeepInMemory">是否常驻内存</param>
        public void IsKeepInMemory(string assetName,bool IsKeepInMemory)
        {
            AssetInfo info = null;
            mDicAaaet.TryGetValue(assetName, out info);

            if (info != null)
            {
                info.isKeepInMemory = IsKeepInMemory;
            }
        }
    #endregion

    #region 资源释放以及监听

        /// <summary>
        /// 把资源压入顶层栈内
        /// </summary>
        /// <param name="assetName">资源名称</param>
        public void AddAssetToName(string assetName)
        {
            if (mAssetStack.Count == 0)
            {
                mAssetStack.Push(new List<string>() { assetName });
            }

            List <string> list = mAssetStack.Peek();
            list.Add(assetName);
        }

        /// <summary>
        /// 开始让资源入栈
        /// </summary>
        public void PushAssetStack()
        {
            List<string> list = new List<string>();
            foreach(KeyValuePair<string,AssetInfo> info in mDicAaaet)
            {
                info.Value.stackCount++;
                list.Add(info.Key);
            }

            mAssetStack.Push(list);
        }

        /// <summary>
        /// 释放栈内资源
        /// </summary>
        public void PopAssetStack()
        {
            if (mAssetStack.Count == 0) return;

            List<string> list = mAssetStack.Pop();
            List<string> removeList = new List<string>();
            AssetInfo info = null;
            for (int i = 0; i < list.Count;i++ )
            {
                if (mDicAaaet.TryGetValue(list[i],out info))
                {
                    info.stackCount--;
                    if (info.stackCount < 1 && !info.isKeepInMemory)
                    {
                        removeList.Add(list[i]);
                    }
                }
            }
            for (int i = 0; i < removeList.Count;i++ )
            {
                if (mDicAaaet.ContainsKey(removeList[i]))
                mDicAaaet.Remove(removeList[i]);
            }

            GC();
        }

        /// <summary>
        /// 释放
        /// </summary>
        public void GC()
        {
            Resources.UnloadUnusedAssets();
            System.GC.Collect();
        }


    #endregion

    void Update()
    {
        if (mInLoads.Count > 0)
        {
            for (int i = mInLoads.Count - 1; i >= 0; i--)
            {
                if (mInLoads[i].IsDone)
                {
                    RequestInfo info = mInLoads[i];
                    SendEvent(EventDef.ResLoadFinish, info);
                    mInLoads.RemoveAt(i);
                }
            }
        }

        while (mInLoads.Count < mProcessorCount && mWaitting.Count > 0)
        {
            RequestInfo info = mWaitting.Dequeue();
            mInLoads.Add(info);
            info.LoadAsync();
        }
    }



    public bool HandleEvent(int id, object param1, object param2)
    {
        switch (id)
        {
            case EventDef.ResLoadFinish:
                RequestInfo info = param1 as RequestInfo;
                if (info != null)
                {
                    if (info.Asset != null)
                    {
                        AssetInfo asset = new AssetInfo();
                        asset.isKeepInMemory = info.isKeepInMemory;
                        asset.asset = info.Asset;
                        if (!mDicAaaet.ContainsKey(info.assetName))
                        {
                            mDicAaaet.Add(info.assetName, asset);
                        }

                        for (int i = 0; i < info.linsteners.Count;i++ )
                        {
                            if (info.linsteners[i] != null)
                            {
                                info.linsteners[i].Finish(info.Asset);
                            }
                        }
                        AddAssetToName(info.assetName);
                    }
                }
                else
                {
                    for (int i = 0; i < info.linsteners.Count; i++)
                    {
                        if (info.linsteners[i] != null)
                        {
                            info.linsteners[i].Failure();
                        }
                    }
                }
                return false;
        }
        return false;
    }

    public int EventPriority()
    {
        return 0;
    }
}

原文链接:http://blog.csdn.net/u013108312/article/details/52351850

2017-06-22 15:35:25 ycl295644 阅读数 2256
  • Unity 值得看的500+ 技术内容列表

    Unity3D是由Unity Technologies开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

由于工作原因最近在看unity的一个IOC框架:StrangeIOC,官方的文档都不是很好理解,找到了一篇比较好的GetStart文章,顺手翻译一下,一来方便自己加深理解,二来还是想共享出来,

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)的示例。

示例 — HelloWorld


Signal
建立一个空Unity项目,下载并且解压Strange框架到Assets文件夹中,我们只需要框架的脚本,把”examples”和”.doc”文件夹去除,在Unity的的结构应该是这样的:
Assets
StrangeIoC
scripts

在Assets文件夹下创建”Game”文件夹,即用来创建Hello World示例的文件夹。文件夹的的结构应该是这样的:

Assets    
Game       
Scenes

Scripts在Scripts文件夹下新建名为HelloWorldSignals.cs的c#脚本,这个类将包含所有用到的signal,让我们coding起来:

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的脚本:

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的类:

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:

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:

using System;

using UnityEngine;

using strange.extensions.context.impl;

namespace Game {
public class HelloWorldBootstrap : ContextView {

void Awake() {
this.context = newHelloWorldContext(this);
}

}
}

用于在Unity中管理Strange context的接口类通常命名为“xxxBootstrap”,当然这只是一个建议,如果你乐意你可以随意起名字。这里唯一需要注意的是继承Strange框架的ContextView类的类需要是一个MonoBehaviour,我们在Awake()里分配了一个我们自定义好的context实例给继承的变量”context”。
创建一个空场景命名为”HelloStrange”,创建一个EmptyObject命名为Bootstrap,把我们之前创建的HelloWorldBootstrap add上来。可以跑一下这个场景,之前程序正确的话,你应该看到控制台的”Hello World”输出了。


详解

Injection in Mediator
到目前为止写这么一大堆东西只是输出一句“HelloWorld”,是不是被Strange搞得头都大了?其实做到现在这一步已经大致为你梳理出来Strange的一些机制了。首先我们有了一个能跑的context,从这一步开始,我们就可以添加view和相应的mediator,还可以使用injection binder把一个实例映射到一些可注入controllers/commands和mediators的接口中,而这些接口并不需要关心这个实例是怎么来的。接下来就是见证奇迹的时刻了!
一般我们做游戏编程的时候,会有一堆单例管理器(singleton managers)比如EnemyManager、AsteroidManager、CombatManager等等,假如需要同一个实例给任意一个管理器去调用有很多解决方案,比如我们可以使用GameObject.Find() 或者为这个类添加一个静态单例(GetInstance() static method),OK,让我们看看有了Strange以后,这样的情形可以怎么去解决:创建一个名为”ISomeManager”的接口,模拟一个上面说的那种manager,在Scripts文件夹创建ISomeManager.cs脚本

namespace Game {
public interface ISomeManager {

/**
* Perform some management
*/
void DoManagement();

}
}

这就是我们示例当中的manager接口,注意:Strange的作者建议我们总是使用一个接口然后通过injectionBinder将它映射到一个真正的实现类,当然,你也可以使用多对多的映射。接下来我们创建一个具体实现类,在Scripts文件夹下创建ManagerAsNormalClass.cs脚本:

using System;

using UnityEngine;

namespace Game {
public class ManagerAsNormalClass : ISomeManager {

public ManagerAsNormalClass() {
}

#region ISomeManager implementation
public void DoManagement() {
Debug.Log("Manager implemented as a normal class");
}
#endregion

}
}

如果你仔细在看你可能会发现这是一个没有MonoBehaviour的manager,别急,一会再介绍怎么bind有MonoBehaviour的

现在我们来创建一个简单的交互场景,效果是当一个Button按下时,ISomeManager的DoManagement函数执行,这里我们有一个要求:用MVC思想—对controll层(ISomeManager)和view层(控制Button触发事件的脚本)完全解耦,view层只需要通知controll层:”hey!button被点击了”,至于接下来发生什么交由controll层进行逻辑处理。

现在缺一个view层,把它创建出来吧—在Game文件夹下创建”View”文件夹,创建HelloWorldView.cs脚本:

using System;

using UnityEngine;

using strange.extensions.mediation.impl;
using strange.extensions.signal.impl;

namespace Game {
public class HelloWorldView : View {

public Signal buttonClicked = newSignal();

private Rect buttonRect = newRect(0, 0, 200, 50);

public void OnGUI() {
if(GUI.Button(buttonRect,"Manage")) {
buttonClicked.Dispatch();
}
}

}
}

这里继承的Strange框架中的View类已经包含了MonoBehaviour。所有使用Strange context的View层类都必须继承这个Strange的View类,我们刚刚创建的View类只有一个交互功能:在点击名为”Manage”的Button后,调用一个 generic signal(通用信号) 。

Strange作者建议对每个View创建对应的Mediator。Mediator是一个薄层,他的作用是让与之对应的View和整个程序进行交互。mediation binder的作用是把View映射到它对应的mediator上。所以接下来为View层创建对应的mediator—在”view”文件夹下创建HelloWorldMediator.cs脚本:

using System;

using UnityEngine;

using strange.extensions.mediation.impl;

namespace Game {
public class HelloWorldMediator : Mediator {

[Inject]
public HelloWorldView view {get;set;}

[Inject]
public ISomeManager manager {get;set;}

public override void OnRegister() {
view.buttonClicked.AddListener(delegate() {
manager.DoManagement();
});
}

}
}

在这段代码里我们可以看到神奇的”Inject”标注(Inject attribute)。这个”Inject”标注只能和变量搭配使用,当一个变量上面有”Inject”标注时,意味着Strange会把这个变量的一个实例自动注入到它对应映射的context中。据此从我们上面的代码来分析,在这里我们获取到了”view”和”manager”的实例,并且不用去关心这些个实例是怎么来的。

OnRegister()是一个可以被重写的方法,它用来标记实例注入完成已经可以使用了,它的意义主要是进行初始化,或者说做准备。在上面的类中,OnRegister方法中为HellowWorldView.buttonClicked signal添加了一个监听器,这个监听器的逻辑是按下就执行manager.DoManagement方法。

接下来就是最后的工作,我们需要把待绑的类映射到Strange Context中。打开我们之前写的HelloWorldContext脚本,在mapBindings()方法中添加代码:

protected override void mapBindings() {
base.mapBindings();

// we bind a command to StartSignal since it is invoked by SignalContext (the parent class) during on Launch()
commandBinder.Bind<StartSignal>().To<HelloWorldStartCommand>().Once();

// bind our view to its mediator
mediationBinder.Bind<HelloWorldView>().To<HelloWorldMediator>();

// bind our interface to a concrete implementation
injectionBinder.Bind<ISomeManager>().To<ManagerAsNormalClass>().ToSingleton();
}

在HelloWorld scene中,添加一个名为”View”的GameObject,add HelloWorldView 脚本,运行场景,你应该能看到当我们按下”Manage”按钮时,控制台输出”Manager implemented as a normal class”。
ISomeManager in action
你会发现Strange自动把HelloWorldMediator脚本挂载到了”View”GameObject上面。注意我们之前并没有手动把HelloWorldMediator脚本挂载到”View”GameObject上。
Where did the mediator came from?

MonoBehaviour Manager
大部分时候,我们需要类似于上面的manager但是实现类是一个MonoBehaviour,这样我们才能使用例如协程、序列化的Unity特性。
接下来创建实现MonoBehaviour接口的manager实例,看看怎么在Strange中进行bind。
创建一个实现MonoBehaviour接口的manager,在Script文件夹下,命名为ManagerAsMonobehaviour.cs

using System;

using UnityEngine;

namespace Game {
public class ManagerAsMonoBehaviour : MonoBehaviour, ISomeManager {

#region ISomeManager implementation
public void DoManagement() {
Debug.Log("Manager implemented as MonoBehaviour");
}
#endregion

}
}

在HelloStrangeScene中,创建一个新的GameObject名为”Manager”,add 上面创建好的 ManagerAsMonobehaviour脚本
编辑HelloWorldContext脚本的mapBindings()方法:

protected override void mapBindings() {
base.mapBindings();

// we bind a command to StartSignal since it is invoked by SignalContext (the parent class) during on Launch()
commandBinder.Bind<StartSignal>().To<HelloWorldStartCommand>().Once();

// bind our view to its mediator
mediationBinder.Bind<HelloWorldView>().To<HelloWorldMediator>();

// REMOVED!!!
//injectionBinder.Bind<ISomeManager>().To<ManagerAsNormalClass>().ToSingleton();

// bind the manager implemented as a MonoBehaviour
ManagerAsMonoBehaviour manager = GameObject.Find("Manager").GetComponent<ManagerAsMonoBehaviour>();
injectionBinder.Bind<ISomeManager>().ToValue(manager);
}

与把ISomeManager映射为一个类型相反,我们把这个ManagerAsMonobehaviour映射为一个实例值(instance value)。
Manager is now a MonoBehaviour

Injection in Command
到目前为止我们为HelloWorldMediator注入了一个ISomeManager的一个实例,并且可以直接使用它。这样做其实并不是很理想,一个Mediator应该是在view层和controller层之间的一个薄层。我们需要尽量使Mediator层不去关心应该在Manager类去做的那部分复杂的逻辑处理代码。虽然这么做也可以,我们还是用signal把这部分映射到command层吧。
编辑HelloWorldSignals.cs脚本,添加一个DoManagementSignal:

using System;

using strange.extensions.signal.impl;

namespace Game {

public class StartSignal : Signal {}

public class DoManagementSignal : Signal {} // A new signal!

}

我们创建command映射到signal:在Controller文件夹下创建一个脚本DoManagementCommand.cs

using System;

using UnityEngine;

using strange.extensions.context.api;
using strange.extensions.command.impl;

namespace Game {
public class DoManagementCommand : Command {

[Inject]
public ISomeManager manager {get;set;}

public override void Execute() {
manager.DoManagement();
}

}
}

在这个类,我们把ISomeManager注入到command类,并且在Execute方法中让它的DoManagement方法执行。
修改HelloWorldMediator类:

using System;

using UnityEngine;

using strange.extensions.mediation.impl;

namespace Game {
public class HelloWorldMediator : Mediator {

[Inject]
public HelloWorldView view {get;set;}

[Inject]
public DoManagementSignal doManagement {get;set;}

public override void OnRegister() {
view.buttonClicked.AddListener(doManagement.Dispatch);
}

}
}

现在我们的mediator类中已经没有任何对ISomeManager接口的调用了。取而代之的是要在mediator类获取到DoManagementSignal的实例,当button点击时,这个类会发出DoManagementSignal。mediator层不需要知道任何manager的事情,它只管发送信号(signal)出去。
最后,在HelloWorldContext.mapBindings()方法中添加这个signal-command映射。

protected override void mapBindings() {
base.mapBindings();

// we bind a command to StartSignal since it is invoked by SignalContext (the parent class) during on Launch()
commandBinder.Bind<StartSignal>().To<HelloWorldStartCommand>().Once();
commandBinder.Bind<DoManagementSignal>().To<DoManagementCommand>().Pooled();// THIS IS THE NEW MAPPING!!!

// bind our view to its mediator
mediationBinder.Bind<HelloWorldView>().To<HelloWorldMediator>();

// bind the manager implemented as a MonoBehaviour
ManagerAsMonoBehaviour manager = GameObject.Find("Manager").GetComponent<ManagerAsMonoBehaviour>();
injectionBinder.Bind<ISomeManager>().ToValue(manager);
}

运行场景,效果和之前一样,但是我们在代码层面把这块代码重构了。

最后
你会注意到这篇文章动不动就提到”作者建议”这样的话,这是因为作者的建议确实是一个比较重要的选择。比如说你可以在你的view层中直接注入各种实例,可能你压根不想去创建什么mediator层!我想说的是你可以根据你的实际需要来决定使用Strange的方法,但是我选择了根据作者的建议来使用它因为对于我来说这样还不错。
如果你把这篇文章看到了这里,你可能会很疑惑:”我为什么要在我的项目里搞这么多复杂又多余的层?”其实在我自己的项目中应用这个框架也是处于探索研究阶段,现在的感受Strange有一个好处是强行让我把每个模块划分了。比如这样一个情形:用Strange Context处理SimpleSQL数据持久化,对于现有的游戏逻辑代码,没有使用Strange的部分,他们间的通信通过signal来进行。
我们还在另外一个RPG项目用了Strange,我希望用了它以后可以像它的口号一样,确实有助于代码间的协作。到目前来看我没法跟你宣称它在我的项目中有多好,因为我们也只是在起步阶段,但是至少到目前为止对于我们来说它工作的还不错。我们的模式是使用多个Context,一个程序员负责一个Context,然后通过signal来与其他人的Context通信。

需要StrangeIoC框架源码的童鞋可以前往unity3d assetstore官方商店自行下载

转载:http://www.unity.5helpyou.com/2645.html


StrangeIOC 下载地址

https://github.com/strangeioc/strangeioc

EventDipatcher方式
http://blog.csdn.net/superlinmeng/article/details/69388224

Unity3D学习系列教程

阅读数 6579

Unity3d描边框效果

阅读数 1673

没有更多推荐了,返回首页