地球人己阻止不了程序猿们学习cocos2d-x了 (第四篇)

  • 本帖最后由 dr_watson 于 2012-7-23 11:25 编辑 *

内容重点: 射击?戏, 框架, 操控, 视窗键盘运用

这次我?备写一个简单的射击?戏作为练习, ?戏里可以控制主角移动, 按钮发射子弹射击飞过来的敌人, 被敌人撞到就 Game Over.

通过这个练习可以熟识一些基本的东西, 像:

  • ?戏框架
  • 操控
  • 声效
  • 粒子效果

4.1 SneakyInput
先说一下关於操控吧, 之前买了两本 cocos2d 的参考书, 都推荐用一个第叁方cocos2d 库叫 SneakyInput, 所以我就拿来试用一下, 结果觉得果然不错, 为?戏加入虚拟操控杆和按钮非常方便!

原装cocos2d 版大家可以在这里下载:

https://github.com/sneakyness/SneakyInput

当然我们需要的是 cocos2d-x 版:

https://github.com/Ntran013/SneakyInput

下载後把里边的源码都加到自己的工程里就可以:

249

在作这个练习时, 正好赶上 cocos2d-x 2.0 第一版的发?, 我比较喜欢试新东西, 所以就第一时间转过去了, 2.0 底层换了用 OpenGL ES 2.0, API 也作出了不少的改动, 正好我刚开始学习 cocos2d, 弄的都是小项目, 说换就换不头痛.

在用 SneakyInput 时, 要有两个小改动才能在 2.0 上编译, 其中一个是 1.0 时已经要改的了:

在SneakyButton和 SneakyJoystick 的 ccTouchBegan(), ccTouchMoved() 里, 要把这句:


CCPoint location = CCDirector::sharedDirector()->convertToGL(touch->locationInView(touch->view()));

改为:


CCPoint location = CCDirector::sharedDirector()->convertToGL(touch->locationInView());

另一个改动, 是在SneakyButton和 SneakyJoystick 的 onEnterTransitionDidFinish() 和 onExit() 里, CCTouchDispatcher 在 1.0 时是一个 Singleton, 但在2.0 被放进入CCDirector, 所以本来的


CCTouchDispatcher::sharedDispatcher()

要改为:


CCDirector::sharedDirector()->getTouchDispatcher()

要在?戏里加上一个 操控杆, 首先我们要?备一个底座和一个操控杆的图像, 接下来, 先要建立一个 SneakyJoystickSkinnedBase:

 SneakyJoystickSkinnedBase *joystickBase = new SneakyJoystickSkinnedBase();
joystickBase->autorelease();
joystickBase->init();
joystickBase->setBackgroundSprite(CCSprite::spriteWithSpriteFrameName("circleBig.png")); // 底座 
joystickBase->setThumbSprite(CCSprite::spriteWithSpriteFrameName("circleSmall.png")); // 操控杆 
joystickBase->setPosition(System::CCPointMake(48, 48));

然後我们要建立一个SneakyJoystick 并设置到 SneakyJoystickSkinnedBase 里:

SneakyJoystick *joystick = new SneakyJoystick();
                
joystick->autorelease();
joystick->initWithRect(CCRectMake(0,0,64,64));

joystickBase->setJoystick(joystick);

this->addChild(joystickBase);

用 SneakyInput 加?戏按钮也是同样简单, 但用的是 SneakyButtonSkinnedBase 和 SneakyButton:

SneakyButtonSkinnedBase *buttonBase = new SneakyButtonSkinnedBase();

buttonBase->autorelease();
buttonBase->init();
buttonBase->setDefaultSprite(CCSprite::spriteWithSpriteFrameName("buttonBlue.png"));
buttonBase->setActivatedSprite(CCSprite::spriteWithSpriteFrameName("buttonOrange.png"));
buttonBase->setPressSprite(CCSprite::spriteWithSpriteFrameName("buttonOrange.png"));

buttonBase->setPosition(CCPointMake(480-48, 48));

SneakyButton *button = new SneakyButton();

button->autorelease();
button->initWithRect(btnRect);
button->setIsToggleable(false);
button->setIsHoldable(true);

buttonBase->setButton(button);

this->addChild(buttonBase);

最後就是在我们的 update(ccTime dt) 里, 拿SneakyInput 传回来的数值对我们的角色等作出相关的操作:


CCPoint scaledV = ccpMult(joystick->getVelocity(), 480);

CCPoint pos = mSprite->getPosition();

pos.x += scaledV.x*dt;
pos.y += scaledV.y*dt;

mSprite->setPosition(pos);

if (button->getIsActive())
{
        // Fire !!!
}

在工程里为了方便使用, 我拿 SneakyInputEx 把 SneakyInput 封包了一下.

