您的位置:首页 > 移动开发 > Cocos引擎

Cocos2dx学习之SimpleGame

2013-12-19 16:57 141 查看
关于如何打开并成功运行SimpleGame项目我就不讨论了。

项目位置: D:\cocos2d-x-2.2.1\cocos2d-x-2.2.1\samples\Cpp\SimpleGame

这个游戏是忍者射击飞镖击中敌人的模式。

首先从程序的入口地址开始:

bool AppDelegate::applicationDidFinishLaunching() {
// 初始化导演类
CCDirector *pDirector = CCDirector::sharedDirector();
//设置OpenGLView渲染方式
pDirector->setOpenGLView(CCEGLView::sharedOpenGLView());
//获取屏幕尺寸CCSize
CCSize screenSize = CCEGLView::sharedOpenGLView()->getFrameSize();
CCSize designSize = CCSizeMake(480, 320);
std::vector<std::string> searchPaths;
//如果屏幕的高度大于320的话,采用的是hd,sd两个文件夹中的两套图片资源可以在Resources文件夹中查看
if (screenSize.height > 320)
{
searchPaths.push_back("hd");
searchPaths.push_back("sd");
pDirector->setContentScaleFactor(640.0f/designSize.height);
}
else
{
searchPaths.push_back("sd");
pDirector->setContentScaleFactor(320.0f/designSize.height);
}
//设置文件工具类的搜索路径
CCFileUtils::sharedFileUtils()->setSearchPaths(searchPaths);

#if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) || (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
CCEGLView::sharedOpenGLView()->setDesignResolutionSize(designSize.width, designSize.height, kResolutionShowAll);
#else
CCEGLView::sharedOpenGLView()->setDesignResolutionSize(designSize.width, designSize.height, kResolutionNoBorder);//游戏显示的适应屏幕的方式设定
#endif

// turn on display FPS
pDirector->setDisplayStats(true);

// set FPS. the default value is 1.0/60 if you don't call this
pDirector->setAnimationInterval(1.0 / 60);

// 创建游戏主场景
CCScene *pScene = HelloWorld::scene();

// 通过Director运行游戏主场景
pDirector->runWithScene(pScene);

return true;
}

CCSprite *player = CCSprite::create("Player.png", CCRectMake(0, 0, 27, 40) ); //创建主角玩家
//设置玩家的坐标在屏幕的左中部
player->setPosition( ccp(origin.x + player->getContentSize().width/2,
origin.y + visibleSize.height/2) );
this->addChild(player);
//添加schedule任务,每过一秒调用一次gameLogic函数(需要注意schedule_selector的使用)
this->schedule( schedule_selector(HelloWorld::gameLogic), 1.0 );
//设置触摸允许
this->setTouchEnabled(true);
//创建敌人的存储CCArray、创建飞镖CCArray来存储飞镖
_targets = new CCArray;
_projectiles = new CCArray;
//schedule没有interval时间的情况下,默认每帧都会调用updateGame函数
this->schedule( schedule_selector(HelloWorld::updateGame) );
//CocosDenshion下面的SimpleAudioEngine初始化,并playBackgroundMusic.
CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic("background-music-aac.wav", true);

上面代码主要需要注意如何正确的去使用schedule这个重要的函数。

CCSprite *target = CCSprite::create("Target.png", CCRectMake(0,0,27,40) );//创建敌人Sprite

//下面代码主要是用来控制敌人出现的Y轴的位置,需要有一个范围
CCSize winSize = CCDirector::sharedDirector()->getVisibleSize();
float minY = target->getContentSize().height/2;
float maxY = winSize.height -  target->getContentSize().height/2;
int rangeY = (int)(maxY - minY);
// srand( TimGetTicks() );
int actualY = ( rand() % rangeY ) + (int)minY;//生成本次敌人精灵的位置actualY

// 将敌人精灵刚好设定在右侧的屏幕边缘处
//而精灵的y值是由函数计算出来的actualY,具有随机性
target->setPosition(
ccp(winSize.width + (target->getContentSize().width/2),
CCDirector::sharedDirector()->getVisibleOrigin().y + actualY) );
this->addChild(target);

// 计算将敌人精灵从屏幕右侧移动到屏幕左侧外的时间,对时间的随机性选择actualDuration会使敌人精灵的移动速度有差异
int minDuration = (int)2.0;
int maxDuration = (int)4.0;
int rangeDuration = maxDuration - minDuration;
// srand( TimGetTicks() );
int actualDuration = ( rand() % rangeDuration ) + minDuration;
// 创建限时动作
//需要注意的是CCCallFuncN类的使用以及callfuncN_selector()的使用当精灵移动结束后需要执行的函数
CCFiniteTimeAction* actionMove = CCMoveTo::create( (float)actualDuration, ccp(0 - target->getContentSize().width/2, actualY) );CCFiniteTimeAction* actionMoveDone = CCCallFuncN::create( this, callfuncN_selector(HelloWorld::spriteMoveFinished));target->runAction( CCSequence::create(actionMove, actionMoveDone, NULL) ); //合并成一个动作序列,依次执行
target->setTag(1);//设置敌人精灵的标签为1
_targets->addObject(target);// 将产生的敌人假如敌人精灵的数组中,统一管理


