使用Cocos2d-x-3.0游戏引擎。编写一个瓦片地图游戏 part03

上一篇地址:http://www.cocoachina.com/bbs/read.php?tid=196685

  1. Collision detected
    碰撞检测

自从我们的精灵能够四处移动后,你一定非常激动。同样也有一些奇怪的事情发生。你大概也已经注意到了。精灵可以穿越任意地点。所以我们接下来就要让它变得更真实。

Through the house

回到瓦片地图编辑器。按下“新图块”菜单并打开新的接下来使用的素材。右击红色的那个,并为之设定新的属性。(注意大小写。不然会出问题。)

现在建造一个新的层,然后取名为“blockage01”。(或者其他你喜欢的名字,只要你能记住就行)在地图上涂上这些红色的矩形块以此标记那些我们的精灵不能移动的区域。

现在,你也许已经知道我们接下来要做什么了!我们编程让程序检测到红色矩形块并获取到它的属性——“Blockage”属性为真。(注意是“true”还是“True”,这是由关联的)使这些区域能被通过或不能。
回到VS。这次我确信你知道应该在哪里精确地放置这些代码。

加在‘HelloWorldScene.h’里面

cocos2d::TMXLayer *_blockage;



加在‘HelloWorldScene.cpp’里面<pre class="brush:cpp; toolbar: true; auto-links: false;">_blockage = _tileMap->layerNamed("Blockage01");

但是这次我么你要设置这些红色瓦片为不可见。

_blockage->setVisible(false);



添加一个新的方法来计算瓦片的坐标<pre class="brush:cpp; toolbar: true; auto-links: false;">class HelloWorld : public cocos2d::Layer
{
public:

cocos2d::Point tileCoordForPosition(cocos2d::Point position);

}
```



In the ‘HelloWorldScene.cpp’:
<pre class="brush:cpp; toolbar: true; auto-links: false;">Point HelloWorld::tileCoordForPosition(Point position)
{
 int x = position.x / _tileMap->getTileSize().width;
 int y = ((_tileMap->getMapSize().height * _tileMap->getTileSize().height) - position.y) / _tileMap->getTileSize().height;
 return Point(x, y);
}
```



原始坐标在画面的左上角。所以你可能对上面的数学公式有点困惑。  <img title = '004.png' src='http://cdn.cocimg.com/bbs/attachment/Fid_41/41_298631_1b6f267e3c8751c.png' > 
                                The original coordinate


我们替换掉原本在“void HelloWorldPlayerPosition(Point position)”的代码<pre class="brush:cpp; toolbar: true; auto-links: false;">void HelloWorld::setPlayerPosition(Point position)
{
    Point tileCoord = this->tileCoordForPosition(position);
    int tileGid = _blockage->getTileGIDAt(tileCoord);
    if (tileGid) {
        auto properties = _tileMap->getPropertiesForGID(tileGid).asValueMap();
        if (!properties.empty()) {
            auto collision = properties"Blockage”].asString();
            if ("True" == collision) {
                return;
            }
        }
    }
    _player->setPosition(position);
}
```



我们把精灵的坐标转换为瓦片的坐标。然后使用方法“getTileGIDAt”获得特定位置GID坐标,然后用GID检查瓦片的属性并决定是否能够穿过。(我们之后将添加更多的功能进“void HelloWorld::setPlayerPosition(Point position)’”)


试试运行你的游戏。这次精灵就不会穿过那些你已经堵塞了的区域。并且红色的瓦片不会显示毕竟我们将其设置为了不可见。


<img title = '005.png' src='http://cdn.cocimg.com/bbs/attachment/Fid_41/41_298631_595e7e517996feb.png' > 
                                      Blockage


打开一个新的瓦片地图材料,并在瓦片地图编辑器中创建一个新的层(我给他这个层命名为‘前景层01’)然后在前景层01上做点农活。。。使用我们之前已经见过的绿色瓦片,在障碍层01上将它们涂绿。(是的。是障碍层01,不是我们之前创建的前景层01)。
<img title = '006.png' src='http://cdn.cocimg.com/bbs/attachment/Fid_41/41_298631_7a97434fc03e0e7.png' > 
New tile map                            Create a new layer
 <img title = '007.png' src='http://cdn.cocimg.com/bbs/attachment/Fid_41/41_298631_68fda2390a0987d.png' > 





在‘HelloWorldScene.h’:里面

<pre class="brush:cpp; toolbar: true; auto-links: false;">cocos2d::TMXLayer *_foreground;
```



在‘HelloWorldScene.cpp’里面<pre class="brush:cpp; toolbar: true; auto-links: false;">_foreground = _tileMap->getLayer("Foreground01");
```



添加这些代码进‘void HelloWorld::setPlayerPosition(Point position)’


<pre class="brush:cpp; toolbar: true; auto-links: false;">auto collectable = properties"Collectable"].asString();
if ("True" == collectable) {
    _blockage->removeTileAt(tileCoord);
_foreground->removeTileAt(tileCoord);


_player->setPosition(position);  
}
```



我们的精灵是一只真正的食草动物。
  <img title = '008.png' src='http://cdn.cocimg.com/bbs/attachment/Fid_41/41_298631_69b2146a204dbdc.png' > 
Herbivore

未完待续~(一日一更)

下一篇地址:http://www.cocoachina.com/bbs/read.php?tid=197159&page=1&toread=1#tpc

不错的,支持一下

楼主的资源能给一份么?:2:

