Cocos2d-x塔防游戏_贼来了——触摸响应1

原创: 任珊

引言

上章我们构建了炮塔的基类,并且创建好了三种不同的炮塔,这章我们就来把它添加到场景。

添加炮塔的方式多种多样,你可以拖动某个区域的炮塔到场景,也可以选中某个炮塔后把它插入到特定的场景区域中。但这里我们将模仿保卫萝卜中添加炮塔的方式(PS:触摸屏幕,弹出一个炮塔选择面板,选中面板中相应的炮塔后,在最初触碰到的位置上创建一个炮塔)。其过程如下图所示:

由此可见,上述添加炮塔的过程需要经过两次触碰操作才能实现,即触碰场景层(也就是PlayLayer)和触碰选择面板层(目前还未创建)。具体流程如下:

  1. 触摸场景层的某个位置,如果该处不是路面,且没有其他炮塔和障碍物,那么则在该处“瓦片”(我们把tmx地图的每一块图块都叫做瓦片)上生成一个炮塔选择面板。上图蓝色选择框的位置与触摸的图块重合。
  2. 选择炮塔面板内的炮塔。如果选中了则在第一次触摸屏幕的“瓦片”上,也就是再蓝色选择框的位置处再创建一个相应类型的炮塔,同时移除选择炮塔面板。

对于一款游戏而言,不管你的动画做得多么生动,特效做得多么炫,算法设计的多么牛逼,对它而言最重要的特性还是与玩家在游戏中的实时交互。

《辞海》中这样定义游戏:以直接获得快感为主要目的,且必须有主体参与互动的活动。由此可见,玩家与游戏的互动对于一款游戏是的多么的重要。在移动平台类游戏中,主要的互动动作基本上都是通过触摸屏幕、重力感应等方式体现的。所以接下来,我们将实现触摸功能。

场景层的响应

触摸响应是玩家在移动平台类游戏中交互体验最直接和普遍的方式。在Cocos2d-x 3.0 中,实现触摸响应的一般流程是:

  1. 重载触摸回调函数
  2. 创建并绑定触摸事件
  3. 实现触摸回调函数

下面就先来看看在PlayerLayer中怎样实现第一步触摸的响应吧。

重载触摸回调函数

打开PlayerLayer.h文件,添加以下成员函数。

bool onTouchBegan(Touch *touch, Event *event) override;

因为在场景层我们只需要触摸屏幕生成选择面板,而它的逻辑并不算复杂,所以,我们只需要重载onTouchBegan函数来响应触摸点击开始事件就足够了。onTouchMoved,onTouchEnded和onTouchCancelled都不用重写,反正本游戏用不着。

创建并绑定触摸事件

接下来,在PlayerLayer的init方法中添加代码,创建绑定触摸事件。

auto touchListener = EventListenerTouchOneByOne::create();
	touchListener->onTouchBegan = CC_CALLBACK_2(PlayLayer::onTouchBegan, this);
	_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);

一般在使用触摸事件时,我们第一步都是创建一个事件监听器,EventListenerTouchOneByOne 表示单点触摸,与之相对的还有表示多点触摸的 EventListenerTouchAllAtOnce。

接下来我们让监听器绑定事件处理函数。

监听器创建完成后我们把它绑定给eventDispatcher事件分发器,eventDispatcher 是 Node 的属性,通过它我们可以统一管理当前节点(如:场景、层、精灵等)的所有事件分发情况。它本身是一个单例模式值的引用,有了这个属性我们能更为方便的调用。

我们将事件监听器 touchListener 添加到事件调度器_eventDispatcher中。其中使用 addEventListenerWithSceneGraphPriority 方法添加的事件监听器优先级固定为0。本部分内容可参考使用Cocos2d-x制作三消类游戏Sushi Crush——第三部分中触摸事件的响应部分。

实现触摸回调函数

绑定好触摸事件后,接着就该实现具体的触摸回调函数了,代码如下:

bool PlayLayer::onTouchBegan(Touch *touch, Event *event)
{
	if(chooseTowerpanle != NULL)
    {
        this->removeChild(chooseTowerpanle);
    }
    auto location = touch->getLocation();    
    checkAndAddTowerPanle(location);
	return true;
}

一旦玩家开始触碰屏幕,我们的程序就会开始调用onTouchBegan方法,移除已有炮塔选择界面,检测触摸处是否可以创建一个炮塔选择界面,并扶正坐标值,使炮塔(框选项)能刚好添加到地图的“瓦片”上。

在游戏开发中,坐标系是一个非常重要的概念,在调用任何函数设置或得到对象的位置时,都必须要明确这个函数使用的哪个坐标系,各种坐标系之间又怎样转换。如果这部分弄混淆的话,那开发中就可能遇到各种各样的问题。所以现在依旧糊涂的看官们可以参考Cocos2d-x 3.0坐标系详解 一文。

