cocos2dx 植物大战僵尸 2 创建卡片层

一般情况下,游戏开始流程如下
进入游戏,获取各种信息,镜头自动向右,然后出现卡片选择界面,另外就是还会有一个判断,就是当前获取的植物个数是否大于个人所拥有的卡槽数,如果大于,就出现卡片选择界面,否则,直接选定所拥有的卡片,然后镜头回转,开始游戏

不难知道,这得实现卡片选择界面和卡片界面,卡片选择界面负责卡片的选择
卡片界面则有两个功能, 在游戏处于卡片选择状态时,点击卡片会从卡片层移出该卡片,并且设置卡片选择层对应的卡片可点击
在处于正在游戏状态下时,卡片则有了正常的cd,选取等功能(在游戏开始时才开始cd和可点击)
那么现在所需要的问题有
1.卡片选择层的卡片如何和卡片层的卡片所对应,
2.获取当然游戏状态,并做出对应操作

当前植物个数≤卡槽数 ->不出现卡片选择界面
当前是特殊关卡 ->不出现卡片选择界面

也就是说,先判断是否出现卡片选择界面,之后再确定是否出现卡片层
另外,卡片层有
1.普通的排列好的卡片层
2.有些自动带的卡片层
3.在场景中出现的卡片
他们对应了3种不同模式 比如砸罐子就是第三种。2,3的共同点就是出现的卡片并不消耗阳光,可直接点击种植

个人分歧
这两个不是同一个,卡片选择单击的一直是卡片选择界面的卡片,放卡片选择完毕,返回的是一个字符串数组,然后再根据这个数组生成对应的卡片,在这里卡片层 并没有单独的事件接收,而是统一从控制器获取,然后再进行派发。卡片在生成时,有的不消耗任何东西,同时也没有cd,蒙版
设置一个类型 分为正常Common和免费Free,来区分上面两种状态。
卡片选择界面在生成卡片时除了有按钮外,还有下面的不可点击的背景,同时,背景也有价值属性
可行:卡片选择有每个植物卡片有两个按钮 一个是上面的可点击的按钮 另一个则是不可点击的背景按钮,不过两个一模一样。然后在选中后,现在卡片层retain 然后在适当位置addChild即可。而没有卡片选择界面则传递字符串数组

其实,应该使用同一个,并且继承自MenuItem,只不过在CardLayer中是通过GameScene的onTouchEnded 来获取按键,这样做是必要的,因为获取按键的有铲子 卡片 地面等,这之间都在GameScene::onTouchEnded种调用


type 当前Terrain的生成条件
card 本关拥有的卡片类型 有三种 plant zombie 和""(空)

GameScene的初始化

bool GameScene::init()
{
	this->preloadResources();

	m_pLevelLayer = LevelLayer::create("level/level1-1.tmx");
	this->addChild(m_pLevelLayer);
	m_pCardLayer = CardLayer::create();
	this->addChild(m_pCardLayer);
	//todo
	//向右移动,之后回调函数
	Size visibleSize = Director::getInstance()->getVisibleSize();
	auto size = m_pLevelLayer->getContentSize();

	auto move = MoveTo::create(4.f,visibleSize.width - size.width);
	CallFunc*end = CallFunc::create([this]()
	{
		this->levelLoadCallback();
	});
	auto seq = Sequence::createWithTwoActions(move,end);
	m_pLevelLayer->runAction(seq);

	return true;
}

判断是否出现卡片选择界面

void GameScene::levelLoadCallback()
{
	//获取当前的关卡状态。来决定是否显示关卡选择界面
	auto levelType = this->getLevelCardShowType();
	if (levelType == "plant")//扩展使用
	{
	}
	else if (levelType == "zombie")
	{
	}
	else
	{
		return;
	}
	//获取当前的数目和卡槽数目
	auto ownPlants = DynamicData::getInstance()->getPlantNameVector();
	auto cardVesselNum = DynamicData::getInstance()->getCardVesselNum();
	//不显示卡片选择界面
	if (ownPlants.size() <= cardVesselNum)
	{
		//添加卡片
		for (unsigned int i = 0;i < ownPlants.size();i++)
		{
			//推导位置 todo
			auto pos = Point(50,i*60 + 50);
			m_pCardLayer->addCard(ownPlants.at(i),CardType::Common,pos);
		}
		this->levelStart();
	}
	else//显示卡片选择界面
	{
	}
}