4.2 ?戏/程序架构

基本上我们把这个小?戏分作3 个场景: 菜单, 主?戏?景和?戏终结画面, 为了方便场景之间的调用, 我弄了一个叫 GameManager 的 singleton, 我们可以运用 GameManger 切换至3个场景中任何一个:
267

250

265

3个场景中最重要的当然是 GameScene, 这个场景再细分为两层, 下层是背景, 上层是玩家角色和敌人等等.

近来喜欢上了"组合好过继承"(Prefer Composition over Inheritance)的理论, 这个理论简单来说就是当我们设定一个新类时, 会把有关的类以组件(component)方式加到新类里而不是让新类继承他们. 举个例子, ?戏里的 Player 类一般我们都习惯把它设定为继承於 CCSprite, 但仔细地想一下, CCSprite 所代表的其实只是一个图像(或动画), 可以说只是 Player 的"样子", 而 Player 除了有个样子, 还会有其他的特性, 比如移动功能, 比如能源宝箱等等…当我们把 Player 继承於 CCSprite 即是意味著它"是" CCSprite, 但我们之後加在 Player 的功能?不是一般 CCSprite 所需要的功能, 所以更好的做法, 是把 CCSprite 设为 Player 的一个组件, 而 Player 除了有一个样子(CCSprite)的组件, 还会有一个控制它移动的组件或一个储存了玩家在?戏拾到的武器的宝箱组件等等… "组合而成"的类的最大好处, 是每个组件都互不相干, 各自负责自己的功能, 而我们可以任意把组件加入或拿走.

当然在这个小练习我并没有时间弄一整套组件系统出来(推荐大家看一下 Game Coding Complete 第4版, 其中有一章专门介绍怎样写一套完整的组件系统), 这里我只把概念简单地运用了一下在 Entity 类的设计, Player, Monster 和 Bullet 都是 Entity, 分别只在於:

Player 的组件是 CCSprite + JoystickController (或 KeyboardController, 见
4.5)
Monster 和 Bullet 的组件是 CCSprite + SimpleMoveController

假如我们在?戏里需要不同移动方法的怪兽, 只要再弄几个不同的Controller 放到不同的怪兽里就可以.

同样的道理, 我们的?戏可能会允许玩家选择不同的操控方法, 那只要我们把不同的操控Controller 类放到 Player 里就行, 非常方便.

4.3 Manager 的运用

写?戏我们通常都会把执行速度放在很重要的位置, 而在?戏进行中用 new 来生成物件会比较慢, 有机会影响?戏的流畅度, 所以这里我用了 MonsterManger 和 BulletManger 来预先生成在?戏里可能会用到最多数目的怪兽和子弹, 一开始先把它们"藏"起来, 在需要时才把他们设好位置和显示出来. 当然用这两个manager 的另一个好处是可以顺便在它们的update 里进行碰撞测试, 看看子弹有没有打中 怪兽 或 怪兽 有没有撞到玩家.

  mMonsterList = CCArray::arrayWithCapacity(MAX_MONSTER);
                mMonsterList->retain();

                for (int i=0;i<MAX_MONSTER;i++)
                {
                        Monster *monster = Monster::monsterWithBatchNode(spriteBatch);
                        monster->SetActive(false);

                        mMonsterList->addObject(monster);
                        
                        parentNode->addChild(monster);

                }

4.4 ?动的背景

一个静止的背景不是太有趣, 所以我们让它动起来吧!

如果只是把现在的背景向左?动, 背景很快会被?出画面, 右边会出现空白. 解决这个问题, 我们可以用两张背景, 当一张向左?动的时候, 另一张就去填补右边出现的空白. 而为了使到两个背景可以"无缝"连接, 第二张背其实是用了跟第一张一样的图像但左右对调了一下.

268

4.5 在 Windows 上测试

我们是用 SneakyInput 在画面上弄了一个操控杆和一个发射子弹的按钮, 在Windows 上测试?很是坑爹, 因为没办法同时的去点操空杆和按钮! 所以我另外写了一个用键盘操控的Controller 在 Windows 环境上用:

void KeyboardController::update(ccTime dt)
{

        if (mListener)
        {
                float xDelta = 0;
                float yDelta = 0;

                if ((GetAsyncKeyState(VK_LEFT) & 0x8000)==0x8000) // 上下左右键移动
                        xDelta = -mMovingSpeed;

                if ((GetAsyncKeyState(VK_RIGHT) & 0x8000)==0x8000)
                        xDelta = mMovingSpeed;

                if ((GetAsyncKeyState(VK_UP) & 0x8000)==0x8000)
                        yDelta = mMovingSpeed;

                if ((GetAsyncKeyState(VK_DOWN) & 0x8000)==0x8000)
                        yDelta = -mMovingSpeed;

                mListener->UpdatePosition(dt, xDelta, yDelta);


                if ((GetAsyncKeyState(VK_LCONTROL) & 0x8000)==0x8000) // 左边CTRL键射击
                {
                        if (!mFireKeyDown)
                        {
                                mFireKeyDown = true;
                                mListener->FirePrimary();
                        }
                }
                else
                        mFireKeyDown = false;

        }

}

