cocos2d-x 3.0游戏实例学习笔记《卡牌塔防》第七步---英雄要升级&属性--解析csv配置文件

/* 说明:

**1.本次游戏实例是《cocos2d-x游戏开发之旅》上的最后一个游戏,这里用3.0重写并做下笔记

**2.我也问过木头本人啦,他说:随便写,第一别完全照搬代码;第二可以说明是学习笔记---好人

**3.这里用cocos2d-x 3.0版本重写,很多地方不同,但是从重写过程中也很好的学习了cocos2d-x

*/

***每一步对应的所有代码以及用到的资源都会打包在最后给出

***为避免代码过多,每一步的代码都做了标记--一看就晓得是第几步实现的避免出错改不回去(难不成还用Git?)

***可以根据设计思路(好吧,那名字太高大上。实际就是这一步要干啥)先自己实现---cocos2d-x本来就是如此,相同的功能有许多不同实现方法;先自己折腾是蛮不错的。

***为了方便移植到手机上,对于每一步都进行编译android测试;因为很多时候代码在win32下可以,编译就会出错,给出的代码会是测试过后的。

本次笔记内容:

1、设计思路

2、思路代码&效果图

3、下次内容预览

4、本次资源&完整代码下载

一:设计思路

1.首先英雄升级体现放置Hero里面upgrade函数,对应升级有不同的效果体现

2.升级操作抽离成一个操作器继承自Node,然后绑定在炮台上,后面点击炮台,出现三个按钮---升级、关闭、删除英雄

3.英雄的升级不只是体现在外表,它也有属性,属性提升---属性就得从csv文件读取

5.由于这后面思路基本借鉴书上的,就不班门弄斧啦---直接在代码中结束

二:思路代码&效果图

首先给英雄升级函数;这里写得比较死,同时只支持4级提升;每一次提升之后就弄一个动作覆盖上去

void Hero::upgrade(){
    Sprite* sprite = getSprite();
	if(sprite == NULL || _level >= 4) {
        return;
    }

	// 增加等级
    _level++;

    // 英雄遮罩
    if(_level == 2) {
        Sprite* heroTop = Sprite::create("sprite/hero/hero_top_1.png");
        Point pos = ccp(sprite->getContentSize().width / 2, sprite->getContentSize().height / 2);
        heroTop->setPosition(pos);
        sprite->addChild(heroTop);
    }
	if(_level == 3) {
		Sprite* heroTop = Sprite::create("sprite/hero/hero_top_2.png");
		heroTop->setOpacity(255);
		Point pos = ccp(sprite->getContentSize().width / 2, sprite->getContentSize().height / 2);
		heroTop->setPosition(pos);
		sprite->addChild(heroTop);

		ActionInterval* rotateBy = RotateBy::create(25.0f, 360, 360);
		ActionInterval* repeat = RepeatForever::create(rotateBy);
		heroTop->runAction(repeat);
	}
	if(_level == 4) {
		Point pos = ccp(sprite->getContentSize().width / 2, sprite->getContentSize().height / 2);
		Sprite* heroTop = Sprite::create("sprite/hero/hero_top_3.png");
		heroTop->setPosition(pos);
		sprite->addChild(heroTop);

		ActionInterval* rotateBy = RotateBy::create(10.0f, 360, 360);
		ActionInterval* repeat = RepeatForever::create(rotateBy);
		heroTop->runAction(repeat);
	}

}

那么对应四个基本的不同效果如下:

lv1----lv2----lv3------lv4

效果是动态的动作

--------------------------------------------------------------

那么看看那操作器 TowerOperator;它需要和Hero以及炮台保存联系,需要这两个成员

class TowerOperator : public Node{
public:
	TowerOperator();
	~TowerOperator();
	static TowerOperator* create(TowerBorder* towerBorder,Hero* hero);
	bool init(TowerBorder* towerBorder,Hero* hero);

private:
	Hero* _hero;
	TowerBorder* _towerBorder;

	void createOprBtns();

