源码:git@github.com:baidang201/ARPG_Zhaoyun.git  

本章在前面的基础上《Cocos2d-x自定义血条及其美化--之游戏开发《赵云要格斗》(4)》设计一个怪物类,并实现怪物的上方显示血条,血条跟随怪物的运动而运动。用到的血条类在上一讲中,平时我们游戏一般怪物都是头顶一个血条的,这里我们就是要实现这个功能。


Cocos2d-x版本:2.2.5

工程环境:windows7+VS2010

打开方式:将工程放在cocos2d-x安装目录下的project文件夹下用VS打开


重要说明:由于TexturePacker试用期结束了,不能再用,所以接下来的动画都不合成plist和整张的PNG。同时,之前的赵云图像太动画效果不是很好,所以换了个赵云的图片。另外,将整个项目的类都分别归档,这样更加容易看懂些,所以hero.h和hero.cpp有些函数进行了更改,同时调用的地方也改了下。这里一定要注意!

20150107172405031.jpg

这是本章的一个效果:

20150107171708370.jpg

20150107175528218.gif

(下一章的效果)


一、更改英雄hero类

赵云的图片:以下中是一部分,动画就是通过读一张一张的PNG图片来实现的(没有再合成Plist和整张PNG)

20150107173027871.jpg

更改后的英雄类Hero.h(类的函数和变量还是不变的,使用方法还是不变的,只不SetAnimation函数参数更改变了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#ifndef __HERO_H__
#define __HERO_H__
#include "cocos2d.h"
using  namespace  cocos2d;
  class  Hero: public  cocos2d::CCNode
  {
      public :
        Hero( void );
       ~Hero( void );
      //根据图片名创建英雄
      void  InitHeroSprite( char  *hero_name);
      //设置动画,num为图片数目,run_directon为精灵脸朝向,false朝右,name_each为name_png中每一小张图片的公共名称部分
      void  SetAnimation( const  char  *name_each, const  unsigned  int  num, bool  run_directon);
     //停止动画
      void  StopAnimation();
      //攻击动画
      void  AttackAnimation( const  char  *name_each, const  unsigned  int  num, bool  run_directon);
      //攻击动画结束
      void  AttackEnd();
      //判断英雄是否运动到了窗口的中间位置,visibleSize为当前窗口的大小  
      bool  JudgePositona(CCSize visibleSize);  
     //判断是否在跑动画
      bool  IsRunning;
      //判断是否在攻击动画
      bool  IsAttack;
      //英雄运动的方向
      bool  HeroDirecton;
      CREATE_FUNC(Hero);
      private :
      CCSprite* m_HeroSprite; //精灵
      char  *Hero_name; //用来保存初始状态的精灵图片名称
 
  };
#endif // __HERO_H__

更改后的英雄类Hero.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include "Hero.h"
USING_NS_CC; 
Hero::Hero( void )
{
     IsRunning= false ; //没在放动画
     HeroDirecton= false ; //向右运动
     Hero_name=NULL;
     IsAttack= false ;
}
 
Hero::~Hero( void )
{
 
}
  void  Hero::InitHeroSprite( char  *hero_name)
  {
     Hero_name=hero_name;
     this ->m_HeroSprite=CCSprite::create(hero_name);
     this ->addChild(m_HeroSprite);
  }
  void  Hero::SetAnimation( const  char  *name_each,unsigned  int  num, bool  run_directon)
  {
      if (HeroDirecton!=run_directon)
      {   HeroDirecton=run_directon;
          m_HeroSprite->setFlipX(run_directon);
      }
      if (IsRunning)
          return ;
      CCAnimation* animation = CCAnimation::create();  
      for int  i=1;i<=num;i++)  
      {  
          char  szName[100] = {0};  
          sprintf (szName, "%s%d.png" ,name_each,i);  
          animation->addSpriteFrameWithFileName(szName);  //加载动画的帧  
      }  
      animation->setDelayPerUnit(0.1f);  
      animation->setRestoreOriginalFrame( true );  
      animation->setLoops(-1);  //动画循环
      if (HeroDirecton!=run_directon)
      {   HeroDirecton=run_directon;
 
      }
      //将动画包装成一个动作
      CCAnimate* act=CCAnimate::create(animation);
     
      m_HeroSprite->runAction(act);
      IsRunning= true ;
 
  }
  void  Hero::StopAnimation()
  {
      if (!IsRunning)
          return ;
     m_HeroSprite->stopAllActions(); //当前精灵停止所有动画
 
     //恢复精灵原来的初始化贴图 
     this ->removeChild(m_HeroSprite,TRUE); //把原来的精灵删除掉
     m_HeroSprite=CCSprite::create(Hero_name); //恢复精灵原来的贴图样子
     m_HeroSprite->setFlipX(HeroDirecton);
     this ->addChild(m_HeroSprite);
     IsRunning= false ;
  }
   void  Hero::AttackAnimation( const  char  *name_each, const  unsigned  int  num, bool  run_directon)
   {
       if (IsAttack)
           return ;
       IsAttack= true ;
       CCAnimation* animation = CCAnimation::create();  
       for int  i=1;i<=num;i++)  
       {  
           char  szName[100] = {0};  
           sprintf (szName, "%s%d.png" ,name_each,i);  
           animation->addSpriteFrameWithFileName(szName);  //加载动画的帧  
       }  
       animation->setDelayPerUnit(0.05f);  
       animation->setRestoreOriginalFrame( true );  
       animation->setLoops(1);  //动画循环
       if (HeroDirecton!=run_directon)
       {   HeroDirecton=run_directon;
 
       }
       //将动画包装成一个动作
       CCAnimate* act=CCAnimate::create(animation);
 
 
       //创建回调动作,攻击结束后调用AttackEnd()
       CCCallFunc* callFunc=CCCallFunc::create( this ,callfunc_selector(Hero::AttackEnd));
       //创建连续动作
      CCActionInterval* attackact=CCSequence::create(act,callFunc,NULL);
       
       m_HeroSprite->runAction(attackact); 
 
   }
   void  Hero::AttackEnd()
   {
       //恢复精灵原来的初始化贴图 
       this ->removeChild(m_HeroSprite,TRUE); //把原来的精灵删除掉
       m_HeroSprite=CCSprite::create(Hero_name); //恢复精灵原来的贴图样子
       m_HeroSprite->setFlipX(HeroDirecton);
       this ->addChild(m_HeroSprite);
        IsAttack= false ;
   }
   bool  Hero::JudgePositona (CCSize visibleSize)
   {
       if ( this ->getPositionX()!=visibleSize.width/2) //精灵到达左边
           return  false ;
       else
           return  true ; //到达中间位置
   }

