三国手机网游的技术分析
一、聊天
效果图
功能描述:玩家可以根据不同的频道,在输入框输入文字聊天,点击玩家名字可以私聊,在设置里面可以屏蔽对应的频道,文字内容有不同颜色,自动换行,输入屏蔽字后会自动屏蔽为***,可以插入表情,输入框文字超过显示宽度后会自动定位到当前输入位置。
技术难点:
1正确获取输入字符串长度
含有中英文数字的字符串比如String sz_str"a和",用sz_str.length()得到的字符长度是3,不正确,
使用下面的函数
static const char utf8_skip_data[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,3, 4, 4, 4, 4, 4, 4, 4, 4, 5,
5, 5, 5, 6, 6, 1, 1
};
static const char *constg_utf8_skip = utf8_skip_data;
#define cc_utf8_next_char(p) (char*)((p) + g_utf8_skip[*(unsigned char *)(p)])
long StringUtil::cc_utf8_strlen (const char * p, int max)
{
long len =0;
const char *start = p;
if (!(p !=NULL || max == 0))
{
return0;
}
if (max <0)
{
while (*p)
{
p = cc_utf8_next_char (p);
++len;
}
}
else
{
if (max== 0 || !*p)
{
return0;
}
p= cc_utf8_next_char (p);
while (p - start < max && *p)
{
++len;
p = cc_utf8_next_char (p);
}
/* only do the last len increment if we got a complete
* char (don't count partial chars)
*/
if (p - start == max)
{
++len;
}
}
return len;
}
int ilength = StringUtil::cc_utf8_strlen(str.c_str(),-1);可以正确得到输入的字数
2屏蔽敏感字
逻辑实现:定义一个屏蔽字工具类,同时通过使用extern CReadBadWords g_CReadBadWords;实现全局调用该类,其中定义2个函数,分别为读取字库ReadWords,替换字符串ReplaceBadWords;
根据策划提供的敏感字库,txt文件,在创建登录游戏窗口的时候读取txt文件,把每一行读取的内容存储到向量中, 然后在输入文字的时候点击"确定"按钮后就开始检查是否有敏感字。
性能优化:
(1)内存优化
在读取txt文件时使用
ifstream fin(BADWORDS);
const int WORD_LENGTH= 128;
char *szStr = new char[WORD_LENGTH];
memset(szStr, 0, sizeof(szStr));
while(!fin.eof())
{
fin.getline(szStr, WORD_LENGTH, '\n');
/* m_vcWords.push_back(szStr); */
m_listWords.push_back(szStr);
szStr = new char[WORD_LENGTH];
memset(szStr, 0, sizeof(szStr));
}
fin.close();
delete []szStr;
在模拟器上运行速度很快,正常,但是在手机上运行的时候就会出现闪退,分析后发现是在手机上长时间操作文件会导致内存增加很多,解决思路是把文件内容一次性加载到内存中,避免长时间操作文件,修改后的程序如下:
FILE* pFile;
long lSize;
char *pszbuffer;
size_t result;
pFile = fopen ( BADWORDS, "rb" );
if (pFile==NULL)
{
return;
}
//obtain file size:
fseek (pFile , 0 , SEEK_END);
lSize = ftell (pFile);
rewind (pFile);
//allocate memory to contain the whole file:
pszbuffer = (char*)malloc (sizeof(char)*lSize);
if (pszbuffer== NULL)
{
return;
}
//copy the file into the buffer:
result = fread(pszbuffer,1,lSize,pFile);
if (result !=lSize)
{
return;
}
/*the whole file is now loaded in the memory buffer. */
char * pszch;
pszch = strtok (pszbuffer,"\n");
m_listWords.push_back(pszch);
while (pszch !=NULL)
{
pszch = strtok (NULL, "\n");
if(pszch!= NULL)
{
m_listWords.push_back(pszch);
}
}
//terminate
fclose (pFile);
free (pszbuffer);
但是在android手机上还是不行,查找资料分析后发现Android手机系统默认是UTF8,
换行符是"\r\n");而不是"\n"
(2)使用高级匹配算法,因为字库内容有1w5千条,担心在读取或者查找匹配敏感字时候会太慢,导致在android平台上程序长时间无法响应,出现ANR现象退出程序,
尝试了
AC多模匹配(AC自动机),sunday算法,kmp算法,trietree
尝试上面算法后发现速度并没有明显提高,改为用最普通的循环,降低了程序的复杂度,速度也能达到满意的效果
上面2个问题具体实现如下:
voidCReadBadWords::ReadWords(vector<string> & vc_words)
{
//获得文件在系统的绝对路径
constchar *pFileName = BADWORDS;
constchar *pPath =CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(pFileName);
if(pPath== NULL)
{
return;
}
//读取的字节数,读取失败则为0
unsignedlong len = 0;
//读取的内容
unsignedchar *pszbuffer = CCFileUtils::sharedFileUtils()->getFileData(pPath,"rb", &len);
if(pszbuffer== NULL)
{
return;
}
char*pszch= strtok ((char *)pszbuffer,"\r\n");
if(pszch!=NULL)
{
stringstrContent(pszch);
vc_words.push_back(strContent);
}
while(pszch!=NULL)
{
pszch= strtok (NULL, "\r\n");
if(pszch!=NULL)
{
stringstrContent(pszch);
vc_words.push_back(strContent);
}
}
//记得释放内存
if(len>0 && pszbuffer) delete[] pszbuffer;
}
void CReadBadWords::ReplaceBadWords(string& strWords)
{
if(strWords.empty()|| strcmp(strWords.c_str(), "") == 0)
{
return;
}
if(!m_vcWords.empty())
{
for(inti=0; i<m_vcWords.size(); i++)
{
StringUtil::replace_all_distinct(strWords,m_vcWords.at(i), "***");
}
}
}
//替换指定字符串中出现的字符串,返回处理后的字符串
voidStringUtil::replace_all_distinct(string& str,const string& old_value,const string& new_value)
{
for(string::size_type pos(0); pos!=string::npos; pos+=new_value.length()) {
if( (pos=str.find(old_value,pos))!=string::npos )
str.replace(pos,old_value.length(),new_value);
else break;
}
}
3输入框文字超过指定长度的时候会超出显示,比如输入框背景图是500,输入的文字宽度是600,那么会超出100显示,
解决办法:裁剪输入框控件的可视区域和显示宽度一样宽
4文字换行时需要截取指定长度的含有中英文的字符串
方法一根据中文在ASCII中的范围判断
#include "stdafx.h"
#include <string>
#include <iostream>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
string s = "你好优快云,我的";
string t;
for(int i=0; i<s.length(); i++)
{
if(s<255 && s>0)//扩充的ASCII字符范围为0-255,如是,处理一个字节
{
t.append(s.substr(i,1));
t.append("/");
}
else//<0,>255的是汉字,处理两个字节
{
t.append(s.substr(i,2));
t.append("/");
++i;
}
}
cout << t << endl;//输出符合要求
return 0;
}
方法二把string转成wstring
wstring str2wstr(string str)
{
size_t len = str.size();
wchar_t * b = (wchar_t *)malloc((len+1)*sizeof(wchar_t));
MBCS2Unicode(b,str.c_str());
wstring r(b);
free(b);
return r;
}
string wstr2str(wstring wstr)
{
size_t len = wstr.size();
char * b = (char *)malloc((2*len+1)*sizeof(char));
Unicode2MBCS(b,wstr.c_str());
string r(b);
free(b);
return r;
}
方法三 把string转成UTF8编码格式,然后再转成Unicode
wchar_t *UTF8ToUnicode( const char* str )
{
int textlen ;
wchar_t *result;
textlen =MultiByteToWideChar( CP_UTF8, 0, str,-1, NULL,0 );
result =(wchar_t *)malloc((textlen+1)*sizeof(wchar_t));
memset(result,0,(textlen+1)*sizeof(wchar_t));
MultiByteToWideChar(CP_UTF8, 0,str,-1,(LPWSTR)result,textlen );
return result;
}
char *UnicodeToUTF8( const wchar_t* str )
{
char* result;
int textlen;
textlen =WideCharToMultiByte( CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL );
result=(char *)malloc((textlen+1)*sizeof(char));
memset(result, 0, sizeof(char) * (textlen + 1 ) );
WideCharToMultiByte(CP_UTF8, 0, str, -1, result, textlen, NULL, NULL );
return result;
}
尝试后上面3种方法在电脑上都可以正常运行,但是在android手机上不行,分析后发现android的操作系统默认是UTF8编码,尝试了用UTF8中中英文所占字节,比如中文判断位数是2位,3位,4位,还是不行,程序如果没有正确截取对,同时,android的容错性比较差,会导致闪退,查看CCLabelIBMFont后,尝试了cc_utf8_strlen,cc_utf8_next_char
解决问题,具体实现如下:
void XLabelView::getStrByLen(intiLen , string strSource , int iFontSize, string&szDes1, string &szDes2)
{
strSource =CG2U::g2u(strSource.c_str()).c_str();
string strText = strSource;
LPCSTR source = strSource.c_str();
int ilength= StringUtil::cc_utf8_strlen(source, -1);
LPCSTR target = source;
char buf[16]= {0};
memset(buf, 0, sizeof(buf));
int width =0;
int k=0;
for(int i =0; i<ilength; i++)
{
source = cc_utf8_next_char(target);
memset(buf, 0, sizeof(buf));
memcpy(buf, target, source - target);
k+= strlen(buf);
CCLabelTTF* pLabelTTF = CCLabelTTF::create(buf, m_fontName,iFontSize);
width +=pLabelTTF->getContentSize().width;
if(width> iLen)
{
szDes1 = strText.substr(0, k);
szDes2 = strText.substr(k);
return;
}
target = source;
}
return;
}
5文字换行
逻辑实现:文字显示的控件都带有前后指针,Front,next分别指向前后一个控件;根据输入的字符串显示宽度和指定宽度比较,如果超出指定宽度,就把指定宽度的字符串所在的显示控件都保持X坐标不变,Y坐标向上平移,继续显示下一行的文字
具体实现如下
//前面的Item位置往上升
XLineItem* pItem = lineItem;
CCPoint pt =pItem->getPosition();
while(pItem != NULL)
{
pt =pItem->getPosition();
//根据是否有图片LineItem抬高的高度不同
if(isHasImg)
{
pt =ccpAdd(pt,ccp(0,iHight));
}
else
{
pt = ccpAdd(pt,ccp(0,pItem->GetContentSize().height));
if(pItem->GetBackGroundBitmap()!=NULL)
{
pt =ccpAdd(pt,ccp(0,iHeightTmp));
}
}
pItem->setPosition(pt);
pItem = pItem->m_pFront;
}
6发送接收信息
实现原理:把输入的字符串按照下面的格式组装,然后发送,接收到后用TinyXml解析
<msg>
<ch font="字体名称" fontsize ="字体大小" fontcolor = "字体颜色"IsP="是否私聊标志">频道名称内容</ch>
<S font="字体名称" fontsize ="字体大小" fontcolor = "字体颜色">发送者用户名</S>
<n font="字体名称" fontsize ="字体大小" fontcolor = "字体颜色">接收者用户名</n>
<t font="字体名称" fontsize ="字体大小" fontcolor = "字体颜色">文本内容</t>
<img>图片地址</img>
</msg>
7玩家名字添加下划线
具体实现:
void XLineItem::setUnderLineColor(ccColor4Bcolor)//设置下划线颜色
{
CCLayerColor*clayercolor = CCLayerColor::create(color);
clayercolor->setPosition(ccp(m_pLabelTTF->getPositionX(),m_pLabelTTF->getPositionY()-0.5));
clayercolor->setContentSize(CCSizeMake(m_pLabelTTF->getContentSize().width,3));
m_pLabelTTF->addChild(clayercolor,1);
}
二、整个系统音效
音效主要分为战斗音乐(战斗中攻击的技能和普通攻击音效),非战斗音乐(比如抽奖,点击按钮,箱子开启等)
实现思路:
设计2张声音配置XML,
Soundsource.XML
内容简介
<?xml version="1.0"encoding="utf-8"?>
<sounds>
<!--游戏中所有声音的配置,用于预加载声音,sound.xml中的声音以这里的为主-->
<sound id= "1" name= "login.ogg" />
<sound id= "2" name= "clickbtn.ogg" />
sound.xml
<?xml version="1.0"encoding="utf-8"?>
<!--id 如果是战斗的技能,ID就是和配置表一致,其他的是自定义; soundname1声音文件名;soundname2声音文件名;soundname3声音文件名;-->
<sounds>
<sound id= "1" soundname1= "clickbtn.ogg" soundname2="" soundname3="" />
<sound id= "2" soundname1= "choujiang.ogg" soundname2="" soundname3="" />
<sound id= "15" soundname1= "zhuchengkejishenjiok.ogg" soundname2="" soundname3="" />
<sound id= "0" soundname1= "jinzhanputong.ogg" soundname2= "" soundname3= "arrow_normal.ogg" />
<sound id= "98" soundname1= "jinzhanputong.ogg" soundname2= "" soundname3= "" />
<sound id= "99" soundname1= "fashiputong.ogg" soundname2= "" soundname3= "" />
在游戏启动时解析上面的xml内容,加载到全局数据配置类中,
设计一个声音工具类,底层封装了cocos2dx的SimpleAudioEngine对应函数,比如下面的
unsigned intSoundUtil::playEffect(LPCSTR pszFilePath)
{
if(!IsEnableEffectMusic())
{
return-1;
}
if(pszFilePath== NULL)
{
return-1;
}
LPCSTR pStr =CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(pszFilePath);
if(pStr ==NULL)
{
return-1;
}
returnCocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect(pszFilePath);
}
其中boolSoundUtil::IsEnableEffectMusic()
{
returnm_bEnableEffectMusic;
}
是游戏启动时从CCUserDefault中读取的是否播放音乐的选项内容
性能优化:考虑声音是加载到内存中,尽可能减少内存的占用量,所以只在需要使用时提前加载对应的声音,使用完后切换到其他窗口时卸载声音,
技术难点
1卸载音效和播放音效之间的切换
在播放音效前,卸载上一个音效,结果导致音效无法播放,分析后发现卸载音效后要过一段时间才能播放音效,改为在每个播放音效的类中的析构函数卸载对应的音效,在构造函数中加载对应的音效,然后在使用音效时直接调用播放函数,同时也解决了每次第一次播放音效的时候无法播放的问题
2播放战斗时,声音和动画无法同步
尝试过在runAction之前和之后播放声音,都无法实现同步,分析后在CCSequence::create中添加回调函数CCCallFunc::create(this,callfunc_selector(CFightPlay::PlayEffectMusic)),即可解决
3按手机音量键调节声音大小
在android平台上的实现:
监听音量键,调用对应的声音工具类
@Override
public booleanonKeyDown(int keyCode, KeyEvent event) {
AudioManager mAudioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
intcurrentVolume = mAudioManager
.getStreamVolume(AudioManager.STREAM_MUSIC);
switch (keyCode){
case KeyEvent.KEYCODE_VOLUME_UP:// 音量增大
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
currentVolume + 1, 1);
break;
case KeyEvent.KEYCODE_VOLUME_DOWN:// 音量减小
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
currentVolume - 1, 1);
break;
default:
break;
}
return true;
// returnsuper.onKeyDown(keyCode, event);
}
三 背包
效果图
功能描述:玩家有5个背包和一个VIP背包,显示所有物品,包括装备,法器,碎片,点击对应装备,在右边显示对应的信息,可以出售,使用,自动整理背包排序,
技术难点
1自动整理背包排序
实现思路:先获取所有物品存储到向量中,然后根据物品的穿戴等级,品质,装备(兵器,防具,坐骑,兵书等)进行比较,然后再进行排序
具体实现:
获取所有物品
vector<IContainerGoods*>CPackSackWnd::__GetAllGoods()
{
vector<IContainerGoods*>v_AllGoods;
//普通背包
IContainer*pContainer = m_pPacketContainer;
if(pContainer == NULL)
{
returnv_AllGoods;
}
intiSize = pContainer->GetSize();
for(intiIndex=0; iIndex < iSize; iIndex++)
{
IContainerGoods*pContainerGoods = pContainer->GetGoods(iIndex);
if(pContainerGoods== NULL)
{
continue;
}
v_AllGoods.push_back(pContainerGoods);
}
//VIP背包
pContainer= m_pVipPacketContainer;
if(pContainer == NULL)
{
returnv_AllGoods;
}
iSize= pContainer->GetSize();
for(intiIndex=0; iIndex < iSize; iIndex++)
{
IContainerGoods*pContainerGoods = pContainer->GetGoods(iIndex);
if(pContainerGoods== NULL)
{
continue;
}
v_AllGoods.push_back(pContainerGoods);
}
returnv_AllGoods;
}
根据条件比较
bool greatermark(IContainerGoods*pContainerGoods1,IContainerGoods* pContainerGoods2)
{
//穿戴等级
if(pContainerGoods1->GetPropNum(en_Goods_PropID_NeedLevel)> pContainerGoods2->GetPropNum(en_Goods_PropID_NeedLevel))
{
returntrue;
}
elseif(pContainerGoods1->GetPropNum(en_Goods_PropID_NeedLevel) <pContainerGoods2->GetPropNum(en_Goods_PropID_NeedLevel))
{
returnfalse;
}
//品质
if(pContainerGoods1->GetPropNum(en_Goods_PropID_Quality)> pContainerGoods2->GetPropNum(en_Goods_PropID_Quality))
{
returntrue;
}
elseif(pContainerGoods1->GetPropNum(en_Goods_PropID_Quality) <pContainerGoods2->GetPropNum(en_Goods_PropID_Quality))
{
returnfalse;
}
//装备
UIDuidGoods1 = pContainerGoods1->GetUID();
IThing*pThing1 = g_GlobalClient.GetThingByUID(uidGoods1);
if(pThing1 != NULL && pThing1->GetThingClass()->IsEquipment())
{
UIDuidGoods2 = pContainerGoods2->GetUID();
IThing*pThing2 = g_GlobalClient.GetThingByUID(uidGoods2);
if(pThing2 != NULL && pThing2->GetThingClass()->IsEquipment())
{
if(pContainerGoods1->GetPropNum(en_Goods_PropID_SubClass)> pContainerGoods2->GetPropNum(en_Goods_PropID_SubClass))
{
returntrue;
}
elseif(pContainerGoods1->GetPropNum(en_Goods_PropID_SubClass) <pContainerGoods2->GetPropNum(en_Goods_PropID_SubClass))
{
returnfalse;
}
}
}
returnfalse;
};
对物品排序
void CPackSackWnd::__Sort()
{
m_AllGoods= __GetAllGoods();
if(m_AllGoods.empty())
{
return;
}
sort(m_AllGoods.begin(),m_AllGoods.end(), greatermark);
}
四布阵
五新手引导
创建手指箭头
functionLUA_Create_ArrowFinger(pDirection,iPosX, iPosY, iWidth, iHeight) ---------pDirection 0 up 1 down 2 left 3 right
pArrowSprite = CCSprite:create(fingerStr)
pArrowSprite:setAnchorPoint(ccp(0.5,0.5))
local ASwidth = pArrowSprite:getContentSize().width
local pos = nil
if(pDirection == 0) then
pArrowSprite:setRotation(90)
pArrowSprite:setFlipY(true)
pArrowSprite:setPosition(ccp(iPosX+iWidth/2,iPosY-ASwidth/2-10))
pos=ccp(0,10)
end
if(pDirection == 1) then
pArrowSprite:setRotation(-90)
pArrowSprite:setPosition(ccp(iPosX+iWidth/2,iPosY+iHeight+ASwidth/2+10))
pos=ccp(0,-10)
end
if(pDirection == 2) then
pArrowSprite:setRotation(0)
pArrowSprite:setPosition(iPosX+iWidth+ASwidth/2+10,iPosY+iHeight/2)
pos = ccp(-10,0)
end
if(pDirection == 3) then
pArrowSprite:setFlipX(true)
pArrowSprite:setPosition(iPosX-ASwidth/2-10,iPosY+iHeight/2)
pos = ccp(10,0)
end
if(pos == nil) then
return
end
pActionMove = CCMoveBy:create(0.5,pos)
pActionBack = pActionMove:reverse()
pSequenMoves = CCSequence:createWithTwoActions(pActionMove,pActionBack)
pArrowSprite:runAction(CCRepeatForever:create(pSequenMoves))
return pArrowSprite
end
创建遮罩层,整个屏幕只有下面的矩形区域才可以点击,并且可以传递回调函数
--------------------遮罩层--------------------------------
function LUA_CreateShadeLayer20(iPosX,iPosY, iWidth, iHeight, functionCallBack)
cclog('LUA_CreateShadeLayer')
localpLayerShade = CCLayer:create()
localsRect = LUA_GetThroughRect20()
if(sRect~= nil) then
cclog('sRect ~= nil')
return nil
end
sRect= CCRectMake(iPosX*fScanX, iPosY*fScanY, iWidth*fScanX, iHeight*fScanY)
LUA_SetThroughRect20(sRect)
localfunction onTouchBegan(x, y)
sRect = LUA_GetThroughRect20()
if(sRect == nil) then
return 0
end
local sPoint = ccp(x, y)
cclog('onTouchBegan:(x='..x..',y='..y..')')
if(sRect:containsPoint(sPoint) == true) then
cclog('in sRect')
if(functionCallBack ~= nil and type(functionCallBack) == 'function')then
functionCallBack()
end
return 0
else
return 1
end
end
localfunction onTouchMoved(x, y)
end
localfunction onTouchEnded(x, y)
return
end
localfunction onTouch(eventType, x, y)
ifeventType == CCTOUCHBEGAN then
returnonTouchBegan(x, y)
elseifeventType == CCTOUCHMOVED then
returnonTouchMoved(x, y)
else
returnonTouchEnded(x, y)
end
end
pLayerShade:setTag(iLayerTag)
pLayerShade:registerScriptTouchHandler(onTouch,false, -128, true)
pLayerShade:setTouchEnabled(true)
returnpLayerShade
end
六退出游戏对话框,调用android平台的原生对话框
技术难点
1cocos2dx jni和android之间的交互
实现步骤
(1)在android项目中的mainActivitiy创建一个handler,并且在handler处理信息中创建一个对话框
private Handler mHandler = new Handler(){
@Override
public voidhandleMessage(Message msg) {
switch (msg.what) {
case SHOW_DIALOG:
newAlertDialog.Builder(sanguo0524.this)
.setTitle("退出游戏")
.setMessage("是否退出游戏?")
.setNegativeButton("取消",
new DialogInterface.OnClickListener(){
@Override
public voidonClick(DialogInterface dialog,
int which) {
dialog.dismiss();
}
})
.setPositiveButton("确定",
newDialogInterface.OnClickListener() {
@Override
public voidonClick(DialogInterface dialog,
int which) {
dialog.dismiss();
XJniHelper.exitApp();
}
}).create().show();
break;
}
}
};
(2)创建一个工具类,提供原生函数的声明,handle接收来自C++层面的消息处理函数
public classXJniHelper {
private static Handler mHandler;
public static native voidexitApp();
public static voidinit(Handler handler) {
XJniHelper.mHandler =handler;
}
private static voidshowTipDialog(final String title, final Stringtext)
{
Messagemsg = mHandler.obtainMessage();
msg.what =sanguo0524.SHOW_DIALOG;
msg.sendToTarget();
}
}
3在cocos2dx项目中,在每个场景类里面添加判断是否android平台
#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include "../proj.android/jni/hellocpp/ExitGameDialog.h"
#endif
并实现CCLayer的监听返回键函数
virtual void keyBackClicked(); //接收手机返回按钮事件
void CAreaScene::keyBackClicked()//接收手机返回按钮事件
{
#if(CC_TARGET_PLATFORM ==CC_PLATFORM_ANDROID)
showTipDialog("退出窗口","是否退出游戏?");
#endif
#if (CC_TARGET_PLATFORM== CC_PLATFORM_IOS)
exit(0);
#endif
}
其中showTipDialog函数是和android项目中对应的调用接收信息,显示对话框的函数
5在ExitGameDialog.h中定义showTipDialog和Java_com_xy_sanguo_uc_XJniHelper_exitApp
6在XJni.h中定义退出游戏函数
void exitApp()
{
CCDirector::sharedDirector()->end();
}
七抽奖
通过改变schedule的时间实现慢,快,慢的转动效果
if(m_iRandNum <5) //慢y
{
schedule(schedule_selector(CActivityLotteryWnd::__ChangeImgBrightness),3 * 0.1);
}
else if(m_iRandNum >=5 && m_iRandNum <20)//快¨¬
{
schedule(schedule_selector(CActivityLotteryWnd::__ChangeImgBrightness),5 * 0.01);
}
else if(m_iRandNum >=20)//快¨¬
{
schedule(schedule_selector(CActivityLotteryWnd::__ChangeImgBrightness),3 * 0.1);
}