• Cocos2dx学习之SimpleGame


    关于如何打开并成功运行SimpleGame项目我就不讨论了。

    项目位置: D:cocos2d-x-2.2.1cocos2d-x-2.2.1samplesCppSimpleGame

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

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

    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);

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

    
    
    
    
  • 相关阅读:
    最短路小变形
    最短路
    bfs + 路径输出
    优先队列
    dijkstra
    重载运算符-operator
    最短路
    二分图匹配
    依赖型的关系建立
    string 类
  • 原文地址:https://www.cnblogs.com/vokie/p/3602081.html
Copyright © 2020-2023  润新知