上一章我們學習了服務(wù)端與客戶端之間的相互通信,現(xiàn)在我們來做一個極簡的聊天系統(tǒng)。如果你身陷荒野也許可以考慮用它來做一個建議文字對話機。
這次我們先從客戶端開始搭建起

通過教程我們將會建立一個上面這樣極簡的界面。
思路:
我們的思路是這樣的,我們做兩個客戶端一個輸入用戶名client1,另外一個輸入用戶名client2,我們的目標是client1通過服務(wù)端可以和client2相互交換消息。
步驟:
先做一個注冊按鈕,然后我們在輸入框里面輸入我們的名字,點擊注冊后注冊按鈕把自己的名字發(fā)給服務(wù)器,以便服務(wù)器日后知道誰是client1誰是client2然后就可以把對應(yīng)的消息發(fā)送給對應(yīng)的客戶端了
當然在注冊之后,這個注冊按鈕也就沒有用了,我們就可以銷毀它。然后頁面上只留下一個發(fā)送按鈕,再次輸入文字,點擊發(fā)送就可以發(fā)送信息了。
開始:
首先我們通過cocos新建一個項目,并且在資源管理器創(chuàng)建如下文件(如果你不會創(chuàng)建可以看看上一個教程)
(1)一個叫Client的場景
(2)一個也叫Client的腳本

然后我們從“內(nèi)置控件”欄拖入如下幾個控件
(1)文本標簽:
用途:用來顯示服務(wù)端發(fā)送過來的信息


(2)文本輸入框
用途:用來輸入要發(fā)送的消息


(3)按鈕
用途:用來觸發(fā)發(fā)送消息的事件


當然在給服務(wù)端發(fā)送消息之前為了讓服務(wù)端知道我們是誰,所以我們要先給自己起一個名字來告訴服務(wù)端,以便服務(wù)端知道有了我們的消息后怎么傳給我們。
所以我們現(xiàn)在發(fā)送按鈕正上方再創(chuàng)建一個一模一樣大小的按鈕。并且把它的文字改為“注冊”覆蓋在發(fā)送按鈕上。
下面是如何修改按鈕的名字:展開按鈕,選中子節(jié)點中的Label節(jié)點,然后在屬性欄如圖處修改。以上所有控件的名字修改都可以參考這個步驟。

由于Cocos默認相同的按鈕重疊只能觸發(fā)最上面按鈕的點擊事件,我們這個教程為了簡單先用這種最簡單的處理方式來闡明道理,將來會和大家好好的聊一下制作規(guī)范。
現(xiàn)在我們來編輯Client腳本
我們首先點擊選中Canvas,然后拖拽Client腳本進入Canvas的屬性欄


然后點擊Client腳本打開

首先我們來監(jiān)聽一下輸入框的文本變化
我們首先在start()函數(shù)最上面聲明一個message的變量用來保存文本框的文本變化。
this.message?=?'';

然后我們回到界面選中輸入框,在它右側(cè)屬性中有一個屬性用來監(jiān)聽文本變化Text Changed,我們把它后面的值賦予 1

接下來我們在腳本中準備一個接入輸入框值變化的函數(shù)(cocos 默認這么寫函數(shù)賦予給輸入框就可以接受文本變化的輸入事件)
onTextChanged:?function(text,?editbox,?customEventData)?{
????????console.log("文本變化",text);
????},
注意這個函數(shù)寫在start()函數(shù)后面,中間用逗號隔開

然后關(guān)鍵的一步來了
由于腳本是放在Canvas上的,所以我們依然選中輸入框(editBox),然后鼠標移動到Canvas上面把Canvas拖入如下位置

拖入Canvas后我們就可以選擇canvas下面掛載的腳本了(client腳本)

最后選擇我們要接收文本變化事件的函數(shù)(就是我們剛才寫的函數(shù))

做到這里我們啟動一下游戲,看看你在輸入框里面輸入一下內(nèi)容,在瀏覽器里面看看打印信息是否是你輸入的信息。

對!你沒有看錯!到此客戶端最關(guān)鍵的一個環(huán)節(jié)已經(jīng)完成了。
服務(wù)端
到此我們先把手上客戶端的活兒放一下!來看看我們的服務(wù)端代碼(我保證這個比客戶端更簡單)
還記我們上一節(jié)課降到的十行代碼嗎?
var?ws?=?require("nodejs-websocket");
var?server?=?ws.createServer(function?(connect)?
{
????connect.on("text",?function?(data)?{
????????console.log('收到消息=',data)
????})
????connect.on("close",?function?(code,?reason)?{});
????connect.on("error",?function?(code,?reason)?{});
}).listen(3000)
console.log('websocket服務(wù)端啟動')
我們還是特別的簡單在客戶端里加上這十行代碼。

但是切記要先用npm安裝websocket模塊,如果你不知道怎么安裝不妨移步到我們的上一篇教程。如果你安裝了會看到如下的項目結(jié)構(gòu):

