您的位置:首页 > 移动开发 > Cocos引擎

Cocos2dx游戏开发系列笔记11:解刨《战神传说》完结篇

2013-12-09 00:00 302 查看
两点:

1 感谢 net19880504 同学,在上篇提到:想让骨头继续写《战神传说》的解刨篇,因为有人在关注而开心。

2 感谢 kanhai 同学,骨头加的哲哲链接虽然是AD性质,但不像一般的AD那样影响阅读,而且骨头也很喜欢这些可爱的介绍,谢谢理解

今晚继续:解刨《战神传说》完结篇



上篇骨头学习了开始菜单和动画,接下来看看其他的类:

————————————————————————————————————————————————

设置类 Options.cpp:



先贴背景图

[cpp]
view plain
copy







CCSprite *sp = CCSprite::create(s_loading);

sp->setAnchorPoint(CCPointZero);

addChild(sp, 0, 1);

出现了一个新的控件,开关控件 CCMenuItemToggle:

[cpp]
view plain
copy







CCMenuItemToggle *toggle = CCMenuItemToggle::createWithTarget(this, menu_selector(Options::setOptions), CCMenuItemFont::create("On"),CCMenuItemFont::create("Off"), NULL);

int selectId = Config::sharedConfig()->getAudioState()? 0 : 1;

toggle->setSelectedIndex(selectId);

在setOptions方法里处理声音开关,全局的背景音乐和音效控制方法:

[cpp]
view plain
copy







void Options::setOptions(CCObject* pSender)

{

bool tmpSound = Config::sharedConfig()->getAudioState();

Config::sharedConfig()->updateAudioState(!tmpSound);

if (Config::sharedConfig()->getAudioState()) {

SimpleAudioEngine::sharedEngine()->resumeAllEffects();

SimpleAudioEngine::sharedEngine()->resumeBackgroundMusic();

}else{

SimpleAudioEngine::sharedEngine()->pauseAllEffects();

SimpleAudioEngine::sharedEngine()->pauseBackgroundMusic();

}

}

还有用指定字体生成label的方法:

[cpp]
view plain
copy







CCLabelBMFont *backLb = CCLabelBMFont::create("Go Back", s_font);

CCMenuItemLabel *goBack = CCMenuItemLabel::create(backLb, this, menu_selector(About::goBack));

Goback 一直在闪,是通过 一个CCRepeatForever的action来实现的,即一直重复模式

[cpp]
view plain
copy







goBack->runAction(CCRepeatForever::create((CCActionInterval*)seq));

————————————————————————————————————————————————

关于页面 About.cpp

这个页面很简单,只有一个长文本需要注意一下:

[cpp]
view plain
copy







CCLabelTTF *about = CCLabelTTF::create(" I recode ....ginal. \n ... ", "Arial", 18, CCSizeMake(winSize.width * 0.85, 320), kCCTextAlignmentLeft);

about = CCLabelTTF::create(" I recode thistion", "Arial", 18, CCSizeMake(winSize.width * 0.85, 320), kCCTextAlignmentLeft);

about->setPosition(ccp(winSize.width / 2, winSize.height / 2 - 20));

about->setAnchorPoint(ccp(0.5, 0.5));

addChild(about);

1 文本中 \n 是有效的
2 长文本区域 CCSizeMake(winSize.width * 0.85, 320)

3 对其方式 kCCTextAlignmentLeft

————————————————————————————————————————————————

游戏开始:






————————————————————————————————————————————————

主角类:Ship.cpp



Ship.cpp的父类是UnitSprite.cpp,这个父类里只在h文件里声明了一个返回键代理,和四个需要子类实现虚方法。

所有的主角飞机,敌机,子弹,的父类都是它。

这四个虚方法分别是:

[cpp]
view plain
copy







virtual void destroy() = 0;

virtual void hurt() = 0 ;

virtual CCRect collideRect() = 0;

virtual bool isActive() = 0;

下面是主角飞机的初始化和动画:

[cpp]
view plain
copy







// init life

CCTexture2D * shipTextureCache = CCTextureCache::sharedTextureCache()->addImage(s_ship01);

CCRect rec = CCRectMake(0, 0, 60, 38);

this->initWithTexture(shipTextureCache, rec);

this->setPosition(m_appearPosition);

// set frame

CCSpriteFrame *frame0 = CCSpriteFrame::createWithTexture(shipTextureCache, CCRectMake(0, 0, 60, 38));

