【Cocos游戏实战】功夫小子第二课之基础类分析和实现

原创 2015年05月29日 22:58:41

本节课的视频教程地址是:第二课在此

如果本教程有帮助到您,希望您能点击进去观看一下,而且现在注册成为极客学院的会员,验证手机号码和邮箱号码会赠送三天的会员时间,会员可以无限制的下载和观看所有的视频,谢谢您的支持!


在开始第二节课之前,这里需要声明的是,

首先:本系列课程是为了结合Cocos2d-x 3.x 的基本组件和核心模块的学习而制作的,开发所使用的版本是3.0,但是代码稍加修改就可以运用在3.X的其他版本上。

其次:本游戏项目是一个非商业化项目,游戏资源和代码都会在后续的课程中释放出来,供大家参考使用,提供的代码所使用的解决方案是最基础和简单的,没有对引擎源码进行改造,提供给有一定Cocos2d-x的初学者,希望学习的人能再此基础上进行思考,并加以改进。

So,各位大神见笑了,可绕道~~


上一节课我们对游戏的基本需求进行了一个大概的分析,知道了游戏的基本内容,游戏中基本角色属性和游戏的基本规则特点。这节课我们就将对我们游戏项目的基本类进行分析和实现。

主要内容是:

  • 游戏项目基本工具类的分析和实现
  • 游戏项目英雄实体类的分析和实现
  • 具有简单AI的怪物实体类的分析和实现

基本的工具类分析和实现

由于我们的游戏内容和属性决定我们游戏中的主角和游戏中怪物会有各种动作,这些动作的创建我这里采用帧动画的形式,因此对于众多的动作,有一个专门的动画创建器是必须而且有利于减少代码的重复的,下面是关于帧动画(Frame By Frame),其基本的解释是在“连续的关键帧”中分解动画动作,也就是在时间轴的每帧上逐帧绘制不同的内容,使其连续播放而成动画。
比如下面主角跑动作的动画帧:

需要注意的是这些动画帧的命名规律。
关于帧动画:

这里的帧缓存是指我们将游戏的图片帧资源进行打包预加载进入游戏缓存,这部分会在后面的课程中详细讲解,这里不详细赘述。

下面就是动画创建器的一些关键部分的代码:
头文件:
/*!
 * \file ActionTool.h
 * \date 2015/05/07 22:10
 *
 * \author SuooL
 * Contact: hu1020935219@gmail.com
 *
 * \brief 工具类:创建动画
 *
 * TODO: long description
 *
 * \note
*/

#ifndef ActionTool_H__
#define ActionTool_H__

#include "cocos2d.h"
USING_NS_CC;

class ActionTool {
public:
	static Animate* animationWithFrameName(const char *frameName, int iloops, float delay);
	static Animate* animationWithFrameAndNum(const char *frameName, int num, float delay);
};

#endif // ActionTool_H__s

Cpp文件
/*!
 * \class ActionTool
 *
 * \ingroup GroupName
 *
 * \brief 动画创建器
 *
 * TODO: long description
 *
 * \note 
 *
 * \author SuooL
 *
 * \version 1.0
 *
 * \date 五月 2015
 *
 * Contact: hu1020935219@gmail.com
 *
 */

#include "ActionTool.h"

