上次讲解了FairyGUI的最简单的热更新办法,并对其中一个Demo进行了修改并做成了热更新的方式。

这次我们来一个更加复杂一些的情况:Emoji.

FairyGUI的   Example 04 - Emoji

场景是一个聊天对话框。玩家可以输入文本和表情,对面的机器人还会回复一句话。回复的对话中还附带一个表情。

Demo分析

FairyGUI的这个Demo展示了如下技巧:

  • 继承UBBParser ,在Demo中使用自定义的表情。
  • 编写了一个MonoBehaviour,管理所有的逻辑。
    1. 动态添加UI项目的各按钮的点击事件
    2. 保存对话内容
    3. 根据对话的类型(发送方和接收方),可以给对话项提供不同的展示资源
    4. 脚本中解析表情,并展示出来。
    5. 当对话条数超过一定数目时,会自动从头部移除最早的对话。

修改为热更新

本次热更新的尝试目标,所有逻辑全部移动到热更新代码中执行。

  1. 首先我们将这个场景另存为F_Emoji。
  2. 导入ActionScript3 虚拟机的unity插件包,并生成热更新项目。如果您不知道怎么操作,可以查看这里
  3. ActionScript3插件包已更新,请下载最新的插件包  v0.96f6 以及以后版本

现在准备工作已完成。

我们将场景中的UIPanel直接删除。我们全部使用脚本来创建这个Panel。

  1. 进入Assets->ASRuntimePlayer,将 AS3Player预设和AS3StartupProgress预设拖到场景上。
  2. 将AS3Player物件下的Action Script Start Up 脚本组件的Document Class 设置为EmojiTest。
    (这一步的作用表示指示脚本解释器启动时,具体创建一个哪种类型的实例)
  3. 现在打开热更新项目,新建一个 EmojiTest 的类。

现在我们首先来说明,如何使用热更新脚本来创建UIPanel。

根据FairyGUI的说明,要从脚本创建UIPanel,则一定要先注册UI所在的包,否则会提示创建失败

原C# Demo中,注册代码是在Awake事件中,并且设计时就拖动到场景上的,而我们这里需要热更新,我们可以选择在EmojiTest的构造函数中,或者直接在包外代码中写入注册代码,如下:

//务必先加入package
UIPackage.addPackage______("UI/Emoji"); var go:GameObject = new GameObject("uip"); var uip:UIPanel = go.addComponent(UIPanel) as UIPanel; uip.gameObject.layer = LayerMask.nameToLayer("UI");
uip.packageName = "Emoji";
uip.componentName = "Main";

将上面的代码写到包外代码中。点击编译,然后在Unity中点击播放:

如图,这里的uip物体就是通过热更新脚本创建的,然后FairyGUI根据我们配置的package和componet,创建了对话的UI。

编写热更新逻辑

现在我们来编写热更新逻辑。但是这次有些不同,我们需要对UBBParser这个类进行一些修改。

FairyGUI的Demo中,为了解析UBB表情,定义了EmojiParser ,继承自UBBParser。并且EmojiParser中,访问了基类的受保护的对象handlers。

然而,我们的ActionScript3脚本如果继承C#类库,是无非直接访问受保护的字段的。因此,我们需要先在Unity工程中,将UBBParser的handlers改为public的成员.

修改完成后,待Unity重新编译项目后,在热更新项目中,使用 bat/CreateUnityAPI.bat 这个批处理文件重新生成一下ActionScript3的API代码。

确保以上步骤完成后,我们着手将C#代码改写为ActionScript3代码.

我们同样可以将逻辑继承自MonoBehaviour,并且也创建一个继承UBBParser的类,由于handlers字段已改为公开成员,因此可以在脚本中访问。