CCSpriteFrame *frame1 = CCSpriteFrame::createWithTexture(shipTextureCache, CCRectMake(60, 0, 60, 38));

CCArray *animFrames = CCArray::create();

animFrames->addObject(frame0);

animFrames->addObject(frame1);

// ship animate

// 这个方法有差异

CCAnimation *animation = CCAnimation::createWithSpriteFrames(animFrames, 0.1);

CCAnimate *animate = CCAnimate::create(animation);

this->runAction(CCRepeatForever::create(animate));

根据 shipTextureCache 来创建两帧 CCSpriteFrame
然后用两帧图片生成 动画,然后播放这个动画,来达到主角飞机一直在闪的效果。

下面这个段代码是幽灵飞机初始化:

[cpp]
view plain
copy







// revive effect

this->m_canBeAttack = false;

CCSprite *ghostSprite = CCSprite::createWithTexture(shipTextureCache, CCRectMake(0, 45, 60, 38));

ccBlendFunc cbl = {GL_SRC_ALPHA, GL_ONE};

ghostSprite->setBlendFunc(cbl);

ghostSprite->setScale(8);

ghostSprite->setPosition(ccp(this->getContentSize().width / 2, 12));

this->addChild(ghostSprite, 3000, 99999);

ghostSprite->runAction(CCScaleTo::create(0.5, 1, 1));

何为幽灵飞机呢,就是主角飞机死掉后,一个由大变小的动画,由代码可看出,首先设置为不可攻击,且放大8倍,然后在半秒内回复到正常大小,且可以被攻击。
ccBlendFunc cbl = {GL_SRC_ALPHA, GL_ONE};

这句代码骨头还不太理解,大意就是用来设置描绘时的颜色混合方案。ccBlendFunc包含了一个src和一个dst,分别表示目标和源的运算因子。

比如 ghostSprite->setBlendFunc(cbl);,这句代码,就是以这个Sprite作为源,Sprite所在位置的其它像素作为目标,进行混合运算.

至于这样做的效果和目的,骨头先mark一下。

受伤方法:很简单,hp减少,颜色改变

[cpp]
view plain
copy







void Ship::hurt()

{

if (m_canBeAttack) {

CCLog("under fire!");

m_HP--;

this->setColor(ccc3(255, 0, 0));

}

}

销毁主角飞船方法:也很简单,更新生命值,播放动画特效,然后把自己从parent删除,最后播放音效。

[cpp]
view plain
copy







void Ship::destroy()

{

CCLOG("destroy one ship");

Config::sharedConfig()->updateLifeCount();

CCLOG("life count is %d",Config::sharedConfig()->getLifeCount());

Effect *effect = Effect::create();

effect->explode(this->getParent(), this->getPosition());

this->removeFromParent();

if (Config::sharedConfig()->getAudioState()){

CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect(s_shipDestroyEffect);

}

}

还有个返回矩形方法:碰撞判断好用的

[cpp]
view plain
copy







CCRect Ship::collideRect()

{

CCPoint pos = getPosition();

CCSize cs = getContentSize();

return CCRectMake(pos.x - cs.width / 2 , pos.y - cs.height / 2, cs.width, cs.height / 2);

}

最后看看射击方法:

[cpp]
view plain
copy







// 子弹发射

this->schedule(schedule_selector(Ship::shoot), 0.16);

每0.16秒调用一下shoot方法:也就是发射子弹的频率。

[cpp]
view plain
copy







void Ship::shoot(float dt)

{

int offset = 13;

CCPoint position = this->getPosition();

CCSize contentSize = this->getContentSize();

Bullet *bullet_a = new Bullet(m_bulletSpeed, "W1.png", 1);

if (bullet_a) {

bullet_a->autorelease();

play_bullet->addObject(bullet_a);

this->getParent()->addChild(bullet_a, bullet_a->m_zorder, 901);

bullet_a->setPosition(ccp(position.x + offset, position.y + 3 + contentSize.height * 0.3));

}else{

delete bullet_a;

bullet_a = 0;

}

Bullet *bullet_b = new Bullet(m_bulletSpeed, "W1.png", 1);

if (bullet_b) {

bullet_b->autorelease();

play_bullet->addObject(bullet_b);

this->getParent()->addChild(bullet_b, bullet_b->m_zorder, 901);

bullet_b->setPosition(ccp(position.x - offset, position.y + 3 + contentSize.height * 0.3));

}else{

delete bullet_b;

bullet_a = 0;

}

}