记得在用的地方要改下SetAnimation,其它地方都不变,只不SetAnimation函数参数更改变了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
void  HelloWorld::update( float  delta)
{
     
     //判断是否按下摇杆及其类型
     CCSize visibleSize1 = CCDirector::sharedDirector()->getVisibleSize(); //得到窗口大小
     switch (rocker->rocketDirection)
     {
     case   1:
         hero->SetAnimation( "hero_run" ,8,rocker->rocketRun);
         if (hero->getPositionX()<=visibleSize1.width-8) //不让精灵超出右边,8可以改成你喜欢的
         {
             if (!hero->JudgePositona(visibleSize1)||mymap->JudgeMap(hero,visibleSize1)) //精灵没到达窗口中间位置或者地图已经移动到边缘了,精灵才可以移动,否则只播放动画
                 hero->setPosition(ccp(hero->getPosition().x+1,hero->getPosition().y));  //向右走
             //下面是移动地图
             mymap->MoveMap(hero,visibleSize1);
         }
         break ;
     case   2:
         hero->SetAnimation( "hero_run" ,8,rocker->rocketRun);
         hero->setPosition(ccp(hero->getPosition().x, hero->getPosition().y+1));    //向上走
         break ;
     case  3:
         hero->SetAnimation( "hero_run" ,8,rocker->rocketRun);
         if (hero->getPositionX()>=8) //不让精灵超出左边,8可以改成你喜欢的
             hero->setPosition(ccp(hero->getPosition().x-1,hero->getPosition().y));    //向左走
         break ;
     case  4:
         hero->SetAnimation( "hero_run" ,8,rocker->rocketRun);
         hero->setPosition(ccp(hero->getPosition().x,hero->getPosition().y-1));    //向下走
         break ;
     case  0:
         hero->StopAnimation(); //停止所有动画和运动
         break ;
     }
 
     //判断是否出动攻击
     if (btn->isTouch)
     {
         if (hero->IsAttack) //英雄没在攻击
             return ;
         hero->AttackAnimation( "hero_attack" ,20,rocker->rocketRun);
         m_pProgressView->setCurrentProgress(m_pProgressView->getCurrentProgress()-10);  //更改血量
     }
 
 
}

效果:和以前相比,赵云的图片更加清楚了些,而且攻击的图片也比较顺了一点(20张图片啊!)

20150107165843436.gif


20150107165855906.gif


二、自定义带血条的怪物

这里的血条用到了前面的自定义血条,思路就是把上篇自定义的血条类ProgressView应用在Monster中,得到Monster类中怪物的位置,然后根据这个位置来设置血条成员变量的位置(一般在上方),最后把怪物精灵和血条类都addchild()进来就行了。


下面这是怪物的资源:(一部分,动画也是通过一张一张的播放的)