这里马上又可以看到"组件" 的好处了, 在玩全不影响 Player 类的情况下, 我们只要把新的 KeyboardController 放进去, 就可以用键盘操控它的移动了!

#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
KeyboardController *controller = KeyboardController::controllerWithParentNode(parent);
#else
JoystickController *controller = JoystickController::controllerWithParentNode(parent);
#endif

CC_BREAK_IF (!controller);

SetController(controller);

266

4.6 粒子系统和声效

我觉得 cocos2d 的粒子系统不是太完美, 只是单发射口(emitter), 似乎很难做出很好很真实的粒子效果(比如 iFighter 2 里的爆炸效果), 这次只是随便的用了一下, 以後有时间再研究研究.

声效方面我也弄了一个 SoundManager 来统一处理, 是个 singleton, 方便任意调用. 在播放音乐时, 有个有趣的发现, SimpleAudioEngine::preloadBackgroundMusic() 里竟然是空的? 不知道引擎小组最後会不会加进这个功能呢?

340

刚接触这个,看到开头说做个简单的小游戏我顿时来了精神。下载后看到代码那么多顿时凌乱,这还叫简单啊,不禁反思自己到底有多菜。。。

很强大,照着做了一遍,学到了很多东西,做了一些修改,在pad上可以跑。

果断支持 哈哈哈 沙发。。。。

顶………………

— Begin quote from ____

hurryerman2012 发表于 2012-6-6 15:04 url

这个必须支持

2.0那里下的哦 楼主?

— End quote

https://github.com/cocos2d/cocos2d-x

?branch 是 gles20, 再? ZIP 下?就可以.

— Begin quote from ____

hurryerman2012 发表于 2012-6-6 15:58 url

主页应该更新了 还是1.2的哦

1>LINK : fatal error LNK1104: 无法打开文件“glew32.lib”

— End quote

github 上的版本可以直接用, 不用再下?其他?西呢.

  • 本帖最后由 dr_watson 于 2012-6-7 18:56 编辑 *

— Begin quote from ____

hurryerman2012 发表于 2012-6-7 17:48 url

里面没这个库哦

— End quote

在cocos2dxplatform hird_partywin32libraries 就有哦!

252

你是由?里下的? 我?才又下了一次??, ???.

253

支持, 谢谢分享…………

同??不好意思, 第一次上?的打包, 我把HelloWorldScene.h ?了但忘了更新vc的工程, ?把AppDelegate.cpp 里的 #include “HelloWorldScene.h” ?掉或再下一次打包. :L

占位:lol:lol:lol:lol:lol:lol:lol
非常好的文章,谢谢了。。。。。。

?於完成了第四篇! :lol:lol

支持大花生啊!

支持花生牛

粒子系统比较简单,这个事情我一周内刚被吐槽过,而且ParticleDeisnger也做不出复杂的效果来。

多谢分享。。。。。。。。。。。。。

— Begin quote from ____

walzer 发表于 2012-6-16 21:50 url

粒子系统比较简单,这个事情我一周内刚被吐槽过,而且ParticleDeisnger也做不出复杂的效果来。 …

— End quote

cocos2d 的粒子系?只有?一?射器, 而且粒子的?化只有start, end, 局限挺大的.

比如一?好的爆炸效果, 要有烟, 有火花, 再好???有碎片, 要多??射器加在一起才能做到. 而爆炸的火花, ?由忽然?光, 然後又?暗, 所以?化要有像key frame 般的?定才可以.

以我接??的2d引擎??, 我?得是torque2D 的粒子系?是比?出色的一?, 要是那?大牛能把它移植到cocos2d 就太好了!

http://www.garagegames.com/products/torque-2d

— Begin quote from ____

star特530 发表于 2012-12-7 09:56 url

刚接触这个,看到开头说做个简单的小游戏我顿时来了精神。下载后看到代码那么多顿时凌乱,这还叫简单啊,不 …

— End quote

你先弄一下cocos2d-x自带的例子再来弄这个,估计思路会清晰一点

现在CocosBuilder正在开发基于时间轴和关键帧的编辑功能,以后包括骨骼动画、更复杂的粒子效果这些,都寄望于此了。

学习了。。。