刘皎
(商洛学院 电子信息与电气工程学院, 商洛 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的应用极大地缩短了开发周期,节约了开发成本。