里面没放背景音乐('background.mp3’文件),所以找一首自己喜欢又不会太大的就好~

我又来了……

你好,认真看了您的教程,这个变量_meta哪里来的?麻烦告知,谢谢

同问!很疑惑!

是“_blockage”···sorry···手痒打错了···············:3:

是“_blockage”···sorry···手痒打错了···············:3:···

真心好贴呀。学习中…

auto collectable = properties"Collectable"].asString();
if (“True” == collectable) {
_blockage->removeTileAt(tileCoord);
_foreground->removeTileAt(tileCoord);
这个应该放在if (!properties.empty()) {}里面吧?我这边报错了可能是我地图画的有问题~
还有~~楼主你终于不用layerNamed了:14:

是滴~~后面“blockage”的判断也是放在if (!properties.empty()) {}里面。。

:14: :14: :14: :14: :14:

这种方式打物理是比较简单,不过精度限制很大,体验很差

我按这个例子写,发现障碍的位置算得不对啊!!!我把障碍显示出来了,发现怪物可以猜到障碍上,貌似坐标翻了一倍。。。不知道是什么情况,还有为什么要每次移动是半个瓦片,而不是一个呢?貌似地图中说每个瓦片的大小是3232,但实际显示出来的大小只有1616左右?求大神指点!!!

Point HelloWorld::tileCoordForPosition(Point position)
{
int x = position.x / _tileMap->getTileSize().width;
int y = ((_tileMap->getMapSize().height * _tileMap->getTileSize().height) - position.y) / _tileMap->getTileSize().height;
return Point(x, y);
}
应该改成
Point HelloWorld::tileCoordForPosition(Point position)
{
int x = position.x / _tileMap->getTileSize().width;
int y = ((_tileMap->getMapSize().height * _tileMap->getTileSize().height - 1) - position.y) / _tileMap->getTileSize().height;
return Point(x, y);
}
不然点到左下角会出错

好的。找时间我会测试下(最近事多)。多谢你的反馈

— Begin quote from ____

引用第14楼石头哥x于2014-05-28 01:32发表的 回 楼主(mercuryrozen) 的帖子 :
我按这个例子写,发现障碍的位置算得不对啊!!!我把障碍显示出来了,发现怪物可以猜到障碍上,貌似坐标翻了一倍。。。不知道是什么情况,还有为什么要每次移动是半个瓦片,而不是一个呢?貌似地图中说每个瓦片的大小是3232,但实际显示出来的大小只有1616左右?求大神指点!!! http://www.cocoachina.com/bbs/job.php?action=topost&tid=196836&pid=962146

— End quote

找到原因了,Touch::getLocation和Sprite::getPosition和Sprite::setPosition用的都是GLView的坐标,而GLView的坐标到逻辑坐标有一个转换关系,需要用Director::getInstance()->getContentScaleFactor()这个比率转一下,这个东东是在 AppDelegate::applicationDidFinishLaunching适配屏幕分辨率的时候改的,

具体修改的地方
void HelloWorld::onTouchEnded(Touch *touch, Event *unused_event)
{

。。。
auto touchLocation = touch->getLocation();

touchLocation = this->convertToNodeSpace(touchLocation);

//touchLocation = _background->convertToNodeSpace(touchLocation);
auto playerPos = _player->getPosition();
auto diff = touchLocation - playerPos;
playerPos = playerPos * Director::getInstance()->getContentScaleFactor();// 将GLVIEW坐标转换成地图逻辑坐标
if (abs(diff.x) > abs(diff.y))
{
if (diff.x > 0)
{
playerPos.x += _tileMap->getTileSize().width;//这里把/2去掉
_player->runAction(actionTo2);
}
else
{
playerPos.x -= _tileMap->getTileSize().width;
_player->runAction(actionTo1);
}
}
else
{
if (diff.y > 0)
{
playerPos.y += _tileMap->getTileSize().height;
}
else
{
playerPos.y -= _tileMap->getTileSize().height;
}
}
。。。

void HelloWorld::setPlayerPosition(Point position)
{

。。。
_player->setPosition(position/Director::getInstance()->getContentScaleFactor());//将地图逻辑坐标坐标转换成GLVEIW坐标

bool HelloWorld::init()
{

。。。
_player = Sprite::create(“029.png”);
_player->setPosition(x + _tileMap->getTileSize().width / 2 / Director::getInstance()->getContentScaleFactor(), y + _tileMap->getTileSize().height / 2 / Director::getInstance()->getContentScaleFactor());//将逻辑坐标转换为GLVIEW坐标
_player->setScale(1.0/Director::getInstance()->getContentScaleFactor());

。。。

LZ,我在这一章编译过程中报错 “game Module 已停止工作”
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
0x012F680C 处的第一机会异常(在 Game.exe 中): 0xC0000005: 读取位置 0x00000000 时发生访问冲突。
0x012F680C 处有未经处理的异常(在 Game.exe 中): 0xC0000005: 读取位置 0x00000000 时发生访问冲突。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
新手求解答

— Begin quote from ____

引用第18楼amoyiki于2014-06-30 00:10发表的 :
LZ,我在这一章编译过程中报错 “game Module 已停止工作”
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
0x012F680C 处的第一机会异常(在 Game.exe 中): 0xC0000005: 读取位置 0x00000000 时发生访问冲突。
0x012F680C 处有未经处理的异常(在 Game.exe 中): 0xC0000005: 读取位置 0x00000000 时发生访问冲突。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
http://www.cocoachina.com/bbs/job.php?action=topost&tid=196836&pid=991699

— End quote

_blockage=_tileMap->layerNamed(“Blockage01”);

会不会编地图的时候,大小写不对