Cocos2dx 学习笔记 -TMX和物理引擎的结合
我使用的 cocos2dx 引擎的版本是3.3.4
尽管Cocos2dx提供了两套物理引擎,但是用的更多的是BOX2的,在IOS平台上多使用chipmunk。
这次我的目标是将TMX地图文件加载到内存中,然后为每个TMX地图中的小方块生成物理形状从而为自己编写的跑酷游戏加上物理引擎。
引擎中现在Scene类提供了静态方法可以生成带有物理的场景。 Scene::initWithPhysics()
这个方法可以让场景具有创建物理世界的基本条件,在跑酷游戏中需要重力的影响,所以应该把场景的重力设置出来。
PhysicsWorld* world=getPhysicsWorld();
world->setGravity(Vec2(0,-10));
然后我们要给物理世界创造一个边界,同时把游戏逻辑和物理场景分开,新建一个Layer的子类来写游戏逻辑。
头文件如下:
#pragma once
#include <string>
#include "cocos2d.h"
#include "Box2D\Box2D.h"
USING_NS_CC;
class PhyLayer :
public Layer
{
public :
Sprite * ball;
Sprite * edgeSp;
//sprite * prop;
PhysicsBody* ballBody;
TMXTiledMap* map;
Sprite* batS;
int mapdx;
CREATE_FUNC(PhyLayer);
bool init();//初始化函数
void loadPhyBody();//加载物理元素
void loadTileMap();//加载地图
void update(float tt);
void contact();
public:
PhyLayer();
~PhyLayer();
};
这是CPP文件
#include "PhyLayer.h"
PhyLayer::PhyLayer()
{
}
PhyLayer::~PhyLayer()
{
}
bool PhyLayer::init()
{
if (!Layer::init())
return false;
loadTileMap();
loadPhyBody();
mapdx = 5;
return true;
}
void PhyLayer::loadPhyBody()
{
auto visibleSize = Director::getInstance()->getVisibleSize();
auto origin = Director::getInstance()->getVisibleOrigin();
edgeSp = Sprite::create();
auto boundBody = PhysicsBody::createEdgeBox(visibleSize, PhysicsMaterial(0.0f, 1.0f, 0.0f), 3);
edgeSp->setPosition(visibleSize.width / 2, visibleSize.height / 2);
edgeSp->setPhysicsBody(boundBody);
addChild(edgeSp);
ball = Sprite::create("ball.png");
ball->setPosition(100, 100);
ballBody = PhysicsBody::createCircle(ball->getContentSize().width / 2, PhysicsMaterial(0.0f, 1.0f, 0.0f));
ballBody->setContactTestBitmask(0xFFFFFFFF);
Vect force = Vect(100.0f, 100.0f);
ballBody->applyImpulse(force);
ballBody->setVelocity(Vec2(150, 150));
ball->setPhysicsBody(ballBody);
addChild(ball);
batS = Sprite::create("block.png");
PhysicsBody* batBody = PhysicsBody::createEdgeBox(batS->getContentSize(), PhysicsMaterial(0.0f, 1.0f, 0.0f));
batS->setPosition(visibleSize.width / 2, 50);
batS->setZOrder(0);
addChild(batS);
batBody->setContactTestBitmask(0xFFFFFFFF);
EventListenerTouchOneByOne * ev1 = EventListenerTouchOneByOne::create();
ev1->onTouchBegan = [](Touch* touch, Event* ev){return true; };
ev1->onTouchMoved = [&](Touch* touch, Event* ev)
{
float x = touch->getDelta().x;
batS->setPositionX(batS->getPositionX() + x);
};
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(ev1, this);
contact();
schedule(schedule_selector(PhyLayer::update));
}
void PhyLayer::loadTileMap()
{
map = TMXTiledMap::create("map.tmx");
map->setPositionX(getPositionX() + 20);
TMXLayer *layer = map->getLayer("map1");
addChild(map);
for (int i = 0; i < 80; i++)
{
for (int j = 0; j < 20; j++)
{
int gid = layer->getTileGIDAt(Vec2(i, j));
if (gid == 0)
{
Sprite* sprite = layer->getTileAt(Vec2(i, j));
if (!sprite)
{
return;
}
PhysicsBody* body = PhysicsBody::createEdgeBox(sprite->getContentSize(), PhysicsMaterial(0.0f, 1.0f, 0.0f));
sprite->setTag(3);
body->setContactTestBitmask(0xFFFFFFFF);
sprite->setPhysicsBody(body);
}
}
}
}
void PhyLayer::update(float tt)
{
int const spd = 50;
float x = ballBody->getVelocity().x;
float y = ballBody->getVelocity().y;
if (x != spd && x != -spd)
{
if (x < 0)
x = -spd;
else
x = spd;
}
if (y != spd && y != -spd)
{
if (y < 0)
y = -spd;
else
y = spd;
}
ballBody->setVelocity(Vec2(x, y));
map->setPositionX(map->getPositionX() - 2);
}
void PhyLayer::contact()
{
EventListenerPhysicsContact *evContact = EventListenerPhysicsContact::create();
evContact->onContactBegin = [](PhysicsContact& contact) -> bool
{
auto bodyA = (Sprite*)(contact.getShapeA()->getBody()->getNode());
auto bodyB = (Sprite*)(contact.getShapeB()->getBody()->getNode());
if (bodyA->getTag() == 3)
{
}
if (bodyB->getTag() == 3)
{
}
return true;
};
_eventDispatcher->addEventListenerWithSceneGraphPriority(evContact, this);
}
其中我要说明的其中的lamda表达式,在C++中的lambda表达式可以看下面的图示
1.lambda-introducer (捕获字段)
2.lambda-parameter-declaration-list(变量列表)
3.mutable-specification(变量是否可以修改,这部分可以省略不写)
4.exception-specification(抛出的异常声明,可以省略不写)
5.lambda-return-type-clause(返回值类型,可以省略不写)
6.compound-statement(函数体部分)
其中捕获字段是[]时,表达式函数体部分是不能访问外部变量的。如果要使得函数体部分可以访问外部变量,可以使用&或者=,其中&表示按引用访问,=表示按值访问,可以指明具体要访问的变量也可不写访问当前所有作用域下的变量。
参数列表
lambda表达式的参数列表基本和函数的一致,不过有如下限制:
参数列表不能有默认参数
不能是可变参数列表
所有的参数必须有个变量名
能否修改捕获的变量
如果在参数列表后加上了 mutable,则表示表达式可以修改按值捕获的外部变量的拷贝。
异常设置
和函数一样,可以用 throw 来限定表达式能够抛出哪些异常。
返回类型
如果设置返回类型,你需要在类型名前面加上 ->。如果你只有一个返回语句的话,返回类型可以省略,编译器将会为你做出判断。
函数体
lambda表达式的函数体和普通函数大致相同。