上一节实现了塔基的行数的确定,这一节就着手于植物的创建。
下面就说说植物类的继承关系
这里说明下,这个游戏我打算使用box2d,不过这里并不是要进行物理模拟,而是仅仅用作物理碰撞,因为有些地方使用box2d会更好,比如,在屋顶场景中有个斜坡,豌豆等子弹碰到后会消失,使用box2d就不难实现,而且使用box2d也便于碰撞调试等等。另外,我也使用了Physics Editor和TexturePacker等软件(这两个软件都是收费的),主要是为了便于开发。
B2Entity 就是box2d entity,就是参与物理碰撞的实体。主要用于那些既参与碰撞,又显示贴图的对象。
/*主要了是为了在发生碰撞时便于类型转换*/
enum class CollisionType
{
None,
Plant,//植物类型
};
class B2Entity:public Entity
{
SDL_SYNTHESIZE(CollisionType,m_collisionType,CollisionType);
protected:
b2Body*m_pBody;
bool m_bActive;//是否存活,如果是则存在box2d碰撞面积
public:
B2Entity();
~B2Entity();
virtual void update(float dt) = 0;
/**初始化刚体
*/
void initBody();
/**为内部的body添加b2Fixture,内部由GB2ShapeCache-x提供支持
@shape 在GB2ShapeCache中 的shape所对应的形体的名称
shape必须在这之前已经加载到GB2ShapeCache中,并且body不能为nullptr
*/
void addFixturesToBody(const std::string &shape);
//清除原来body的全部fixture
void clearFixtures();
//获取当前形状
b2Fixture*getFixtureList()const;
void setActive(bool active);
bool isActive()const;
};
这里主要说明的就是CollisionType,box2d在发生碰撞时,如果需要对碰撞进行监听,就需要继承自吧b2ContactListener的类实现BegainContact和EndContact等方法,因为在box2d中碰撞的都是形状fixture,所以需要获取fixture的body,然后再根据body->GetUserData()获取刚体所对应的实体,然后再根据实体进行一些逻辑判断
B2Entity::~B2Entity()
{
if(m_pBody)//回收内存
{
auto pWorld = GameScene::getWorld();
if (pWorld)
pWorld->DestroyBody(m_pBody);
}
m_pBody = nullptr;
}
void B2Entity::initBody()
{
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
//创建刚体
b2World*pWorld = GameScene::getWorld();
m_pBody = pWorld->CreateBody(&bodyDef);
//设置刚体用户数据
m_pBody->SetUserData(this);
}
void B2Entity::addFixturesToBody(const std::string &shape)
{
GB2ShapeCache::getInstance()->addFixturesToBody(m_pBody,shape);
}
void B2Entity::clearFixtures()
{
//先清除原先的fixture
for(auto fixture = m_pBody->GetFixtureList();fixture;)
{
auto temp = fixture;
fixture = fixture->GetNext();
m_pBody->DestroyFixture(temp);
}
}
b2Fixture*B2Entity::getFixtureList()const
{
if(m_pBody)
return m_pBody->GetFixtureList();
return nullptr;
}
void B2Entity::setActive(bool active)
{
if(m_pBody == nullptr)
return;
m_pBody->SetActive(active);
m_bActive = active;
}
bool B2Entity::isActive()const
{
return m_bActive;
}
在这里使用了GB2ShapeCache-x.h文件,这个是和PhysicsEditor的导出文件所对应的一个单例类,主要负责形状的创建和绑定刚体。
clearFixture()是为了便于重用。还有就是setActive无法在b2ContactListener的回调函数中进行使用,这个需要注意。
Plant则是植物类的父类,目前也没什么内容,待以后扩展使用。
class PlantDelegate
{
public:
virtual ~PlantDelegate(){}
virtual void makeSun(int number,FiniteTimeAction*action,const Point&bornPos)=0;
};
class Plant : public B2Entity
{
SDL_SYNTHESIZE(float,m_fColdDownTime,ColdDownTime);//总cd
SDL_SYNTHESIZE(int,m_nHitPoint,HitPoint);//血量
SDL_SYNTHESIZE(ActiveTime,m_activeTime,ActiveTime);//活动时间
SDL_SYNTHESIZE(string,m_plantName,PlantName);//植物名称
protected:
PlantDelegate*m_pDelegate;
public:
Plant();
~Plant();
void setDelegate(PlantDelegate*delegate);
virtual void update(float dt);
};
Plant类中保存了一些基本的属性,还有一个就是PlantDelegate,这个使用的是委托,目前仅仅有一个makeSun方法,使用委托的好处就是解耦,这样Plant就不需要知道PlantDelegate是什么,只要它实现了对应的方法就可以了
接着就是PlantLayer植物层和PlantFactory植物工厂的实现了,植物层现在没什么内容,目前仅仅涉及了植物的逻辑更新和植物的产生,内部调用了植物工厂方法
bool PlantLayer::init()
{
m_pPlantFactory = PlantFactory::create();
m_pPlantFactory->retain();
return true;
}
//进行植物的逻辑更新 TODO
void PlantLayer::update(float dt)
{
for (auto it = m_plants.begin();it != m_plants.end();)
{
auto plant = *it;
plant->update(dt);
it++;
}
}
另外植物工厂不是单例类,在植物层中保存了它的指针。
void GameScene::onTouchEnded(Touch*touch,SDL_Event*event)
{
auto pos = touch->getLocation();
//是否点击了ProductLayer
auto product = m_pProductLayer->getClickedProduct(pos);
if (product)
{
product->setClicked(true);
this->collectProduct(product);
return;
}
auto nodePos = m_pLevelLayer->convertToNodeSpace(pos);
//是否点击了某一个terrain
auto terrain = m_pLevelLayer->getClickedTerrain(nodePos);
//所有操作对要经过terrain
if (terrain == nullptr)
return;
auto selectedCard = m_pCardLayer->getSelectedCard();
//存在选中的卡片,则阳光足够,cd完成
if (selectedCard)
{
bool bRet = false;
bRet = this->tryPlanting(selectedCard,terrain);
//创建完成
if (bRet)
{
//减少阳光值
this->subSun(selectedCard->getWorth());
//取消点击
m_pCardLayer->unselectedCard();
//该卡片开始cd
selectedCard->setCurCD(selectedCard->getCD());
}
}
//是否点击了铲子 TODO
}
和以前类似,只不过如果创建成功就减去阳光 取消卡片的选中,同时卡片开始cd
bool GameScene::tryPlanting(Card*card,Terrain*terrain)
{
auto topPlant = terrain->getTopPlant();
//获取必要物品
auto necessaryItem = card->getNecessoryItem();
//升级植物
if (!necessaryItem.empty() && topPlant != nullptr
&& topPlant->getPlantName() == necessaryItem)
{
//创建新植物
//移除旧植物TODO
}
//种植植物
else if (necessaryItem.empty())
{
//地形上的植物为空 并且地形符合,则创建
if (topPlant == nullptr && card->isIncludeTerrain(terrain->getTerrainType()))
{
auto plant = m_pPlantLayer->makePlant(card->getCardName());
//再设置一些基础属性
plant->setActiveTime(card->getActiveTime());
plant->setPosition(terrain->getPosition());
plant->setDelegate(this);
//添加到entity layer 场景
auto entityLayer = this->getEntityLayer();
entityLayer->addChild(plant);
//设置塔基内部的植物
terrain->setInnerPlant(plant);
return true;
}
//存在植物,但如果是容器植物,则继续判断
else if(topPlant != nullptr)
{
}
}
return false;
}
在这里进行了植物的创建,设置了一些属性,并且添加到了场景之中,其他的则是以后扩展使用的。
下一节实现向日葵