	//**7**按钮事件
	void closeEvent(Ref* pSender,Control::EventType event);
	void upgradeEvent(Ref* pSender,Control::EventType event);
	void deleteEvent(Ref* pSender,Control::EventType event);
};

实现

TowerOperator::TowerOperator(){
	_hero = NULL;
	_towerBorder = NULL;
}
TowerOperator::~TowerOperator(){
	CC_SAFE_RELEASE(_hero);
	CC_SAFE_RELEASE(_towerBorder);
}

TowerOperator* TowerOperator::create(TowerBorder* towerBorder,Hero* hero){
	TowerOperator* towerOperator = new TowerOperator();
	if(towerOperator && towerOperator->init(towerBorder,hero)){
		towerOperator->autorelease();
	}
	else{
		CC_SAFE_DELETE(towerOperator);
	}
	return towerOperator;
}

bool TowerOperator::init(TowerBorder* towerBorder, Hero* hero){
	CC_SAFE_RETAIN(towerBorder);
	_towerBorder = towerBorder;

	CC_SAFE_RETAIN(hero);
	_hero = hero;

	createOprBtns();
	return true;
}

void TowerOperator::createOprBtns(){
    LabelTTF* title = LabelTTF::create("Close", "Arial", 25);

	auto m_pos = this->getPosition();

    ControlButton* closeBtn = ControlButton::create(title, Scale9Sprite::create("Button/opr_btn_nor.png"));
	closeBtn->setBackgroundSpriteForState(Scale9Sprite::create("Button/opr_btn_light.png"), Control::State::HIGH_LIGHTED);
	closeBtn->addTargetWithActionForControlEvents(this,
		cccontrol_selector(TowerOperator::closeEvent), Control::EventType::TOUCH_UP_INSIDE);
	closeBtn->setPosition(
		ccp(m_pos.x+100,m_pos.y));
	this->addChild(closeBtn);

    title = CCLabelTTF::create("Delete", "Arial", 25);
    ControlButton* deleteBtn = ControlButton::create(title, Scale9Sprite::create("Button/opr_btn_nor.png"));
    deleteBtn->setBackgroundSpriteForState(Scale9Sprite::create("Button/opr_btn_light.png"), Control::State::HIGH_LIGHTED);
	deleteBtn->addTargetWithActionForControlEvents(this,
		cccontrol_selector(TowerOperator::deleteEvent), Control::EventType::TOUCH_UP_INSIDE);
	deleteBtn->setPosition(
		ccp(m_pos.x-100,m_pos.y));
	this->addChild(deleteBtn);

    title = CCLabelTTF::create("Upgrade",  "Arial", 25);
    ControlButton* upgradeBtn = ControlButton::create(title, Scale9Sprite::create("Button/opr_btn_nor.png"));
    upgradeBtn->setBackgroundSpriteForState(Scale9Sprite::create("Button/opr_btn_light.png"), Control::State::HIGH_LIGHTED);
	upgradeBtn->addTargetWithActionForControlEvents(this,
		cccontrol_selector(TowerOperator::upgradeEvent), Control::EventType::TOUCH_UP_INSIDE);
	upgradeBtn->setPosition(
		ccp(m_pos.x,m_pos.y+100));
	this->addChild(upgradeBtn);
}

void TowerOperator::closeEvent(Ref* pSender,Control::EventType event){
	CCLOG("close!");
	this->removeFromParentAndCleanup(true);
}
void TowerOperator::upgradeEvent(Ref* pSender,Control::EventType event){
	CCLOG("upgrade!");
	_hero->upgrade();
}
void TowerOperator::deleteEvent(Ref* pSender,Control::EventType event){
	CCLOG("delete!");
	_towerBorder->deleteHero();
	this->removeFromParentAndCleanup(true);
}

三个按钮,这里也要注意的是相对位置的放置问题,也就是son & parent的位置问题

三个按钮的函数比较简单,那么对应删除操作,是由——_towerBorder去完成的,其函数为:

void TowerBorder::deleteHero(){
	_hero->removeFromParentAndCleanup(true);
	CC_SAFE_RELEASE_NULL(_hero);
}

并且,要注意,不要用_hero直接从parent删除,不然导致这个炮台下次无法继续添加

