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

懒骨头(http://blog.csdn.net/iamlazybone QQ:124774397 )


两点:

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

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


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

Cocos2dx游戏开发系列笔记11:解刨《战神传说》完结篇_第1张图片


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

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

设置类 Options.cpp:

Cocos2dx游戏开发系列笔记11:解刨《战神传说》完结篇_第2张图片

先贴背景图

  CCSprite *sp = CCSprite::create(s_loading);
    sp->setAnchorPoint(CCPointZero);
    addChild(sp, 0, 1);

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

    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方法里处理声音开关,全局的背景音乐和音效控制方法:

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的方法:

    CCLabelBMFont *backLb = CCLabelBMFont::create("Go Back", s_font);
    CCMenuItemLabel *goBack = CCMenuItemLabel::create(backLb, this, menu_selector(About::goBack));
Goback 一直在闪,是通过 一个CCRepeatForever的action来实现的,即一直重复模式
 
  goBack->runAction(CCRepeatForever::create((CCActionInterval*)seq));

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

关于页面 About.cpp

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

   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文件里声明了一个返回键代理,和四个需要子类实现虚方法。

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

这四个虚方法分别是:

    virtual void destroy() = 0;
    virtual void hurt() = 0 ;
    virtual CCRect collideRect() = 0;
    virtual bool isActive() = 0;

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

    // 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 

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

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

    // 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减少,颜色改变

void Ship::hurt()
{
    if (m_canBeAttack) {
        CCLog("under fire!");
        m_HP--;
        this->setColor(ccc3(255, 0, 0));
    }
}
销毁主角飞船方法:也很简单,更新生命值,播放动画特效,然后把自己从parent删除,最后播放音效。

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);
    }
}

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

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);
}


最后看看射击方法:

  // 子弹发射
    this->schedule(schedule_selector(Ship::shoot), 0.16);
每0.16秒调用一下shoot方法:也就是发射子弹的频率。

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


首先看下构造方法:

Bullet::Bullet(int speed, const char *weapon, int attactMode)
在里面初始化子弹的速度,生命,类型等等。

更新方法:

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

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倍的动画,和一个渐渐消失的动画。

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


Cocos2dx游戏开发系列笔记11:解刨《战神传说》完结篇_第3张图片

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

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

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

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));
}

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

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  绝大部分游戏都使用这种状态机机制。



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

    // 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);
 	加上游戏状态:分数和剩余生命	   // 每秒调一次 scoreCounter函数
    schedule(schedule_selector(GameLayer::scoreCounter), 1);
还有根据配置类,来选择是否播放游戏背景音乐

 
    if (Config::sharedConfig()->getAudioState()) {
        SimpleAudioEngine::sharedEngine()->playBackgroundMusic(s_bgMusic, true);
    }
初始化方法结束。


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

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掉。

void GameLayer::checkIsCollide()
{
    CCObject *units;
    CCObject *bullets;
    CCObject *enemybs;
	//这里是相对于每个敌人
    CCARRAY_FOREACH(enemy_items, units)
    {
        UnitSprite *enemy = dynamic_cast(units);
		//这里是相对于主角的子弹
        CCARRAY_FOREACH(play_bullet, bullets)
        {
            UnitSprite *bullet = dynamic_cast(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(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()方法里,主要就是更新顶部的分数和生命值。

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进入和退出的两个方法里,分别注册和注销系统的触摸事件。

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()里加逻辑了,主要就是让自己的飞机跟着移动。

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方法,然后手动停止音效,停止特效。

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像素。然后两个背景对象交替显示。

// 无限滚动地图,采用两张图循环加载滚动
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里贴点文本、图片,然后一个重新开始按钮

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 即可。

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

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

 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循环: 

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:解刨《战神传说》完结篇_第4张图片


爽吧 哈哈哈


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

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

身体要紧,得休息了。

晚安:)


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

Cocos2dx游戏开发系列笔记13:一个横版拳击游戏Demo-中

Cocos2dx游戏开发系列笔记12:一个横版拳击游戏Demo-上

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の妙舍》创始人,很有自己想法。

 





你可能感兴趣的:(cocos2dx)