关卡开始

void GameScene::levelStart()
{
	//镜头向左回转,之后开始游戏
	DelayTime*delayTime = DelayTime::create(0.5f);
	MoveTo*move = MoveTo::create(3.f,Point(-100,0));
	CallFunc*end = CallFunc::create([this]()
	{
		this->scheduleUpdate();

	});

	auto seq = Sequence::create(delayTime,move,end,nullptr);
	m_pLevelLayer->runAction(seq);
}

同时增加了

StaticData 负责各种静态数据的读取

   <!--向日葵-->
    <key>SunFlower</key>
    <dict>
      <key>desc</key>
      <string></string>
      <key>worth</key>
      <integer>50</integer>
      <key>CD</key>
      <real>7.5</real>
      <key>ready time</key>
      <real>0</real>
      <key>active</key>
      <string>all</string>
      <key>needful terrain</key>
      <array>
        <string>Lawn</string>
        <string>LilyPad</string>
        <string>FlowerPot</string>
      </array>
    </dict>
    <!--双生向日葵-->
    <key>TwinSunflower</key> 
    <dict>
      <key>desc</key>
      <string></string>
      <key>worth</key>
      <integer>150</integer>
      <key>CD</key>
      <real>50</real>
      <key>ready time</key>
      <real>20</real>
      <key>active</key>
      <string>all</string>
      <key>necessary item</key>
      <string>SunFlower</string>
      <key>needful terrain</key>
      <array>
        <string>Lawn</string>
        <string>LilyPad</string>
        <string>FlowerPot</string>
      </array>
    </dict>

StaticData的读取

void StaticData::parseCard()
{
	//获取卡片路径
	auto path = STATIC_DATA_STRING("card_plist_path");

	auto valueMap = FileUtils::getInstance()->getValueMapFromFile(path);

	for (auto card : valueMap)
	{
		auto cardName = card.first;
		auto properties = card.second.asValueMap();

		CardStruct cardStruct;

		for (auto iter = properties.begin();iter != properties.end();iter++)
		{
			auto sName = iter->first;

			if (sName == "desc")
				cardStruct.desc = iter->second.asString();
			else if (sName == "worth")
				cardStruct.worth = iter->second.asInt();
			else if (sName == "CD")
				cardStruct.cd = iter->second.asFloat();
			else if (sName == "ready time")
				cardStruct.readyTime = iter->second.asFloat();
			else if (sName == "active")
			{
				auto sType = iter->second.asString();
				cardStruct.active = StaticData::convertStrToActiveType(sType);
			}
			//解析需要的地形		
			else if (sName == "needful terrain")
			{
				auto needfulTerrains = properties.at("needful terrain").asValueVector();
				for (auto sTerrain : needfulTerrains)
				{
					auto terrainType = StaticData::convertStrToTerrainType(sTerrain.asString());
					cardStruct.terrains.push_back(terrainType);
				}
			}
			else if (sName == "necessary item")
			{
				cardStruct.necessoryItem = iter->second.asString();
			}
		}
		m_cardStructMap.insert(make_pair(cardName,cardStruct));
	}
}

DynamicData 负责动态数据的读取和保存 ,目前仅仅保存了 拥有的植物 和拥有的卡槽数目

bool DynamicData::init()
{
	//todo
	//获取卡槽个数
	m_nCardVesselNum = UserDefault::getInstance()->getIntegerForKey("card-vessel-num",6);
	auto plantStr = UserDefault::getInstance()->getStringForKey("plant","Peashooter,SunFlower,TwinSunflower");
	//进行字符串的解析
	auto vec = StringUtils::split(plantStr,',');
	for (auto v:vec)
	{
		m_plants.push_back(v.asString());
	}

	return true;
}