那么我们的操作器得展示出来;回想往炮台加英雄的时候,是在触摸事件中,次炮台没有英雄的时候就添加,那么如果有,我们就显示操作器就是啦:

void TowerBorder::showOperator(){
	auto towerOperator = TowerOperator::create(this,_hero);
	CC_SAFE_RETAIN(towerOperator);
	towerOperator->setPosition(ccp(0,0));
	this->addChild(towerOperator);
}
//这里的setPostion以及操作器里面的setPos也和前面说过的相对位置有关
bool HeroManager::initWithLevel(int curLevel){
	/********************省略*****************/
	listener->onTouchEnded = [=](Touch* touch,Event* event){
	        /********************省略*****************/
		if(clickBorder->getHero() == NULL){
			Hero* hero = Hero::createFromCsvFileByID(1);
			hero->setPosition(clickBorder->getPosition());
			this->addChild(hero);
			clickBorder->bindHero(hero);
		}
		else{
			clickBorder->showOperator();
		}
	};

	_eventDispatcher->addEventListenerWithSceneGraphPriority(listener,this);

	//------------------------------------------------------------

	return true;
}

效果如下:

那么,我们英雄的升级有了外表的体现啦

可是我们的英雄一直没有属性上的配置-----------------这要通过csv 来弄;

首先需要说明的是:

csv是一种简单的文件,你可以用Excel 来弄,外观像表格,最后保存的格式为csv就是的。但是用记事本打开,就会发现实际上是用逗号分隔开的如图:

----------------------

那么这里的csv需要解析,然后放到我们的cocos2d-x 里面,使用这些属性;

具体的也是木头书上的,我这里不过多说;但是在3.0版本中,也修改了不少地方。同时为了移植到手机上,也做了相应的修改;这里解析csv文件有三个类:

FileLoadUtil---文件加载  、StringUtil---字符串解析 、CsvUtil---由前两个配合完成最终解析;如果相应了解其具体工作,最好的方式就是一步步调试,那样很容易理解

---------------------------------------------------------------------------------------------------------------

那么看看那英雄的属性  Hero.h里面:

enum EnumHeroType{
	en_HeroType_Normal
};
enum EnumHeroPropConfType{
	enHeroPropConf_ID,		// 英雄ID
	enHeroPropConf_Name,		// 英雄名字
	enHeroPropConf_Type,		// 英雄类型
	enHeroPropConf_ModelID,		// 英雄模型ID
	enHeroPropConf_BaseAtk,			// 基础攻击力
	enHeroPropConf_AtkSpeed,		// 攻击间隔(单位:秒)
	enHeroPropConf_AtkRange,		// 攻击范围(半径)
	enHeroPropConf_UpgradeAtkBase,	// 升级攻击加成系数
	enHeroPropConf_UpgradeCostBase,	// 升级消耗基础值
};
//--------------下面是类的一些成员-
	//**7**
	CC_SYNTHESIZE(EnumHeroType,_heroType,HeroType);      //英雄类型
	CC_SYNTHESIZE(int,_baseAtk,BaseAtk);                 //基础攻击
	CC_SYNTHESIZE(int,_curAtk,CurAtk);                   //当前攻击--eg:升级之后
	CC_SYNTHESIZE(int,_atkSpeed,AtkSpeed);               //攻击速度(间隔)
	CC_SYNTHESIZE(int,_atkRange,AtkRange);               //攻击范围(半径)
	CC_SYNTHESIZE(int,_upgradeCostBase,UpgradeConstBase);//升级消耗
	CC_SYNTHESIZE(float,_upgradeAtkBase,UpgradeAtkBase); //升级攻击加成