这里就是生成两个子弹对象Bullet,一左一右。
骨头以后会在这里大作文章的,比如改成四排子弹,八排子弹,霰弹等等。哈哈哈哈,这是一件相当过瘾的事情!

下面紧接着看看Bullet.cpp 对象:

————————————————————————————————————————————————

子弹类:Bullet.cpp

首先看下构造方法:

[cpp]
view plain
copy







Bullet::Bullet(int speed, const char *weapon, int attactMode)

在里面初始化子弹的速度,生命,类型等等。
更新方法:

这里面主要是更新位置,比如dt是0.5秒,即一秒钟更新两次,所以每次位移变化量就应该是速度×dt,并且判断没hp了就设置为不可用。

[cpp]
view plain
copy







void Bullet::update(float dt)

{

CCPoint position = this->getPosition();

position.x -= m_velocityx * dt;

position.y -= m_velocityy * dt;

setPosition(position);

if (m_Hp <= 0) {

m_active = false;

}

}

下面是子弹的销毁方法:
播放特效,然后把当前子弹从子弹列表中删除,然后从父控件删除,最后播放一个放大2倍的动画,和一个渐渐消失的动画。

[cpp]
view plain
copy







void Bullet::destroy()

{

// 子弹爆炸特效

CCSprite *explode = CCSprite::create(s_hit);

ccBlendFunc cb = {GL_SRC_ALPHA, GL_ONE };

explode->setBlendFunc(cb);

explode->setPosition(this->getPosition());

explode->setRotation(CCRANDOM_0_1() * 360);

explode->setScale(0.75);

getParent()->addChild(explode, 9999);

play_bullet->removeObject(this);

enemy_bullet->removeObject(this);

this->removeFromParent();

CCCallFuncN *removeExplode = CCCallFuncN::create(explode, callfuncN_selector(Bullet::removeExplode));

explode->runAction(CCScaleBy::create(0.3, 2, 2));

explode->runAction(CCSequence::create(CCFadeOut::create(0.3), removeExplode, NULL));

}

————————————————————————————————————————————————

敌机类:Enemy.cpp



敌机类和主角飞机类应该是大同小异。

射击相关:每隔m_delayTime秒发射一次子弹

[cpp]
view plain
copy







this->schedule(schedule_selector(Enemy::shoot),this->m_delayTime);

[cpp]
view plain
copy







void Enemy::shoot(float dt)

{

CCPoint pos = this->getPosition();

Bullet *bullet = new Bullet(m_bulletSpeed, "W2.png", m_attackMode);

bullet->autorelease();

enemy_bullet->addObject(bullet);

getParent()->addChild(bullet, m_zOrder, 900);

bullet->setPosition(ccp(pos.x, pos.y - getContentSize().height * 0.2));

}

销毁方法,见每行注释,跟主角飞机是一样的。

[cpp]
view plain
copy







void Enemy::destroy()

{

// 更新分数

Config::sharedConfig()->setScoreValue(m_scoreValue );

// 爆炸特效和闪光特效

Effect *effect = Effect::create();

effect->explode(this->getParent(), getPosition());

effect->spark(this->getPosition(),this->getParent(), 1.2, 0.7);

// 敌机爆炸,从敌机数组删除

enemy_items->removeObject(this);

// 删除精灵

this->removeFromParent();

// 声音

if (Config::sharedConfig()->getAudioState()) {

SimpleAudioEngine::sharedEngine()->playEffect(s_explodeEffect);

}

}

————————————————————————————————————————————————

主要逻辑类:GameLayer.cpp

初始化init方法里:

启动触摸 this->setTouchEnabled(true);

初始化各种数组:play_bullet = CCArray::create(); play_bullet->retain();

游戏状态: m_state = statePlaying;//statePlaying=0 绝大部分游戏都使用这种状态机机制。



在屏幕顶端加上游戏状态:分数和剩余生命

[cpp]
view plain
copy







// ship life

CCTexture2D *shipTexture = CCTextureCache::sharedTextureCache()->addImage(s_ship01);

CCSprite *life = CCSprite::createWithTexture(shipTexture, CCRectMake(0, 0, 60, 38));

life->setScale(0.6);

life->setPosition(ccp(30,winSize.height-23));

addChild(life, 1, 5);

[cpp]
view plain
copy







<span style="white-space:pre"> </span>加上游戏状态:分数和剩余生命<span style="white-space: pre;"> </span> // 每秒调一次 scoreCounter函数

