在上一课中,我们学习了通过不同层的Joystick控制角色人物的移动,但是人物到处跑,还是无法实现我们的效果。此篇上半部分,限定Hero的移动范围是他不要跑出地图或者是墙上(墙的范围参考第一篇) ;下半部分,我们要通过更新MapLayer来实现地图滚动效果。

开发环境

Win64:vs2010

Cocos2d-x v3.4Final

TexturePackerGUI

MapEdit

代码构建A

角色Role

Hero

为什么我会在updateSelf中重复写了好几个坐标但是就用到一次?

在这里更新一下updateSelf

void Hero::updateSelf()//刷新自己

{

if(this->getCurrActionState() == ACTION_STATE_WALK)

{

Vec2 currentP= this->getPosition(); //当前坐标

Vec2 expectP = currentP + this->getVelocity(); //期望坐标

Vec2 actualP = expectP; //实际坐标

float mapWidth = global->tileMap->getContentSize().width; //整张地图宽度

float herofat = this->getBodyBox().actual.size.width/2; //角色横向宽度,以受攻击的bodybox为准

////不能跑到墙上去

//if(expectP.y<0 || !global->tileAllowMove(expectP))

float maptileHeight = global->tileMap->getTileSize().height;

if(expectP.y < 0 || expectP.y > maptileHeight * 3 )

{

actualP.y =currentP.y;

}

//不能跑出地图外面

if(expectP.x < herofat || expectP.x >= mapWidth - herofat)

{

//if(!global->tileAllowMove(expectP))

actualP.x = currentP.x;

}

this->setPosition(actualP);

this->setLocalZOrder( Director::getInstance()->getVisibleSize().height - this->getPositionY());

}

}


20150209155346795.png

这是Hero的BodyBox(蓝色)和HitBox(红色),当我们移动时以蓝色框宽度一半作为判断条件,免得Hero跑出边缘。

之后还要注册Global的tileMap,不然Hero::updateSelf()调用都是NULL从而报错。

Game

MapLayer

.cpp

在initMapWithFile最后插入

1global->tileMap=TileMap;

结果

我们的Hero没办法跑出地图了!


20150209153012668.gif

接下来实现,当Hero跑到新地点时,地图能够同步移动过去。

代码构建B

开始之前我不得不强调和感叹下文件结构的重要性。

之前参考别人的代码写的地图滚动,使用了Hero的速度作为参数,到后期出现奇葩的BUG:

受到攻击时候是不能移动的–>Hero受到攻击–>摇杆还是在传递Hero速度–>地图滚没了

这次调整了文件结构,所有关于地图的变化全部放在MapLayer中进行。并且使用了一个泛用性很高的2D横版移动算法,可以同时跟踪X轴和Y轴。

Game

MapLayer

.h

添加用于更新地图的定时器代码啊

1

2void update(float dt);

void setViewpointCenter(Point pos);

.cpp

init的注释或者是加新的

1this->scheduleUpdate();

实现方法是:

void MapLayer::update(float dt)

{

this->setViewpointCenter(global->hero->getPosition());

}

void MapLayer::setViewpointCenter(Point pos) //这个是移动地图,同时跟踪X,Y轴标准算法

{

Size winSize = Director::getInstance()->getWinSize();

auto _map = global->tileMap;

//如果主角坐标小于屏幕的一半,则取屏幕中点坐标,否则取对象的坐标

int x = MAX(pos.x, winSize.width/2);

int y = MAX(pos.y, winSize.height/2);

//如果X、Y的坐标大于右上角的极限值,则取极限值的坐标(极限值是指不让地图超出屏幕造成出现黑边的极限坐标 )

x = MIN(x, (_map->getMapSize().width * _map->getTileSize().width) - winSize.width/2);

y = MIN(y, (_map->getMapSize().height * _map->getTileSize().height) - winSize.height/2);

//对象当前所在坐标

Point actualPosition = Vec2(x, y);

//计算屏幕中点和所要移动的目的点之间的距离

Point centerOfView = Vec2(winSize.width/2, winSize.height/2);

Point viewPoint = centerOfView - actualPosition;

//设定一下地图的位置 ,这里一定要注意,单纯移动自己或者是_MAP移动都是无效的,在这里足足卡了好长时间

//_map->setPosition(viewPoint);

//this->setPosition(viewPoint);

//global->gameLayer->setPosition(viewPoint);

this->getParent()->setPosition(viewPoint);

}

虽然算法不是从这个地方找到的,但是这位解释这个算法的原理很好:


20150209155908093.jpg

(图片来源地址:雨松MOMO带你走进游戏开发的世界之主角的移动与地图的平滑滚动)

结果


20150209154748215.gif

结语

本篇实现了边缘检测+地图滚动。即是要求人物在移动的时候,保持地图滚动,因为地图足够长的时候,不能让人物一出场就跑出屏幕的范围,而要使其在屏幕上跑完整个地图。

由于本Demo全部资源很少,目前手机基是功能过剩,所以就不实现卡马克卷轴算法。