• 用ECMAScript4 ( ActionScript3) 实现Unity的热更新 -- 使用FairyGUI (二)


    上次讲解了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,编译脚本,生成安卓包:

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

  • 相关阅读:
    学习Linux二(创建、删除文件和文件夹命令)
    合理的需求
    两种事件触发的jquery导航菜单
    JS中this关键字
    Hibernate的session问题
    JQUERY图片特效
    学习Linux一(安装VMware和Ubuntu)
    A标签跳转问题
    WEBSERVICE简介
    IE下设置Cursor的一点记录
  • 原文地址:https://www.cnblogs.com/ashei/p/9042742.html
Copyright © 2020-2023  润新知