Cocos2dx学习之SimpleGame

关于如何打开并成功运行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);

感觉终于算是搞懂了。
这个游戏也就此分析完毕。~~~~~~~~~~~~~~~~~~~~~~~



posted @ 2013-12-19 16:57  Mr轨迹  阅读(296)  评论(0编辑  收藏  举报