用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");
}
运行后:
最后 附上源码: