Unity3d开发(十八) 监听编辑器状态改变,制定自定义回调
2016-11-24 11:52
387 查看
div#cpcontent2 {height:215px;width:215px;float:left;}
div#cpmenu {height:200px;float:left;}
div#cpcontent {height:200px;width:150px;float:left;}
文章作者:松阳 本文出自 阿修罗道,禁止用于商业用途,转载请注明出处。 原文链接:http://blog.csdn.net/fansongy/article/details/53318791
做编辑器插件时,我总是想要拿到监听编辑器的状态变化。比如在打开编辑器开始运行自己的服务。这时就需要用户打开编辑器的事件。再比如我希望在游戏退出运行模式之前,把一些编辑的东西缓存出来,然后对这些数据做自动化处理,那么我就需要退出运行模式的事件。诸如此类吧。
另一方面,我希望用观察者模式,并且能自动化注册。因为我注意到,导入资源时的
自动注册是借助C#的反射,通过
{
/// <summary>
/// Join the Nights Watch
/// </summary>
void Enroll();
/// <summary>
/// Before to Enter Wild
/// </summary>
void PrepareForBattle();
/// <summary>
/// To the Weirwood outside the wall
/// </summary>
void FaceWeirwood();
/// <summary>
/// Back To the Castle Black
/// </summary>
void OpenTheGate();
/// <summary>
/// Tell Vow to the Old God
/// </summary>
void Vow();
}实例类如下:using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
[InitializeOnLoad]
public class NightsWatch
{
#region Public Attributes
#endregion
#region Private Attributes
private static List<ICrow> m_crows = new List<ICrow>();
#endregion
#region Public Methods
static NightsWatch()
{
if (!EditorApplication.isPlayingOrWillChangePlaymode)
{
EditorApplication.update += WelcomeToCastleBlack;
}
else
{
EditorApplication.update += BeyondTheWall;
}
}
static void WelcomeToCastleBlack()
{
EditorApplication.update -= WelcomeToCastleBlack;
//Debug.Log("Welcome To castle black");
m_crows.Clear();
var crows = GetAllImplementTypes<ICrow>(System.AppDomain.CurrentDomain);
foreach (var eachCrow in crows)
{
eachCrow.Enroll();
m_crows.Add(eachCrow);
}
EditorApplication.update += WaitForWild;
}
static void WaitForWild()
{
if (EditorApplication.isPlayingOrWillChangePlaymode)
{
foreach (var eachCrow in m_crows)
{
eachCrow.PrepareForBattle();
}
EditorApplication.update -= WaitForWild;
}
}
static void BeyondTheWall()
{
EditorApplication.update -= BeyondTheWall;
//Debug.Log("Welcome To The Wild");
m_crows.Clear();
var crows = GetAllImplementTypes<ICrow>(System.AppDomain.CurrentDomain);
foreach (var eachCrow in crows)
{
eachCrow.FaceWeirwood();
m_crows.Add(eachCrow);
}
EditorApplication.update += WaitForCrowReturn;
}
static void WaitForCrowReturn()
{
if (!EditorApplication.isPlayingOrWillChangePlaymode )
{
//Debug.Log("Open the Door");
EditorApplication.update -= WaitForCrowReturn;
foreach (var eachCrow in m_crows)
{
eachCrow.OpenTheGate();
}
EditorApplication.update += WelcomeToCastleBlack;
}
}
public static void CrowsVow()
{
foreach (var eachCrow in m_crows)
{
eachCrow.Vow();
}
}
[MenuItem("Land/CastleBlack")]
public static void MakeVow()
{
NightsWatch.CrowsVow();
}
#endregion
#region Override Methods
#endregion
#region Private Methods
public static T[] GetAllImplementTypes<T>(System.AppDomain aAppDomain) where T : class
{
var result = new List<T>();
var assemblies = aAppDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
var types = assembly.GetTypes();
foreach (var type in types)
{
if (typeof(T).IsAssignableFrom(type))
{
if (!type.IsAbstract)
{
var tar = assembly.CreateInstance(type.FullName) as T;
result.Add(tar);
}
}
}
}
return result.ToArray();
}
#endregion
}
简单解释一下,所有的接口都是按照冰与火之歌中的剧情定义。当在编辑状态下时,会创建对应的实例类,并调用
public class JonSnow : ICrow
{
public void Enroll()
{
Debug.Log(this + " join the NightWatch!");
}
public void PrepareForBattle()
{
Debug.Log(this + " follow your lead!");
}
public void FaceWeirwood()
{
Debug.Log("I'm the wolf in the north");
}
public void OpenTheGate()
{
Debug.Log(this + " request enter Castle Black");
}
public void Vow()
{
Debug.Log(this + " For The Watch");
}
}一个是Samwell:
public class Samwell : ICrow
{
public void Enroll()
{
Debug.Log(this + " I came form Lord Randyll Tarly,and I even his oldest son ...");
}
public void PrepareForBattle()
{
Debug.Log(this + " is not ready yet...");
}
public void FaceWeirwood()
{
Debug.Log("I'm a useless warrior,but may be ... helpful");
}
public void OpenTheGate()
{
Debug.Log(this + " also want enter");
}
public void Vow()
{
Debug.Log(this + " For The ... alive");
}
}
其中红线是点击Play操作,绿线是停止Unity运行的操作,红线以上的日志是打开unity或重新编译时输出的。一切按照预期实现,收工。如果你觉得这篇文章对你有帮助,可以顺手点个顶,不但不会喜当爹,还能让更多人能看到它...
div#cpmenu {height:200px;float:left;}
div#cpcontent {height:200px;width:150px;float:left;}
文章作者:松阳 本文出自 阿修罗道,禁止用于商业用途,转载请注明出处。 原文链接:http://blog.csdn.net/fansongy/article/details/53318791
做编辑器插件时,我总是想要拿到监听编辑器的状态变化。比如在打开编辑器开始运行自己的服务。这时就需要用户打开编辑器的事件。再比如我希望在游戏退出运行模式之前,把一些编辑的东西缓存出来,然后对这些数据做自动化处理,那么我就需要退出运行模式的事件。诸如此类吧。
另一方面,我希望用观察者模式,并且能自动化注册。因为我注意到,导入资源时的
AssetImporter回调就是这样做的。用户只需要实现一个接口,就可以收到回调。极大的简化了扩展流程。编辑器代码又不必考虑效率问题,借助C#的反射,可以很容易的实现这种功能。
概述
整套框架的启动核心是属性InitializeOnLoad。当Unity3d运行或启动时,会重新加载有脚本。当使用这个宏时,编辑器会自动将被标注的类实例化到内存中。因此我们可以利用这个特性,在它的构造函数中拉起我们整个服务。 这里有个小技巧。在启动Unity编辑器的情况下,如果在构造函数中创建对象,会被其他清除函数干掉。我认为是脚本初始化顺序,或是场景切换引起的,具体原因得问Unity了。为了解决这个问题,我借助了
update函数,跳了一帧执行应有的逻辑。
自动注册是借助C#的反射,通过
GetAssemblies和
GetTypes获取到所有的类,然后创建出对应的实例。
包装
这个类我觉得有个特别适合的名字——NightWatch。如果你没看过冰与火之歌,可能理解这个框架还算有点难度。总的说来,这个框架讲述了一个少年加入守夜人队伍,并去长城之外战斗的故事...
实现
接口类如下:public interface ICrow{
/// <summary>
/// Join the Nights Watch
/// </summary>
void Enroll();
/// <summary>
/// Before to Enter Wild
/// </summary>
void PrepareForBattle();
/// <summary>
/// To the Weirwood outside the wall
/// </summary>
void FaceWeirwood();
/// <summary>
/// Back To the Castle Black
/// </summary>
void OpenTheGate();
/// <summary>
/// Tell Vow to the Old God
/// </summary>
void Vow();
}实例类如下:using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
[InitializeOnLoad]
public class NightsWatch
{
#region Public Attributes
#endregion
#region Private Attributes
private static List<ICrow> m_crows = new List<ICrow>();
#endregion
#region Public Methods
static NightsWatch()
{
if (!EditorApplication.isPlayingOrWillChangePlaymode)
{
EditorApplication.update += WelcomeToCastleBlack;
}
else
{
EditorApplication.update += BeyondTheWall;
}
}
static void WelcomeToCastleBlack()
{
EditorApplication.update -= WelcomeToCastleBlack;
//Debug.Log("Welcome To castle black");
m_crows.Clear();
var crows = GetAllImplementTypes<ICrow>(System.AppDomain.CurrentDomain);
foreach (var eachCrow in crows)
{
eachCrow.Enroll();
m_crows.Add(eachCrow);
}
EditorApplication.update += WaitForWild;
}
static void WaitForWild()
{
if (EditorApplication.isPlayingOrWillChangePlaymode)
{
foreach (var eachCrow in m_crows)
{
eachCrow.PrepareForBattle();
}
EditorApplication.update -= WaitForWild;
}
}
static void BeyondTheWall()
{
EditorApplication.update -= BeyondTheWall;
//Debug.Log("Welcome To The Wild");
m_crows.Clear();
var crows = GetAllImplementTypes<ICrow>(System.AppDomain.CurrentDomain);
foreach (var eachCrow in crows)
{
eachCrow.FaceWeirwood();
m_crows.Add(eachCrow);
}
EditorApplication.update += WaitForCrowReturn;
}
static void WaitForCrowReturn()
{
if (!EditorApplication.isPlayingOrWillChangePlaymode )
{
//Debug.Log("Open the Door");
EditorApplication.update -= WaitForCrowReturn;
foreach (var eachCrow in m_crows)
{
eachCrow.OpenTheGate();
}
EditorApplication.update += WelcomeToCastleBlack;
}
}
public static void CrowsVow()
{
foreach (var eachCrow in m_crows)
{
eachCrow.Vow();
}
}
[MenuItem("Land/CastleBlack")]
public static void MakeVow()
{
NightsWatch.CrowsVow();
}
#endregion
#region Override Methods
#endregion
#region Private Methods
public static T[] GetAllImplementTypes<T>(System.AppDomain aAppDomain) where T : class
{
var result = new List<T>();
var assemblies = aAppDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
var types = assembly.GetTypes();
foreach (var type in types)
{
if (typeof(T).IsAssignableFrom(type))
{
if (!type.IsAbstract)
{
var tar = assembly.CreateInstance(type.FullName) as T;
result.Add(tar);
}
}
}
}
return result.ToArray();
}
#endregion
}
简单解释一下,所有的接口都是按照冰与火之歌中的剧情定义。当在编辑状态下时,会创建对应的实例类,并调用
Enroll函数,这相当于Jon刚刚进入CastleBlack。当点击Play运行时,会先调用
PrepareForBattle,相当于在城堡中准备出征。当游戏开始运行时,会调用
FaceToWeirWood,这里对应的是城外那颗鱼梁木,一般出征之前都是要去祈祷一下。然后当游戏运行结束时,会调用
OpenTheGate,对应出征回来,在长城下面喊门。然后有个
Vow接口,这个是用来点名的,城堡里的乌鸦都要列队答“道”。
使用
新建两个实例: 一个是JonSnow:public class JonSnow : ICrow
{
public void Enroll()
{
Debug.Log(this + " join the NightWatch!");
}
public void PrepareForBattle()
{
Debug.Log(this + " follow your lead!");
}
public void FaceWeirwood()
{
Debug.Log("I'm the wolf in the north");
}
public void OpenTheGate()
{
Debug.Log(this + " request enter Castle Black");
}
public void Vow()
{
Debug.Log(this + " For The Watch");
}
}一个是Samwell:
public class Samwell : ICrow
{
public void Enroll()
{
Debug.Log(this + " I came form Lord Randyll Tarly,and I even his oldest son ...");
}
public void PrepareForBattle()
{
Debug.Log(this + " is not ready yet...");
}
public void FaceWeirwood()
{
Debug.Log("I'm a useless warrior,but may be ... helpful");
}
public void OpenTheGate()
{
Debug.Log(this + " also want enter");
}
public void Vow()
{
Debug.Log(this + " For The ... alive");
}
}
测试
当写好代码编译完成时,就能在输出中看到他俩到长城去报道了。点击运行程序,关闭运行程序,会分别有日志输出,效果如下:其中红线是点击Play操作,绿线是停止Unity运行的操作,红线以上的日志是打开unity或重新编译时输出的。一切按照预期实现,收工。如果你觉得这篇文章对你有帮助,可以顺手点个顶,不但不会喜当爹,还能让更多人能看到它...
相关文章推荐
- Android开发:自定义界面的Dialog,并且实现按钮的监听回调
- Andriod开发 网络状态改变监听(这样说可能不太合理)
- WF 自定义活动开发常见问题(进入第二个流程状态,数据传递失败的解决方法)
- FormEditor 开发记录5 编辑器的Dirty状态设置 以及保存
- MS CRM 2011的自定义与开发(6)——表单编辑器(第二部分)
- MS CRM 2011的自定义和开发——表单编辑器
- 【Android游戏开发二十三】自定义ListView【通用】适配器并实现监听控件!
- asp.net控件开发基础(11) --------自定义视图状态管理
- android 自定义 radiobutton 文字颜色随选中状态而改变
- 【Android游戏开发二十三】自定义ListView【通用】适配器并实现监听控件!
- 使用GMF开发可视化界面编辑器(二)——自定义Figure
- 游戏引擎Andengine总结(四):自定义可以改变显示状态的菜单选项
- android之通过phoneStateListener监听电话状态改变
- MS CRM 2011自定义与开发(3)——实体编辑器
- 【Android游戏开发二十三】自定义ListView【通用】适配器并实现监听控件!
- MS CRM 2011的自定义与开发(6)——表单编辑器(第一部分)
- 自定义控件开发5--自定义属性编辑器(Modal,DropDown)
- 【Android游戏开发二十三】自定义ListView【通用】适配器并实现监听控件!
- MS CRM 2011的自定义与开发(5)——关系编辑器
- MS CRM 2011的自定义和开发(7)——视图编辑器(第一部分)