用AE做了个项目后,还是决定转到cocos2d-x上来做2D,因为跨平台确实吸引人。个人觉得AE还是个很不错的2D引擎,有着JAVA一贯的简单易用。而且AE中许多概念和cocos2d-x相通,比如Entity 对应CCNode,Modifier 对应Action, Scene, Layer,都有粒子系统和物理引擎支持等。
AE自带例子中有一个贪吃蛇的例子,使用的图片资源也少,学习cocos2d-x过程中新手实践一番
一、准备:
AE 游戏例子,AndEngineExamples中位于org.andengine.examples.game.snake包中的游戏,cocos2d-x, 我下载的是cocos2d-2.1rc0-x-2.1.
二、操作:
- 用ccx VS的win32模板添加一个项目Snake, 这是一个HelloWorld的程序,注意:如果这个工程没有放在CCX的目录下,要自己配置包含目录,因为模板使用了$(SolutionDir)这样的路径。
2. 将AndEngineExamples\assets\mfx下的两个声音文件,game_over.ogg,munch.ogg; font下的Plok.ttf;AndEngineExamples\assets\gfx下的snake*.png, frog.png拷到resource目录下。
3. 观察原游戏目录,依次实现对应类。
首先是常量定义,这个不用说了,添加一个头文件SnakeConstants.h,如:
#ifndef SNAKECONSTANTS_H_
#define SNAKECONSTANTS_H_
class SnakeConstants
{
public:
static const int CELLS_HORIZON = 16;
static const int CELLS_VERTICAL = 12;
static const int CELL_WIDTH = 32;
static const int CELL_HEIGHT = CELL_WIDTH;
static const int CAMERA_WIDTH = CELLS_HORIZON * CELL_WIDTH;
static const int CAMERA_HEIGHT = CELLS_VERTICAL * CELL_HEIGHT;
static const int LAYER_COUNT = 4;
static const int LAYER_BACKGROUND = 0;
static const int LAYER_FOOD = 1;
static const int LAYER_SNAKE = 2;
static const int LAYER_UI = 3;
};
#endif
接口ICellEntity, 添加一个ICellEntity.h
#define ICELL_ENTITY_H_ #ifndef ICELL_ENTITY_H_ class ICellEntity { public: virtual const int GetCellX() const= 0; virtual const int GetCellY() const= 0; virtual void SetCell(const ICellEntity& cell) = 0; virtual void SetCell(const int cellX, const int cellY) = 0; virtual bool IsInSameCell(const ICellEntity& cell) const = 0; }; #endif
#ifndef DIRECTION_H_ #define DIRECTION_H_ #include "SnakeConstants.h" enum Direction { DIR_NULL, UP = 1, DOWN, LEFT, RIGHT }; static int AddtoX(const Direction direction, const int dx) { switch(direction) { case UP: case DOWN: return dx; case LEFT: return dx - 1; case RIGHT: return dx + 1; } return dx; } static int AddtoY(const Direction direction, const int dy) { switch(direction) { case LEFT: case RIGHT: return dy; case UP: return dy + 1; case DOWN: return dy - 1; } return dy; } static Direction Opposite(const Direction direction) { switch(direction) { case UP: return DOWN; case DOWN: return UP; case LEFT: return RIGHT; case RIGHT: return LEFT; default: return DIR_NULL; } } #endif用ICellEntity.h接口实现CellEntity类,这个类是没有动画的,用于主角的尾巴。这里使用了继承CCSprite的方式,
class CellEntity : public ICellEntity, public CCSprite
因为ccx默认的锚点在中心,所以把位置加上格子一半,定义在中心
this->setPosition(ccp(mCellX*SnakeConstants::CELL_WIDTH+16, mCellY*SnakeConstants::CELL_HEIGHT+16));
实现一个简单帧动画的类,AnimatedCellEntity, 构造函数中显示了一种使用帧动画的方式:
mCellX = cellx;
mCellY = celly;
animation = CCAnimation::create();
CCImage* image = new CCImage();
image->autorelease();
image->initWithImageFile(filename);
CCTexture2D* texture = new CCTexture2D();
texture->initWithImage(image);
mTileW = texture->getPixelsWide() / col;
mTileH = texture->getPixelsHigh();
for (int i = 0; i < col; ++i)
{
animation->addSpriteFrameWithTexture(texture, CCRectMake(i*mTileW, 0, mTileW, mTileH));
}
CCSpriteFrame* frm = ((CCAnimationFrame*)(animation->getFrames()->objectAtIndex(0)))->getSpriteFrame();
this->initWithSpriteFrame(frm);
animation->setDelayPerUnit(0.5f);
mAni = CCRepeatForever::create(CCAnimate::create(animation));
this->SetCell(cellx, celly);
void AnimatedCellEntity::Animate( const float interval ) { animation->setDelayPerUnit(interval); this->runAction(mAni); }
用上面的两个类实现SnakeHead,SnakeTailPart
蛇头应当占两个格子,而且旋转的中心在头部的1/4处,所以要更改锚点,并放大,这点和AE还是有些不同的
this->setAnchorPoint(ccp(0.5f, 0.75f)); this->setScale(2.0f);最后新建一个类,Snake继承自CCNode,其中包含一个头和一个CCArray表示尾部。
#include "cocos2d.h" #include "Direction.h" #include "Snake.h" #include "SnakeTailPart.h" Snake::Snake( const Direction dir, int cellx, int celly ) { m_pSnakeHead = new SnakeHead(cellx, celly); m_pSnakeHead->autorelease(); this->addChild(m_pSnakeHead); this->SetDirection(dir); } void Snake::SetDirection( const Direction dirction ) { if(mLastMoveDirection != Opposite(dirction)) { mDirection = dirction; m_pSnakeHead->SetRotation(dirction); } } Snake::~Snake() { // CC_SAFE_DELETE(m_pSnakeHead); ////释放数组内的元素!, NO NEED! //CCObject* object; //CCARRAY_FOREACH(&marrTail, object) //{ // CC_SAFE_DELETE(object); //} //marrTail.removeAllObjects(); } int Snake::GetNextX() { return AddtoX(mDirection, m_pSnakeHead->GetCellX()); } int Snake::GetNextY() { return AddtoY(mDirection, m_pSnakeHead->GetCellY()); } boolean Snake::Move() { mLastMoveDirection = mDirection; if(m_bGrow) { m_bGrow = false; /* If the snake should grow, * simply add a new part in the front of the tail, * where the head currently is. */ SnakeTailPart* newTailPart = new SnakeTailPart(m_pSnakeHead); this->addChild(newTailPart); marrTail.insertObject(newTailPart, 0); } else { if(marrTail.count() > 0) { /* First move the end of the tail to where the head currently is. */ SnakeTailPart* tailEnd = (SnakeTailPart*)marrTail.lastObject(); marrTail.removeLastObject(false); tailEnd->SetCell(*m_pSnakeHead); marrTail.insertObject(tailEnd, 0); } } /* The move the head into the direction of the snake. */ m_pSnakeHead->SetCell(GetNextX(), GetNextY()); /* Check if head collides with tail. */ for(int i = marrTail.count() - 1; i >= 0; i--) { if(m_pSnakeHead->IsInSameCell(*(SnakeTailPart*)marrTail.objectAtIndex(i))) { return false; } } return true; }逻辑部分就完了。
把模板中的HelloWorldScene改为自己的SnakeScene,实现显示和触摸控制
#ifndef SNAKESCENE_H__ #define SNAKESCENE_H__ #include "cocos2d.h" #include "SimpleAudioEngine.h" #include "Direction.h" #include "Snake.h" #include "Frog.h" class SnakeScene : public cocos2d::CCLayer { public: // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone virtual bool init(); // there's no 'id' in cpp, so we recommand to return the exactly class pointer static cocos2d::CCScene* scene(); // a selector callback void menuCloseCallback(CCObject* pSender); //四个方向 void upCallBack(CCObject* sender); void downCallBack(CCObject* sender); void leftCallBack(CCObject* sender); void rightCallBack(CCObject* sender); //schedule void ScheduleTick1(float dt); void GameCircle(float dt); // implement the "static node()" method manually CREATE_FUNC(SnakeScene); private: void SetFrogToRandomCell(); void OnGameOver(); void HandleNewSnakePosition(); //声音的使用非常简洁! void PlayMunchSound(); void PlayGameOverSound(); private: int m_nScore; bool m_bGameRunning; //分数 CCLabelTTF* mScoreText; //游戏结束 CCLabelTTF* mGameOverText; //游戏开始前的文字 CCLabelTTF* mTitleText; Snake* mSnake; Frog* mFrog; }; #endif在init中:
依照原游戏,分四层:
for(int i = 0; i < SnakeConstants::LAYER_COUNT; ++i)
{
this->addChild(CCLayer::create());
}
四个方向键:
//4个方向键: CCMenuItemImage* pUpImage = CCMenuItemImage::create("u1.png", "u2.png", this, menu_selector(SnakeScene::upCallBack)); pUpImage->setAnchorPoint(ccp(0.5, 0)); pUpImage->setPositionY(6.0f); CCMenuItemImage* pDownImage = CCMenuItemImage::create("d1.png", "d2.png", this, menu_selector(SnakeScene::downCallBack)); pDownImage->setAnchorPoint(ccp(0.5, 1)); pDownImage->setPositionY(-6.0f); CCMenuItemImage* pLeftImage = CCMenuItemImage::create("b1.png", "b2.png", this, menu_selector(SnakeScene::leftCallBack)); pLeftImage->setAnchorPoint(ccp(1, 0.5)); pLeftImage->setPositionX(-6.0f); CCMenuItemImage* pRightImage = CCMenuItemImage::create("f1.png", "f2.png", this, menu_selector(SnakeScene::rightCallBack)); pRightImage->setAnchorPoint(ccp(0, 0.5)); pRightImage->setPositionX(6.0f); CCMenu* ArrowMenu = CCMenu::create(pUpImage, pDownImage, pLeftImage, pRightImage, NULL); ArrowMenu->setPosition(100, 100); layer->addChild(ArrowMenu);游戏主循环:
schedule(schedule_selector(SnakeScene::GameCircle), 0.5f);
声音播放:
void SnakeScene::PlayMunchSound()
{
CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect("munch.ogg");
}
运行后:
最后 附上源码:
src-of-demo