同时在Init函数中

	//**7**
	CsvUtil* csvUtil = CsvUtil::getInstance();
	Size csvSize = csvUtil->getFileRowColNum("cvs/Hero.cvs");

	const char* chHeroID = __String::createWithFormat("%d",heroID)->getCString();

	int line = csvUtil->findValueInWithLine(chHeroID,enHeroPropConf_ID,"cvs/Hero.cvs");

	if(line < 0){
		return false;
	}

	setID(heroID);
	setModeID(csvUtil->getInt(line,enHeroPropConf_ModelID,"cvs/Hero.cvs"));
	setBaseAtk(csvUtil->getInt(line,enHeroPropConf_BaseAtk,"cvs/Hero.cvs"));
	setCurAtk(getBaseAtk());
	setAtkSpeed(csvUtil->getInt(line,enHeroPropConf_AtkSpeed,"cvs/Hero.cvs"));
	setAtkRange(csvUtil->getInt(line,enHeroPropConf_AtkRange,"cvs/Hero.cvs"));
	setUpgradeAtkBase(csvUtil->getFloat(line,enHeroPropConf_UpgradeAtkBase,"cvs/Hero.cvs"));
	setUpgradeConstBase(csvUtil->getInt(line,enHeroPropConf_UpgradeCostBase,"cvs/Hero.cvs"));
//----------------升级----------------------

	// 增加英雄攻击力
	setBaseAtk(getBaseAtk() * _upgradeAtkBase);
	setCurAtk(getBaseAtk());

那么最后,在AppDelegate的构造函数中,

AppDelegate::AppDelegate() {
	CC_SAFE_RETAIN(CsvUtil::getInstance());
}

AppDelegate::~AppDelegate(){
	CC_SAFE_RELEASE(CsvUtil::getInstance());
}

OK,本次完啦

三:下次内容预览

英雄的属性看不出,是因为没有怪兽让它欺负,那么后面就要添加怪物,并且让它按照我们之前编辑的路线行走啦

四:

-----------------------------------

源码&资源

--------------------------------------

个人愚昧观点,欢迎指正与讨论

时间: 2024-10-11 13:08:47

cocos2d-x 3.0游戏实例学习笔记《卡牌塔防》第七步---英雄要升级&属性--解析csv配置文件的相关文章

cocos2d-x 3.0游戏实例学习笔记《卡牌塔防》第九步---英雄发动攻击