还有就是Card

对于卡片,则继承自MenuItem,原因是为了以后的卡片选择界面作的扩展,卡片类型分为两类
第一个是普通的卡片,即有cd,需要阳光
第二种则是消耗品卡片,一次性卡片,不需要阳光
这两种 除了阳光和消耗品之外,其他都是相同的。
卡片的属性是读取自card.plist的(分为植物卡片和僵尸卡片,但它们在卡片上来说没有区别)
价值 cd 准备时间 必要的Terrain类型,需不需要前置植物(植物的升级作的扩展)

综上所述,卡片负责检测种植的准备,即阳光是否充足,地形是否适合,cd是否完成等等,而并不考虑它要创建的是什么东西。
class Card : public MenuItem
{
	SDL_SYNTHESIZE(CardType,m_type,CardType);//卡片类型

	SDL_SYNTHESIZE(float,m_fCurCD,CurCD);//当前cd
	SDL_SYNTHESIZE(float,m_fCD,CD);//冷却时间
	SDL_SYNTHESIZE(int,m_nWorth,Worth);//花费
	SDL_SYNTHESIZE(std::string,m_description,Description);//描述
	SDL_SYNTHESIZE(std::string,m_cardName,CardName);//卡片名称
	SDL_SYNTHESIZE(string,m_necessoryItem,NecessoryItem);
	SDL_SYNTHESIZE(ActiveTime,m_activeTime,ActiveTime);
private:
	Sprite*m_pNormalSprite;//正常的图片
	Sprite*m_pDisableSprite;//无法点击的图片
	ProgressTimer*m_pCDTimer;//显示cd
	LabelAtlas*m_pNumberLabel;//显示数字
	vector<TerrainType> m_terrainTypes;
public:
	Card();
	virtual ~Card();
	//创建消耗性卡片,这种卡片不消耗阳光,但是一次性用品
	static Card* createConsumableCard(const std::string &name);
	//正常卡片
	static Card* create(const string&name,int number);

	bool init(const std::string& name);
	bool init(const string&name,int number);

	//使当前变暗,仅仅适用于卡片选择场景
	void setDarken();
	void setNeedfulTerrains(const vector<TerrainType>&terrains);

	virtual void setEnabled(bool enabled);

	virtual void update(float dt);
private:
	void updateSelf(Object*);//检查
	void setNormalSprite(const string&name);
	void setDisableSprite(const string&name);
};

卡片层 目前仅仅实现了addCard

bool CardLayer::addCard(const std::string &name,CardType type,const Point&pos)
{
	Card*card = nullptr;
	//获取加载的对应的结构体
	auto cardStruct = StaticData::getInstance()->getCardStruct(name);

	if (cardStruct == nullptr)
	{
		return false;
	}

	if (type == CardType::Common)
	{
		card = Card::create(name,cardStruct->worth);
		//设置准备时间
		card->setCurCD(cardStruct->readyTime);
		card->setCD(cardStruct->cd);
	}
	else if (type == CardType::Consumable)
	{
		card = Card::createConsumableCard(name);
	}
	//设置一些基础属性
	card->setNeedfulTerrains(cardStruct->terrains);
	card->setNecessoryItem(cardStruct->necessoryItem);
	card->setActiveTime(cardStruct->active);
	card->setPosition(pos);

	m_cards.push_back(card);
	this->addChild(card);

	return true;
}


另外,我所使用的并不是cocos2dx,而是SDL_Engine,是基于SDL的个人模仿cocos2dx的一个游戏引擎,当然,个人并不是为了装13,而是cocos2dx开发人员让我有些心寒,每一次更新都会废弃一大波API,并且cocosstudio什么的也不再维护等等,让我特别没有安全感,所以就自己写了个类似于cocos2dx的引擎,不过它们的上层API是很相似的,并且有不同的我也会注明。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值