改写《魔塔》后篇04:添加物品和门

           我们接下来会继续向游戏地图中添加更多的元素,包括各种物品和门。物品有诸如血瓶、钥匙、各种道具和装备之类,勇士可以拾取物品,并获得相应的属性提升或技能,而门只有三种:黄门、蓝门和红门,分别用对应颜色的钥匙才能打开。
(1)在地图上绘制物品和门
      切换到Tiled编辑器,打开游戏地图文件0.tmx。首先添加两个块层item和door,分别用于绘制物品和门。将素材item.png和door.png复制到工程的Resource目录下。
      选择菜单栏中的“地图-->新图块”,导入两个图片素材,然后就可以绘制物品和门了。

      接下来直接运行游戏,我们发现地图现在充实多了,有点真正游戏的样子了。

               

(2)我们希望勇士遇到地图上的物品时,头上会冒出一行文字说明,如“获取小血瓶,生命值+100”,然后将地图上的物品移除,并增加到勇士自己的背包当中。下面来看代码实现。

      首先要在GameConstants.h中增加一种碰撞类型kItem,接着为GameMap类增加获取itemLayer的方法:

CC_PROPERTY_READONLY(CCTMXLayer*,itemLayer,ItemLayer);

      记得要在extraInit方法中为itemLayer和doorLayer赋值:

itemLayer=this->layerNamed("item");

      然后在Hero的checkCollision方法中添加itemLayer的碰撞检测,方法与墙壁和怪物的碰撞检测一致:

//获取物品层对应的图块ID
	tileGid=sGlobal->gameMap->getItemLayer()->tileGIDAt(targetTileCoord);
	//如果图块ID不为0,表示有物品
	if(tileGid)
	{
		//拾取物品
		pickUpItem();
		return kItem;
	}

      目前我们没有对物品进行区分。未来的拾取物品逻辑可以在这个方法中补充,根据物品的ID在地图上显示不同的提示语,以及获得不同的功效等。pickUpItem要先在.h文件里声明,其实现如下:

//拾取物品
void Hero::pickUpItem()
{
	//显示提示消息
	sGlobal->gameLayer->showTip("get an item, hp +100",this->getPosition());
	//将物品从地图上移除
	sGlobal->gameMap->getItemLayer()->removeTileAt(targetTileCoord);
}

      我们还需要对Hero的move方法稍作修改,目前在checkCollision返回不为kNone类型时,就不允许勇士移动。但拾取物品时,勇士仍然可以行走,所以需要修改判断是否允许移动的条件为kWall和kEnemy时禁止移动,而kNone和kItem时允许勇士通行:

//调用checkCollision检测碰撞类型,如果是墙壁,则只需要设置勇士的朝向
	CollisionType collisionType=checkCollision(targetPosition);
	if(collisionType==kWall||collisionType==kEnemy)
	{
		setFaceDirection((HeroDirection)direction);
		return;
	}

(3)添加门

        在地图上添加门的方法仍然与添加墙壁、怪物和物品的方法基本相同。不同之处是,勇士在执行打开门的操作后,门需要播放一段打开的动画。

        还是一步一步来,在GameConstants.h中增加一种碰撞类型kDoor。在GameMap中增加doorLayer的相关代码,包括初始化get方法。这里就不再列出了。接下来修改Hero的checkCollision方法,增加门的碰撞检测:

	//获取门对应的坐标图块ID
	tileGid=sGlobal->gameMap->getDoorLayer()->tileGIDAt(targetTileCoord);
	if(tileGid)
	{
		//打开门
		openDoor(tileGid);
		return kDoor;
	}

      openDoor方法的实现如下,其中我们需要保存正在打开的门的图块ID,在更新动画时需要用到。然后开启一个定时器每隔0.1s更新门的动画。

//打开门
void Hero::openDoor(int gid)
{
	//如果门正在被开启,则返回
	if(isDoorOpening)
		return;
	//保存正在被开启的门的初始GID
	targetDoorGID=gid;
	isDoorOpening=true;
	//定时器更新门动画
	schedule(schedule_selector(Hero::updteOpenDoorAnimation),0.1f);
}

      在updateOpenDoorAnimation方法中,先计算下一帧的图块ID。我们根据door.png分析每帧的位置来计算:TileMap的图块编号方式是横向递增1。拿黄门举例,第1行第1列为第一帧的图块,假设其ID为startGID,那么第2行相应位置的图块ID就为startGID+4,第三帧和第四帧的图块ID分别为startGID+8和startGID+12.计算出下一帧的图块ID后,判断其是否超过startDID+12,是则说明动画结束,可以将门从地图上删除,并且取消定时器,否则更新动画至下一帧。

//更新开门动画
void Hero::updteOpenDoorAnimation(float dt)
{
	//计算动画下一帧的图块ID,TileMap的图块编号方式是横向递增1,
	//所以每列相同的位置的图块ID相差了每行图块的个数
	int nextGID=sGlobal->gameMap->getDoorLayer()->tileGIDAt(targetTileCoord)+4;
	//如果超过了第4帧动画,就将当前位置的图块删除,并取消定时器
	if(nextGID-targetDoorGID>4*3){
		sGlobal->gameMap->getDoorLayer()->removeTileAt(targetTileCoord);
		unschedule(schedule_selector(Hero::updteOpenDoorAnimation));
		isDoorOpening=false;
	}else{
	    //更新动画至下一帧
	    sGlobal->gameMap->getDoorLayer()->setTileGID(nextGID,targetTileCoord);
	}
}

     最后,还要修改一下Hero的move方法,将遇到门的情形归结到禁止勇士移动的种类中:

//调用checkCollision检测碰撞类型,如果是墙壁、怪物、门,则只需要设置勇士的朝向
	CollisionType collisionType=checkCollision(targetPosition);
	if(collisionType==kWall||collisionType==kEnemy||collisionType==kDoor)
	{
		setFaceDirection((HeroDirection)direction);
		return;
	}

     到目前为止,我们成功地在游戏地图中添加了物品和门,并且实现了勇士和它们之间的交互。相信大家已经熟悉了在TileMap上添加元素的方法。那么就请尽情发挥想象力,为游戏扩展更丰富的功能吧。

           改写《魔塔》后篇04:添加物品和门_第1张图片


你可能感兴趣的:(cocos2d-x,魔塔)