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