本人初学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; }
#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; }