20150107174912859.jpg

Monster.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#ifndef __Monster_H__
#define __Monster_H__
#include "cocos2d.h"
#include "ProgressView.h"
USING_NS_CC;
class  Monster: public  cocos2d::CCNode
{
     public :
     Monster( void );
     ~Monster( void );
     //根据图片名创建怪物,不带血条
     void  InitMonsterSprite( char  *name);
     //带血条的怪物 
     void  InitMonsterSprite( char  *name, char  *xue_back, char * xue_fore);
     //设置动画,num为图片数目,run_directon为精灵脸朝向,false朝右,name_each为name_png中每一小张图片的公共名称部分
     void  SetAnimation( const  char  *name_each, const  unsigned  int  num, bool  run_directon);
     //停止动画
     void  StopAnimation();
     //攻击动画
     void  AttackAnimation( const  char  *name_each, const  unsigned  int  num, bool  run_directon);
     //攻击动画结束
     void  AttackEnd();
     //返回英雄
     CCSprite* GetSprite();
     //判断是否在跑动画
     bool  IsRunning;
     //判断是否在攻击动画
     bool  IsAttack;
     //英雄运动的方向
     bool  MonsterDirecton;
     CREATE_FUNC(Monster);
     private :
     CCSprite* m_MonsterSprite; //怪物精灵
     char  *Monster_name; //用来保存初始状态的精灵图片名称
     ProgressView*  Monster_xue; //怪物血条
     
};
#endif // __HERO_H__

Monster.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include "Monster.h"
USING_NS_CC; 
Monster::Monster( void )
{
     IsRunning= false ; //没在放动画
     MonsterDirecton=TRUE; //向右运动
     Monster_name=NULL;
     IsAttack= false ;
     Monster_xue=NULL;
}
 
Monster::~ Monster( void )
{
 
}
CCSprite* Monster::GetSprite()
{
     return  m_MonsterSprite;
}
void   Monster::InitMonsterSprite( char  *name)
{
     Monster_name=name;
     this ->m_MonsterSprite=CCSprite::create(name);
         m_MonsterSprite->setFlipX(MonsterDirecton);
     this ->addChild(m_MonsterSprite);
}
void  Monster::InitMonsterSprite( char  *name, char  *xue_back, char * xue_fore)
{
     InitMonsterSprite(name);
     //设置怪物的血条 
     Monster_xue =  new  ProgressView();  
     Monster_xue->setPosition(ccp(m_MonsterSprite->getPositionX()+25, m_MonsterSprite->getPositionY()+50)); //设置在怪物上头  
     //Monster_xue->setScale(2.2f);  
     Monster_xue->setBackgroundTexture(xue_back);  
     Monster_xue->setForegroundTexture(xue_fore);  
     Monster_xue->setTotalProgress(300.0f);  
     Monster_xue->setCurrentProgress(300.0f); 
     this ->addChild(Monster_xue);
}
void   Monster::SetAnimation( const  char  *name_each,unsigned  int  num, bool  run_directon)
{
     if (MonsterDirecton!=run_directon)
     {   MonsterDirecton=run_directon;
     m_MonsterSprite->setFlipX(run_directon);
     }
     if (IsRunning||IsAttack)
         return ;
     CCAnimation* animation = CCAnimation::create();  
     for int  i=1;i<=num;i++)  
     {  
         char  szName[100] = {0};  
         sprintf (szName, "%s%d.png" ,name_each,i);  
         animation->addSpriteFrameWithFileName(szName);  //加载动画的帧  
     }  
     animation->setDelayPerUnit(2.8f / 14.0f);  
     animation->setRestoreOriginalFrame( true );  
     animation->setLoops(-1);  //动画循环
     //将动画包装成一个动作
     CCAnimate* act=CCAnimate::create(animation);
     m_MonsterSprite->runAction(act);
     IsRunning= true ;
 
}
void   Monster::StopAnimation()
{
     if (!IsRunning)
         return ;
     m_MonsterSprite->stopAllActions(); //当前精灵停止所有动画
     //恢复精灵原来的初始化贴图 
     this ->removeChild(m_MonsterSprite,TRUE); //把原来的精灵删除掉
     m_MonsterSprite=CCSprite::create(Monster_name); //恢复精灵原来的贴图样子
     m_MonsterSprite->setFlipX(MonsterDirecton);
     this ->addChild(m_MonsterSprite);
     IsRunning= false ;
}
void   Monster::AttackAnimation( const  char  *name_each, const  unsigned  int  num, bool  run_directon)
{
     if (IsAttack||IsRunning)
         return ;
     CCAnimation* animation = CCAnimation::create();  
     for int  i=1;i<=num;i++)  
     {  
         char  szName[100] = {0};  
         sprintf (szName, "%s%d.png" ,name_each,i);  
         animation->addSpriteFrameWithFileName(szName);  //加载动画的帧  
     }  
     animation->setDelayPerUnit(2.8f / 14.0f);  
     animation->setRestoreOriginalFrame( true );  
     animation->setLoops(1);  //动画循环1次  
     //将动画包装成一个动作
     CCAnimate* act=CCAnimate::create(animation);
     //创建回调动作,攻击结束后调用AttackEnd()
     CCCallFunc* callFunc=CCCallFunc::create( this ,callfunc_selector(Monster::AttackEnd));
     //创建连续动作
     CCActionInterval* attackact=CCSequence::create(act,callFunc,NULL);
 
     m_MonsterSprite->runAction(attackact);  
     IsAttack= true ;
 
}
void  Monster::AttackEnd()
{
     //恢复精灵原来的初始化贴图 
     this ->removeChild(m_MonsterSprite,TRUE); //把原来的精灵删除掉
     m_MonsterSprite=CCSprite::create(Monster_name); //恢复精灵原来的贴图样子
     m_MonsterSprite->setFlipX(MonsterDirecton);
     this ->addChild(m_MonsterSprite);
     IsAttack= false ;
}