处理触摸事件时touch对象中保存的坐标是屏幕坐标,我们在使用时必须要将它转换到Cocos2d坐标系。getLocation方法可以完成这一转换。

checkAndTowerPanle函数是用于检测和创建炮塔选择界面的方法。检测玩家触摸位置是否可以创建选择面板的条件有两个:第一,该处是空地(根据tmx文件属性判定);第二,该处没有其他炮塔。

  • 判定是草地的方法:为地图中是草地的瓦片添加属性canTouch,并把它标记为1,在程序中读它的值就可以实现了。
  • 判定有无其他炮塔的方法:根据地图的瓦片块数,把地图划分为一个MAPWIDTH * MAPHEIGHT的炮塔矩阵,使用TowerBase **towerMatrix数组来存储这个矩阵数据,如果该数组的某个位置为NULL,则该处尚且为空,可以创建一个炮塔。

下面先看看它怎么实现。

void PlayLayer::checkAndAddTowerPanle(Point position)
{
	// 1
	Point towerCoord = convertTotileCoord(position);
	Point matrixCoord = convertToMatrixCoord(position);
        // 2
	int gid = bgLayer->getTileGIDAt(towerCoord);
	auto tileTemp = map->getPropertiesForGID(gid).asValueMap();
	// 3
	int TouchVaule;
    int MatrixIndex = static_cast( matrixCoord.y * MAP_WIDTH + matrixCoord.x );	if (tileTemp.empty())
	{
		TouchVaule = 0;
	}else
	{
		TouchVaule = tileTemp.at("canTouch").asInt();
	}
	// 4
    auto tileWidth = map->getContentSize().width / map->getMapSize().width;
	auto tileHeight = map->getContentSize().height / map->getMapSize().height;
	towerPos = Point((towerCoord.x * tileWidth) + tileWidth/2 -offX, map->getContentSize().height - (towerCoord.y * tileHeight) - tileHeight/2);
    // 5
    if (1 == TouchVaule && towerMatrix[MatrixIndex]==NULL)
	{
		addTowerChoosePanle(towerPos);
	}
	else{
		auto tips = Sprite::createWithSpriteFrameName("no.png");
		tips->setPosition(towerPos);
		this->addChild(tips);
		tips->runAction(Sequence::create(DelayTime::create(0.8f),
                                         CallFunc::create(CC_CALLBACK_0(Sprite::removeFromParent, tips)),
                                         NULL));
	}
}

1. 把传入的Cocos2d坐标系分别转换为tiledMap坐标和数组坐标。tiledMap坐标的(0, 0) 坐标在左上角,而数组坐标的(0 , 0角。
2. 分别求瓦片的全局标和数组下标。对于tiledMap的每一个瓦片来说,它都有一个全局标识量,瓦片的GID范围从正整数1开始到瓦片地图中tile的总量。得到了瓦片的GID就可以获取该块瓦片的全值在左。
3. 获取瓦片信息。在此之前,我们需要重新编辑一下tmx文件,如下图所示,我们把它的空白处改为用一透明的瓦片来填充(因为这样的话,我们就可以在地图的下层贴任意的背景图了,同时这样也省去了编辑的麻烦),该瓦片有一个canTouch属性,其值为1,这也表明了此瓦片上可以创建炮塔。

4. 修正炮塔面板坐标。其实这就是个把地图坐标转换为屏幕坐标的过程,需要注意的是,计算坐标值时我们应该减去之前修正误差的那部分距离,这样才能确保准确。
5. 如果满足该处是空地且无其他炮塔的条件,那么则在该处创建炮塔选择界面;否则在该处添加一个提示错误的图片,不时这个图片会被移除。

下面是转换坐标的两个函数,同样地,在转换中我们需要加上修正tiledMap数值的那段距离offX,既把裁剪掉的那部分地图补会来,否则又会造成错位。

// 把本地坐标(OpenGL坐标)转换为地图坐标
Point PlayLayer::convertTotileCoord(Point position)
{
	int x = (position.x + offX)/ map->getContentSize().width * map->getMapSize().width;
	int y =map->getMapSize().height- position.y / map->getContentSize().height * map->getMapSize().height;

return Point(x, y); } // 把本地坐标(OpenGL坐标)转换为数组坐标 Point PlayLayer::convertToMatrixCoord(Point position) { int x = (position.x + offX)/ map->getContentSize().width * map->getMapSize().width; int y = position.y / map->getContentSize().height * map->getMapSize().height; t)reurn Point(x, y); }

到目前为止,我们就可以准确的找的哪块“瓦片”上可以创建炮塔了。后面我们将通过addTowerChoosePanle(towerPos)方法先创建一个炮塔选择面板,再在选择面板中选择相应的炮塔,更多内容请继续关注下一章教程吧。

标签: cocos2d-x教程

?>