第一步 游戏测评
第二步 小项目练手
遇到的问题:
1. SpringBoard无法启动应用程序 Clean程序,或者注销电脑试试。
2. 当设备变化是,图片资源被拉伸变形。采用合适的屏幕自适应方法。
3. 要创建工具类,将复用次数较多的函数放入工具类中
4. 自定义的CCLayer重写了OnEnter之后,,一定要先调用基类的OnEnter,CCLayer::OnEnter(); 因为在CCLayer和CCNode中的OnEnter之中做了一些初始化工作。我再OnEnter中播放动画,没有反应,原因就是没有调用基类的OnEnter。
5. 头文件尽量用全路径。
e.g. #include "GameTools/GameTools.h"
6. 注释插件
7. 手机游戏排行
googleplay排行
gamelook
appstore
appannie
8. 使用菜单的时候一般设置menu->setPosition(CCPointZero); ,菜单项的坐标是相对于菜单的父节点的。例如,sprite->addChild(menu),这时,添加坐标如果写menuItem->setPosition(ccp(0,0),则菜单项是在sprite的(0,0)处。 添加菜单项的时候,先添加的在下面,后添加的在上面。
e.g. create(moreGames,parents,NULL), 如果有交集,则moreGame菜单项在parents之下。
如果想给菜单指定zOrder或者tag则 CCMenu* menu = CCMenu::create(); menu->addChild(menuItem1, zOrder, tag);
9. 节点的锚点
锚点是相对于父节点来说的,不影响其孩子的添加位置。孩子添加还是相对于左下角的点。
e.g.
CCSize size = CCDirector::sharedDirector()->getWinSize();
CCSprite* sp1 = CCSprite::create("Default.png",CCRectMake(0,0,200,200));
sp1->setAnchorPoint(ccp(1,1));
sp1->setPosition(ccp(size.width, size.height));
addChild(sp1,1);
这里,锚点设置为(1,1)所以图片会紧贴着屏幕的右上角
CCSprite* sp2 = CCSprite::create("CloseNormal.png");
sp2->setPosition(ccp(0,0));
sp1->addChild(sp2);
这里sp2添加到sp1中,仍然是在sp1的左下角。
10. 如何屏蔽后端事件:
1). 重写 registerWithTouchDispatcher()函数,
virtual void registerWithTouchDispatcher();
void DialogLayer::registerWithTouchDispatcher()
{
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, -128, true); //注册并设置当前面板触摸的优先级
}
2). 设置可以触摸
setTouchEnabled(true);
3). ccTouchBegan返回true
11.
http://www.andersriggelsen.dk/glblendfunc.php CCRenderTexture混合渲染参数在线设置 |
12.对于CCEditBox和CCLabelTTF默认字色为白色,另外CCEditBox中没有设置对其方式相关的函数。在CCLabelTTF中,可以用 setHorizontalAlignment ( kCCTextAlignmentLeft ); 设置左对齐。
13. 初始化一个层的适合,在Init函数尽量不要写会改变的代码。因为Init在创建层的时候只运行一次。而每次进入界面均要改变的代码在OnEnter中写比较好,但是不要忘了调用基类的OnEnter。
例如,想要在页面2设置一个按钮,点击后会切换中英文(包括图片、文字、语音),此时,在页面1的OnEnter函数中写这些代码比较好。当PopScene到页面1的时候,依然会走OnEnter。
但是在OnEnter中加载的东西,要在OnExit中释放。不然每次都会加载。
14.消息队列实现界面交互。
1). 在Layer1中新建消息队列。
static MessageQueue* s_messageSqueue; ---Layer1.h
MessageQueue* GameLayer::s_messageSqueue =newMessageQueue();
2).消息处理函数
void handleMsg(float dt);---Layer1.h
3).开启消息队列 ---
schedule(schedule_selector(GameLayer::handleMsg));---Layer1.cpp--init()
4).实现消息处理函数
void GameLayer::handleMsg(float dt){
if(GameLayer::s_messageSqueue->size() !=0){
Message* m = NULL;
while ((m = GameLayer::s_messageSqueue->pop()) !=NULL) {
switch (m->getMesType()) {
case Message_Type_ScrollScreen_:
MoveBackground();
break;
default:
break;
}
delete m;
m = NULL;
}
}
}
5).Layer2中发送消息,并添加到消息队列。
Message* msg = Message::instance();
msg->setMesType(Message_Type_ScrollScreen_);
GameLayer::s_messageSqueue->push(msg);
15. 获取点击位置,并转化为对应节点中的坐标
CCSetIterator it = pTouches->begin();
CCTouch *touch = (CCTouch *)(*it);
//转换到对应节点下的坐标
CCPoint converNodePoint = node->convertTouchToNodeSpace(touch);
16. 野指针问题 一般是数组越界或者是指针new了没删除
17.做一个东西的时候,先观察他的功能,功能较多的时候,要封装成一个类,方便以后拓展。比如拼图的碎片。当点击拖动的时候,会跟随移动,当松手但是没有拖到合适位置的时候会弹回去,拖到合适位置会嵌进去。
18.今天遇到好多Bug,是一个野指针引起的。不是数组越界,也不是new的问题。自定义了一个Chip类继承自CCSprite,使用的时候,Chip * chip = (Chip*)Chip::createSpriteWithFrameName("XXX.png");出了问题。
野指针形成原因: 一)、数组越界造成非法使用内存
解决方法: 1)、访问数组的时候注意访问的长度是否超过下标
比如:二维数组array[][4],访问的时候 for(int i = 0; i < num; i++){ array[0][i]; (要确定num一定不能超过4) }
2)、尽量用链表(stl容器)代替数组;
二)、new出来的东西一定要delete掉、cocos2d的对象保证一个原则:对象create之后要么add child进去,要么retain它,但是后面一定要手动release掉、也可以自己写一个create方法,但是注意:new之后设置它为autorelease
三)、强转类型: 出错实例:
1)、class A{ }
class B{ }
B* b = new B; A* a = (A*)B; (最无脑的强转方法,程序不报错,但是两个类型没有一点关系,一用a就错) 2)、class A{ }
class B : public A{ char* str[100]; }
A* a = new A; B* b = (B*)a; (这样强转没问题,基类转化成派生类,但是b调用自己的对象str的时候就会出问题,因为你的b没有初始化这个属性,直接从A强转过来的,有野指针的危险。今天上午的bug就是这个问题)
关于强转类型建议不要用上面的c的形式,尽量使用static_cast和 dynamic_cast方法做类型转换 基类转化与派生类的转化 使用static_cast方法:例: static_cast<B*>A这样避免第一个出错实例的发生,会在编译报错提醒的! 同类型之间的转化使用dynamic_cast方法:例:array->addChild(Menu*) Menu* menu = dynamic_cast<Menu*>array->anyObject() 如果类型不是menu返回是null,这样更安全,详细查看http://www.cnblogs.com/jerry19880126/archive/2012/08/14/2638192.html
四)、变量生命周期结束,还去访问它 (问题不太常见) 例如: A* p; int a{ A* a; p = &a; }
p->func(); (a的内存被释放了)
五)、指针成员变量一定要在构造置成NULL (编码规范,需要特别注意,如果不初始化就使用它,这是天生的野指针啊!) |
19.在程序中尽量少用Tag,因为会很乱。如果一定要用,则要用宏或者枚举定义,而不要直接用数字。同一个节点如果添加了2个一样的tag就容易出问题。因此多个同类的节点可以加到链表或者向量中。而此向量作为一个类的成员。当别的类想要访问的时候,只需要取得此链表即可。
创建 std::vector<Chip*> m_chipV;
判空 if (node-> m_chipV . empty ()) return ;遍历 std::vector<Chip*>::iterator iter = node->m_chipV.begin();
for (; iter != node->m_chipV.end(); iter++) { }
20. CCPoint pos = a->getPosition()获取的坐标,是相对于父节点以及a的锚点的坐标。因此屏幕自适应的时候,如果锚点与父节点不变,则再次设置a->setPosition(pos);则相对位置不变。 比如当在节点1中拖着一个节点2运动并打印坐标,则打印出来的坐标是相对于节点2锚点在节点1中的坐标。
21.关于retain
一般只有在一个函数中创建cocos2d类型变量,但是脱离了此函数作用域去用这个变量,并且在此函数中没有addChild,此时要在此函数中retain()。 当不用此变量的时候在release掉。
CCSprite* a;
void A(){
a = CCSprite::createWithTexture();
a->retain();
}
void B(){
a->fsaf
}
22.关于回调函数
CCCallFuncN::create(CCObject* pSelectorTarget, SEL_CallFuncN selector)
CCCallFuncND::create(CCObject* pSelectorTarget, SEL_CallFuncND selector, void* d)
均可接受一个节点参数,谁掉用这个回调函数,传入的就是谁。
回调函数在CCSequence中使用时要注意,CCSequence中的函数是顺序执行,而CCSequence外面的代码也会执行。
例如:
CCDelayTime* dtime = CCDelayTime::create(0.5);
call1回调函数中 打印 cclog("call1");
call2回调函数中 打印cclog("call2");
CCSequence* seq = CCSequence ::create(call1,dtime,call2,NULL);
CCLog("abc");
此时执行结果为 call1
abc
call2
在dtime期间就已经开始往下执行了。所以如果在回调函数call2中对指针有所操作,而在CCSequence后面又释放了这个指针或者其他操作,则会对call2产生影响。
23.游戏运行卡死,但是不报错,往往是死循环导致的。检查卡死的附近的代码
24.少用静态变量,用户数据写到UserDefault中。
CCLog("%s",CCFileUtils::sharedFileUtils()->getWritablePath().c_str());可以查到文件存放的路径
在Library中的Preferences文件夹中用plist存储
CCFileUtils::sharedFileUtils()->getWritablePath() 此命令可以获得不同设备中的读写路径。
CCFileUtils::sharedFileUtils()->fullPathForFilename("xxx.xx"); 在Resource中查找文件 xxx.xx,并且返回该文件在库中的路径(不是Resource下文件的路径)。如果xxx.xx在Resource的某个文件夹中, 则需要写出文件夹的名字,如"yy/xxx.xx"
25.关于onEnter、onExit、构造与析构函数
当A进入B,先调用B的构造函数->B的init->A的onExit->B的onEnter->A的析构函数。
若A中有计时器,也是在A的onExit中取消的。例如,如果在B的构造清除缓存,而在A中计时器播放动画,则会出现错误。此时可以将清除缓存、加载缓存、等操作放在B的onEnter中
26.
算法里面出现log(A)的时候,一定要判断A为非0 或者 除法 (A/B)这种可能导致没有意义的表达式 除数和指数幂 非0判断 有的时候很容易遗漏 造成的后果跟野指针差不多 各种奇怪bug 不过这个还好一点 一旦出现除数为0或者指数幂为0 的出来的值是个这个东西 -1.#IND00000000000 只要输出这个东西 那就是这类问题了 |
27.关于retain和release
如果创建出来的对象没有在本帧内被addChild(引用计数+1)则,会被释放。例如CCArray,创建出来一般不会被addChild,此时则需要retain。而在不用的时候要记得releas。不然不会被释放。
另一种情况,创建出来的对象,被加入了autorelease。则不需要在手动进行内存管理。比如,对象已经被addchild,则将其removefromparent的时候,会判断引用计数是否为0,为0了就释放了。不需要在做release相关的操作。
28.在使用水波纹的时候,有时候会报错,
#if CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP
glDrawElements(GL_TRIANGLE_STRIP, (GLsizei) n*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(m_pIndices[0])) );
#else-------这里报错,原因是Ios使用的是VAO,解决方法见下面
glDrawElements(GL_TRIANGLES, (GLsizei) n*6,GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(m_pIndices[0])) );
#endif // CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP
#if CC_REBIND_INDICES_BUFFER
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
#endif
在cocos2dx/include/ccConfiger.h中 将
#ifndef CC_TEXTURE_ATLAS_USE_VAO
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC)
#define CC_TEXTURE_ATLAS_USE_VAO1-------->改成0
#else
/* Some Windows display adapter driver cannot support VAO. */
/* Some android devices cannot support VAO very well, so we disable it by default for android platform. */
/* Blackberry also doesn't support this feature. */
#define CC_TEXTURE_ATLAS_USE_VAO0
#endif
#endif
28. 在xcode编译项目的时候报错
ld: -pie can only be used when targeting iOS 4.2 or later
clang: error: linker command failed with exit code 1 (use -v to see invocation)
是因为选择的ios版本过低,点击项目->Deployment Info,将Deployment Target改成4.3以上
29.使用STL容器时,尽量不要把同一个元素加入两个容器中,这样当想要删除这个元素的时候,需要在两个容器中分别删除,如果经常需要插入删除,则用链表比用向量好。
30. 今天遇到一个奇葩的问题,在ios真机可以运行的游戏到了安卓下面就闪退,这里总结下闪退的原因:1.掉帧严重 2.死循环 3.plist加密(我遇到的就是这个原因)
31.回调函数的使用
static CCCallFunc * create(CCObject* pSelectorTarget,SEL_CallFunc selector);
参数二:是函数名,参数一:是函数所属的类,所以一般使用this
如果需要在回调函数中对一个不属于此函数的局部变量进行操作,则可以使用带参数的回调函数
static CCCallFuncND * create(CCObject* pSelectorTarget,SEL_CallFuncND selector,void* d);
参数一、二与CCCallFunc的相同,参数三即为要附带的参数
FuncA(CCNode* pSender)
{
//这里的pSender就是传过来的局部变量
CCCallFuncND* callND_canbeHit =CCCallFuncND::create(this,callfuncND_selector(Tollgate11::callND_canbeHit), (void*)pSender);
}
32. node->boundingBox().containsPoint(pos); 要求pos和node是同一个父节点,在同一个坐标系下。
33. CCOrbitCamera中有7个参数,分别为旋转的时间,起始半径,半径差,起始z角,旋转z角差,起始x角,旋转x角差
起始z角如果写0,则认为是与屏幕平行的。旋转z角差,即z轴旋转角度(不是停止的角度)