上面需要注意的是关于动作执行完以后的回调函数的使用,也就是CCCallFuncN:create(this,callfuncN_selector(回调函数名))

其次,就是CCSequence的使用了,需要注意的是CCSequence::create(动作1,动作2,动作3.....,后面必须有NULL)

否则会报错

CCArray中的方法,addObject()添加一个obj对象到数组中。

void HelloWorld::spriteMoveFinished(CCNode* sender) //精灵移动结束的回调函数
{
CCSprite *sprite = (CCSprite *)sender;//将CCNode强转
this->removeChild(sprite, true);//精灵移动结束需要从Layer中移除

if (sprite->getTag() == 1)  // 根据精灵的Tag标签来区分当前的精灵是敌人
{
_targets->removeObject(sprite); //使用CCArray中的removeObject方法,来将已经移动结束的敌人从数组中移除
//由于敌人已经移动结束了,说明敌人精灵已经移动到屏幕的左侧,按游戏规则,游戏结束
GameOverScene *gameOverScene = GameOverScene::create();
gameOverScene->getLayer()->getLabel()->setString("You Lose :[");
CCDirector::sharedDirector()->replaceScene(gameOverScene);  //跳转到游戏结束场景

}
else if (sprite->getTag() == 2) // 当前的精灵是飞镖的话,飞镖移动结束后的回调,需要处理的是将飞镖从CCArray中移除
{
_projectiles->removeObject(sprite);
}
}


void HelloWorld::registerWithTouchDispatcher()
{
//注册添加屏幕监听,并且优先级设置为0
CCDirector::sharedDirector()->getTouchDispatcher()->addStandardDelegate(this,0);
}


void HelloWorld::updateGame(float dt)
{
CCArray *projectilesToDelete = new CCArray;
CCObject* it = NULL;
CCObject* jt = NULL;

// for (it = _projectiles->begin(); it != _projectiles->end(); it++)
CCARRAY_FOREACH(_projectiles, it) //通过it迭代访问_projectiles数组
{
CCSprite *projectile = dynamic_cast<CCSprite*>(it);//强转成精灵
CCRect projectileRect = CCRectMake(   //获取CCRect包围
projectile->getPosition().x - (projectile->getContentSize().width/2),
projectile->getPosition().y - (projectile->getContentSize().height/2),
projectile->getContentSize().width,
projectile->getContentSize().height);

CCArray* targetsToDelete =new CCArray;

// for (jt = _targets->begin(); jt != _targets->end(); jt++)
CCARRAY_FOREACH(_targets, jt) //使用jt迭代访问_targets敌人精灵数组
{
CCSprite *target = dynamic_cast<CCSprite*>(jt);
CCRect targetRect = CCRectMake(  //获取CCRect包围
target->getPosition().x - (target->getContentSize().width/2),
target->getPosition().y - (target->getContentSize().height/2),
target->getContentSize().width,
target->getContentSize().height);

// if (CCRect::CCRectIntersectsRect(projectileRect, targetRect))
if (projectileRect.intersectsRect(targetRect)) //通过CCRect包围来检测是否发生了碰撞,注意函数intersectsRect的使用
{
targetsToDelete->addObject(target);//发生了碰撞,即飞镖击中了敌人,将敌人精灵放入待删除的数组中
}
}

// for (jt = targetsToDelete->begin(); jt != targetsToDelete->end(); jt++)
CCARRAY_FOREACH(targetsToDelete, jt)  //使用jt迭代访问targetsToDelete敌人精灵数组

{
CCSprite *target = dynamic_cast<CCSprite*>(jt);
_targets->removeObject(target); //从_targets中移除敌人精灵
this->removeChild(target, true);  //并从layer中移除敌人精灵
//击中的敌人数+1
_projectilesDestroyed++;
if (_projectilesDestroyed >= 5)//如果击中的敌人数目大于等于5个,玩家胜利
{
GameOverScene *gameOverScene = GameOverScene::create();
gameOverScene->getLayer()->getLabel()->setString("You Win!");
CCDirector::sharedDirector()->replaceScene(gameOverScene);
}
}

if (targetsToDelete->count() > 0) //如果击中了的敌人,就将飞镖精灵放入待删除的数组中
{
projectilesToDelete->addObject(projectile);
}
targetsToDelete->release();//释放targetsToDelete数组资源
}

// for (it = projectilesToDelete->begin(); it != projectilesToDelete->end(); it++)
CCARRAY_FOREACH(projectilesToDelete, it) //通过迭代的方式移除projectile并清理资源
{
CCSprite* projectile = dynamic_cast<CCSprite*>(it);
_projectiles->removeObject(projectile);
this->removeChild(projectile, true);
}
projectilesToDelete->release();
}