package
{
/**
* ...
* @author
*/
public class EmojiTest
{ public function EmojiTest()
{ } } }
import fairygui.Emoji;
import fairygui.EventContext;
import fairygui.FitScreen;
import fairygui.GButton;
import fairygui.GComponent;
import fairygui.GList;
import fairygui.GObject;
import fairygui.GRichTextField;
import fairygui.GRoot;
import fairygui.GTextInput;
import fairygui.ScrollBarDisplayType;
import fairygui.UIConfig;
import fairygui.UIPackage;
import fairygui.UIPanel;
import fairygui.utils.UBBParser;
import system.Char;
import system._Object_;
import system.collections.generic.Dictionary_Of_UInt32_Emoji;
import unityengine.Application;
import unityengine.GameObject;
import unityengine.KeyCode;
import unityengine.LayerMask;
import unityengine.MonoBehaviour;
import unityengine.Random; //在脚本中继承UBBParser。逻辑照搬C# Demo
//给每个表情提供一个handler,这个handler处理此表情的资源。
class EmojiParser extends UBBParser
{
private static var _instance:EmojiParser;
public static function get inst():EmojiParser
{
if (_instance == null)
_instance = new EmojiParser();
return _instance; } private static var TAGS:Vector.<String> =Vector.<String>([
"88","am","bs","bz","ch","cool","dhq","dn","fd","gz","han","hx","hxiao","hxiu" ]);
public function EmojiParser ()
{
for each (var ss in TAGS)
{
this.handlers[":"+ss] = OnTag_Emoji;
}
} private function OnTag_Emoji( tagName:String, end:Boolean, attr:String):String
{
return "<img src='" + UIPackage.getItemURL("Emoji", tagName.substr(1).toLowerCase()) + "'/>";
}
} /**
* 对话消息,不必多说
*/
class Message
{
public var sender:String;
public var senderIcon:String;
public var msg:String;
public var fromMe:Boolean;
} /**
* 将Demo中的逻辑用热更新脚本改写。
* 我们同样可以继承自MonoBehaviour
*/
class EmojiMain extends MonoBehaviour
{
var _mainView:GComponent;
var _list:GList;
var _input1:GTextInput;
var _input2:GTextInput;
var _emojiSelectUI1:GComponent;
var _emojiSelectUI2:GComponent; var _messages:Vector.<Message>; var _emojies:Dictionary_Of_UInt32_Emoji; function Awake()
{
//UIPackage.AddPackage("UI/Emoji"); UIConfig.verticalScrollBar = "ui://Emoji/ScrollBar_VT";
UIConfig.defaultScrollBarDisplay = ScrollBarDisplayType.Auto;
} function Start()
{
Application.targetFrameRate = 60; _messages = new Vector.<Message>(); _mainView = UIPanel( this.getComponent(UIPanel)).ui; _list = _mainView.getChild("list").asList;
_list.setVirtual();
_list.itemProvider = GetListItemResource;
_list.itemRenderer = RenderListItem; //给按钮添加处理事件。
_input1 = _mainView.getChild("input1").asTextInput;
_input1.onKeyDown.add(__inputKeyDown1); _input2 = _mainView.getChild("input2").asTextInput;
_input2.onKeyDown.add(__inputKeyDown2); //作为demo,这里只添加了部分表情素材
_emojies = new Dictionary_Of_UInt32_Emoji();
for (var i:uint = 0x1f600; i < 0x1f637; i++)
{
var url:String = UIPackage.getItemURL("Emoji", i.toString(16));
if (url != null)
_emojies.add(i, Emoji.constructor_(url));
}
_input2.emojies = _emojies; _mainView.getChild("btnSend1").onClick.add(__clickSendBtn1);
_mainView.getChild("btnSend2").onClick.add(__clickSendBtn2); //添加发送表情按钮的事件
_mainView.getChild("btnEmoji1").onClick.add(__clickEmojiBtn1);
_mainView.getChild("btnEmoji2").onClick.add(__clickEmojiBtn2); _emojiSelectUI1 = UIPackage.createObject("Emoji", "EmojiSelectUI").asCom;
_emojiSelectUI1.fairyBatching = true;
_emojiSelectUI1.getChild("list").asList.onClickItem.add(__clickEmoji1); _emojiSelectUI2 = UIPackage.createObject("Emoji", "EmojiSelectUI_ios").asCom;
_emojiSelectUI2.fairyBatching = true;
_emojiSelectUI2.getChild("list").asList.onClickItem.add(__clickEmoji2);
} function AddMsg(sender:String, senderIcon:String, msg:String, fromMe:Boolean):void
{
var isScrollBottom:Boolean = _list.scrollPane.isBottomMost; var newMessage:Message = new Message();
newMessage.sender = sender;
newMessage.senderIcon = senderIcon;
newMessage.msg = msg;
newMessage.fromMe = fromMe;
_messages.push(newMessage); if (newMessage.fromMe)
{
if (_messages.length == 1 || Random.range(0, 1) < 0.5)
{
var replyMessage:Message = new Message();
replyMessage.sender = "FairyGUI";
replyMessage.senderIcon = "r1";
replyMessage.msg = "Today is a good day. " + Char.convertFromUtf32( 0x0001f600 ).toString();
replyMessage.fromMe = false;
_messages.push(replyMessage);
}
} if (_messages.length > 100)
{
_messages.splice(0, _messages.length - 100);
}
//_messages.RemoveRange(0, _messages.Count - 100); _list.numItems = _messages.length; if (isScrollBottom)
_list.scrollPane.scrollBottom();
} function GetListItemResource( index:int):String
{
var msg: Message= _messages[index];
if (msg.fromMe)
return "ui://Emoji/chatRight";
else
return "ui://Emoji/chatLeft";
} function RenderListItem( index:int, obj:GObject):void
{
var item:GButton = GButton(obj);
var msg:Message= _messages[index];
if (!msg.fromMe)
item.getChild("name").text = msg.sender;
item.icon = UIPackage.getItemURL("Emoji", msg.senderIcon); //Recaculate the text width
var tf:GRichTextField = item.getChild("msg").asRichTextField;
tf.emojies = _emojies;
tf.width = tf.initWidth;
tf.text = EmojiParser.inst.parse(msg.msg);
tf.width = tf.textWidth; } function __clickSendBtn1( context:EventContext):void
{
var msg:String = _input1.text;
if (msg.length == 0)
return; AddMsg("Unity", "r0", msg, true);
_input1.text = "";
} function __clickSendBtn2( context:EventContext):void
{
var msg:String = _input2.text;
if (msg.length == 0)
return; AddMsg("Unity", "r0", msg, true);
_input2.text = "";
} function __clickEmojiBtn1( context:EventContext):void
{
GRoot.inst.showPopup__(_emojiSelectUI1, GObject(context.sender), _Object_( false));
} function __clickEmojiBtn2( context:EventContext):void
{
GRoot.inst.showPopup__(_emojiSelectUI2, GObject(context.sender), _Object_( false));
} function __clickEmoji1( context:EventContext):void
{
var item:GButton= GButton(context.data);
_input1.replaceSelection("[:" + item.text + "]");
} function __clickEmoji2( context:EventContext):void
{
var item:GButton = GButton(context.data);
_input2.replaceSelection(Char.convertFromUtf32( parseInt(UIPackage.getItemByURL(item.icon).name,16) ));
} function __inputKeyDown1( context:EventContext):void
{
if (context.inputEvent.keyCode == KeyCode.Return)
_mainView.getChild("btnSend1").onClick.call();
} function __inputKeyDown2( context:EventContext):void
{
if (context.inputEvent.keyCode == KeyCode.Return)
_mainView.getChild("btnSend2").onClick.call();
} } //务必先加入package
UIPackage.addPackage______("UI/Emoji"); var go:GameObject = new GameObject("uip"); var uip:UIPanel = go.addComponent(UIPanel) as UIPanel; uip.gameObject.layer = LayerMask.nameToLayer("UI");
uip.packageName = "Emoji";
uip.componentName = "Main"; //将逻辑代码挂载到UIPanel上。
go.addComponent(EmojiMain);