/* 说明: **1.本次游戏实例是<cocos2d-x游戏开发之旅>上的最后一个游戏,这里用3.0重写并做下笔记 **2.我也问过木头本人啦,他说:随便写,第一别完全照搬代码:第二可以说明是学习笔记---好人 **3.这里用cocos2d-x 3.0版本重写,很多地方不同,但是从重写过程中也很好的学习了cocos2d-x */ ***每一步对应的所有代码以及用到的资源都会打包在最后给出 ***为避免代码过多,每一步的代码都做了标记--一看就晓得是第几步实现的避免出错改不回去(难不成还用Git

cocos2d-x 3.0游戏实例学习笔记《卡牌塔防》第一步---开始界面&amp;关卡选择

/* 说明: **1.本次游戏实例是<cocos2d-x游戏开发之旅>上的最后一个游戏,这里用3.0重写并做下笔记 **2.我也问过木头本人啦,他说:随便写,第一别完全照搬代码:第二可以说明是学习笔记---好人 **3.这里用cocos2d-x 3.0版本重写,很多地方不同,但是从重写过程中也很好的学习了cocos2d-x */ ***每一步对应的所有代码以及用到的资源都会打包在最后给出 ***为避免代码过多,每一步的代码都做了标记--一看就晓得是第几步实现的避免出错改不回去(难不成还用Git

cocos2d-x 3.0游戏实例学习笔记《卡牌塔防》第一步---開始界面&amp;amp;关卡选择

/* 说明: **1.本次游戏实例是<cocos2d-x游戏开发之旅>上的最后一个游戏,这里用3.0重写并做下笔记 **2.我也问过木头本人啦.他说:随便写,第一别全然照搬代码:第二能够说明是学习笔记---好人 **3.这里用cocos2d-x 3.0版本号重写,非常多地方不同.可是从重写过程中也非常好的学习了cocos2d-x */ ***每一步相应的全部代码以及用到的资源都会打包在最后给出 ***为避免代码过多.每一步的代码都做了标记--一看就晓得是第几步实现的避免出错改不回去(难不成还用

cocos2d-x 3.0游戏实例学习笔记 《卡牌塔防》第0步---知识点总结&amp;效果预览

/* 说明: **1.本次游戏实例是<cocos2d-x游戏开发之旅>上的最后一个游戏,这里用3.0重写并做下笔记 **2.我也问过木头本人啦,他说:随便写,第一别完全照搬代码:第二可以说明是学习笔记 **3.这里用cocos2d-x 3.0版本重写,很多地方不同,但是从重写过程中也很好的学习了cocos2d-x */ 环境:Win7 .cocos2d-x 3.0final .C++ .VS2012 笔记思路:后面就按这种思路写笔记啦 1.首先给出每次实现的效果截图 2.然后给出设计思路 3.

cocos2d-x 3.0游戏实例学习笔记 《跑酷》移植到android手机

说明:这里是借鉴:晓风残月前辈的博客,他是将泰然网的跑酷教程,用cocos2d-x 2.X 版本重写的,目前我正在学习cocos2d-X3.0 于是就用cocos2d-X 3.0重写,并做相关笔记 好吧,自从上次<跑酷>完结之后,就什么没做什么的,主要是修复了一点点bug ,也在相应的文章里面做出了相应的改动.不过,只能在window上看着玩又有什么太大意思呢!!!于是乎又尝试着弄到手机上试试--OK,初生牛犊不要脸,哦不!不怕喷.这里记录一下我的移植过程.(这里会用到新的大小的资源.待会也会

cocos2d-x 3.0游戏实例学习笔记 《跑酷》一 开始界面

开始界面和前辈的基本上差不多:cocos2d-x游戏开发 跑酷(一)开始界面     但是也有不同 下面先给出游戏过程中用到的所有资源 开始界面定义为:MainScene 有如下功能: 1.背景 2.进入游戏界面按钮 3.预先加载背景音乐文件 .h文件 #ifndef __MainScene__H__ #define __MainScene__H__ #include "cocos2d.h" class MainScene : cocos2d::Layer{ public: virtu

cocos2d-x 3.0游戏实例学习笔记 《跑酷》第六步--物理碰撞检测(1)

说明:这里是借鉴:晓风残月前辈的博客,他是将泰然网的跑酷教程,用cocos2d-x 2.X 版本重写的,目前我正在学习cocos2d-X3.0 于是就用cocos2d-X 3.0重写,并做相关笔记 那么这一步,我们先不急着给主角和金币岩石碰撞检测,我这里把cocos2d-x 3.0 的物理碰撞检测单独抽取出来,之前看了cocos的官网,有一个教程:用新物理引擎完成碰撞检测,就是一个车和猫的碰撞检测,车撞倒猫,猫就消失.我之前也单独试了下.但是那个教程的代码好像完整性不好,不能运行...于是我又借

coco2d-x 3.0游戏实例学习笔记 《跑酷》 二 游戏界面--全新的3.0物理世界

从这里开始,就和之前前辈的有很多不同啦. 在MainScene中,开始按钮中,我们就要通过回调函数,进入到我们的游戏场景啦. 那么在游戏场景中我们定义为:PlayScene ,而且是一个带物理世界的场景,cocos2d-x 3.0中要创建物理Scene是很简单的,因为它都帮我们封装好了. 那么这一步,我们在PlayScene 中主要完成以下功能: 1.物理世界创建 2.创建一个地面的物理刚体 相关知识个人见解: 所谓的创建物理世界,个人觉得是一个抽象的东东,作为新手,开始并不能理解,开始你可以就

cocos2d-x 3.0游戏实例学习笔记 《跑酷》第四步--地图循环&amp;主角添加动作

这一步当中,我们主要完成以下功能: 1.地图的无限滚动---让主角看起来真的是在跑动 2.给主角添加Jump跳跃和crouch下蹲动作 那么首先来让背景滚动起来,在PlayScene.h中添加: //初始化背景 void initBG(); //用update函数让地图滚动 virtual void update(float dt); //背景精灵 cocos2d::Sprite* bgSprite1; cocos2d::Sprite* bgSprite2; cocos2d::Sprite* g