zoukankan      html  css  js  c++  java
  • 用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,编译脚本,生成安卓包:

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

  • 相关阅读:
    Maximum Flow Exhaustion of Paths Algorithm
    ubuntu下安装java环境
    visualbox使用(二)
    vxworks一个超级奇怪的错误(parse error before `char')
    February 4th, 2018 Week 6th Sunday
    February 3rd, 2018 Week 5th Saturday
    February 2nd, 2018 Week 5th Friday
    February 1st, 2018 Week 5th Thursday
    January 31st, 2018 Week 05th Wednesday
    January 30th, 2018 Week 05th Tuesday
  • 原文地址:https://www.cnblogs.com/ashei/p/9042742.html
Copyright © 2011-2022 走看看