Animate* ActionTool::animationWithFrameName(const char *frameName, int iloops, float delay)
{
<span style="white-space:pre">	</span>SpriteFrame* frame = NULL;
<span style="white-space:pre">	</span>Animation* animation = Animation::create();
<span style="white-space:pre">	</span>int index = 1;
<span style="white-space:pre">	</span>// 创建帧对象 
<span style="white-space:pre">	</span>do
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>String * name = String::createWithFormat("%s%d.png", each_name, index++);
<span style="white-space:pre">		</span>frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(name->getCString());
<span style="white-space:pre">		</span>if (frame == NULL)
<span style="white-space:pre">		</span>{
<span style="white-space:pre">			</span>break;
<span style="white-space:pre">		</span>}
<span style="white-space:pre">		</span>animation->addSpriteFrame(frame);
<span style="white-space:pre">	</span>} while (true);
<span style="white-space:pre">	</span>animation->setDelayPerUnit(delay);  // 每帧播放间隔
<span style="white-space:pre">	</span>animation->setRestoreOriginalFrame(true);
<span style="white-space:pre">	</span>animation->setLoops(iloops);
<span style="white-space:pre">	</span>// 创建动画动作
<span style="white-space:pre">	</span>Animate* action = Animate::create(animation);
<span style="white-space:pre">	</span>return action;
}

Animate* ActionTool::animationWithFrameAndNum(const char *frameName, int framecount, float delay)
{
	SpriteFrame* frame = NULL;
	Animation* animation = Animation::create();
	// 遍历图片帧
	for (int index = 1; index <= framecount; index++)
	{
		String * name = String::createWithFormat("%s%d", frameName, index++);
		frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(name->getCString());
		animation->addSpriteFrame(frame);
	}
<pre code_snippet_id="678947" snippet_file_name="blog_20150529_2_9859587" name="code" class="cpp"><span>	</span>animation->setDelayPerUnit(delay);  // 每帧播放间隔
<span>	</span>animation->setRestoreOriginalFrame(true);
<span>	</span>animation->setLoops(iloops);
Animate* animate = Animate::create(animation);/*// 第二中实现方式,用一个帧图片的向量数组,创建AnimationVector<SpriteFrame*> animFrames;char str[20];for (int k = 1; k <= framecount; k++){sprintf(str, "%s%d.png", frameName, k);SpriteFrame *frame = SpriteFrameCache::getInstance()->spriteFrameByName(str);animFrames.pushBack(frame);}return Animate::create(Animation::createWithSpriteFrames(animFrames, delay));*/return animate;}


其次的工具类便是一些基本的全局文件,包括:
  • 枚举文件
  • 全局宏定义文件
  • 全局变量文件

英雄实体类的基本分析和实现

进行的基本步骤是:
  • 确定主角所具有的属性
  • 确定主角所具有的方法
  • 编码实现主角英雄类
首先是英雄的一些基本属性,包括

而具有的方法则分为:

下面是英雄类的一些关键代码(由于代码有些长,且抽象起来都是类似的代码,只贴一些关键部分):

头文件中的一些方法和属性:
public:
	// 根据图片名创建英雄
	void InitHeroSprite(char *hero_name, int iLevel);
	// 返回当前英雄
	Sprite* GetSprite();

	// 设置动画,run_directon为精灵脸朝向,false朝右,frameName为图片帧名字
	void SetAnimation(const char *frameName, float delay, bool run_directon);
	// 停止动画
	void StopAnimation();
	// 跳起动画
	void JumpUpAnimation(const char *name_each, float delay, bool run_directon);
	// 跳落动画
	void JumpDownAnimation(const char *name_each, float delay, bool run_directon);
	// 跳落动画结束
	void JumpEnd();
	// 攻击动画
	void AttackAnimation(const char *name_each, float delay, bool run_directon);
	// 攻击动画结束
	void AttackEnd();
	// 死亡动画
	void DeadAnimation(const char *name_each, float delay, bool run_directon);
	// 死亡动画结束
	void DeadEnd();
	// 受伤动画
	void HurtByMonsterAnimation(const char *name_each, float delay, bool run_directon);
	// 受伤动画结束
	void HurtByMonsterEnd();
	// 判断英雄是否运动到了窗口的中间位置,visibleSize为当前窗口的大小  
	bool JudgePositosn(Size visibleSize);

	bool IsDead;

	// HP & MP 值
	float m_iCurrentHp;
	float m_iTotleHp;
	float m_iCurrentMp;
	float m_iTotleMp;

	float percentage;
	float m_iSpeed;

	bool m_bIsAction;          // 查看当前是否已经在打怪了
	bool m_bIsJumping;         // 查看是否在跳
	bool IsRunning;  // 判断是否在跑动画
	bool IsAttack;  // 判断是否在攻击动画
	
	bool IsHurt; // 判断是否受伤
	
	bool HeroDirecton; // 英雄运动的方向
	bool m_bCanCrazy; // 判断是否处于狂暴状态

	CREATE_FUNC(Hero);
