劉皎
(商洛學院 電子信息與電氣工程學院, 商洛 726000)
隨著智能手機的普及和4G網絡覆蓋的增加,以及國內三大運營商5G網絡的試點開始,手機網絡游戲正在日益興盛[1-2]。智能手機變得越發強大,以及騰訊、網易等公司大力發展手游,未來幾年手游將出現爆發式增長[3-4]。目前iOS和Android游戲營收巨大。手機游戲作為Android系統的一個重要組成部分,如何在手機平臺上開發出一款高效的游戲正在被越來越多的開發者所重視。Cocos2d-x是一種可以支持多種操作系統的游戲引擎,并且是開源的[5-7],可以極大地提升游戲開發效率。Cocos2d-x圍繞Cocos2d跨平臺提供的框架,封裝了復雜OpenGL圖形接口,并抽象出精靈和動作等概念,降低了游戲開發的難度,簡化了開發過程。本文基于Visual Studio C++2013開發平臺,采用Cocos2d-x游戲引擎框架,設計了一款適用于Android的休閑麻將手游。
游戲在windows8.1系統下開發,基于Microsoft Visual C++2013開發平臺,采用了cocos2d-x引擎框架。
Microsoft Visual C++2013具有強大的功能和友好的界面,是目前最熱門的C++語言開發環境。Visual C++提供的集成開發環境、MFC類庫和應用程序框架極大地促進了C++中Windows應用程序的開發。它可以完成源代碼的編寫、用戶界面的設計、消息映射、編譯連接和調試運行等工作。Visual C++的應用能夠明顯縮短程序編輯、編譯及連接的時間花費,在大型軟件的研發中其優勢更為顯著[8]。
Cocos2d-x是目前較為流行的游戲開發引擎,當前市場上多數的2D游戲都是采用它進行開發的[9-10]。基于Cocos2d-x引擎的基本框架,游戲開發者可以在一個平臺進行開發然后將其移植到其它平臺進行編譯,例如,可以將開發的游戲移植到 Android、iOS、Mac、Windows 等平臺之上。Cocos2d-x引擎框架中幾個重要概念之間的關系如圖1所示。
在整個框架中,導演Director是框架的核心,負責整個游戲的運行;場景Scene是游戲中的各個界面,包含了登錄、注冊、游戲大廳、設置、主界面及退出界面等;布景Layer的功能就是在界面上添加各種有效信息,比如游戲登錄加載條、游戲分享、簽到等信息;精靈Sprite是直接繪制在屏幕上的2D位圖,一般可以用一個子矩陣或一個圖片來創建,用來顯示一些玩家信息,如積分、得分等;動作Action可以單獨加到精靈上,也可以多個動作組合加入到精靈上,使其完成一系列動作。

圖1 Cocos2d-x引擎框架中幾種基本概念間的關系圖
基于Cocos2d-x麻將游戲的總體結構如圖2所示。
它主要包括引擎模塊、數據模塊、網絡模塊、游戲邏輯、UI界面、聲音模塊和AI。引擎模塊即為Cocos2d-x引擎;數據模塊即為玩具信息和游戲資源,玩具信息包括玩家的注冊信息、游戲過程中產生的數據、玩家的戰績等;游戲資源包括背景音樂和界面圖片等;網絡模塊采用http協議;游戲邏輯包括擲骰子、發牌、出牌、吃碰杠、胡牌及自摸等;UI界面由登錄界面、匹配界面和游戲界面等組成;聲音模塊包括背景音樂及聲音音效,用戶可以自行選擇音效處于打開或關閉狀態。AI包括了局面分析和分支預測等。

圖2 游戲功能框架圖
手游的開發一般為兩個部分,即服務器和客戶端[11-12],其框架圖如圖3所示。

(a) 客戶端框架圖