schedule(schedule_selector(GameLayer::scoreCounter), 1);

还有根据配置类,来选择是否播放游戏背景音乐

[cpp]
view plain
copy







if (Config::sharedConfig()->getAudioState()) {

SimpleAudioEngine::sharedEngine()->playBackgroundMusic(s_bgMusic, true);

}

初始化方法结束。

碰撞检测方法:判断两个矩形是否相交

[cpp]
view plain
copy







bool GameLayer::collide(UnitSprite *a, UnitSprite *b)

{

if(!a || !b)

{

return false;

}

CCRect aRect = a->collideRect();

CCRect bRect = b->collideRect();

if (aRect.intersectsRect(bRect)) {

return true;

}

return false;

}

然后使用上面的碰撞检测方法,看看有没有飞机受伤:注释很详细,就是遍历套遍历。碰撞上了就调用hurt(),越界了就destroy掉。

[cpp]
view plain
copy







void GameLayer::checkIsCollide()

{

CCObject *units;

CCObject *bullets;

CCObject *enemybs;

//这里是相对于每个敌人

CCARRAY_FOREACH(enemy_items, units)

{

UnitSprite *enemy = dynamic_cast<UnitSprite*>(units);

//这里是相对于主角的子弹

CCARRAY_FOREACH(play_bullet, bullets)

{

UnitSprite *bullet = dynamic_cast<UnitSprite*>(bullets);

if (this->collide(enemy, bullet)) {//判断敌人和子弹

enemy->hurt();

bullet->hurt();

}

//越界删除

if (!(m_screenRec.intersectsRect(bullet->boundingBox()))) {

bullet->destroy();

}

}

if (collide(enemy, m_ship)) {//判断敌人和主角

if (m_ship->isActive()) {

enemy->hurt();

m_ship->hurt();

}

}

if (!(m_screenRec.intersectsRect(enemy->boundingBox()))) {

enemy->destroy();

}

}

//相对于每个敌人的子弹

CCARRAY_FOREACH(enemy_bullet, enemybs)

{

UnitSprite *enemyb = dynamic_cast<UnitSprite*>(enemybs);

if (enemyb) {

if (collide(enemyb, m_ship)) {//判断叠人子弹和主角

if (m_ship->isActive()) {

enemyb->hurt();

m_ship->hurt();

}

}

if (!m_screenRec.intersectsRect(enemyb->boundingBox())) {

enemyb->destroy();

}

}

}

}

在updateUI()方法里,主要就是更新顶部的分数和生命值。

[cpp]
view plain
copy







void GameLayer::updateUI()

{

if (m_tempScore < Config::sharedConfig()->getScoreValue()) {

m_tempScore += 5;

}

// char score[20];

// char s[] = "Score:";

// sprintf(score, "%d", m_tempScore);

// m_lbScore->setString(strcat(s, score));

char lifecount[2];

sprintf(lifecount, "%d",Config::sharedConfig()->getLifeCount());

m_lifeCount->setString(lifecount);

}

关于触摸事件:在CCScene进入和退出的两个方法里,分别注册和注销系统的触摸事件。

[cpp]
view plain
copy







void GameLayer::onEnter()

{

CCDirector* pDirector = CCDirector::sharedDirector();

pDirector->getTouchDispatcher()->addTargetedDelegate(this, 0, true);

CCLayer::onEnter();

}

void GameLayer::onExit()

{

CCDirector* pDirector = CCDirector::sharedDirector();

pDirector->getTouchDispatcher()->removeDelegate(this);

CCLayer::onExit();

}

接下来就可以在 ccTouchMoved()里加逻辑了,主要就是让自己的飞机跟着移动。

[cpp]
view plain
copy







void GameLayer::ccTouchMoved(cocos2d::CCTouch *touch, cocos2d::CCEvent *event)

{

if ((m_state == statePlaying) && m_ship) {

CCPoint pos = touch->getDelta();

CCPoint currentPos = m_ship->getPosition();

currentPos = ccpAdd(currentPos, pos);

currentPos = ccpClamp(currentPos, CCPointZero, ccp(winSize.width, winSize.height));

m_ship->setPosition(currentPos);

}

}

眼前一亮:
发现两个很有用的方法:

ccpAdd:两点相加

ccpCliamp:保证pos点落在两点确定的矩形之间。

暂停游戏方法:框架自带的pause方法,然后手动停止音效,停止特效。

