如何用cocos2d-x来开发简单的Uphone游戏:(二) 移动的精灵

简介:

三、添加一个精灵

 

我们先用个简单的方式,把player, projectile, target三个PNG文件拷贝到 D:\Work7\NEWPLUS\TDA_DATA\UserData 目录下,这使其可以在模拟器上直接通过文件路径访问到。Uphone有其资源打包的方式,图片和音乐都可以打包到动态库文件内,这个另外会有教程描述,我们这里先让事情简单化。

                 


关于cocos2d坐标系统的规则,简而言之就是左下角为原点,向上向右按像素递增,这在Wenderlich的原文中有详细描述,我们这里就不再赘述了。直接切入代码

现在我们在HelloWorldScene.cpp里面,找到bool HelloWorld::init()函数,把它替换成下面代码

 

复制代码
bool  HelloWorld::init()
{
  
//////////////////////////////
   //  1. super init first
   if  (  ! CCLayer::init() )
  {
    
return   false ;
  }

  
/////////////////////////// //
   //  2. add a menu item with "X" image, which is clicked to quit the program
  
//     you may modify it.

  
//  add a "close" icon to exit the progress. it's an autorelease object
  CCMenuItemImage  * pCloseItem  =  CCMenuItemImage::itemFromNormalImage(  " CloseNormal.png "
                                                                      
" CloseSelected.png "
                                                                      
this ,
                                                                      menu_selector(HelloWorld::menuCloseCallback) );
  pCloseItem -> setPosition( ccp(CCDirector::getSharedDirector() -> getWinSize().width  -   20 20 ) );

  
//  create menu, it's an autorelease object
  CCMenu *  pMenu  =  CCMenu::menuWithItems(pCloseItem, NULL);
  pMenu -> setPosition( CGPointZero );
  
this -> addChild(pMenu);

  
/////////////////////////// //
   //  3. add your codes below...

  CGSize winSize  =  CCDirector::getSharedDirector() -> getWinSize();
  CCSprite  * player  =  CCSprite::spriteWithFile( " Player.png "
                                              CGRectMake( 0 0 27 40 ) );
  player -> setPosition( ccp(player -> getContentSize().width / 2 , winSize.height / 2 ) );
  
this -> addChild(player);

  
return   true ;
}
复制代码

 

其实我们只修改了 // 3. add your codes below 这段。

cocos2d-x和cocos2d-iphone的接口有细微的差别,不过你一旦习惯了这个差别,写起代码来就会很顺手。

我们抛开添加"X"退出按钮的一段,单纯地看一下这两段init函数的差异.

 

