怎样制作基于Cocos2d-x的SLG游戏-第6章

原创: @涵紫任珊

怎样制作基于Cocos2d-x的SLG游戏-第1章
怎样制作基于Cocos2d-x的SLG游戏-第2章
怎样制作基于Cocos2d-x的SLG游戏-第3章
怎样制作基于Cocos2d-x的SLG游戏-第4章
怎样制作基于Cocos2d-x的SLG游戏-第5章
怎样制作基于Cocos2d-x的SLG游戏-第6章
怎样制作基于Cocos2d-x的SLG游戏-第7章

前篇教程中我们已经创建好了播种界面,而本章我们将继续对单独的瓦片进行操作,完成在土地上播种的功能,同时我们将添加其他类似的界面来操作瓦片,如:清理界面(可以将树木、房屋等等从地图中清除),收获界面(收获成熟的农作物)等等。

再次说明,由于模拟经营类游戏的操作多样性,本系列教程将不会尽善尽美的实现整个SLG游戏的所有功能,这里只选取其中有代表的一些功能来实现,有兴趣的同学欢迎在本游戏的基础上进行扩展。本游戏最终将要实现的功能是这样的:

  1. 点击商品项和其他障碍物所在层(这里统称第2层)上的瓦片,将出现一个清理界面,该界面上有一把叉子,玩家点击叉子可将点击的瓦片从地图中移除,不过点击土块系瓦片除外。
  2. 点击空土块瓦片将出现上节中创建的播种界面,玩家选取播种层中的农作物选项,就可以在土块上栽种相应的种子。
  3. 种子种下去的同时,该处瓦片上将会生成一个计时面板,用于纪录种子多久后成熟。如果该处瓦片再次处于未选中状态,那这个计时面板将会隐藏起来,直到种子完全成熟后该处的计时界面才会被销毁。
  4. 种子成熟后,瓦片上的农作物将会变为相应的成熟作物。如果这时选中这些成熟的作物,那么将出现一个收获界面。收取作物后瓦片又将变为什么都没有的空土块。

以上就是对单独瓦片进行操作的全部功能,后面我们将会一一实现。

播种

上一章已经创建好播种界面,所以我们接下来首先来实现播种这项功能。
1、 根据游戏功能需求,定义如下的一个枚举类型来标示玩家所点击的瓦片类型:

typedef enum // 定义瓦片类型。如果需要扩展,可这这里定义更多类型。
{
    GROUD = 1,
    GROUD_CROP = 2,
    CROP_HARVEST,
    OTHER
} TileType;

2、 接着在玩家开始触碰屏幕的地方(既onTouchesBegan函数中)判断所触碰瓦片的类型。如下,在onTouchesBegan函数的if (gid != 0)中添加以下一段代码:

    switch (gid)
    {
        case 9: // 触碰到的是空土块
            tileType = TileType::GROUD;
            break;
        // 触碰到的是种了农作物幼苗的土块
        case 18:  
        case 20:
        case 22:
            tileType = TileType::GROUD_CROP;
            break;
        // 触碰到种植有成熟农作物的土块
        case 19:
        case 21:
        case 23:
            tileType = TileType::CROP_HARVEST;
            break;
        // 其他情况,也就是可以被清除的其他瓦片类型
        default:
            tileType = TileType::OTHER;
            break;
        }

以上代码中的gid对应了下图中的瓦片项。
iso-test-128

3、 当玩家松开手指的时候,创建播种界面。
在onTouchesEnded函数的if(press)中添加如下的代码:

    if(tileType == GROUD)
    {
        auto panel = SeedChooseLayer::create();
        panel->setPosition(screenPos);
        this->addChild(panel, 10, SEEDPANEL_TAG);
    }

其中SEEDPANEL_TAG是播种界面的标示,我们可以用它来判断播种界面是否存在,是否移除该界面等操作。其他界面也同样有着自己的标示,如下所示:

#define SEEDPANEL_TAG           888
#define HARVESTPANEL_TAG        889
#define REMOVEPANEL_TAG         890

4、 创建播种界面后,我们就该播种了。
不过首先请打开瓦片地图文件,我们来整理下瓦片层:在最底层的地图层上只放置草地瓦片,在草地层之上的层用来放置障碍物和商品项,最上层置空,用来设置提醒项。同时,修改之前的图层名字,规范代码逻辑(不然以后自己都看不懂自己写的什么),如下图所示:
p2
打开游戏主场景GameScene,添加update函数,并实现它。如下所示:

