cocos2d-x学习札记(17)-简单游戏实例1-双枪手
cocos2d-x学习笔记(17)--简单游戏实例1--双枪手
cocos2d-x学习笔记(17)--简单游戏实例--双枪手
从最初的学习笔记到现在,一直在讲的都是一些基础操作,没有实际去做出一个游戏,有些人可能会疑问之前学得实不实用。为了更好地运用和巩固前面的知识,现在开始,我开始开发一个很简单的游戏--双枪手,是游戏总得有个名字的吧,可能起得不怎么样,大家见谅见谅!
这次游戏我会用到cocos2d-x下面一些知识(前面都学过的):
(1)地图
(2)粒子系统
(3)场景的切换
(4)精灵
(5)按钮
(6)回调函数
(7)动作
(8)精灵表单
step1:创建cocos2d-win32工程,并命名为myGame01;(以后还会陆续写一些游戏)
step2:这个游戏主要分成两大部分,第一部分是游戏的菜单部分,第二部分是游戏运行场景部分。我就先说第二部分吧!因为第一部分菜单按钮中的“开始按钮”会调用第二部分场景,所以从第二部分开始讲起,就不会牵涉到第一部分的代码;两者分开来写,思路会更清晰些,也方便日后代码的修改。
示意图:
由于About Game跳转到的场景比较简单这里不算进第二部分场景,可以直接忽略;
在HelloWorldScene.h中添加场景2的类:
class GameLayer:public CCLayerColor { protected: CCMutableArray<CCSprite*>* m_targets;//用于记录移动的靶子 CCMutableArray<CCSprite*>* m_fires;//用于记录发射的子弹 CCTMXTiledMap* m_map;//tmx地图 CCSprite *m_player;//发射炮弹的主角 public: GameLayer(); ~GameLayer(); void addTarget();//添加移动的靶子 void spriteMoveFinished(CCNode* pSender);//处理那些移动到屏幕之外的靶子 void gameLogic(ccTime dt); void ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent);//响应用户在屏幕上的点击事件 void update(ccTime dt);//处理每一帧画面中的对象 void moveMap(ccTime dt);//移动地图 void menuCloseCallback(CCObject* pSender);//退出游戏 };
在HelloWorldScene.cpp中添加响应成员函数:
/************************************************************************/ /* GameLayer */ /************************************************************************/ GameLayer::GameLayer() { setIsTouchEnabled(true); increaseX = 0;//初始化 m_targets = new CCMutableArray<CCSprite*>(NULL); m_fires = new CCMutableArray<CCSprite*>(NULL); CCSize size = CCDirector::sharedDirector()->getWinSize(); //添加关闭按钮 CCMenuItemImage *pCloseItem = CCMenuItemImage::itemFromNormalImage( "CloseNormal.png", "CloseSelected.png", this, menu_selector(GameLayer::menuCloseCallback)); pCloseItem->setPosition(ccp(CCDirector::sharedDirector()->getWinSize().width - 20, 20)); CCMenu* pMenu = CCMenu::menuWithItems(pCloseItem, NULL); pMenu->setPosition(CCPointZero); addChild(pMenu, 10); //添加player精灵 m_player = CCSprite::spriteWithFile("player.png", CCRectMake(0, 0, 40, 40)); m_player->retain(); //addChild(m_player, 1); //m_player->setPosition(ccp(player->getContentSize().width / 2, size.height / 2)); //添加地图 m_map = CCTMXTiledMap::tiledMapWithTMXFile("map.tmx"); m_map->retain(); m_map->setPosition(CCPointZero); addChild(m_map, 3); CCTMXObjectGroup* objGroup = m_map->objectGroupNamed("object"); CCStringToStringDictionary* startPoint = objGroup->objectNamed("player"); float posX = startPoint->objectForKey("x")->toFloat(); float posY = startPoint->objectForKey("y")->toFloat(); CCTexture2D* playerTexture = CCTextureCache::sharedTextureCache()->addImage("Player.png"); m_player->setPosition(ccp(posX, posY)); m_map->addChild(m_player); this->schedule(schedule_selector(GameLayer::gameLogic), 1.0); this->schedule(schedule_selector(GameLayer::update)); this->schedule(schedule_selector(GameLayer::moveMap), 1.0); } GameLayer::~GameLayer() { if(m_targets) { m_targets->release(); m_targets = NULL; } if(m_fires) { m_fires->release(); m_fires = NULL; } } void GameLayer::menuCloseCallback(CCObject* pSender) { //跳转到游戏开始处 CCScene* scene = new CCScene(); CCLayer* layer = new GameMenuLayer(); scene->addChild(layer); CCDirector::sharedDirector()->replaceScene(scene); scene->release(); layer->release(); } void GameLayer::moveMap(ccTime dt) { if(increaseX >= 2700)//地图移动到尽头,不能再移动 return; increaseX += 50; CCActionInterval* move = CCMoveBy::actionWithDuration(1, ccp(-50,0)); m_map->runAction(move); m_player->runAction(move->reverse()); } void GameLayer::addTarget() { CCSize size = CCDirector::sharedDirector()->getWinSize(); //添加Target精灵 CCSprite* target = CCSprite::spriteWithFile("Target.png", CCRectMake(0, 0, 27, 40)); addChild(target, 10); target->setPosition(ccp(27, 40)); //target的位置 float minY = target->getContentSize().height/2; float maxY = size.height - target->getContentSize().height/2; float rangeY = maxY - minY; float actualY = (rand() % int(rangeY)) + minY; target->setPosition(ccp( size.width + (target->getContentSize().width / 2), actualY )); //target的移动速度 int minDuration = 2; int maxDuration = 4; int rangeDuration = maxDuration - minDuration; int actualDuration = (rand() % rangeDuration) + minDuration; //target的动作 CCFiniteTimeAction* actionMove = CCMoveTo::actionWithDuration((float)actualDuration, ccp(0 - target->getContentSize().width / 2, actualY)); CCFiniteTimeAction* actionMoveDone = CCCallFuncN::actionWithTarget(this, callfuncN_selector(GameLayer::spriteMoveFinished)); target->runAction(CCSequence::actions (actionMove, actionMoveDone, NULL)); target->setTag(1); m_targets->addObject(target); } void GameLayer::spriteMoveFinished(CCNode* pSender) { CCSize size = CCDirector::sharedDirector()->getWinSize(); CCSprite* sprite = (CCSprite* )pSender; this->removeChild(sprite, true); if (sprite->getTag() == 1) { m_targets->removeObject(sprite); } else if (sprite->getTag() == 2) { m_fires->removeObject(sprite); } } void GameLayer::gameLogic(ccTime dt) { this->addTarget(); } void GameLayer::ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent) { CCTouch* touch = (CCTouch*)(pTouches->anyObject()); CCPoint location = touch->locationInView(touch->view()); location = CCDirector::sharedDirector()->convertToGL(location); CCSize size = CCDirector::sharedDirector()->getWinSize(); //注意:由于游戏中使用两发子弹,所有要添加两个精灵 CCSprite* fire = CCSprite::spriteWithFile("fire.png", CCRectMake(0,0,10,10)); fire->setPosition(ccp(10, size.height / 2)); CCSprite* fire2 = CCSprite::spriteWithFile("fire.png", CCRectMake(0,0,10,10)); fire2->setPosition(ccp(10, size.height / 2)); float offX = location.x - fire->getPosition().x; float offY = location.y - fire->getPosition().y; if(offX <= 0)return ; //粒子效果 CCParticleSystem* particle = CCParticleSun::node(); fire->addChild(particle); particle->setStartSize(5); particle->setTexture(CCTextureCache::sharedTextureCache()->addImage("pow.png")); particle->setPosition(ccp(5, 5)); /* 注意:此处(5, 5)是particle想对于fire精灵的位置,即particle在fire中的坐标位置,由于fire图标大小事10x10, 那么particle在屏幕中的位置就是和fire重合,而不是在靠近(0, 0)的位置 */ //粒子效果2 CCParticleSystem* particle2 = CCParticleSun::node(); fire2->addChild(particle2); particle2->setStartSize(5); particle2->setTexture(CCTextureCache::sharedTextureCache()->addImage("pow.png")); particle2->setPosition(ccp(5, 5)); /* 注意:此处(5, 5)是particle想对于fire精灵的位置,即particle在fire中的坐标位置,由于fire图标大小事10x10, 那么particle在屏幕中的位置就是和fire重合,而不是在靠近(0, 0)的位置 */ addChild(fire, 10); addChild(fire2, 10); float realX = size.width + (fire->getContentSize().width / 2); float ratio = (float)offY / (float)offX; float realY = (realX* ratio) + fire->getPosition().y; CCPoint realDest = ccp(realX, realY); float offRealX = realX - fire->getPosition().x; float offRealY = realY - fire->getPosition().y; float length = sqrtf((offRealX * offRealX) + (offRealY*offRealY)); float velocity = 480/1; // 480pixels/1sec float realMoveDuration = length/velocity; fire->runAction( CCSequence::actions( CCMoveTo::actionWithDuration(realMoveDuration, ccp(realDest.x, realDest.y - 30)), CCCallFuncN::actionWithTarget(this, callfuncN_selector(GameLayer::spriteMoveFinished)), NULL) ); fire2->runAction( CCSequence::actions( CCMoveTo::actionWithDuration(realMoveDuration, ccp(realDest.x , realDest.y + 30)), CCCallFuncN::actionWithTarget(this, callfuncN_selector(GameLayer::spriteMoveFinished)), NULL) ); fire->setTag(2); m_fires->addObject(fire); fire2->setTag(2); m_fires->addObject(fire2); CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect("gun.wav"); } void GameLayer::update(ccTime dt) { CCMutableArray<CCSprite*>*projectilesToDelete = new CCMutableArray<CCSprite*>; CCMutableArray<CCSprite*>::CCMutableArrayIterator it, jt ; for(it = m_fires->begin(); it != m_fires->end(); it++) { CCSprite* fire = *it; CCRect fireRect = CCRectMake( fire->getPosition().x - (fire->getContentSize().width / 2), fire->getPosition().y - (fire->getContentSize().height / 2), fire->getContentSize().width, fire->getContentSize().height); CCMutableArray<CCSprite*>* targetsToDelete = new CCMutableArray<CCSprite*>; for(jt = m_targets->begin(); jt != m_targets->end(); jt++) { CCSprite* target = *jt; CCRect targetRect = CCRectMake( target->getPosition().x - (target->getContentSize().width / 2), target->getPosition().y - (target->getContentSize().height / 2), target->getContentSize().width, target->getContentSize().height); if(CCRect::CCRectIntersectsRect(fireRect, targetRect)) { targetsToDelete->addObject(target); CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect("bomb.wav"); CCSprite* pow = CCSprite::spriteWithFile("pow.png"); CCParticleSystem* particle = CCParticleSmoke::node(); pow->addChild(particle, 2); particle->setTexture(CCTextureCache::sharedTextureCache()->addImage("rain.png")); particle->setPosition(ccp(20, 20)); particle->setDuration(1); particle->setStartSize(1); particle->setEndSize(1); pow->setTag(3); addChild(pow, 10); pow->setPosition(ccp(target->getPosition().x, target->getPosition().y)); CCAction* action = CCFadeOut::actionWithDuration(1); pow->runAction(action); } } for(jt = targetsToDelete->begin(); jt != targetsToDelete->end(); jt++) { CCSprite* target = *jt; m_targets->removeObject(target); removeChild(target, true); } if(targetsToDelete->count() > 0) { projectilesToDelete->addObject(fire); } targetsToDelete->release(); } for(it = projectilesToDelete->begin(); it != projectilesToDelete->end(); it++) { CCSprite* fire = * it; m_fires->removeObject(fire); removeChild(fire, true); } projectilesToDelete->release(); }
step3:然后就是场景1中的类:
class GameMenuLayer:public CCLayerColor { public: GameMenuLayer(); ~GameMenuLayer(); void onEnter(); void startGameCallback(CCObject* pSender); void aboutGameCallback(CCObject* pSender); void exitGameCallback(CCObject* pSender); void backGameCallback(CCObject* pSender); };
在HelloWorldScene.cpp中添加成员函数:
/************************************************************************/ /* GameMenuLayer */ /************************************************************************/ GameMenuLayer::GameMenuLayer() { CCSize size = CCDirector::sharedDirector()->getWinSize(); //添加三个按钮 CCLabelTTF* pStartLabel = CCLabelTTF::labelWithString("Start Game", "Arial", 30); CCLabelTTF* pExitLabel = CCLabelTTF::labelWithString("Exit Game", "Arial", 30); CCLabelTTF* pAboutLabel = CCLabelTTF::labelWithString("About Game", "Arial", 30); CCMenuItemLabel* pStartItem = CCMenuItemLabel::itemWithLabel(pStartLabel, this, menu_selector(GameMenuLayer::startGameCallback)); CCMenuItemLabel* pExitItem = CCMenuItemLabel::itemWithLabel(pExitLabel, this, menu_selector(GameMenuLayer::exitGameCallback)); CCMenuItemLabel* pAboutItem = CCMenuItemLabel::itemWithLabel(pAboutLabel, this, menu_selector(GameMenuLayer::aboutGameCallback)); CCMenu* pStartMenu = CCMenu::menuWithItems(pStartItem, NULL); CCMenu* pExitMenu = CCMenu::menuWithItems(pExitItem, NULL); CCMenu* pAboutMenu = CCMenu::menuWithItems(pAboutItem, NULL); pStartItem->setPosition(ccp(size.width / 2, size.height / 2 + 50)); pExitItem->setPosition(ccp(size.width / 2, size.height / 2 - 50 )); pAboutItem->setPosition(ccp(size.width / 2, size.height / 2 )); pStartMenu->setPosition(CCPointZero); pExitMenu->setPosition(CCPointZero); pAboutMenu->setPosition(CCPointZero); addChild(pStartMenu, 2); addChild(pExitMenu, 2); addChild(pAboutMenu, 2); //添加背景 CCSprite* bk = CCSprite::spriteWithFile("background.png"); addChild(bk, 0); bk->setPosition(ccp(size.width / 2, size.height / 2)); //添加动画 CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("sunshine.plist"); CCSpriteBatchNode* spritesheet = CCSpriteBatchNode::batchNodeWithFile("sunshine.png"); addChild(spritesheet, 1); CCMutableArray<CCSpriteFrame*>* animFrames = new CCMutableArray<CCSpriteFrame*>; char str[20]; for (int i=1; i<=6; i++) { sprintf(str, "sun%d.png", i); animFrames->addObject(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(str)); } CCAnimation* animation = CCAnimation::animationWithFrames(animFrames, 0.1f); CCSprite* sun = CCSprite::spriteWithSpriteFrameName("sun1.png"); sun->setPosition(ccp(0, 0)); CCActionInterval* action = CCRepeatForever::actionWithAction(CCAnimate::actionWithAnimation(animation, false)); sun->runAction(action); spritesheet->addChild(sun); //太阳运动轨迹 CCActionInterval* move1 = CCMoveBy::actionWithDuration(3,ccp(size.width / 4, size.height / 4)); CCActionInterval* move1_back = move1->reverse(); CCActionInterval* move2 = CCMoveBy::actionWithDuration(3,ccp(size.width / 4, -size.height / 4)); CCActionInterval* move2_back = move2->reverse(); spritesheet->runAction(CCRepeatForever::actionWithAction((CCActionInterval*)(CCSequence::actions(move1, move1,move2,move2, move2_back,move2_back,move1_back,move1_back, NULL)))); } void GameMenuLayer::aboutGameCallback(CCObject* pSender) { CCSize size = CCDirector::sharedDirector()->getWinSize(); //显示关于的内容 CCLabelTTF* label = CCLabelTTF::labelWithString("Made by wenbanana \n Email:294299195@qq.com", "Arial", 30); label->setPosition(ccp(size.width / 2, size.height / 2)); CCScene* s = CCScene::node(); CCLayer* layer = CCLayer::node(); layer->addChild(label, 1); //返回按钮 CCLabelTTF* pBackLabel = CCLabelTTF::labelWithString("Back", "Arial", 30); CCMenuItemLabel* pBackItem = CCMenuItemLabel::itemWithLabel(pBackLabel, this, menu_selector(GameMenuLayer::backGameCallback)); CCMenu* pBackMenu = CCMenu::menuWithItems(pBackItem, NULL); pBackItem->setPosition(ccp(size.width - 50, 50)); pBackMenu->setPosition(CCPointZero); layer->addChild(pBackMenu, 1); s->addChild(layer); CCDirector::sharedDirector()->replaceScene(s); //s->release(); //layer->release(); } void GameMenuLayer::startGameCallback(CCObject* pSender) { //跳转到游戏中 CCScene* scene = new CCScene(); CCLayer* layer = new GameLayer(); scene->addChild(layer); CCDirector::sharedDirector()->replaceScene(scene); scene->release(); layer->release(); } void GameMenuLayer::exitGameCallback(CCObject* pSender) { CCDirector::sharedDirector()->end(); } void GameMenuLayer::backGameCallback(CCObject* pSender) { CCSize size= CCDirector::sharedDirector()->getWinSize(); //跳转到游戏中 CCScene* scene = new CCScene(); CCLayer* layer = new GameMenuLayer(); scene->addChild(layer); CCDirector::sharedDirector()->replaceScene(scene); scene->release(); layer->release(); } GameMenuLayer::~GameMenuLayer() { } void GameMenuLayer::onEnter() { CCLayer::onEnter(); }
step4:最后运行程序,效果如下:
开始界面
游戏运行界面