【cocos3.x+tilemap】制作rpg小游戏(二)遮挡与碰撞
前面制作了地图,现在就可以在工程中使用了。现在只实现了遮挡与碰撞,后续再实现点击屏幕移动、寻路算法、npc交互等。 实现遮挡与碰撞,都需要解决一个核心问题——当前角色到底在哪一块瓦片上,也就是cocos坐标如何转化为TileMap瓦片坐标,这个问题搞了n久,最后实现方法还是很麻烦,如果有人知道有更好的方式,请联系我^_^ 一、坐标转换参考: Picking Tile in staggered isometric map 其原理就在第一篇回答中,不过其最后一步转换是flash中通过取色来完成的,cocos中无法使用,就使用第二篇文章中的利用简单二元方程坐标系来判断点在线上还是线下来完成。 简单说一下原理就是:
具体上代码:
//小矩形中,两点确定一条直线y=ax+b,通过判断cx在线上对应点与cy的关系,来判断点与线的关系
//return:0:线中,1:线下面,-1:线上面
int Map45Scene::getPosOfLine(float ax,float ay,float bx,float by,float cx,float cy){
//计算斜率
float slope = (by - ay) / (bx - ax);
//计算截距
float yIntercept = ay - ax * slope;
//计算cx对应的线上的解
float cSolution = cx * slope + yIntercept;
//通过判断解与cy的关系,判断在线上还是线下
if(cy < cSolution){
return -1;
}else if(cy > cSolution){
return 1;
}else{
return 0;
}
}
Vec2 Map45Scene::getTilePosAtPos(const Vec2 &pos){
//pos应该转换为相对地图的坐标
auto mapPos = _map->convertToNodeSpaceAR(pos);
//auto mapPos = pos;
auto tileSize = _map->getTileSize();
auto mapSize = _map->getMapSize();
//将其转换为左上角坐标系
mapPos.y = mapSize.height * tileSize.height / 2 + tileSize.height / 2 - mapPos.y;
//取到小矩形的位置
int sx = int(mapPos.x / tileSize.width * 2);
int sy = int(mapPos.y / tileSize.height * 2);
int tx,ty;
//判断小矩形中线的位置
if(sx %2 == sy %2){
//如果线斜率是大于0
//取线上的两个点
float ax = sx * tileSize.width;
float ay = (sy + 1) * tileSize.height;
float bx = (sx + 1) * tileSize.width;
float by = sy * tileSize.height;
if(getPosOfLine(ax,ay,bx,by,mapPos.x,mapPos.y) < 0){
//在线上面
if(sy % 2 == 1){
//偶数行上
tx = int(mapPos.x / tileSize.width);
ty = int(mapPos.y / tileSize.height) * 2;
}else{
//奇数行上
tx = int((mapPos.x - tileSize.width / 2) / tileSize.width);
ty = int((mapPos.y - tileSize.height / 2) / tileSize.height) * 2 + 1;
}
}else if(getPosOfLine(ax,mapPos.y) >= 0){
//线下面或正好在线上
if(sy % 2 == 1){
//奇数行上
tx = int((mapPos.x - tileSize.width / 2) / tileSize.width);
ty = int((mapPos.y - tileSize.height / 2) / tileSize.height) * 2 + 1;
}else{
//偶数行上
tx = int(mapPos.x / tileSize.width);
ty = int(mapPos.y / tileSize.height) * 2;
}
}
}else{
//线斜率小于0
float ax = sx * tileSize.width;
float ay = sy * tileSize.height;
float bx = (sx + 1) * tileSize.width;
float by = (sy + 1) * tileSize.height;
if(getPosOfLine(ax,mapPos.y) < 0){
if(sy % 2 == 0){
//奇数行上
tx = int((mapPos.x - tileSize.width / 2) / tileSize.width);
ty = int((mapPos.y - tileSize.height / 2) / tileSize.height) * 2 + 1;
}else{
tx = int(mapPos.x / tileSize.width);
ty = int(mapPos.y / tileSize.height) * 2;
}
}else{
if(sy % 2 == 0){
//偶数行上
tx = int(mapPos.x / tileSize.width);
ty = int(mapPos.y / tileSize.height) * 2;
}else{
tx = int((mapPos.x - tileSize.width / 2) / tileSize.width);
ty = int((mapPos.y - tileSize.height / 2) / tileSize.height) * 2 + 1;
}
}
}
return Vec2(tx,ty);
}
下面看取到TileMap坐标后的应用 二、碰撞检测前面制作地图时,我们加了一个blocks用于画玩家不可以穿越的块,我们现在可以取到一个cocos坐标所对应的地图瓦片坐标,这个碰撞功能的实现就很简单了:
代码如下: void Map45Scene::dealPlayerPos(float dt){
//玩家移动速度
float speed = MAP45_PLAYER_SPEED;
//两个方向的速度增量
float xDelta = speed * dt;
float yDelta = xDelta * _map->getTileSize().height / _map->getTileSize().width;
Vec2 pos;
//确定移动方向
if(_status == BTN_DOWN){
pos.x -= xDelta;
pos.y -= yDelta;
}else if(_status == BTN_UP){
pos.x += xDelta;
pos.y += yDelta;
}else if(_status == BTN_LEFT){
pos.x -= xDelta;
pos.y += yDelta;
}else if(_status == BTN_RIGHT){
pos.x += xDelta;
pos.y -= yDelta;
}
auto newPos = _normal->getPosition() + pos;
auto tile = getTileAtPosition(newPos,_map->getLayer("blocks"));
if(tile) {
//如果有阻挡,不移动
return;
}
//地图移动代替角色移动
_map->setPosition(_map->getPosition() - pos);
_normal->setPosition(_normal->getPosition() + pos);
_anim->setPosition(_normal->getPosition() + pos);
}
三、遮挡我们虽然用45度斜角制作了一个2.5D的世界,看起来有了点立体感,但人物即使位于树、墙等的后面,还是会遮挡掉前面的物体,这样一下子就不真实了,所以我们需要动态修改玩家的渲染层级,来让玩家位于树前面遮挡树,树后面则被树遮挡。 1.为瓦片地图添加不同的渲染层级,我们需要在制作地图时,为图层添加OpenGl的Z order属性
2.开启OpenGl的深度渲染:注意在scene显示前调用 Director::getInstance()->setProjection(cocos2d::Director::Projection::_2D);
Director::getInstance()->setDepthTest(true);
3.我们知道了z order的算法,又可以根据玩家位置知道玩家位置的瓦片地图坐标,就可以直接动态设置玩家的vertexz属性,达到我们的目的: void Map45Scene::updatePlayer(){
auto pos = getTilePosAtPos(_normal->getPosition());
auto mapSize = _map->getMapSize();
//设置玩家的vertexz属性
_normal->setPositionZ( pos.y - mapSize.height);
_anim->setPositionZ( pos.y - mapSize.height);
}
四、结语其他实现后续再补充。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- 将数据从localStorage通过AJAX发送到PHP并将其保存在HTML文
- c# – 如何检测KeyDown或KeyUp WPF事件中的方括号?
- 最全的 Swift 4 新特性解析
- 苹果开发 笔记(74)GDataXML 解析 xml
- Beta 2 更新:Swift 2.1 Playground 使用值放置方法
- Cocos2d-js_____Sprite 和 SpriteBatchNode
- ruby-on-rails – 基于本地或CircleCI的不同Rubocop结果
- 在页面中插入flash
- ruby-on-rails – Grouped Collection选择按字母顺序排列的
- Oracle – 为什么要使用软件包而不是独立的过程或函数