void GameScene::update(float dt)
{
    // 通过标示得到场景中的子节点
     auto seedPanel = this->getChildByTag(SEEDPANEL_TAG);
    // 判断瓦片是否为空土块,并且是否已经创建好了播种界面
    if(tileType == GROUD && seedPanel!= NULL)
    {
        // 得到选择的农作物类型
        auto type = ((SeedChooseLayer*)seedPanel)->getCurrType();
        // 根据农作物类型种植相应的农作物
        switch (type)
        {
            case WHEAT:
                map->getLayer("goodsLayer")->setTileGID(18, touchObjectPos);
                this->removeChild(seedPanel);
                break;
            case CORN:
                map->getLayer("goodsLayer")->setTileGID(20, touchObjectPos);
                this->removeChild(seedPanel);
                break;
            case CARROT:
                map->getLayer("goodsLayer")->setTileGID(22, touchObjectPos);
                this->removeChild(seedPanel);
                break;            
            default:
                break;
        }
    }
}

不要忘了在init函数中加上scheduleUpdate(),这样才会每一帧都调用update()。

5、 当玩家触摸到屏幕其他地方时,一定要把已有的播种界面清除,以确保场景中最多只有一个播种界面。所以回到onTouchesBegan函数中,清除已有播种界面。

    auto seedPanel = this->getChildByTag(SEEDPANEL_TAG);
    if(seedPanel){
        this->removeChild(seedPanel);
    }

现在运行我们的代码,就可以像下图一样在土地上播种庄稼了。

p3

清理瓦片

接下来我们创建一个清理界面,用来移除除土块以外的其他瓦片。它同播种界面原理一样,其定义如下:

class RemoveLayer: public Layer
{
public:
    virtual bool init() override;
    bool onTouchBegan(Touch *touch, Event *event);
    CREATE_FUNC(RemoveLayer);
    CC_SYNTHESIZE(bool, remove, Remove);

private:
    Sprite* fork;
};

该界面只有一个叉子供玩家选择,所以我们用布尔类型的变量来标示玩家是否选中它。其事件回调函数代码如下:

bool RemoveLayer::onTouchBegan(Touch *touch, Event *event)
{
    auto target = static_cast(event->getCurrentTarget());

    Point locationInNode = target->convertTouchToNodeSpace(touch);
    Size size = target->getContentSize();
    Rect rect = Rect(0, 0, size.width, size.height);

    if (rect.containsPoint(locationInNode))
    {
        target->setOpacity(180);
        // 判断是否选中叉子
        if (target == fork)
        {
            remove = true;
        }
        return true;
    }
    return false;
}

实现了清理界面后,按照创建和销毁播种界面的思路把清理界面添加到游戏场景中,既在onTouchesEnded中创建清理面板,在onTouchesBegan中销毁该清理面板,并在update函数中检测更新:

    if(NULL != removePanel && tileType == OTHER)
    {
        if(((RemoveLayer*)removePanel)->getRemove() == true)
        {
            map->getLayer("goodsLayer")->removeTileAt(touchObjectPos);
            this->removeChild(removePanel);
        }
    }

这样一来,我们就可以移除地图中的看不顺眼的瓦片了。

收获

运行游戏,当前效果如下图所示:

p4

此时点击长有幼苗的土块,将会出现一个记录庄稼成熟时间的面板出现,当时间到时,庄稼将会成熟。这个时候如果再点击庄稼,玩家就可以所获它了。

由于计时面板的创建有别于其他小界面的创建,所以这里我们先跳过这一步,先来创建收获面板。
收获界面同清理界面基本是一致的,它将会在庄稼成熟的时候被创建,其定义如下:

class HarvestLayer: public Layer
{
public:
    virtual bool init() override;
    bool onTouchBegan(Touch *touch, Event *event);
    CREATE_FUNC(HarvestLayer);
    CC_SYNTHESIZE(bool, harvest, Harvest);

private:
    Sprite* harvestSprite;
};

变量harvest用来标示玩家是否选中收获项,HarvestLayer的实现很简单,这里就不赘述了。

本章所用资源已更新,点此可进行下载,文章中的源代码将在本系列教程结束时上传,敬请期待。

标签: none

?>