[cpp]
view plain
copy







void GameLayer::doPause(CCObject *pSender)

{

CCDirector::sharedDirector()->pause();

SimpleAudioEngine::sharedEngine()->pauseBackgroundMusic();

SimpleAudioEngine::sharedEngine()->pauseAllEffects();

PauseLayer *pauseLayer = PauseLayer::create();

addChild(pauseLayer,9999);

}

接下来是这个类里最复杂的方法了:背景滚动
感觉这个demo里,背景滚动的方法弄的有些复杂了。

其实前背景后背景,两个背景使用不同的速度来滚动,一个3秒滚动48像素,一个3秒滚动200像素。然后两个背景对象交替显示。

[cpp]
view plain
copy







// 无限滚动地图,采用两张图循环加载滚动

void GameLayer::initBackground()

{

m_backSky = CCSprite::create(s_bg01);

m_backSky->setAnchorPoint(ccp(0, 0));

m_backSkyHeight = m_backSky->getContentSize().height;

addChild(m_backSky, -10);

// Tile map

m_backTileMap = CCTMXTiledMap::create(s_level01);

addChild(m_backTileMap, -9);

m_backTileMapHeight = m_backTileMap->getMapSize().height * m_backTileMap->getTileSize().height;

m_backSkyHeight -= 48;

m_backTileMapHeight -= 200;

m_backSky->runAction(CCMoveBy::create(3, ccp(0, -48)));

m_backTileMap->runAction(CCMoveBy::create(3, ccp(0, -200)));

schedule(schedule_selector(GameLayer:: movingBackground),3);

}

// 这里就是视差背景了

void GameLayer::movingBackground(float dt)

{

m_backSky->runAction(CCMoveBy::create(3, ccp(0, -48)));

m_backTileMap->runAction(CCMoveBy::create(3, ccp(0, -200)));

// 每次移动48

m_backSkyHeight -= 48;

// 每次移动200

m_backTileMapHeight -= 200;

// 图的顶部到达屏幕顶部时

if (m_backSkyHeight <= winSize.height) {

if (!m_isBackSkyReload) {

// 如果另一张图还没加载则create一个

m_backSkyRe = CCSprite::create(s_bg01);

m_backSkyRe->setAnchorPoint(ccp(0, 0));

addChild(m_backSkyRe, -10);

m_backSkyRe->setPosition(ccp(0, winSize.height));

// 反转标志位

m_isBackSkyReload = true;

}

// 第二张图紧接着第一张图滚动

m_backSkyRe->runAction(CCMoveBy::create(3, ccp(0, -48)));

}

// 第一张图完全经过屏幕

if (m_backSkyHeight <= 0) {

m_backSkyHeight = m_backSky->getContentSize().height;

// 移除第一张的精灵

this->removeChild(m_backSky, true);

// 指向第二张图的精灵

m_backSky = m_backSkyRe;

// 第二张的精灵指针置空

m_backSkyRe = NULL;

// 反转标志位

m_isBackSkyReload = false;

}

if (m_backTileMapHeight <= winSize.height) {

if (!m_isBackTileReload) {

m_backTileMapRe = CCTMXTiledMap::create(s_level01);

this->addChild(m_backTileMapRe, -9);

m_backTileMapRe->setPosition(0, winSize.height);

m_isBackTileReload = true;

}

m_backTileMapRe->runAction(CCMoveBy::create(3, ccp(0, -200)));

}

if (m_backTileMapHeight <= 0) {

m_backTileMapHeight = m_backTileMap->getMapSize().height * m_backTileMap->getTileSize().height;

this->removeChild(m_backTileMap, true);

m_backTileMap = m_backTileMapRe;

m_backTileMapRe = NULL;

m_isBackTileReload = false;

}

}

————————————————————————————————————————————————

游戏结束类:GameOver.cpp

很简单,init里贴点文本、图片,然后一个重新开始按钮

[cpp]
view plain
copy







void GameOver::playAgain(CCObject* pSender)

{

CCScene *scene = CCScene::create();

scene->addChild(GameLayer::create());

CCDirector::sharedDirector()->replaceScene(CCTransitionFade::create(1.2, scene));

}

————————————————————————————————————————————————

游戏暂停类:PauseLayer.cpp

就是一个大的全屏按钮,点击暂停按钮后,游戏暂停,此按钮开始监听,任意触摸屏幕后,游戏继续,无他。