可以将如上代码直接写到热更新脚本中,编译。

然后在Unity中点击播放,我们看到我们的热更已经生效!

如此,我们即可完全使用热更代码处理FairyGUI的对话聊天模块。

打包到安卓手机

您可以将这个场景导出到手机上测试。

当打包时,有可能会遇到脚本错误。这是因为FairyGUI的某些代码使用了宏编译,在windows状态下有这个类,而安卓状态下确没有。因此我们可以将这些类型配置到导出API的工具中,

声明它不导出。以这个案例而言,则是将如下配置写入genapi.config.xml的<notcreatetypes>配置节中:

     <item value="FairyGUI.CopyPastePatch"></item>
      

然后再次导出API,编译脚本,生成安卓包:

如此我们就看到了手机上运行的效果。

用ECMAScript4 ( ActionScript3) 实现Unity的热更新 -- 使用FairyGUI (二)的更多相关文章

  1. 用ECMAScript4 ( ActionScript3) 实现Unity的热更新 -- 使用FairyGUI (一)

    我们的热更新脚本在实际使用中,当然也要支持常用的第三方组件,例如这里介绍一个非常实用的第三方UI库:FairyGUI. 什么是FairyGUI 这里照搬FaiyGUI官网的介绍: 重新定义 UI 制作 ...

  2. 用ECMAScript4 ( ActionScript3) 实现Unity的热更新

    unity热更新是一个经久不衰的话题.除了最常见的lua之外,还有如JSBinding,C#等等.这里介绍一个使用ECMAScript4进行热更新的方案.它吸收了各家的优点,特色很鲜明. 项目地址: ...

  3. 用ECMAScript4 ( ActionScript3) 实现Unity的热更新 -- 使用原型链和EventTrigger

    原型链是JS的必备,作为ECMAScript4,原型链也是支持的. 特别说明,ActionScript3是支持完整的面向对象继承支持的,原型链只在某些非常特殊的情况下使用. 本文旨在介绍如何使用原型链 ...

  4. 用ECMAScript4 ( ActionScript3) 实现Unity的热更新 -- Demo分析

    如何创建工程 下载最新的Unity发布插件包. 打开Unity,新建一个项目 将插件包导入 在菜单中点击ASRuntime/Create ActionScript3 FlashDevelop HotF ...

  5. 用ECMAScript4 ( ActionScript3) 实现Unity的热更新 -- 使用第三方组件

    Unity开发中,常常会用到一些第三方组件.本文以实例介绍如何在热更新脚本中使用这些第三方组件. 首先说明几个基本步骤: 第三方组件通常是以dll或者源码方式提供的,它们本身往往无法热更. 我们在脚本 ...

  6. 用ECMAScript4 ( ActionScript3) 实现Unity的热更新 -- CustomYieldInstruction 自定义中断指令

    ActionScript3脚本引擎为了方便热更新逻辑开发,提供的从脚本继承Unity类库功能在一些情况下可以提供开发的便利. 这次来建立一个示例,演示一下如何在脚本中自定义协程中断指令 Unity中的 ...

  7. 用ECMAScript4 ( ActionScript3) 实现Unity的热更新 -- 热更新Live2D

    live2D是一个很强大的2D动画组件.我们可以使用AS3脚本对它进行热更新. live2D在Unity中的使用请看这里: 如何获取Live2D 总得来说,我们可以先去live2D官网下载它的Unit ...

  8. 用ECMAScript4 ( ActionScript3) 实现Unity的热更新 -- 在脚本中使用MonoBehaviour

    继上次分析了热更新的Demo后,这次来介绍如何在热更新代码中使用MonoBehaviour. MonoBehaviour挂载到GameObject对象上的脚本的基类.平常Unity开发时,简单的做法就 ...

  9. 用ECMAScript4 ( ActionScript3) 实现Unity的热更新 -- 操作符重载和隐式类型转换

    C#中,某些类型会定义隐式类型转换和操作符重载.Unity中,有些对象也定义了隐式类型转换和操作符重载.典型情况有:UnityEngine.Object.UnityEngine.Object的销毁是调 ...