(b) 服務端框架圖
圖3 游戲功能模塊框架圖
圖3(a)為客戶端的三大模塊:引擎入口模塊、界面模塊、邏輯模塊。引擎入口模塊主要包括 AppDelegate,主要用于游戲程序的邏輯初始化,并創建運行程序的入口界面,即第一個游戲界面場景;界面模塊包括了場景模塊Scene和圖層模塊Layer;邏輯模塊包括了人工智能模塊AI、麻將規則模塊Rule和游戲整體控制模塊GameControl。
圖3(b)為服務端的三大模塊:數據模塊,工具模塊和網絡服務模塊。數據模塊保羅了存儲用戶信息PlayDate;網絡服務模塊包括服務器端數據ServerData和控制服務器通信ServerAgentThread;工具模塊包括了輸入/輸出工具I/OUtil、傳輸信息轉換工具ConvertUtil控制服務器端邏輯工具RuleUtil。
游戲的程序設計主要實現的功能類有:游戲主場景、觸摸事件、用戶操作、游戲結束場景類、游戲場景類、撲克類、游戲界面類。該游戲中的幾段主要代碼如下。
(1) 游戲場景類
//類函數
Scene* HelloWorld::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::create();
// 'layer' is an autorelease object
auto layer = HelloWorld::reate();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
(2) 觸摸事件
TouchListenr->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
TouchListenr->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved, this);
TouchListenr->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded, this);
void HelloWorld::onTouchMoved(Touch* touch, Event* event)
{
//獲取精靈對象
auto sprite = static_cast
//改變精靈的位置
sprite->setPosition(sprite->getPosition() + touch->getDelta());
}
void HelloWorld::onTouchEnded(Touch* touch, Event* event)
{
//獲取精靈對象并取得精靈的矩陣
auto sprite = static_cast
//獲取觸摸點的坐標
Point pointEnd = touch->getLocation();
Point pointOld = m_fSelectPos;
unsigned int dLenX = abs(pointEnd.x - pointOld.x);
unsigned int dLenY = abs(pointEnd.y - pointOld.y);
if (dLenX < 200 && dLenY < 200)
{
sprite->setPosition(pointOld);
}
else//出牌
{
BYTE tagValue = sprite->getTag();
BYTE cbCardData = sprite->getZOrder();
BYTE wChair = tagValue / 14;
BYTE bIndex = tagValue % 14;
BYTE disCount = m_iDisCardCount[wChair];
if (GetCanOutCard(wChair))//可以出牌
{
//刪除精靈
if (m_VecCard[wChair].size() > bIndex)
m_VecCard[wChair].erase(m_VecCard[wChair].begin() + bIndex);
//丟棄
m_VecDisCard[wChair].push_back(sprite);
//設置丟棄位置
sprite->setPosition(g_fDisCardPos[wChair][disCount]);
sprite->setTag(0xFF); //設置不可觸摸
m_iDisCardCount[wChair]++;
//刪除索引
BYTE delIndex = SwitchToCardIndex(cbCardData);
if (m_cbCardIndex[wChair][delIndex] > 0)
m_cbCardIndex[wChair][delIndex]--;
//手牌重排
ReSortCard(wChair);
//出牌精靈
m_pOutCardSprite = sprite;
//玩家出牌
OnUserOutCard(wChair, cbCardData);
}
else
{
sprite->setPosition(pointOld);
}
}
cocos2d::log("touch [%d %d]", m_iSelect / 14, m_iSelect % 14);
}
(3) 用戶操作(碰、杠、胡操作)
switch (cbTargetAction)//玩家的主動操作
{
case WIK_PENG: //碰牌操作
{
//if (m_cbOutCardCount <= 4)
//m_bGenZhuangFlag = false;
//刪除
BYTE cbRemoveCard[] = { cbTargetCard, cbTargetCard };
RemoveCard(m_cbCardIndex[wTargetUser], cbRemoveCard, 2);
//碰牌處理
RemoveCardSprite(wTargetUser,cbRemoveCard, 2,m_wOutCardUser);
//按鈕動作清0
SetAllBtnOff();
//設置變量
m_wCurrentUser = wTargetUser;//玩家操作結束后設置操作玩家為當前玩家
ZeroMemory(m_bCanSendCard, sizeof(m_bCanSendCard));
m_bCanOutCard[wTargetUser] = true;
break;
}
case WIK_GANG: //杠牌操作
{
//增加明杠動作
m_pITableFrame->AddPrivateAction(wChairID, PrivateAction_Ming_Gang);
if (m_cbOutCardCount <= 4)
m_bGenZhuangFlag = false;
//刪除牌,被動動作只存在捉杠
BYTE cbRemoveCard[] = { cbTargetCard, cbTargetCard, cbTargetCard };
RemoveCard(m_cbCardIndex[wTargetUser], cbRemoveCard, CountArray(cbRemoveCard));
//杠牌處理
RemoveCardSprite(wTargetUser, cbRemoveCard, 3, m_wOutCardUser);
//按鈕動作清0
SetAllBtnOff();
//設置變量
m_wCurrentUser = wTargetUser;//玩家操作結束后設置操作玩家為當前玩家
ZeroMemory(m_bCanSendCard, sizeof(m_bCanSendCard));
m_bCanOutCard[wTargetUser] = true;
break;
}
default:
assert(FALSE);
return false;
}
游戲測試時,首先在Android手機上下載游戲的apk文件并安裝,安裝完成后點擊手機上的游戲圖標進入游戲的開始界面,如圖4所示。

圖4 游戲開始界面
該界面包括了登錄、注冊功能:玩家啟動游戲后進入登錄選擇界面,在登錄選擇界面玩家可以選擇賬號登錄或者注冊成功后直接登錄游戲。如果玩家有游戲賬號,直接輸入游戲賬號和密碼進行登錄。如果玩家沒有游戲賬號,則可以注冊一個新的賬號。如果玩家選擇注冊一個新的游戲賬號登錄,則會跳轉到注冊界面,填寫游戲賬號、密碼、性別等相關的個人信息,注冊成功后即可進入游戲大廳開始游戲,游戲的場景界面如圖5所示。

圖5 游戲場景界面
然后,對麻將游戲的各個功能進行了逐一測試,均達到了預期效果,且整個測試過程中游戲運行穩定,界面清晰,達到了實際應用要求。
本文基于Cocos2d-x游戲引擎開發了一款麻將游戲,主要利用Cocos2d-x進行圖形渲染、游戲框架構建、應用程序和圖形交互界面的設計,采用C++語言進行編程。本文首先對游戲開發環境介紹了;接著在完成需求分析的基礎上搭建出游戲的功能框架結構,對其各個功能模塊進行代碼編輯和調試;最后應用黑盒測試方法對該游戲的功能模塊進行了測試。測試結果表明,本次開發的需求分析合理,游戲運行流暢、功能齊全,并且Cocos2d-x的應用極大地縮短了開發周期,節約了開發成本。