貳:極簡聊天系統(tǒng)

上一章:壹:十行代碼帶你打穿客戶端和服務(wù)端的通信

上一章我們學習了服務(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)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容