注意,demo里的代码有问题,点击不能重开游戏,只需要注释掉 if 即可。

[cpp]
view plain
copy







bool PauseLayer::ccTouchBegan(cocos2d::CCTouch *touch, cocos2d::CCEvent *event)

{

// 因为回调调不到了,所以resume写在了这里

CCRect rect = menu->getChildByTag(10)->boundingBox();

// if (rect.containsPoint(touch->getLocation())) {

CCLog("touch play");

CCDirector::sharedDirector()->resume();

SimpleAudioEngine::sharedEngine()->resumeAllEffects();

SimpleAudioEngine::sharedEngine()->resumeBackgroundMusic();

removeFromParent();

// }

return true;

}

————————————————————————————————————————————————


配置类:Config.cpp


全局配置,比如各种不同的敌机的参数初始化。

[cpp]
view plain
copy







EnemyType enemyType;

enemyType.type = 0;

enemyType.textureName = "E0.png";

enemyType.bulletType = "W2.png";

enemyType.hp = 1;

enemyType.moveType = 0;

enemyType.scoreValue = 15;

m_enemyTypes.push_back(enemyType);

enemyType.type = 1;

enemyType.textureName = "E1.png";

enemyType.bulletType = "W2.png";

enemyType.hp = 2;

enemyType.moveType = 0;

enemyType.scoreValue = 40;

m_enemyTypes.push_back(enemyType);

————————————————————————————————————————————————
最后,来点过瘾的。

把Ship.cpp里的设计方法void Ship::shoot(float dt) 加个for循环:

[cpp]
view plain
copy







void Ship::shoot(float dt)

{

int offset = 13;

CCPoint position = this->getPosition();

CCSize contentSize = this->getContentSize();

for(int i=1;i<10;i++){//--------------新增

Bullet *bullet_a = new Bullet(m_bulletSpeed, "W1.png", 1);

if (bullet_a) {

bullet_a->autorelease();

play_bullet->addObject(bullet_a);

this->getParent()->addChild(bullet_a, bullet_a->m_zorder, 901);

//-------------修改offset*i

bullet_a->setPosition(ccp(position.x + offset*i, position.y + 3 + contentSize.height * 0.3));

}else{

delete bullet_a;

bullet_a = 0;

}

Bullet *bullet_b = new Bullet(m_bulletSpeed, "W1.png", 1);

if (bullet_b) {

bullet_b->autorelease();

play_bullet->addObject(bullet_b);

this->getParent()->addChild(bullet_b, bullet_b->m_zorder, 901);

//-------------修改offset*i

bullet_b->setPosition(ccp(position.x - offset*i, position.y + 3 + contentSize.height * 0.3));

}else{

delete bullet_b;

bullet_a = 0;

}

}//-------------新增

}

看效果



爽吧 哈哈哈

————————————————————————————————————————————————

大体就这么多,真正消化掉这些东西的话,还要自己动手敲。

身体要紧,得休息了。

晚安:)

------------------- 飞船起飞--------------------

Cocos2dx游戏开发系列笔记11:解刨《战神传说》完结篇

Cocos2dx游戏开发系列笔记10:解刨《战神传说》

Cocos2dx游戏开发系列笔记9:android手机上运行《战神传说》,并解决横竖屏即分辨率自适应问题

Cocos2dx游戏开发系列笔记8:开搞一个射击游戏《战神传说》//就个打飞机的

Cocos2dx游戏开发系列笔记7:一个简单的跑酷游戏《萝莉快跑》的消化(附下载)

Cocos2dx游戏开发系列笔记6:怎样让《萝莉快跑》的例子运行在vs和手机上

Cocos2dx游戏开发系列笔记5:继续润色《忍者飞镖射幽灵》

Cocos2dx游戏开发系列笔记4:怎样新加一个Scene类?

Cocos2dx游戏开发系列笔记3:牛刀小试->忍者飞镖射幽灵的Demo

Cocos2dx游戏开发系列笔记2:一个刚创建的cocos2dx中的demo里都有什么

Cocos2dx游戏开发系列笔记1:一个崭新的开始,cocos2dx2.2+ndkr9+Cygwin+vs2012游戏开发环境搭建

-------------------- 飞船降落--------------------

最后,骨头介绍一下陪在身边的哲哲(右边就是低调的哲哲)

哲哲,小名 YIYI ,手工爱好者,文艺范,手艺人,《YiYiの妙舍》创始人,很有自己想法。



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