閱讀404 返回首頁    go 阿裏雲 go 技術社區[雲棲]


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

  上一篇:go Cocos2dx 3.0 過渡篇(九)淺談容器Map
  下一篇:go 簡單哈希-hdoj-1425-sort