Cocos2dx入門小遊戲---Runner教程
本人初學cocos2dx,網上找了個例子,寫一遍,自己領悟一翻,現在把自己的理解配上注釋,
一步步深入理解這個小程序,希望可以幫助初始入門的朋友們。
首先建立一個Entity實體類,以便怪物類和玩家類可以繼承: #include "cocos2d.h" /** * @brief 精靈實體類 * * 綁定精靈、獲取精靈 */ class Entity : public cocos2d::CCNode { public: /** @{ * @name 構造函數、析構函數、 */ /// 構造函數 Entity(); /// 析構函數 virtual ~Entity(); /** @} */ /** @{ * @name getter、setter方法 */ /** * @brief 獲取精靈 */ cocos2d::CCSprite *getSprite(); /** * @brief 綁定精靈 */ void setSprite(cocos2d::CCSprite *pSprite); /** @} */ private: cocos2d::CCSprite *m_pSprite; ///< 精靈對象 };
#include "Entity.h" USING_NS_CC; Entity::Entity() : CCNode(), m_pSprite(NULL) { return; } Entity::~Entity() { // 使用自帶的宏,方便安全 CC_SAFE_RELEASE_NULL(m_pSprite); return; } CCSprite *Entity::getSprite() { return m_pSprite; } void Entity::setSprite(cocos2d::CCSprite *pSprite) { this->m_pSprite = pSprite; CC_SAFE_RETAIN(this->m_pSprite); // 防止被釋放 this->addChild(m_pSprite); return; }
接下來,先創建一個有特效效果的文本顯示類:
/** * @brief 文字飄動特效效果類 */ class FlowTextLabel : public cocos2d::CCNode { public: /** * 構造函數 */ FlowTextLabel(); /** * 析構函數 */ virtual ~FlowTextLabel(); /** * 初始化函數 */ virtual bool init(); /** * 創建FlowTextLabel對象,自動釋放對象 */ CREATE_FUNC(FlowTextLabel); /** * @brief 顯示文本 * @param pText 文本 * @param pos 顯示的位置 */ void showText(const char *pText, cocos2d::CCPoint pos); /** * @brief 隱藏文本 */ void hideText(); private: cocos2d::CCLabelTTF *m_pText; ///< 文本文字 };
#include "FlowTextLabel.h" USING_NS_CC; FlowTextLabel::FlowTextLabel() : m_pText(NULL) { CCNode::CCNode(); return; } FlowTextLabel::~FlowTextLabel() { CC_SAFE_RELEASE_NULL(m_pText); CCNode::~CCNode(); return; } bool FlowTextLabel::init() { bool bRet = false; do { CC_BREAK_IF(!CCNode::init()); m_pText = CCLabelTTF::create("", "Arial", 30); CC_BREAK_IF(!m_pText); m_pText->setColor(ccc3(255, 0, 0)); m_pText->setVisible(false); this->addChild(m_pText); bRet = true; } while (0); return bRet; } void FlowTextLabel::showText(const char *pText, CCPoint pos) { m_pText->setString(pText); m_pText->setPosition(pos); m_pText->setVisible(true); m_pText->setAnchorPoint(ccp(1, 0)); // 先放大後縮小效果 CCScaleTo *pScaleLarge = CCScaleTo::create(0.3f, 2.5f, 2.5f); CCScaleTo *pScaleSmall = CCScaleTo::create(0.4f, 0.5f, 0.5f); // 動作回調 CCCallFunc *pCallFunc = CCCallFunc::create(this, callfunc_selector(FlowTextLabel::hideText)); CCActionInterval *pActions = CCSequence::create(pScaleLarge, pScaleSmall, pCallFunc, NULL); this->runAction(pActions); return; } void FlowTextLabel::hideText() { m_pText->setVisible(false); // 從父節點移除,並移除節點的動作和回調函數 m_pText->removeFromParentAndCleanup(true); return; }
接下來,創建一個繼承於Entity的類,也就是玩家類:
#include "cocos2d.h" #include "Entity.h" /** * @brief 玩家類 * 玩家動作:跳、受到攻擊攻擊傷害、吃掉金幣 */ class Player : public Entity { public: /// /// 構造函數、析構函數、初始化函數、創建玩家函數 Player(); virtual ~Player(); virtual bool init(); CREATE_FUNC(Player); /** * @brief 玩家跳起 */ void jumpBegin(); /** * @brief 玩家跳完畢 */ void jumpEnd(); /** * @brief 玩家與怪物碰撞(玩家受到攻擊傷害) */ void hit(); /** * @brief 獲取得到的金幣 */ int getMoney(); /** * @brief 獲取碰撞範圍 */ cocos2d::CCRect getBoundingBox(); /** * @brief 恢複數據到原始狀態 */ void resetDataToNormal(); /** * @brief 動作結束 */ void actionEnd(); private: bool m_bIsJumping; ///< 玩家是否正處於跳起狀態 int m_nMoney; ///< 玩家獲取的金幣 };
#include "Player.h" #include "FlowTextLabel.h" #include "SimpleAudioEngine.h" USING_NS_CC; using namespace CocosDenshion; Player::Player() : Entity(), m_bIsJumping(false), m_nMoney(0) { return; } Player::~Player() { return; } bool Player::init() { return true; } void Player::jumpBegin() { // 如果沒有玩家或者如果玩家還在跳躍中,則不重複跳躍 if (!getSprite() || m_bIsJumping) { return; } m_bIsJumping = true; // 創建動作 // 1.秒內跳起200高度,隻跳一次 CCJumpBy *pJumpBy = CCJumpBy::create(1.0f, ccp(50, 0), 200, 1); // 這種也算是一種動作吧 CCCallFunc *pCallFunc = CCCallFunc::create(this, callfunc_selector(Player::jumpEnd)); // 瞬時動作 CCActionInterval *pJumpActions = CCSequence::create(pJumpBy, pCallFunc, NULL); this->runAction(pJumpActions); return; } void Player::jumpEnd() { m_bIsJumping = false; return; } void Player::hit() { do { CC_BREAK_IF(!getSprite()); // 獲取金幣特效提示 FlowTextLabel *pFlowLabel = FlowTextLabel::create(); CC_BREAK_IF(!pFlowLabel); this->addChild(pFlowLabel); pFlowLabel->showText("+15", getSprite()->getPosition()); m_nMoney += 15; // 創建4種動作對象 // 0.1秒內,x軸方向向左移動20,y方向不變 CCMoveBy *pBackMoveBy = CCMoveBy::create(0.1, ccp(-20, 0)); // 0.1秒內,x軸方向右向移動20,y方向不變 CCMoveBy *pForwardMoveBy = CCMoveBy::create(0.1, ccp(20, 0)); // 0.1秒內,x方向逆時間旋轉5度,y方向順時間旋轉0度 CCRotateBy *pBackRotateBy = CCRotateBy::create(0.1, -5.0, 0); // 0.1秒內,x方向順時針旋轉5度,y方向不變 CCRotateBy *pForwardRotateBy = CCRotateBy::create(0.1, 5.0, 0); // 創建並行動作 // 過程為:玩家0.1秒內先後退內20寬度,並0.1秒內逆時針旋轉5度 // 再0.1秒內向前移動20寬度,並0.1秒內順時針旋轉5度,回複到原來的狀態 CCSpawn *pBackActions = CCSpawn::create(pBackMoveBy, pBackRotateBy, NULL); CCSpawn *pForwardActions = CCSpawn::create(pForwardMoveBy, pForwardRotateBy, NULL); // 動作執行完畢時的回調 CCCallFunc *pCallFunc = CCCallFunc::create(this, callfunc_selector(Player::actionEnd)); CCActionInterval *pActions = CCSequence::create(pBackActions, pForwardActions, pCallFunc, NULL); this->runAction(pActions); SimpleAudioEngine::sharedEngine()->playEffect("hitEffect.wav"); } while (0); return; } void Player::resetDataToNormal() { m_bIsJumping = false; this->setPosition(ccp(200, 500 / 4)); this->setScale(1.0f); this->setRotation(0); return; } int Player::getMoney() { return m_nMoney; } CCRect Player::getBoundingBox() { CCRect rect = CCRectZero; do { CC_BREAK_IF(!getSprite()); // 獲取玩家可視範圍大小,用於檢測玩家的碰撞範圍 CCSize spriteSize = getSprite()->getContentSize(); // 獲取玩家的中心位置 CCPoint entityPos = this->getPosition(); // 獲取玩家左下角x坐標 float leftBottomX = entityPos.x - spriteSize.width / 2; // 獲取玩家左下角y坐標 float leftBottomY = entityPos.y - spriteSize.height / 2; rect = CCRectMake(leftBottomX, leftBottomY, spriteSize.width, spriteSize.height); } while (0); return rect; } void Player::actionEnd() { this->setScale(1.0f); this->setRotation(0); return; }
接下來,創建怪物類:
#include "cocos2d.h" #include "Entity.h" #include "Player.h" /** * @brief 怪物類(也就是金幣類),繼承於實體類 */ class Monster : public Entity { public: /// 構造函數 Monster(); /// 析構函數 virtual ~Monster(); /// 初始化函數 virtual bool init(); /// 創建怪物對象,自動釋放 CREATE_FUNC(Monster); /** * @brief 顯示怪物(金幣) */ void show(); /** * @brief 隱藏怪物(金幣) */ void hide(); /** * @brief 重置怪物數據(金幣) */ void reset(); /** * @brief 怪物是否活著(金幣) */ bool isAlive(); /** * @brief 怪物是否與指定玩家對象相碰撞(金幣) * @param pPlayer 玩家對象 */ bool isCollideWithPlayer(Player *pPlayer); private: bool m_bIsAlive; ///< 怪物是否還活著 };
#include "Monster.h" USING_NS_CC; Monster::Monster() : Entity(), m_bIsAlive(false) { return; } Monster::~Monster() { return; } bool Monster::init() { bool bRet = false; do { // bRet = true; } while (0); return bRet; } void Monster::show() { if (NULL != getSprite()) { this->setVisible(true); // 標記為活動狀態 m_bIsAlive = true; } return; } void Monster::hide() { do { CC_BREAK_IF(!getSprite()); this->setVisible(false); reset(); m_bIsAlive = false; } while (0); return; } void Monster::reset() { do { CC_BREAK_IF(!getSprite()); // 範圍在800 ~ 2000 this->setPosition(ccp(800 + CCRANDOM_0_1() * 2000, 200 - CCRANDOM_0_1() * 100)); } while (0); return; } bool Monster::isAlive() { return m_bIsAlive; } bool Monster::isCollideWithPlayer(Player *pPlayer) { bool bRet = false; do { CC_BREAK_IF(!pPlayer); // 獲取玩家碰撞範圍 CCRect rect = pPlayer->getBoundingBox(); // 獲取怪物(金幣)的中心位置 CCPoint monsterPos = this->getPosition(); bRet = rect.containsPoint(monsterPos); } while (0); return bRet; }
創建怪物管理類:
#include "cocos2d.h" #include "Player.h" const int g_nMaxMonsterNumber = 10; class MonsterManager : public cocos2d::CCNode { public: MonsterManager(); virtual ~MonsterManager(); virtual bool init(); CREATE_FUNC(MonsterManager); // 更新繪製 void update(float delta); /** * @brief 設置玩家 * @param pPlayer 玩家對象指針 */ void setPlayer(Player *pPlayer); private: /** * @brief 創建怪物(金幣) */ void createMonsters(); private: cocos2d::CCArray *m_pArrMonsters; ///< 怪物對象數組 Player *m_pPlayer; ///< 玩家對象 };
#include "MonsterManager.h" #include "Monster.h" USING_NS_CC; MonsterManager::MonsterManager() : CCNode(), m_pArrMonsters(NULL), m_pPlayer(NULL) { return; } MonsterManager::~MonsterManager() { CC_SAFE_DELETE_ARRAY(m_pArrMonsters); CC_SAFE_RELEASE_NULL(m_pPlayer); return; } bool MonsterManager::init() { bool bRet = false; do { createMonsters(); // 啟用update this->scheduleUpdate(); bRet = true; } while (0); return bRet; } void MonsterManager::createMonsters() { m_pArrMonsters = CCArray::create(); CC_SAFE_RETAIN(m_pArrMonsters); // 防止數組被釋放 Monster *pMonster = NULL; CCSprite *pSprite = NULL; for (int i = 0; i < g_nMaxMonsterNumber; i++) { pMonster = Monster::create(); CC_BREAK_IF(!pMonster); pSprite = CCSprite::create("monster.png"); CC_BREAK_IF(!pSprite); pMonster->setSprite(pSprite); pMonster->reset(); this->addChild(pMonster); // 將怪物添加到管理器中 m_pArrMonsters->addObject(pMonster); } return; } void MonsterManager::update(float delta) { CCObject *pObject = NULL; Monster *pMonster = NULL; CCARRAY_FOREACH(m_pArrMonsters, pObject) { pMonster = dynamic_cast<Monster *>(pObject); // 如果怪物是否處於活動狀態 if (pMonster->isAlive()) { // 左移3個寬度 pMonster->setPositionX(pMonster->getPositionX() - 3); // 若到達屏幕最左,超出屏幕就隱藏 if (pMonster->getPositionX() < 0) { pMonster->hide(); } else if (pMonster->isCollideWithPlayer(m_pPlayer)) { m_pPlayer->hit(); pMonster->hide(); } } else { // 非活動狀態 pMonster->show(); } } return; } void MonsterManager::setPlayer(Player *pPlayer) { this->m_pPlayer = pPlayer; CC_SAFE_RETAIN(m_pPlayer); return; }
最後,就是需要創建一個遊戲場景:
#include "cocos2d.h" #include "Player.h" #include "cocos-ext.h" USING_NS_CC; using namespace extension; /** * @brief 遊戲運行場景 */ class GameRunningScene : public CCLayer { public: GameRunningScene(); virtual ~GameRunningScene(); virtual bool init(); static CCScene *scene(); CREATE_FUNC(GameRunningScene); virtual void update(float delta); private: void initBackground(); ///< 初始化關卡背景 void createJumpButton();///< 創建跳躍按鈕 void jumpEvent(CCObject *pSender, CCControlEvent event); // 響應點擊事件 void createScoreLabel();///< 分數 void createTimeSlider();///< 創建時間條 CCSprite *m_pBgSprite1; ///< 背景1 CCSprite *m_pBgSprite2; ///< 背景2 Player *m_pPlayer; ///< 玩家 CCLabelTTF *m_pScoreLabel; ///< 分類標簽 CCControlSlider *m_pTimeSlider; ///< 時間條 int m_nScore; ///< 得分 int m_nCurrentTime; ///< 當前時間 };
#include "GameRunningScene.h" #include "MonsterManager.h" #include "SimpleAudioEngine.h" using namespace CocosDenshion; GameRunningScene::GameRunningScene() : m_pBgSprite1(NULL), m_pBgSprite2(NULL), m_nCurrentTime(0), m_pTimeSlider(NULL), m_pPlayer(NULL), m_pScoreLabel(NULL), m_nScore(0) { CCLayer::CCLayer(); return; } GameRunningScene::~GameRunningScene() { CC_SAFE_RELEASE_NULL(m_pBgSprite1); CC_SAFE_RELEASE_NULL(m_pBgSprite2); CC_SAFE_RELEASE_NULL(m_pPlayer); CC_SAFE_RELEASE_NULL(m_pScoreLabel); CC_SAFE_RELEASE_NULL(m_pTimeSlider); CCLayer::~CCLayer(); return; } CCScene *GameRunningScene::scene() { CCScene *pScene = NULL; do { pScene = CCScene::create(); CC_BREAK_IF(!pScene); GameRunningScene *pLayer = GameRunningScene::create(); CC_BREAK_IF(!pLayer); pScene->addChild(pLayer, 1); } while (0); return pScene; } bool GameRunningScene::init() { bool bRet = false; do { CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize(); CCSprite *pSprite = CCSprite::create("sprite.png"); m_pPlayer = Player::create(); CC_BREAK_IF(!m_pPlayer); m_pPlayer->setSprite(pSprite); m_pPlayer->setPosition(ccp(200, visibleSize.height / 4)); this->addChild(m_pPlayer, 1); // 初始化背景圖片 initBackground(); // 創建按鈕 createJumpButton(); // 設置啟用CCNode的update函數,遊戲會在每一幀調用update函數 this->scheduleUpdate(); // 怪物管理器 MonsterManager *pMonsterManager = MonsterManager::create(); CC_BREAK_IF(!pMonsterManager); pMonsterManager->setPlayer(m_pPlayer); this->addChild(pMonsterManager, 4); // 創建分數標簽 createScoreLabel(); // 創建時間條 createTimeSlider(); SimpleAudioEngine::sharedEngine()->playBackgroundMusic("bgMusic.wav", true); bRet = true; } while (0); return bRet; } void GameRunningScene::initBackground() { CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize(); m_pBgSprite1 = CCSprite::create("tollgateBG.jpg"); m_pBgSprite1->setPosition(ccp(visibleSize.width / 2, visibleSize.height / 2)); this->addChild(m_pBgSprite1, 0); m_pBgSprite2 = CCSprite::create("tollgateBG.jpg"); m_pBgSprite2->setPosition(ccp(visibleSize.width / 2 * 3, visibleSize.height / 2)); m_pBgSprite2->setFlipX(true); this->addChild(m_pBgSprite2, 0); return; } void GameRunningScene::update(float delta) { // 地圖大小 CCSize mapSize = m_pBgSprite1->getContentSize(); // 地圖1和地圖2的X坐標 int posX1 = m_pBgSprite1->getPositionX(); int posX2 = m_pBgSprite2->getPositionX(); int nMapScrollSpeed = 5; // 地圖滾動的速度 posX1 -= nMapScrollSpeed; posX2 -= nMapScrollSpeed; // 創建無限循環 if (posX1 <= -mapSize.width / 2) { posX1 = mapSize.width + mapSize.width / 2; posX2 = mapSize.width / 2; } if (posX2 <= -mapSize.width / 2) { posX1 = mapSize.width / 2; posX2 = mapSize.width + mapSize.width / 2; } m_pBgSprite1->setPositionX(posX1); m_pBgSprite2->setPositionX(posX2); // 得分 m_nScore = m_pPlayer->getMoney(); m_pScoreLabel->setString(CCString::createWithFormat("Score: %d", m_nScore)->getCString()); m_pTimeSlider->setValue(--m_nCurrentTime); return; } void GameRunningScene::createJumpButton() { CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize(); do { // 按鈕標題 CCLabelTTF *pJumpLabel = CCLabelTTF::create("Jump", "Arial", 35); CC_BREAK_IF(!pJumpLabel); // 按鈕狀態圖片 CCScale9Sprite *pJumpNormalSprite = CCScale9Sprite::create("button.png"); CC_BREAK_IF(!pJumpNormalSprite); CCScale9Sprite *pJumpLightSprite = CCScale9Sprite::create("buttonHighlighted.png"); CC_BREAK_IF(!pJumpLightSprite); // 創建按鈕 CCControlButton *pJumpButton = CCControlButton::create(pJumpLabel, pJumpNormalSprite); CC_BREAK_IF(!pJumpButton); pJumpButton->setPosition(visibleSize.width - 80, 50); pJumpButton->setBackgroundSpriteForState(pJumpLightSprite, CCControlStateHighlighted); pJumpButton->addTargetWithActionForControlEvents(this, cccontrol_selector(GameRunningScene::jumpEvent), CCControlEventTouchDown); this->addChild(pJumpButton); } while (0); return; } void GameRunningScene::jumpEvent(cocos2d::CCObject *pSender, CCControlEvent event) { m_pPlayer->jumpBegin(); return; } void GameRunningScene::createScoreLabel() { m_nScore = m_pPlayer->getMoney(); CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize(); m_pScoreLabel = CCLabelTTF::create( CCString::createWithFormat("Score: %d", m_nScore)->getCString(), "Arial", 35); m_pScoreLabel->setAnchorPoint(ccp(0, 1)); m_pScoreLabel->setPosition(ccp(0, visibleSize.height)); this->addChild(m_pScoreLabel); return; } void GameRunningScene::createTimeSlider() { CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize(); m_nCurrentTime = 10000; m_pTimeSlider = CCControlSlider::create(CCSprite::create("background.png"), CCSprite::create("progress.png"), CCSprite::create("sliderThumb.png")); m_pTimeSlider->setPosition( ccp(m_pTimeSlider->getContentSize().width / 2, visibleSize.height - m_pTimeSlider->getContentSize().height - m_pScoreLabel->getContentSize().height )); m_pTimeSlider->setTouchEnabled(false); m_pTimeSlider->setMaximumValue(10000); m_pTimeSlider->setMinimumValue(0); m_pTimeSlider->setValue(m_nCurrentTime); this->addChild(m_pTimeSlider, 3); return; }
最後更新:2017-04-03 12:56:12