在上面的代码中,我们需要知道这样一个事实,那就是: 对于飞镖来说,移除分为2种情况,第一种是没有击中敌人精灵的移除方式,第二种是击中敌人精灵的移除方式

但是他们都做了同样的操作,具体是从飞镖数组中移除,从Layer层中移除。对于敌人精灵同样如此。

void HelloWorld::ccTouchesEnded(CCSet* touches, CCEvent* event)  //触摸屏幕弹起时调用
{
// 获取触摸点(需要注意如何从CCSet中获取object,以及如何使用CCTouch对象来获取触摸点坐标)
CCTouch* touch = (CCTouch*)( touches->anyObject() ); //anyObject方法会返回第一个obj,如果是空的话,返回null
CCPoint location = touch->getLocation();

CCLog("++++++++after  x:%f, y:%f", location.x, location.y);

// Set up initial location of projectile
CCSize winSize = CCDirector::sharedDirector()->getVisibleSize();
CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
CCSprite *projectile = CCSprite::create("Projectile.png", CCRectMake(0, 0, 20, 20));//创建飞镖精灵
projectile->setPosition( ccp(origin.x+20, origin.y+winSize.height/2) ); //设置飞镖的位置

// 计算x,y坐标差值
float offX = location.x - projectile->getPosition().x;
float offY = location.y - projectile->getPosition().y;

// 禁止飞镖向后射击以及向上下设计
if (offX <= 0) return;

// Ok to add now - we've double checked position
this->addChild(projectile);

// Determine where we wish to shoot the projectile to
float realX = origin.x+winSize.width + (projectile->getContentSize().width/2);//计算飞镖飞出的终点x值
float ratio = offY / offX;  //其实就是三角函数tan值
float realY = (realX * ratio) + projectile->getPosition().y;//根据realX的值计算出对应的realY值
CCPoint realDest = ccp(realX, realY);//飞镖即将飞出屏幕的真实坐标

// 计算飞镖在屏幕中飞行的距离
float offRealX = realX - projectile->getPosition().x;
float offRealY = realY - projectile->getPosition().y;
float length = sqrtf((offRealX * offRealX) + (offRealY*offRealY));//飞行距离
float velocity = 480/1; // 480pixels/1sec
float realMoveDuration = length/velocity;//计算飞行的时间

//上面计算关于飞行距离,飞行时间主要是让飞镖可以匀速飞行,看起来不会很奇怪,同样注意CCSequence和CCCallFuncN及callfuncN_selector的使用
projectile->runAction( CCSequence::create(
CCMoveTo::create(realMoveDuration, realDest),
CCCallFuncN::create(this,
callfuncN_selector(HelloWorld::spriteMoveFinished)),
NULL) );

// 设置精灵标签,将精灵加入飞镖精灵数组中,同意管理,访问
projectile->setTag(2);
_projectiles->addObject(projectile);
//声音的控制
CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect("pew-pew-lei.wav");
}


这里我模糊的地方在于

gameOverScene->getLayer()->getLabel()->setString("You Win!");

参考他人的作答:



上面给我一些启示,继续查看源代码中的CC_SYNTHESIZE_READONLY这个宏定义

如下:

/** CC_SYNTHESIZE_READONLY is used to declare a protected variable.
We can use getter to read the variable.
@param varType : the type of variable.
@param varName : variable name.
@param funName : "get + funName" is the name of the getter.
@warning : The getter is a public inline function.
The variables and methods declared after CC_SYNTHESIZE_READONLY are all public.
If you need protected or private, please declare.
*/
#define CC_SYNTHESIZE_READONLY(varType, varName, funName)\
protected: varType varName;\   //创建protected属性的varType类型的varName变量
public: virtual varType get##funName(void) const { return varName; }   //创建public virtual varType类型的get方法,返回varName变量


再回过头来看看GameOverScene.h中的这2句代码:

CC_SYNTHESIZE_READONLY(GameOverLayer*, _layer, Layer);

CC_SYNTHESIZE_READONLY(cocos2d::CCLabelTTF*, _label, Label);

感觉终于算是搞懂了。

这个游戏也就此分析完毕。~~~~~~~~~~~~~~~~~~~~~~~



                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: