一般情况下,游戏开始流程如下
进入游戏,获取各种信息,镜头自动向右,然后出现卡片选择界面,另外就是还会有一个判断,就是当前获取的植物个数是否大于个人所拥有的卡槽数,如果大于,就出现卡片选择界面,否则,直接选定所拥有的卡片,然后镜头回转,开始游戏
不难知道,这得实现卡片选择界面和卡片界面,卡片选择界面负责卡片的选择
卡片界面则有两个功能,
在游戏处于卡片选择状态时,点击卡片会从卡片层移出该卡片,并且设置卡片选择层对应的卡片可点击
在处于正在游戏状态下时,卡片则有了正常的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是很相似的,并且有不同的我也会注明。