随机推荐

  1. unity 快速创建小地图

    先写一个纹理遮罩shader Shader "Unlit/TexMask" { Properties { _MainTex ("Texture", 2D) =  ...

  2. Java 反射之JDK动态代理

    Proxy提供用于创建动态代理类和代理对象的静态方法,它也是所有动态代理类的父类.如果我们在程序中为一个或多个接口动态地生成实现类,就可以使用Proxy来创建动态代理类:如果需要为一个或多个接口动态的 ...

  3. 【一天一道LeetCode】 #3 Longest Substring Without Repeating Characters

    一天一道LeetCode (一)题目 Given a string, find the length of the longest substring without repeating charac ...

  4. Android 之dragger使用

    1.依赖的注入和配置独立于组件之外,注入的对象在一个独立.不耦合的地方初始化,这样在改变注入对象时,我们只需要修改对象的实现方法,而不用大改代码库. 2.依赖可以注入到一个组件中:我们可以注入这些依赖 ...

  5. OpenCV矩阵运算

    矩阵处理 1.矩阵的内存分配与释放 (1) 总体上: OpenCV 使用C语言来进行矩阵操作.不过实际上有很多C++语言的替代方案可以更高效地完成. 在OpenCV中向量被当做是有一个维数为1的N维矩 ...

  6. coco2dx添加类报错

    最近刚开始学习2dx,用的vs编辑器,现在说说我使用时碰到的一点小问题: 我使用的类添加向导,但是添加的类在win32目录下,而且编译的时候总是提示找不到 .h 文件 其实,这样添加类不是很好,可以在 ...

  7. 使用kubeadm搭建Kubernetes(1.10.2)集群(国内环境)

    目录 目标 准备 主机 软件 步骤 (1/4)安装 kubeadm, kubelet and kubectl (2/4)初始化master节点 (3/4) 安装网络插件 (4/4)加入其他节点 (可选 ...

  8. C# /VB.NET 创建PDF项目符号列表和多级编号列表

    使用项目符号和编号,可以让文档的层次结构更清晰.更有条理,也更容易突出重点.在编辑文档的过程中,我个人也比较偏爱项目标号来标注文章重点信息.在之前的文章中,介绍了如何在Word中来创建项目标号和编号列 ...

  9. wait和notify的理解与使用

    1.对于wait()和notify()的理解 对于wait()和notify()的理解,还是要从jdk官方文档中开始,在Object类方法中有: void notify() Wakes up a si ...

  10. Spring中对象和属性的注入方式

    一:Spring的bean管理 1.xml方式 bean实例化三种xml方式实现 第一种 使用类的无参数构造创建,首先类中得有无参构造器(重点) 第二种 使用静态工厂创建 (1)创建静态的方法,返回类 ...