作遊戲聊天系統,要注意這些坑 關於FLASH中圖文混排聊天框的小結

筆者最近剛作了個聊天系統,開發過程當中踩了很多的坑,在此總結下經驗教訓,以便回顧參考,也但願他人看到後能夠少走彎路。這裏不全是貼代碼,主要提供聊天系統的實現思路,以及須要注意的點。html

 

聊天框圖文混排正則表達式

  在關於FLASH中圖文混排聊天框的小結一文中已經總結了幾種圖文混排的實現方式。對於不須要拉伸縮放的表情聊天框,能夠直接用AS3Textfield類本身實現兩層結構的文本類,這種是最簡單,筆者也是採用了這種作法。api

  聊天輸入框若是沒有特殊要求,不須要支持顯示錶情movieclip(後面簡稱mc),則通常採用AS3TextInput組件足矣,即只能輸入純文本。(在我接觸的不少款網遊中,聊天輸入框都是純文本的)工具

  既然輸入框只支持純文本,那怎麼插入表情呢?這就須要實現圖片與文字的互相轉化了。實現原理並不難,簡單來講,就是當玩家從表情面板選中表情時,自動將其轉換成表情代碼(格式自定),插入輸入框中。在玩家發送消息後,進行文本解析,利用正則表達式將聊天消息裏的表情代碼解析替換成佔位符(其實就是空格),而後在相應位置上將表情mc顯示出來。post

  原理不難,難在實現細節。這裏總結需注意的細節。網站

  一、表情格式。表情格式不要選擇過於複雜,過多字數的格式,越簡單越好。筆者在初次實現時選擇的格式是」[img]兩位數字[/img]」,這樣插入一個表情,實際等同於輸入了13個字符,若是聊天限制字數以微博140個字做爲標準,那隻能插入10個表情,顯然不合理。此外,簡單的表情格式方便高頻玩家直接手動輸入表情代碼,體驗更好。this

  二、在玩家打字的過程當中,有可能中途點擊表情面板去插入表情,此時舞臺焦點就再也不爲輸入框(即輸入框光標再也不閃動,玩家插入表情後會發現不能繼續打字),爲了無損玩家聊天體驗,須要在插入表情代碼後,重設舞臺焦點爲輸入框:url

public function setFocus():void
{
      //將舞臺焦點設置爲聊天輸入框
      Context.stage.focus = textField;
      //將聊天光標設置到文本末尾            
      textField.setSelection(textField.length, textField.length);
}

  三、半角空格≠全角空格,關於佔位符的選擇。在前文提過,咱們須要利用正則表達式將表情替換成佔位符,才能給表情movieclip預留足夠位置顯示。筆者建議佔位符必定要使用全角空格(中文輸入法中Shift+Space可切換半角/全角),由於行末恰好是全角空格時,文本會自動換行,半角空格則不會。若是使用半角空格做爲佔位符,就會出現一種狀況,位於行末的表情代碼恰好被替換成幾個半角空格,即便該行的其餘文本,加上幾個空格的寬度已經超出了textField所設置的width值,該行文本仍然不會換行。這樣就致使表情mc被添加到文本的外邊。以下圖所示,紅框內爲Textfieldwidth,因爲使用半角空格做爲佔位符,有個表情mc華麗麗地跳脫出了文本框。所以,請使用全角空格作佔位符!spa

  

  四、正則表達式解析表情代碼,這個能夠說是整個圖文混排文本最關鍵的代碼了,其實也只寥寥幾十行代碼:code