接下來我們來給所有連接到服務(wù)端的客戶端,注冊一個連接(相當于登記了它的身份證號碼和可以給它打電話的電話號碼,以便我們給這個客戶端轉(zhuǎn)發(fā)消息)
我們先在此處創(chuàng)建兩個變量用來保存連接進來的兩個客戶端
let?client1?=?null;
let?client2?=?null;

接下來我們需要設(shè)計一個和客戶端溝通的方式,以便區(qū)分客戶端連接進來的意圖(是登錄還是平常的發(fā)消息)
在這里我們通過json格式簡單的設(shè)計一個通信協(xié)議
{‘name’:'client1','message':'login'}
其中name代表發(fā)來消息的人士誰,message代表發(fā)來消息的內(nèi)容,如果內(nèi)容為login代表是注冊否則就是正常的往來消息。
當然因為服務(wù)端和客戶端之間的消息傳遞之前都要轉(zhuǎn)換成字符串來傳遞,所以我們首先需要把收到的消息從json字符串轉(zhuǎn)化為json對象以便我們來拆分需要的信息
let?json=JSON.parse(result);//json字符串轉(zhuǎn)對象
let name =?json.name;
let message =??json.message;

接下來我們先檢測一下用戶這次發(fā)來消息的意圖是否是注冊
//注冊
????????if(message?==?'login')
????????{
????????}
然后判斷一下是client1注冊 還是client2注冊,如果是client1注冊,我們就讓client1 = connect
if(name?==?'client1'){
????client1?=?connect;
????console.log('client1注冊成功!')
}
細心的朋友應(yīng)該已經(jīng)看出來了
??client1?=?connect;
這行代碼就相當于服務(wù)端儲存了client1這個客戶端的電話號碼了,后面我們可以用這個connect作為給客戶端發(fā)送信息的索引。
完整的注冊代碼如下:
?//注冊
????????if(message?==?'login')
????????{
????????????if(name?==?'client1'){
????????????????client1?=?connect;
????????????????console.log('client1注冊成功!')
????????????}
????????????else??if(name?==?'client2'){
????????????????client2?=?connect;
????????????????console.log('client2注冊成功!')
????????????}?
????????????return;
????????}

到此我們完成了注冊的功能,現(xiàn)在可以順藤摸瓜繼續(xù)來完成兩個客戶端通過服務(wù)端消息轉(zhuǎn)發(fā)的功能(相當于服務(wù)端是一個中間的傳話人)
在這里我們也簡單的規(guī)定一個服務(wù)端發(fā)送消息給客戶端的格式
{‘name’:'client2','message','給客戶端1請安!'}
我們可以用一個簡單的方式來把這個消息封裝成一個json對象(姑且起名叫data)
let?data?=?{};
data.name?=?'client1';
data.message?=?json.message;
那么如果我們接收到客戶端1發(fā)送來的消息就直接轉(zhuǎn)給客戶端2,反之亦然
if(name?==?'client1'){
? ? ?let?data?=?{};
? ? ?data.name?=?'client1';
? ? ?data.message?=?json.message;
?????let?jsonStr=JSON.stringify(data);//轉(zhuǎn)化成字符串,才能通過服務(wù)端轉(zhuǎn)發(fā)消息
? ? client2.sendText(jsonStr);//發(fā)送消息給客戶端2
}
所以總的代碼如下:
if(name?==?'client1'){
? ? ?let?data?=?{};
? ? ?data.name?=?'client1';
? ? ?data.message?=?json.message;
?????let?jsonStr=JSON.stringify(data);
? ? client2.sendText(jsonStr);
}
else?if(name?==?'client2'){
????let?data?=?{};
? ? data.name?=?'client2';
? ? data.message?=?json.message;
? ? ?let?jsonStr=JSON.stringify(data);
? ? ?client1.sendText(jsonStr);
}??

到這里我們服務(wù)端的代碼已經(jīng)全部完成。
回到客戶端
我們再次回到客戶端把注冊和發(fā)送消息的邏輯完善
注冊
還記得上面我們提到了客戶端要給服務(wù)端發(fā)送消息的約定格式嗎
{‘name’:'client1','message':'login'}
于是我們就用一個變量來專門儲存當前用戶的名字
this.clientName?=?'';

然后我們用clientName來裝載最初用戶設(shè)置的名字
this.clientName?=?text;

此時我們給注冊按鈕綁定一個事件,讓點擊注冊后客戶端給服務(wù)端發(fā)送注冊信息。
register(){
????????//組裝消息
????????let?data?=?{};
????????data.name?=?this.clientName;
????????data.message?=?'login';
????????//轉(zhuǎn)化為字符串發(fā)送
????????let?jsonStr=JSON.stringify(data);
????????//發(fā)送
????????this.ws.send(jsonStr);
????}