private:
	Sprite* m_HeroSprite; // 精灵
	char *Hero_name; // 用来保存初始状态的精灵图片名称
CPP文件的部分方法的实现代码:
// 受伤
void Hero::HurtByMonsterAnimation(const char *name_each, float delay, bool run_directon)
{
	if (IsHurt || IsDead)
		return;
	//受伤优先
	if (IsRunning || IsAttack)
	{
		m_HeroSprite->stopAllActions();//当前精灵停止所有动画
		//恢复精灵原来的初始化贴图 
		this->removeChild(m_HeroSprite, true);//把原来的精灵删除掉
		m_HeroSprite = Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName(Hero_name));//恢复精灵原来的贴图样子
		m_HeroSprite->setFlippedX(HeroDirecton);
		this->addChild(m_HeroSprite);
		IsRunning = false;
		IsAttack = false;
	}

	Animate* action = ActionTool::animationWithFrameName(name_each, 1, delay);
	//创建回调动作,受伤动画结束调用HurtEnd()
	CallFunc* callFunc = CallFunc::create(this, callfunc_selector(Hero::HurtByMonsterEnd));
	//创建连续动作
	ActionInterval* hurtackact = Sequence::create(action, callFunc, NULL);

	m_HeroSprite->runAction(hurtackact);
	IsHurt = true;


}

// 受伤结束
void Hero::HurtByMonsterEnd()
{
	m_iCurrentHp -= 20.0f;
	IsHurt = false;
	percentage = m_iCurrentHp / m_iTotleHp * 100.0f;
	if (m_iCurrentHp < 0.0f)
	{
		DeadAnimation("dead", 0, HeroDirecton);
	}
}

// 死亡
void Hero::DeadAnimation(const char *name_each, float delay, bool run_directon)
{
	m_HeroSprite->stopAllActions();
	// 调整方向
	if (HeroDirecton != run_directon)
	{
		HeroDirecton = run_directon;
		m_HeroSprite->setFlippedX(run_directon);
	}
	// 创建动作
	Animate* act = ActionTool::animationWithFrameName(name_each, 1, delay);
	//创建回调动作,攻击结束后调用AttackEnd()
	CallFunc* callFunc = CallFunc::create(this, callfunc_selector(Hero::DeadEnd));
	//创建连续动作
	ActionInterval* attackact = Sequence::create(act, callFunc, NULL);
	m_HeroSprite->runAction(attackact);
	Director::getInstance()->getScheduler()->setTimeScale(0.5);
}

// 死亡结束
void Hero::DeadEnd()
{
	IsDead = true;
	//恢复死亡的样子
	this->removeChild(m_HeroSprite, true);  //把原来的精灵删除掉
	m_HeroSprite = Sprite::createWithSpriteFrame(SpriteFrameCache::getInstance()->getSpriteFrameByName("monsterDie6.png")); //恢复死亡的样子
	m_HeroSprite->setFlippedX(HeroDirecton);
	this->addChild(m_HeroSprite);

}

// 判断位置
bool Hero::JudgePositosn(Size visibleSize)
{
	if (this->getPositionX() > (visibleSize.width / 2.0 + 2.0) || (this->getPositionX() < visibleSize.width / 2.0 - 2.0)) // 精灵到达mid?
		return false;
	else
		return true;//到达中间位置
}

怪物类的分析和实现