复制代码
//  cpp with cocos2d-x
bool  HelloWorld::init()
{
   if  ( CCLayer::init() )
  {
    CGSize winSize  =  CCDirector::getSharedDirector() -> getWinSize();
    CCSprite  * player  =  CCSprite::spriteWithFile( " Player.png "
                                     CGRectMake( 0 0 27 40 ) );
    player -> setPosition( ccp(player -> getContentSize().width / 2
                               winSize.height / 2 ) );
    this -> addChild(player);
  }
   return   true ;
}
复制代码
复制代码
//  objc with cocos2d-iphone
- (id) init
{
   if ( (self = [super init] )) 
  {
    CGSize winSize  =  [[CCDirector sharedDirector] winSize];
    CCSprite  * player  =  [CCSprite spriteWithFile: @" Player.png "
                                  rect:CGRectMake( 0 0 27 40 ) ];
    player.position  =  ccp(player.contentSize.width / 2
                          winSize.height / 2 );
    [self addChild:player];
  }
   return  self;
复制代码

 

转换要点

1. 虽然VC++中有super关键字,但linux下用gcc编译时可不认。所以C++中不能用super::init(),而必须老老实实地指定是执行父类CCLayer::init()方法

 

2. 由于cpp里没有property的概念,所以在objc里涉及property的地方,我们都用了get/set函数代替。于是,访问CCDirector.SharedDirector属性的代码,就变成了CCDirector::getSharedDirector()函数调用,shared开头小写的s,也变成了大写的S。同样规则,取得winSize属性的地方,则变成了getWinSize()函数调用。这段代码中还有player->getContentSize()也受此影响。但访问结构体中的变量,如winSize中的width和height则不需用getter封装。

 

3. 设置类的属性,如player.position = ,也改用setter实现,变成player->setPosition(...)


4. C++函数调用不需像OBJC那样在每个参数前面说明这参数是干什么用的,比如rect:CGRectMake(...),只需直接输入参数即可。另一方面, cocos2d-x仿照iOS实现了CGGeometry的一些函数,你可以在cocos2dx\include\CGGeometry.h里看到它们。除了CGRectMake,还有CGPointMake, CGSizeMake, CGPointZero, CGSizeZero, CGRectZero.


5. cocos2d-x所有的游戏元素, sprite,layer,scene,label,action等,都是new在heap上,并且用指针传递的,因此调用其成员函数一定是用->号,而不像objc里的点号


6. cpp里用this替代了objc的self关键字

 

7. cocos2d-x里的init函数改成返回bool类型了。由于cpp里没有objc的"id"关键字,所以cocos2d-iphone里返回id的地方,都改成返回明确的类指针,或者bool型变量


好了,我们编译运行一下,可以看到带头大哥一袭黑衣,很猥琐地躲在黑色背景上,只露出一双杀红了的眼睛。为了游戏性,我们需要把背景颜色改成白的。只要简单地修改,使HelloWorld不是继承CCLayer,而是继承CCColorLayer就行了。


在HelloWorldScene.h中,修改HelloWorld类声明如下。

 (左边为cpp代码,也就是读者现在应该使用的,右边为Cocos2dSimpleGame原来使用cocos2d-iphone的objc代码,用以对比参考。本系列后面的行文也都如此)

//  cpp with cocos2d-x
class  HelloWorld :  public  cocos2d::CCColorLayer
//  objc with cocos2d-iphone
@interface HelloWorld : CCColorLayer

 

然后在HelloWorld::init()函数实现中,修改刚开始的 

if  (  ! CCLayer::init() )
{
    
return   false ;
}

变成

if  (  ! CCColorLayer::initWithColor( ccc4( 255 , 255 , 255 , 255 ) ) )
{
    
return   false ;
}

这里小改了一下逻辑,原版objc里是如果super init成功,就BALA-BALA做后面的工作;我喜欢防御性编程,如果失败则先做出错处理、跳出,然后才继续写正确流程。这么做有两个好处,一是不会写到后面漏掉了错误处理,二是不用做太多层的if嵌套。这个是题外话了。抛开if的逻辑,我们来对比一下这句super init在cpp和objc的区别

//  cpp with cocos2d-x
if  ( CCColorLayer::initWithColor( ccc4( 255 , 255 , 255 , 255 ) )
//  objc with cocos2d-iphone
if  ( self  =  [super initWithColor:ccc4( 255 , 255 , 255 , 255 )] )

 

转换要点

1. 首先,cpp的继承默认为private继承,所以类声明的继承处public关键字不可少 

2. cocos2d-iphone的作者Ricardo Quesada建议我们采用C++的命名空间把cocos2d整个库包起来。而我们在这里既不想直接到头文件里using namespace cocos2d;感染了所有包含这个头文件的CPP文件,也不想把class HelloWorld归到cocos2d命名空间内,所以HelloWorldScene.h头文件里只好在每个cocos2d类前面加上命名空间cocos2d::


编译后运行,你就可以看到带头大哥孤独地站在白色背景上了,寂寞得泪流满面


 

 

四、移动目标


有了带头大哥后,我们就需要添加一些虾兵蟹将让大哥砍。英雄人物一般不喜欢砍木桩,所以我们就用void addTarget()方法在屏幕右边创建一些跑龙套的小兵,让他们以随机速度向左移动。


先到HelloWorldScene.h里添加函数声明 void addTarget(); 然后回到HelloWorldScene.cpp里实现函数

复制代码
//  cpp with cocos2d-x
void  HelloWorld::addTarget()
{
  CCSprite  * target  =  CCSprite::spriteWithFile( " Target.png "
                                        CGRectMake( 0 , 0 , 27 , 40 ) );

   //  Determine where to spawn the target along the Y axis
  CGSize winSize  =  CCDirector::getSharedDirector() -> getWinSize();
   int  minY  =  target -> getContentSize().height / 2 ;
   int  maxY  =  winSize.height  -   target -> getContentSize().height / 2 ;
   int  rangeY  =  maxY  -  minY;
  srand( TimGetTicks() );
   int  actualY  =  ( rand()  %  rangeY )  +  minY;

   //  Create the target slightly off-screen along the right edge,
   //  and along a random position along the Y axis as calculated
  target -> setPosition( 
    ccp(winSize.width  +  (target -> getContentSize().width / 2 ), 
    actualY) );
   this -> addChild(target);

   //  Determine speed of the target
   int  minDuration  =  ( int ) 2.0 ;
   int  maxDuration  =  ( int ) 4.0 ;
   int  rangeDuration  =  maxDuration  -  minDuration;
  srand( TimGetTicks() );
   int  actualDuration  =  ( rand()  %  rangeDuration )  +  minDuration;

   //  Create the actions
  CCFiniteTimeAction *  actionMove  =  
    CCMoveTo::actionWithDuration( (ccTime)actualDuration, 
        ccp( 0   -  target -> getContentSize().width / 2 , actualY) );
  CCFiniteTimeAction *  actionMoveDone  =  
    CCCallFuncN::actionWithTarget(  this
                callfuncN_selector(HelloWorld::spriteMoveFinished));
  target -> runAction( CCSequence::actions(actionMove, 
                     actionMoveDone, NULL) );
}
复制代码
复制代码
//  objc with cocos2d-iphone
- ( void )addTarget 
{
  CCSprite  * target  =  [CCSprite spriteWithFile: @" Target.png "
                                rect:CGRectMake( 0 0 27 40 )];

   //  Determine where to spawn the target along the Y axis
  CGSize winSize  =  [[CCDirector sharedDirector] winSize];
   int  minY  =  target.contentSize.height / 2 ;
   int  maxY  =  winSize.height  -  target.contentSize.height / 2 ;
   int  rangeY  =  maxY  -  minY;

   int  actualY  =  (arc4random()  %  rangeY)  +  minY;

   //  Create the target slightly off-screen along the right edge,
   //  and along a random position along the Y axis as calculated
  target.position  =  
    ccp(winSize.width  +  (target.contentSize.width / 2 ), 
    actualY);
  [self addChild:target];

   //  Determine speed of the target
   int  minDuration  =   2.0 ;
   int  maxDuration  =   4.0 ;
   int  rangeDuration  =  maxDuration  -  minDuration;

   int  actualDuration  =  (arc4random()  %  rangeDuration)  +  minDuration;

   //  Create the actions
  id actionMove  =  
    [CCMoveTo actionWithDuration:actualDuration
          position:ccp( - target.contentSize.width / 2 , actualY)];
  id actionMoveDone  =  
    [CCCallFuncN actionWithTarget:self
                selector:@selector(spriteMoveFinished:)];
  [target runAction:[CCSequence actions:actionMove, 
                    actionMoveDone, nil]]; 
复制代码

 

这里用callfuncN_selector(HelloWorld::spriteMoveFinished)回调了spriteMoveFinished方法,我们需要实现之。同样别忘记在头文件里加入声明, 然后实现之

//  cpp with cocos2d-x
void  HelloWorld::spriteMoveFinished(CCNode *  sender)
{
  CCSprite  * sprite  =  (CCSprite  * )sender;
   this -> removeChild(sprite,  true );
}
//  objc with cocos2d-iphone
- ( void )spriteMoveFinished:(id)sender 
{
  CCSprite  * sprite  =  (CCSprite  * )sender;
  [self removeChild:sprite cleanup:YES]; 

 

转换要点

1. 随机函数。在iphone上可以用arc4random()直接生成随机函数,而uphone上还是用传统的方法,先获取毫秒级时间(这个函数在uphone上是TimGetTickes()),用srand(int)塞进去作为random seed,然后再调用rand()生成随机数。其中srand和rand是C标准库函数

 

2. objc中的YES和NO,在cpp中变成true和false,这个容易理解

 

3. 回调函数.在objc中用 selector:@selector(spriteMoveFinished),在cpp中实现就比较复杂了,具体可以看cocos2dx\include\selector_protocol.h里面的声明。总之每种可能出现selector的地方,都有唯一的函数指针类型与之匹配。一共有5种回调函数类型

  • schedule_selector
  • callfunc_selector
  • callfuncN_selector
  • callfuncND_selector
  • menu_selector

具体使用时,可以看所用函数的变量类型定义来决定。比如使用CCTimer::initWithTarget方法,第二个参数是SEL_SCHEDULE类型,到selector_protocol.h里查一下,可以看到对应是schedule_selector(_SELECTOR)宏,所以调用时就需要在类里头实现一个void MyClass::MyCallbackFuncName(ccTime)函数,然后把schedule_selector(MyClass::MyCallbackFuncName)作为CCTimer::initWithTarget的第二个参数传入。

 

 

有了addTarget后,我们需要定时地调用它。所以在init函数返回前增加这个函数调用

//  cpp with cocos2d-x
//  Call game logic about every second
this -> schedule( schedule_selector(HelloWorld::gameLogic),  1.0  );
//  objc with cocos2d-iphone
//  Call game logic about every second
[self schedule:@selector(gameLogic:) interval: 1.0 ];

 

然后实现gameLogic这个回调函数

//  cpp with cocos2d-x
void  HelloWorld::gameLogic(ccTime dt)
{
     this -> addTarget();
}
//  objc with cocos2d-iphone
- ( void )gameLogic:(ccTime)dt
{
    [self addTarget];
}

 

不要忘记在头文件里增加函数声明,并且应为public函数,否则回调是调用不到的。


编译运行,现在你应该看到小喽啰们张牙舞爪地向大哥扑过来。于是拯救世界、维护人类和平的重任就交给大哥了。



本文转自Walzer博客园博客,原文链接:http://www.cnblogs.com/walzer/archive/2010/10/10/1847100.html,如需转载请自行联系原作者


 

相关文章
|
4月前
|
图形学
【Unity3D开发小游戏】Unity3D零基础一步一步教你制作跑酷类游戏
【Unity3D开发小游戏】Unity3D零基础一步一步教你制作跑酷类游戏
|
10月前
|
存储 图形学 Android开发
【游戏开发】使用unity创建2D游戏
Unity是一种非常强大的游戏引擎,可以帮助你创建各种类型的游戏,包括2D游戏。在本文中,我们将教你如何使用Unity创建2D游戏。首先,你需要确保你已经下载并安装了最新版本的Unity。如果你还没有安装Unity,可以从官方网站下载免费的Unity Hub来管理你的Unity版本。
388 0
|
11月前
游戏开发零基础入门教程(11):游戏积木之动画
让我们直接从示例开始,上一节中我们让“战机”发射子弹,击中“敌机”,然后敌机直接隐藏。这个过程过于粗糙,在真实的游戏中,敌机被击中后往往会伴随着爆炸动画以及音效。
57 0
|
图形学
unity案例入门(拾取游戏)
案例简述这个案例实现一个非常简单的拾取宝物游戏,主角是一个小球,玩家通过键盘控制小球拾取全部宝物。 键盘控制物体移动 Rigidbody rd;public int force = 10; void Start () {rd = GetComponent ();//获得物体的刚体组件}void Update () {float h = Input.
1559 0
|
图形学
Unity 3D游戏-塔防类游戏源码:重要方法和功能的实现
Unity-塔防游戏源码 本文提供全流程,中文翻译。Chinar坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 —— 高分辨率用户请根据需求调整网页缩放比例) ...
1849 0
|
前端开发 图形学
Unity 3D游戏-贪吃蛇类游戏源码:重要方法和功能的实现
贪吃蛇类游戏源码 本文提供全流程,中文翻译。Chinar坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 —— 高分辨率用户请根据需求调整网页缩放比例) ...
2117 0