戴培山,馬凌堯,范敏
(1.中南大學(xué)計(jì)算學(xué)院,湖南長沙 410083;2.湖南女子學(xué)院社會(huì)發(fā)展與管理學(xué)院,湖南長沙 410004)
卡牌游戲是一種棋類游戲,自產(chǎn)生以來,就因其具有多樣化的玩法、自由的空間、休閑娛樂、智力挑戰(zhàn)和互動(dòng)性強(qiáng)等特點(diǎn)而受到各個(gè)年齡段和地區(qū)人群的喜愛。撲克牌的傳入在一定程度上統(tǒng)一了中國上一代人的卡牌游戲類型并影響至今。同時(shí),撲克牌的玩法也因中國的地大物博而變得多種多樣,越來越多極具地方性特色的撲克牌游戲都在其發(fā)源地廣受歡迎[1]。
隨著移動(dòng)互聯(lián)網(wǎng)技術(shù)的高速發(fā)展和智能移動(dòng)設(shè)備的普及,社會(huì)進(jìn)步得更快。人們將更多時(shí)間用于工作,導(dǎo)致閑暇時(shí)間變得零碎。這種現(xiàn)象促使移動(dòng)在線游戲的出現(xiàn),并逐漸取代傳統(tǒng)的線下游戲??ㄅ祁愑螒虻木€上運(yùn)營也因此引來了熱潮。
由于受眾規(guī)模和開發(fā)效益等因素的影響,目前市場(chǎng)上絕大多數(shù)的卡牌類游戲都以全國性的卡牌游戲?yàn)橹?。而具有地方性特色的卡牌游戲很少有團(tuán)隊(duì)進(jìn)行開發(fā)。許多在外求學(xué)或是工作的異鄉(xiāng)人很難在休閑時(shí)間找到可以和自己一起娛樂的玩家。在線地方特色卡牌游戲可以解除空間上的限制,不論大家在哪里,都可以在線上體驗(yàn)到全國各地的卡牌玩法??臻g、時(shí)間上的自由、玩法的多樣性、娛樂與益智共存等特點(diǎn)將會(huì)吸引越來越多的受眾人群。此類游戲的實(shí)現(xiàn)將為大家提供一個(gè)方便、豐富的交友娛樂平臺(tái)。
20世紀(jì)以來,移動(dòng)互聯(lián)網(wǎng)的出現(xiàn)改變著新世紀(jì)人們的生活方式,在廣大的游戲市場(chǎng)中異軍突起的便是網(wǎng)絡(luò)游戲[2]。自此之后,在短短的10 年里,國內(nèi)涌現(xiàn)出了一大批游戲公司,從剛開始的盛大、金山到現(xiàn)在的騰訊、網(wǎng)易。國內(nèi)主流的游戲基本上都誕生在這些大游戲公司。廣大的游戲玩家為這些公司帶來了不錯(cuò)的經(jīng)濟(jì)效益,也促使一大批計(jì)算機(jī)從業(yè)者開始從事游戲開發(fā)工作。作為新興產(chǎn)業(yè),游戲產(chǎn)業(yè)從萌芽走向成熟只用了短短15年的時(shí)間[3],并且已經(jīng)成為網(wǎng)絡(luò)經(jīng)濟(jì)不可或缺的組成部分。其中棋牌類游戲無疑是屬于元老級(jí)的存在,并在歷史的潮流中不停地發(fā)展演變[4]。除全國性游戲平臺(tái)外,各種各樣的地方性平臺(tái)涌現(xiàn)出來,尤其在北方地區(qū)較為明顯。隨著地方性棋牌游戲火熱程度的加深,游戲平臺(tái)的目標(biāo)受眾也更加細(xì)化,出現(xiàn)了專門為溫州市民研發(fā)的“溫州牛牛”等游戲平臺(tái),游戲內(nèi)的圖片和方言聊天充滿著濃郁的地方特色。在博大精深的中華文化孕育之下,全國各地誕生了不計(jì)其數(shù)的卡牌玩法,仍有許多地方游戲沒有得到線上化,這也促進(jìn)著棋牌游戲的進(jìn)一步發(fā)展。
本文卡牌游戲采用Unity2019 游戲引擎開發(fā),編程工具為Visual Studio 2019,編程語言為C#語言。
主要包括游戲項(xiàng)目的整體框架、游戲運(yùn)行流程、游戲規(guī)則設(shè)計(jì)、游戲功能實(shí)現(xiàn)。
此卡牌游戲規(guī)則[5]如下:
①游戲共有52 張卡牌,數(shù)值為3、4、5、6、7、8、9、10、J、Q、K、A、2,每個(gè)數(shù)值共有4 張,分別為4 個(gè)不同花色:紅心、黑桃、方片、梅花,沒有大小王。
②游戲開始時(shí),3名玩家各隨機(jī)發(fā)放5張卡牌,系統(tǒng)隨機(jī)設(shè)置首位出牌玩家,并多發(fā)給其1張牌。
③玩家出牌類型可包括:
單牌:單獨(dú)1張卡牌,如:3、5、7等;
對(duì)子:兩張數(shù)值相同的卡牌,如44、77、QQ等;
順子:至少3張數(shù)值相連的卡牌,如:456、JQK,但KA2、A23、234不能出牌;
炸彈:至少3 張數(shù)值相同的卡牌,如:555、6666、JJJ等。
④所有玩家依次選擇是否出牌,合法的出牌如下:
出牌類型應(yīng)和場(chǎng)上出牌配型相同,且數(shù)值只能大于1,如:3只能被4壓,不可被5壓,對(duì)55只能被66壓,456只能被567壓;
單張2 可以壓所有的單牌,對(duì)2 可以壓所有的對(duì)子;
炸彈可以壓所有的非炸彈牌,4 張牌的炸彈不論數(shù)值都可以壓3張牌的炸彈,牌數(shù)相同的炸彈,數(shù)值高的炸彈可以壓數(shù)值低的炸彈,場(chǎng)上每打出一套炸彈,場(chǎng)上的倍數(shù)就×2。
⑤當(dāng)玩家出的牌使其余兩位玩家都無法出牌管上時(shí),該玩家摸一張牌,繼續(xù)出牌。
⑥當(dāng)場(chǎng)上有玩家將手牌出完時(shí),該玩家獲得勝利,其余玩家失敗,勝利玩家獲得100 經(jīng)驗(yàn)和“1 000×倍數(shù)”的豆子,失敗玩家獲得10經(jīng)驗(yàn)并失去“1 000×倍數(shù)”的豆子。
本游戲?qū)崿F(xiàn)了可多人在線娛樂的唬牌游戲,主要功能如圖1所示。
圖1 唬牌游戲功能架構(gòu)圖
圖2 客戶端運(yùn)行流程圖
圖4 UDP傳輸模型
圖5 Socket抽象層
圖6 C/S連接圖
3.2.1 服務(wù)器功能
服務(wù)器端主要為本設(shè)計(jì)提供了網(wǎng)絡(luò)通信、數(shù)據(jù)緩存和邏輯處理三大部分的功能。1)網(wǎng)絡(luò)通信。多人在線的關(guān)鍵在于不同的客戶端可以同時(shí)進(jìn)入一個(gè)房間內(nèi)進(jìn)行游戲,玩家的操作可以在房間內(nèi)廣播,戰(zhàn)斗的信息會(huì)實(shí)時(shí)地傳遞到房間內(nèi)的每個(gè)客戶端,玩家之間可以在房間內(nèi)進(jìn)行文字聊天,服務(wù)器端的網(wǎng)絡(luò)通信[6]使得這一部分的功能順利實(shí)現(xiàn)。2)數(shù)據(jù)緩存。在整個(gè)設(shè)計(jì)過程中,有許多信息需要服務(wù)器進(jìn)行保存,以方便所有的客戶端既可以訪問到這些數(shù)據(jù),又可以節(jié)省存儲(chǔ)空間。本文中,服務(wù)器端存儲(chǔ)了所有玩家的共享信息,如:賬號(hào)密碼庫、卡牌庫、房間庫、用戶信息庫等,客戶端可以通過相對(duì)應(yīng)的操作碼向服務(wù)器發(fā)起請(qǐng)求來訪問這些數(shù)據(jù)。3)邏輯處理。邏輯模塊是整個(gè)系統(tǒng)順利運(yùn)行的核心所在。
在服務(wù)器端,實(shí)現(xiàn)了賬號(hào)密碼的部分合法性判斷、房間匹配機(jī)制、戰(zhàn)斗回合制管理、出牌合法邏輯塊、卡牌發(fā)放與回收、戰(zhàn)斗結(jié)算等功能。這些算法的實(shí)現(xiàn)在邏輯和內(nèi)存管理上為客戶端的處理提供了方便的接口,統(tǒng)一了客戶端的操作碼。
客戶端功能??蛻舳嗽跒橥婕姨峁┝艘粋€(gè)簡潔友好的頁面基礎(chǔ)上,實(shí)現(xiàn)了卡牌游戲的各項(xiàng)基本功能,主要包括以下6 部分:1)賬號(hào)注冊(cè)和登錄。用戶可以自己注冊(cè)游戲賬號(hào),設(shè)置賬號(hào)登錄密碼,由服務(wù)器端進(jìn)行密碼是否合法的判斷并給予用戶相關(guān)提示;用戶可以使用自己已注冊(cè)的正確的賬號(hào)密碼登錄游戲,體驗(yàn)唬牌游戲世界。2)用戶信息初始化。當(dāng)注冊(cè)的賬號(hào)初次登錄時(shí),用戶可以自定義自己的游戲名稱,名稱確認(rèn)之后,由系統(tǒng)進(jìn)行用戶信息的初始化,所有用戶初始等級(jí)為1級(jí),初始豆子數(shù)量為1 000。3)房間匹配機(jī)制。系統(tǒng)會(huì)設(shè)置匹配房間列表,為所有準(zhǔn)備進(jìn)入大廳的玩家分配房間,以供給玩家游戲使用;當(dāng)房間內(nèi)玩家全部退出房間之后,系統(tǒng)會(huì)回收該房間,給其他準(zhǔn)備進(jìn)入大廳的玩家使用。4)游戲戰(zhàn)斗功能。當(dāng)所有玩家進(jìn)入房間準(zhǔn)備之后,戰(zhàn)斗場(chǎng)景開始,系統(tǒng)隨機(jī)為玩家發(fā)牌,3 名玩家可以按照游戲規(guī)則順利進(jìn)行游戲;在游戲結(jié)束之后,系統(tǒng)會(huì)根據(jù)玩家的勝負(fù)情況為玩家增加經(jīng)驗(yàn)值并獎(jiǎng)懲一定數(shù)量的豆子。5)在線聊天功能。房間內(nèi)的玩家可以通過快捷喊話按鈕,選擇自己想要發(fā)送消息,系統(tǒng)會(huì)將文字信息展示在房間內(nèi),并播放相關(guān)語音。6)設(shè)置功能。在設(shè)置面板,用戶可以開啟/關(guān)閉背景音樂,也可以自由調(diào)節(jié)背景音樂的聲音大小。
在C/S 模式中,客戶端和服務(wù)器之間需要進(jìn)行頻繁的信息傳遞。而這兩者之間的通信方式一般為HTTP 通信和Socket 通信兩種[7]。這兩個(gè)在功能上都可以滿足客戶端與服務(wù)器之間的數(shù)據(jù)傳輸。但是不同于HTTP 通信中的“請(qǐng)求響應(yīng)方式”,Socket 通信則更為直接,它會(huì)在雙方連接成功的基礎(chǔ)上直接進(jìn)行信息傳遞,不需要請(qǐng)求和響應(yīng)。所以本文用Socket通信實(shí)現(xiàn)客戶端和服務(wù)器之間的通信。
1)TCP和UDP協(xié)議
在實(shí)現(xiàn)Socket 通信時(shí),經(jīng)常使用到運(yùn)輸層的兩個(gè)協(xié)議TCP協(xié)議和UDP協(xié)議[8]。TCP指的是用戶數(shù)據(jù)報(bào)協(xié)議,可以提供面向連接和可靠傳輸兩個(gè)服務(wù),但是沒有時(shí)間保證和最小傳輸速率保證,常見的面向連接服務(wù)就是“三次握手”的過程。雖然可以隨時(shí)進(jìn)行數(shù)據(jù)傳輸,但是由于傳輸過程會(huì)消耗一定的時(shí)間,所以并不能確保做到實(shí)時(shí)傳輸。
UDP指的是傳輸控制協(xié)議,它不需要像TCP協(xié)議那樣一定要雙方連接起來,它可以直接發(fā)送,但是并不能確保信息是否被對(duì)方接收成功,也不能確保對(duì)方收到的報(bào)文是否出錯(cuò)。常用于實(shí)時(shí)消息的傳輸。
本設(shè)計(jì)實(shí)現(xiàn)的Socket 通信是基于TCP 協(xié)議[9]設(shè)計(jì)的。
2)Socket抽象層
3)C/S模式連接流程圖
UI(User Interface) 是指提供給用戶一個(gè)簡潔、美觀的頁面。它包含視覺方面的文字、動(dòng)畫、圖片,以及聽覺上的音效和BGM,同時(shí)還要有常見的人機(jī)交互組件,如點(diǎn)擊按鈕和滑動(dòng)滾動(dòng)條等。隨著時(shí)代的進(jìn)步,人們對(duì)UI界面的需求量和質(zhì)量要求都有所提高,這使得開發(fā)者需要更高的技術(shù)水平來滿足用戶需求。UI框架為開發(fā)者提供了一套完整的基礎(chǔ)工具。開發(fā)者可以利用這些框架,進(jìn)行基本的UI 設(shè)計(jì)。UI 可以幫助游戲開發(fā)者進(jìn)行基本的UI 設(shè)計(jì),優(yōu)質(zhì)的UI 框架可以在保證系統(tǒng)效率的同時(shí),降低代碼耦合度[10]。本文使用UI消息框架主要作為一個(gè)消息傳遞中心,各個(gè)邏輯單元發(fā)出來的消息處理都會(huì)經(jīng)過MsgCenter 進(jìn)行轉(zhuǎn)發(fā)。
在玩撲克牌時(shí),需要對(duì)一副撲克牌進(jìn)行順序打亂,保證所有玩家獲取到每張卡牌的概率是相等的,從而增加整個(gè)游戲的隨機(jī)性[11],常見的洗牌方法有兩種:全局洗牌法和局部洗牌法[12]。
這2種洗牌法都是基于一個(gè)數(shù)組實(shí)現(xiàn)的。
全局洗牌法會(huì)按照數(shù)組的索引,從1~54 依次生成一個(gè)余數(shù)value,value=rand%54,其中rand為系統(tǒng)產(chǎn)生的隨機(jī)數(shù),將當(dāng)前索引的值和余數(shù)對(duì)應(yīng)索引的卡牌進(jìn)行交換,直到遍歷到最后一個(gè)結(jié)束,以此達(dá)到洗牌的目的。然而,這樣實(shí)現(xiàn)的話就會(huì)導(dǎo)致前面洗過的牌可能會(huì)再次被遍歷到,所以可以將余數(shù)value 的計(jì)算方式進(jìn)行改進(jìn),令value = index + rand % (54 - index),這樣就可以保證索引牌和之前沒有交換過的牌進(jìn)行交換,更高效地洗牌。
在本設(shè)計(jì)中,仍然采用數(shù)組的形式,每個(gè)數(shù)組元素都是一個(gè)卡牌模型,通過打亂索引實(shí)現(xiàn)了洗牌算法,具體實(shí)現(xiàn)在如下。
包含52張基本卡牌,并實(shí)現(xiàn)了對(duì)這些卡牌的基本算法。
1)創(chuàng)建卡牌庫。
通過兩層for循環(huán),第一層循環(huán)花色,第二層循環(huán)點(diǎn)數(shù),依次將52 張卡牌模型添加到CardQueue 隊(duì)列中,對(duì)每張卡牌命名為“花色+點(diǎn)數(shù)”,如:“紅桃3”為“Heart Three”。
2)洗牌算法
通過for 循環(huán)創(chuàng)建的卡牌隊(duì)列一定是一個(gè)花色和點(diǎn)數(shù)成規(guī)律的隊(duì)列,為了方便發(fā)牌時(shí)隨機(jī)出隊(duì)列的復(fù)雜,這里直接實(shí)現(xiàn)洗牌算法,發(fā)牌時(shí)選擇洗過之后的卡牌庫,依次出隊(duì)列發(fā)牌得到的就是隨機(jī)分發(fā)的卡牌了。
算法實(shí)現(xiàn):創(chuàng)建一個(gè)新的卡牌隊(duì)列newList,通過foreach方法依次讀取CardQueue隊(duì)列中的卡牌。每次都在0到newList.count+1中隨機(jī)生成一個(gè)整數(shù)索引index,并在newList列表的index位置處插入讀取到的卡牌,直到CardQueue中的卡牌全部被讀取過。由于index索引的位置是通過Random隨機(jī)生成的,所以即使讀取的卡牌有順序,但卡牌的插入位置是無序的,結(jié)果仍然可以得到一個(gè)無序的卡牌列表。清空原有的CardQueue 隊(duì)列,將newList 列表中的卡牌依次拿出并添加到CardQueue隊(duì)列中,這樣就完成了對(duì)52張卡牌的洗牌。
房間傳輸對(duì)象是一個(gè)DTO(Data Transfer Objects,數(shù)據(jù)傳輸對(duì)象)模型。它包括了用戶id與用戶模型的映射列表、已準(zhǔn)備的玩家列表、玩家進(jìn)入房間的順序列表UIdList。除了基本的進(jìn)入、離開、準(zhǔn)備的初始化外,還實(shí)現(xiàn)了對(duì)房間內(nèi)玩家座次的定位。對(duì)于每個(gè)客戶端而言,由于其進(jìn)入房間的順序不同,在UidList 列表中的位置也不一樣。但是要求每個(gè)客戶端都將自身放在最下方的位置,這就要求每個(gè)客戶端根據(jù)自己在UidList 列表中的序號(hào)來顯示自己左右兩邊分別是幾號(hào)玩家。如圖7所示。
圖7 玩家座次圖
圖8 游戲的開始界面
面。包括注冊(cè)賬號(hào)和開始游戲的功能。點(diǎn)擊注冊(cè)賬號(hào),會(huì)進(jìn)行一些合法性判斷。
若兩次輸入密碼不匹配或者注冊(cè)賬號(hào)已經(jīng)存在就會(huì)導(dǎo)致注冊(cè)失敗。登錄成功后可以創(chuàng)建玩家角色,如圖9所示。開始游戲后,會(huì)首先尋找可進(jìn)入的游戲房間,如圖10 所示。游戲過程中還可以設(shè)置背景音樂,還設(shè)有玩家進(jìn)入游戲玩家等待,準(zhǔn)備開始游戲等功能。所有玩家準(zhǔn)備好后,游戲開始,隨機(jī)選出一名玩家作為第一個(gè)出牌者。出牌之后,在其他玩家的頁面可以看到桌面卡牌信息。圖10為三人在線玩游戲的畫面。出牌結(jié)束后,顯示勝利和失敗,其中勝利結(jié)果如圖12所示,退出游戲后,服務(wù)器收到反饋。
圖9 創(chuàng)建游戲角色
圖10 尋找可進(jìn)入的房間
圖11 三人在線玩游戲的畫面
圖12 勝利玩家視角