分析的步骤和上面分析英雄类的是一样的,不同的一部分是我们游戏中的怪物要有一定的AI,关于AI的如何设计这里不详细的展开细说了,而且这里所实现的是十分基础和简单的AI(或者不能称之为AI),设定一个全屏的仇恨范围,怪物在自己活着或者英雄或者的状态下会一直尝试跟随英雄并尝试攻击英雄。具体的AI设计大家可以自行Google或者看视频中的一部分讲解。
贴出一些关键部分的代码:
public:
	Monster(void);
	~Monster(void);
	// 公有属性
	// 怪物种类
	int m_iType;
	//判断是否在跑动画
	bool IsRunning;
	//判断是否在攻击动画
	bool IsAttack;
	//判断是否在受伤动画
	bool IsHurt;
	//判断是否死亡
	bool Isdead;
	//怪物运动的方向
	bool MonsterDirecton;

	// 方法
	// 根据图片名创建怪物,不带血条的怪物
	void InitMonsterSprite(char *name, char *a, char *die, char *walk, char *dieLast, int m_iLevel);
	//返回英雄
	Sprite* GetSprite();

	//设置走动动画,num为图片数目,run_directon为精灵脸朝向,false朝右,name_each为name_png中每一小张图片的公共名称部分
	void SetAnimation(const char *name_each, bool run_directon, float delay, int iLoops);
	//停止走动动画
	void StopAnimation();

	//攻击动画
	void AttackAnimation(const char *name_each, bool run_directon, float delay, int iLoops);
	//攻击动画结束
	void AttackEnd();

	//受伤动画
	void HurtAnimation(const char *name_each, bool run_directon, float delay, int iLoops);
	//受伤动画结束
	void HurtEnd();

	//死亡动画
	void DeadAnimation(const char *name_each,  bool run_directon, float delay, int iLoops);
	//死亡动画结束
	void DeadEnd();

	//怪物死亡闪烁结束
	void BlinkEnd();

	//在可视范围内,怪物跟随英雄运动
	void FollowRun(Hero* m_hero, GameMap* m_map);
	//判断是否攻击
	void JudegeAttack(float dt);


	//怪物启动监听英雄
	void StartListen(Hero* m_hero, GameMap* m_map);
	//监听函数,每隔3秒检测下,计算英雄与怪物的距离
	void updateMonster(float delta);
	//更新函数,如果英雄在可视范围内,不断触发
	void update(float delta);

	CREATE_FUNC(Monster);

private:
	Sprite* m_MonsterSprite;     // 怪物精灵
	char *Monster_name;            // 用来保存初始状态的精灵图片名称
	char *Monster_a;                    // 怪物攻击帧
	char *Monster_die;                // 死亡帧
	char *Monster_walk;             // 行走帧
	char *Die_name;

	Hero* my_hero;                     // 当前英雄
	GameMap* my_map;           // 当前地图
	float dis;                                   // 当前怪物和英雄的距离
	int   m_iHP;
这里具体实现部分代码可以参考http://cn.cocos2d-x.org/tutorial/show?id=2242

这节课的基本内容就是这些,下节课我们将进行学习游戏项目相关界面分析与实现
上面的代码质量很差,实现的方式也很基础,仅提供给大家一个实现的参考,希望想学习的朋友能够自己思考,不断改进和求精,写出完全属于自己的代码。

想要观看的视频,请戳这里:第二课在此如果本教程有帮助带您,希望您能点击进去观看一下,而且现在注册成为极客学院的会员,验证手机号码和邮箱号码会赠送三天的会员时间,会员可以无限制的下载和观看所有的视频,谢谢您的支持!





【Cocos游戏实战】功夫小子第六课之游戏主功能场景的分析和实现

经过前面五节课的学习我们目前还剩的未完成的工作就是游戏的核心场景——游戏场景。这节课我们就来学习如何实现游戏的核心场景。(核心场景的逻辑功能完成放在下一课。) 根据我们在第一课中对整个核心游戏场景的...
  • hu1020935219
  • hu1020935219
  • 2015年07月12日 16:47
  • 3163