/**
* 設置文本 外部添加內容請使用此方法
*/        
public function set htmlText(value:String):void
{
      _htmlTxt = value;
      _textField.htmlText = value;
      checkImg();
      addChildAt(_textField, 0);
}
public static const REG_IMG:RegExp =  /#\d{2}/ig;
/**
* 查找圖片標籤
*/ private function checkImg():void { var content:String = _textField.text; var result:Array = []; var count:int; var objImg:Object; while(true) { //表情 objImg = REG_IMG.exec(content); if(objImg != null) { //#00 和下面的替換相差1個字符 objImg.index -= count * 1; result[result.length] = objImg; count++; }else break; } if(result.length > 0) { _htmlTxt = _htmlTxt.replace(REG_IMG, "  ");//注:<font> </font> 用的全角空格 才能自動換行 _textField.htmlText = _htmlTxt; var obj:Object; for(var i:int=0; i< result.length; i++) { obj = result[i]; if(obj != null) { CaculatContent(obj.index,obj[0]); } } } }      /** * 計算表情標籤 */ private function CaculatContent(startIndex:int,value:String):void { var mcName:String = value.slice(1, value.length); var displayObj:Sprite = Reflection.createMovieClipInstance("Movie"+int(mcName)) as Sprite; if(displayObj == null) return; var rect:Rectangle = _textField.getCharBoundaries(startIndex); if(rect != null) { displayObj.x = rect.x; displayObj.y = rect.y; addChild(displayObj); } }    

 

字符過濾

  對消息敏感內容的過濾通常交由後臺負責,前臺負責過濾處理文本中的特殊字符,如html標籤字符,轉義字符等。

  遊戲聊天框裏的一段文本可能有不一樣樣式,不一樣顏色,通常人名還要手型顯示,支持點擊。所以通常使用htmlText方法設置文字,而不是text方法。htmlText是一個比text更爲複雜的方法,它接受html標籤。請看下面這段代碼:

       var str:String = "第一行<br>第二行\n第三行\r第四行:\t<u><a href='http://www.baidu.com'>這是個網站連接</a></u><img src='https://www.baidu.com/img/bdlogo.png'></img>";
var text:TextField = new TextField(); text.wordWrap = true; text.multiline = true; text.width = 200; text.height = 200; this.addChild(text); text.htmlText = str;

  運行結果以下:

  能夠看到html標籤以及轉義字符,都實際起到了做用。若是不作過濾處理,就有可能被外掛製做者加以利用,在聊天包中的消息字段插入這些字符,用於刷屏以及散佈非法連接,圖片。

  所以須要過濾掉轉義字符,html標籤,將它們變成單純的顯示文本。下面貼代碼:

package 
{
    /**
     * 正則表達式過濾字符工具
     * @author ShuchangLiu
     */
    public class HtmlRegexpUtil
    {
        
        public function HtmlRegexpUtil()
        {
        }
        
        /**
         * 進行字符過濾
         * @param input
         * @return 
         */        
        public static function filter(input:String):String {   
            input = replaceTag(input);
            input = replaceSlash(input);
            return input;
        }   
        
        /**
         * 過濾轉義字符
         * @param input
         * @return 
         */        
        public static function replaceSlash(input:String):String
        {
            input = input.replace(/\n/g, "\\n");
            input = input.replace(/\r/g, "\\r");
            input = input.replace(/\t/g, "\\t");
            return input;
        }
        
        /**  
         *   
         * 基本功能:替換標記以正常顯示  
         * <p>  
         *   
         * @param input  
         * @return String  
         */  
        public static function replaceTag(input:String):String
        {
            if (!hasSpecialChars(input)) {   
                return input;   
            }   
            var filtered:String = "";   
            var c:String;
            for (var i:int = 0; i <= input.length - 1; i++) {   
                c = input.charAt(i);   
                switch (c) {   
                    case '<':   
                        filtered += "&lt;";
                        break;   
                    case '>':   
                        filtered += "&gt;";
                        break;   
                    case '"':   
                        filtered += "&quot;";
                        break;   
                    case '&':   
                        filtered += "&amp;";
                        break;   
                    default:   
                        filtered += c;
                }   
                
            }   
            return (filtered.toString());  
        }
        
        /**  
         *   
         * 基本功能:判斷標記是否存在  
         * <p>  
         *   
         * @param input  
         * @return boolean  
         */  
        public static function hasSpecialChars(input:String):Boolean {   
            var flag:Boolean = false;   
            if ((input != null) && (input.length > 0)) {   
                var c:String;
                for (var i:int = 0; i <= input.length - 1; i++) {   
                    c = input.charAt(i);   
                    switch (c) {   
                        case '>':   
                            flag = true;   
                            break;   
                        case '<':   
                            flag = true;   
                            break;   
                        case '"':   
                            flag = true;   
                            break;   
                        case '&':   
                            flag = true;   
                            break;   
                    }   
                }   
            }   
            return flag;   
        }
        
    }
}

  再看上面的例子,對文本進行過濾處理後的效果。

如今這些html標籤,轉義字符就失去相應的做用,只是單純的文字了。在實際開發中,除了對這兩點進行處理,還能夠再進一步過濾掉網址。

 

 

當心掉入Textfield的坑!textWidth > width textHeight > height 

 

  相信大部分aser都很篤定,width >=textWidth, height >=textHeight是絕對成立的,難道文本實際寬度/高度還能超過咱們設置的寬度/高度嗎? 很遺憾,是的!在絕大部分狀況下,Textfield實例的textWidth <=width,但不是100%成立,特定狀況下,textWidth > width 。究其緣由是其api實現並不完美,而致使這種狀況的罪魁禍首,又是前文說起的半角空格!看下面的代碼:

var str:String = "1                                                              2";
var text:TextField = new TextField();
text.wordWrap = true;
text.multiline = true;
text.width = 50;
this.addChild(text);
text.htmlText = str;

trace(text.width);        //result:50
trace(text.textWidth);    //result:192

  筆者開發時由於不瞭解此點,掉進了坑。在MMORPG遊戲中,若是在場景(附近)頻道說話,除了在聊天框顯示消息,一般場景人物還會彈出聊天冒泡。聊天冒泡皮膚則根據文字寬高動態調整寬度、高度。所以,咱們能夠經過下面代碼簡單調整皮膚寬度。

_skin = new BubbleRoundRectWithArrowSkin();
addChildAt(_skin.display, 0);
_message = new TextField();
_message.multiline = true;
_message.wordWrap = true;
_message.width = TEXT_MAX_WIDTH;
addChild(_message);
var bubbleWidth:int;
bubbleWidth = _message.textWidth+ _skin.borderWidth;
bubbleHeight = _message.textHeight+ _skin.borderHeight;
_skin.setSize(bubbleWidth, bubbleHeight);

  注意,上面代碼並不是用width方法,而是用textWidth方法去得到實際的文本寬度。不管_message 文本內容是什麼,在沒有設置autoSize的狀況下,_message.width的值是固定不變的,都爲TEXT_MAX_WIDTH。但這裏可能會出現問題,即實際文本寬度textWidth > width,致使聊天框背景會遠遠寬於文本。以下圖所示:

 

  使用TextField類顯示文本,當文本行尾爲半角空格時(即英文輸入的空格),哪怕文本寬度已超出width值時,也不會自動換行。這樣當玩家輸入大量空格時,聊天框沒有及時換行,致使聊天框被拉得過長,從而影響了場景顯示。

  這是Textfieldapi實現得很差之處,所以最好還本身判斷寬高度是否超出了限制

if(_message.textWidth> TEXT_MAX_WIDTH)
  bubbleWidth = TEXT_MAX_WIDTH + _skin.borderWidth;
else
  bubbleWidth = _message.textWidth+ _skin.borderWidth;
if(_message.textHeight> TEXT_MAX_HEIGHT)
  bubbleHeight = TEXT_MAX_HEIGHT + _skin.borderHeight;
else    
  bubbleHeight = _message.textHeight+ _skin.borderHeight;

  這樣的代碼纔能有效限制聊天框背景的寬高,避免某個玩家文字過長的冒泡,遮擋掉遊戲場景的大部分顯示。另外,還能夠經過限制同屏場景聊天冒泡的最大數量,給聊天冒泡設置半透明等方法,來緩解冒泡遮擋場景的體驗問題。

相關文章
相關標籤/搜索