來到主界面,選中注冊按鈕,給注冊按鈕綁定這個事件函數(shù)
選中注冊按鈕ClickEnvents里面添加一個按鈕事件,設(shè)置如圖:

繼續(xù)選中注冊按鈕,鼠標點擊Canvas不放拖動到ClickEnvents的第一個選項

選擇Canvas上綁定的Client腳本

最后選中register腳本

最后加入我們上一節(jié)課中的socket連接代碼
?this.ws?=?new?WebSocket("ws://127.0.0.1:3000");
????????this.ws.onopen?=?function?(event)?{
????????????console.log("連接服務(wù)端");
????????};
????????this.ws.onmessage?=?function?(event)?{
????????????console.log("接收服務(wù)端信息:?"?+?event.data);
????????};
????????this.ws.onerror?=?function?(event)?{
????????????console.log("異常關(guān)閉");
????????};
????????this.ws.onclose?=?function?(event)?{
????????????console.log("斷開連接");
????????};

啟動服務(wù)端,再啟動客戶端,輸入用戶名client1,點擊注冊!

如果出現(xiàn)如下信息,恭喜!我們的注冊系統(tǒng)起效果了。

聊天
接下來我們要實現(xiàn)客戶端的聊天功能
首先我們先要再創(chuàng)建一個變量,來判斷當前是在注冊階段還是在聊天階段
?this.bRegistered?=?false;

然后我們在用戶點擊了注冊之后,就可以銷毀注冊按鈕了,同時把狀態(tài)切換成聊天狀態(tài)。
為此我們需要把register函數(shù)略作改造
register(event,?customEventData)

然后調(diào)整到聊天模式,?獲取當前按鈕的節(jié)點,最后把注冊按鈕摧毀。
this.bRegistered?=?true;//調(diào)整到聊天模式?
let button?=?event.target;//獲取當前按鈕節(jié)點
button?.destroy();//摧毀按鈕

在設(shè)置完成狀態(tài)后我們就保持用戶名不變了,同時message變量開始接受用戶輸入的信息
?if(!this.bRegistered)//只有在沒有注冊時才會有改變用戶名
?{
?????this.clientName?=?text;
}
??else????????{? ? ? ? ? ?
?this.message?=?text;? ? ? ?
?}? ?
至此我們可以運行一次項目看看是否注冊按鈕有順利的被移除

接下來就是最后一步了!
我們按照之前定義好的消息協(xié)議給服務(wù)端發(fā)送消息。
首先我們?yōu)榘l(fā)送按鈕定定義一個事件函數(shù)
?sendMessage(event,?customEventData){
//組裝消息
????????let?data?=?{};
????????data.name?=?this.clientName;
????????data.message?=?this.message;
????????let?jsonStr=JSON.stringify(data);//轉(zhuǎn)化為字符串發(fā)送
????????this.ws.send(jsonStr);//發(fā)送
?}

然后依葫蘆畫瓢給“發(fā)送按鈕”綁定這個函數(shù)

最后開啟服務(wù)端,再開啟兩個客戶端,一個注冊名為client1 另外一個注冊名為client2


兩個客戶端都注冊成功之后,就可以暢快的發(fā)送消息了。
最后一步我們把客戶端接收到的對方的消息,顯示在label上

我們現(xiàn)在properties處引入Label
?messageLabel:cc.Label

然后回到主界面,選中canvas后
把label拖入我們腳本里面的?messageLabel 引用處

最后我們要在接收消息的地方把消息顯示到label上

因為在此處回調(diào)中,我們無法使用this來找到當前對象,所以我們在start()函數(shù)里面定義一個Self變量來代表this
?let?Self?=?this;?

最后我們稍微把消息做一個處理,就可以給這個label賦值
let?json=JSON.parse(event.data);//json字符串轉(zhuǎn)對象
????????????let?name?=??json.name;
????????????let?message?=??json.message;
????????????//拼接最終顯示字符串
????????????let?str?=?name+'發(fā)來:'+message;
????????????//給label賦值
????????????Self.messageLabel.string?=?str;
最終代碼如下

你最后發(fā)送了消息能夠在另外一個客戶端顯示就大功告成了

最后一個小知識Cocos 是可以直接用手機來調(diào)試不需要打任何包。
只要你的手機和電腦連接在同一個局域網(wǎng)。
通過微信掃描此處二維碼,就可以實現(xiàn)兩臺手機聊天

當然如果你想用手機連接需要修改一處連接ip
你可以在cmd里面輸入
ipconfig
命令
查到自己的ip

用自己的電腦作為服務(wù)器,只需要在客戶端的代碼里把下圖處改為你電腦的ip即可

最后:如果你有什么疑問和建議隨時歡迎來到群里和我溝通,我將盡快為您解答
本教程所有代碼均在github上分享:下載
下一章:叁:極簡聊天系統(tǒng)