【Cocos游戏实战】功夫小子第八课之游戏打包和相关问题说明

至此,功夫小子系列的Cocos2d-x的实战文章就结束了。 再次贴个目录以便检索: 功夫小子专栏地址:http://blog.csdn.net/column/details/suoolcocosh...
  • hu1020935219
  • hu1020935219
  • 2015年07月29日 16:12
  • 2087

git深入了解

刚开始学习git时 只是记载下git常用指令,但是却不是很理解其中的含义下面,我就来说说我的理解 当然在了解之前我们说一下 工作区:就是你在电脑里能看到的目录。 stage区:一般存放在 “.g...
  • drdongshiye
  • drdongshiye
  • 2017年12月20日 17:47
  • 64

SQL优化总结---百万数据

网上关于SQL优化的教程很多,但是比较杂乱。近日有空整理了一下,写出来跟大家分享一下,其中有错误和不足的地方,还请大家纠正补充。 这篇文章我花费了大量的时间查找资料、修改、排版,希望大家阅读之后,感...
  • drdongshiye
  • drdongshiye
  • 2017年11月22日 16:11
  • 84

【Cocos游戏实战】功夫小子第四课之设置功能和图籍场景的实现

第三课我们学习了过渡场景和主开始菜单的分析和实现。这节课我们讲学习下如何实现主...
  • hu1020935219
  • hu1020935219
  • 2015年06月18日 22:34
  • 1819

【Cocos游戏实战】功夫小子第三课之过渡场景和开始菜单的实现

本节课的视频教程地址是:第三课在此 如果本教程有帮助到您,希望您能点击进去观看一下,而且现在注册成为极客学院的会员,验证手机号码和邮箱号码会赠送三天的会员时间,手机端首次也可以领取五天的会员时间哦(即...
  • hu1020935219
  • hu1020935219
  • 2015年06月08日 19:02
  • 2270

【Cocos游戏实战】功夫小子第五课之帮助场景和选关功能的实现

功夫小子之帮助场景和选关功能的实现转载请注明出处:http://blog.csdn.net/suool/article/details/46661231 本节课的视频教程地址是: 第五课在此 ...
  • hu1020935219
  • hu1020935219
  • 2015年06月27日 16:25
  • 2189

Cocos2d-x游戏暂停、继续游戏、重新开始界面的实现---之游戏开发《赵云要格斗》(10)

本文要实现游戏中的暂停、重新开始。将当前界面截图,然后用这张图去构造一个层(以这张图为背景),然后加个按钮,主界面点暂停时,pushScene(),然后转到游戏暂停界面,当在游戏暂停界面点继续游戏po...
  • Evankaka
  • Evankaka
  • 2015年01月21日 00:07
  • 11235

【Cocos游戏实战】功夫小子第一课需求分析和开发环境的基本配置

第一课的视频教程在此处。(请戳进去) 在开发一个手机游戏之前,我们要首先分析一个游戏的基本特点,包括游戏的基本角色和属性,以及游戏的基本功能,游戏的基本规则,将整个游戏的基本流程画出来。 然后在对我...
  • hu1020935219
  • hu1020935219
  • 2015年05月17日 16:11
  • 5158

最佳实战:用Cocos2d-x3.x和C++11编写2048游戏以及游戏AI(全民2048 Android版上线啦)

话说,年仅19岁的意大利人Gabriele Cirulli于2014年3月完成并发布在github上,游戏设计来自于《1024》,而《1024》灵感来源于《Threes!》的移动端游戏。然而游戏飙升的...
  • booirror
  • booirror
  • 2015年05月05日 00:46
  • 6883
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【Cocos游戏实战】功夫小子第二课之基础类分析和实现
举报原因:
原因补充:

(最多只允许输入30个字)