使用方法:

  • HelloWorldScene.h添加头文件 #include "Monster.h"

  • HelloWorldScene.h添加成员变量: Monster *monster1;//怪物种类1

  • HelloWorldScene.cpp的Init()函数进行初始化:


这是不带血条的怪物:

1
2
3
4
5
6
//添加怪物
monster1=Monster::create();
monster1->InitMonsterSprite( "monster.png" );
//monster1->InitMonsterSprite("monster.png","xue_back.png","xue_fore.png");
monster1->setPosition(ccp(visibleSize.width-150,visibleSize.height/2));
this ->addChild(monster1,1);

20150107171622125.jpg

这是带血条的怪物:

1
2
3
4
5
6
//添加怪物
monster1=Monster::create();
//monster1->InitMonsterSprite("monster.png");
monster1->InitMonsterSprite( "monster.png" , "xue_back.png" , "xue_fore.png" );
monster1->setPosition(ccp(visibleSize.width-150,visibleSize.height/2));
this ->addChild(monster1,1);

20150107171708370-(1).jpg

好了,这一篇就结束了,下一篇我们将会来讲讲智能怪物,让怪物动起来并能出动攻击!

下面是血条跟随怪物移动的效果。


源码下载:血条跟随怪物运动

//

总结下TexturePacker纹理的调用和多个图片调用的区别。

1  压缩纹理的调用

将压缩纹理图片载入全局纹理缓存CCSpriteFrameCache-》获取精灵框帧CCSpriteFrame,将特定序列的精灵框帧增加到列表-》使用列表初始化动画Animation-》使用动画包装成动作CCAnimate

2 多个图片的调用

使用创建动画对象Animation->动画增加每一帧的图片->动画包装成动作CCAnimate

:如下是示例代码:


《1  压缩纹理
    CCSpriteFrameCache *m_frameCache=CCSpriteFrameCache::sharedSpriteFrameCache();
     m_frameCache->addSpriteFramesWithFile(name_plist,name_png);
     //用一个列表保存所有的CCSpriteFrameCache
     Vector<CCSpriteFrame*>  frameArray = Vector<CCSpriteFrame*>();
     unsigned int i;
     for(i=startIndex;i<=num;i++)
     {
         CCSpriteFrame* frame=m_frameCache->spriteFrameByName(CCString::createWithFormat("%s%d.png", actNameInNamePng, i)->getCString());
         frameArray.pushBack(frame);
     }
     //使用列表创建动画对象
     Animation* animation=Animation::createWithSpriteFrames(frameArray);
     animation->setLoops(-1);//表示无限循环播放
     animation->setDelayPerUnit(0.1f);//每两张图片的时间隔,图片数目越少,间隔最小就越小
     //将动画包装成一个动作
     CCAnimate* act=CCAnimate::create(animation);
     m_HeroSprite->runAction(act);

《2  多个图片的调用
CCAnimation* animation = CCAnimation::create();  
for( int i=1;i<=num;i++)  
{  
char szName[100] = {0};  
sprintf(szName,"%s%d.png",name_each,i);  
animation->addSpriteFrameWithFileName(szName); //加载动画的帧  
}  
animation->setDelayPerUnit(2.8f / 14.0f);  
animation->setRestoreOriginalFrame(true); //动画播放结束后,回到初始帧 
animation->setLoops(-1); //动画循环
//将动画包装成一个动作
CCAnimate* act=CCAnimate::create(animation);
m_MonsterSprite->runAction(act);
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