Bullet(Cocos2dx)之凸多面体形状和组合形状

接高度地形图和三角形网格,同样是复杂图形的创建。

先来看看效果


左边通过提供的顶点生成的多面体,右边创建一个由球体与长方体组成的组合形状

 

首先来了解凸多面体

根据提供的顶点创建一个凸多面体形状,无论给定的顶点顺序是怎样的,都是创建一个由这些顶点组成的凸多面体。

先来看看btConvexHullShape的解释及构造函数

///this constructor optionally takes in a pointer to points. Each point is assumed to be 3 consecutive btScalar (x,y,z), the striding defines the number of bytes between each point, in memory.
///It is easier to not pass any points in the constructor, and just add one point at a time, using addPoint.
///btConvexHullShape make an internal copy of the points.
btConvexHullShape(const btScalar* points=0,int numPoints=0, int stride=sizeof(btVector3));

第一个参数就是顶点数据,第二个是顶点个数,第三个是每个顶点占得字节。

同时提出不需要一次将所有的顶点全部给出,以后可以添加:

void addPoint(const btVector3& point, bool recalculateLocalAabb = true);

增加的顶点信息,是否重新计算AABB包围盒

 

对于提供顶点数据,用户不需要一直保存,看源码可知btConvexHullShape会用btAlignedObjectArray<btVector3>m_unscaledPoints;

来保存这些顶点数据。

 

来个示例

看上图,为什么会出现一个是三棱锥,而一个却不是呢?,其实两个形状是一样的,请看我慢慢道来。

前面的解释btConvexHullShape会根据提供的数据建立多面体,我们要牢记在心。

通过查看继承关系图,我们知道btConvexHullShape继承自btPolyhedralConvexShape

btPolyhedralConvexShape,提供了

///optional method mainly used to generate multiple contact points by clipping polyhedral features (faces/edges)
///experimental/work-in-progress
virtual bool	initializePolyhedralFeatures(int shiftVerticesByMargin=0);

可以这样理解,将提供的点格式化成多面体,也就是说提供顶点后就可以调用initializePolyhedralFeatures

格式化成多面体。

上图下方是按我们提供的顶点的顺序生成的结果,上方为格式化后的结果

 

下面来看看笔者是如何加入到PhysicsWorld3D

btRigidBody* PhysicsWorld3D::addConvexHull(const float* floatData, int numPoints, const btVector3& position, bool bPoly, const PhysicsMaterial3D& material)
{
	btConvexHullShape* colShape = new btConvexHullShape(floatData, numPoints, sizeof(btVector3));
	if (bPoly)
	{
		colShape->initializePolyhedralFeatures();
	}

	auto body = getBody(colShape, position, material);
	_world->addRigidBody(body);

	return body;
}

btRigidBody* PhysicsWorld3D::addConvexHull(std::vector<btVector3>& points, const btVector3& position, bool bPoly, const PhysicsMaterial3D& material)
{
	auto body = addConvexHull(points[0].m_floats, points.size(), position, bPoly, material);

	return body;
}

提供两个重载函数,一个是直接将包含顶点数据的浮点数据指针作为参数, 另一个是将顶点数组作为参数。

提供第二个重载的目的就是为了从直接将raw文件的数据传入来生成多面体,还记得raw文件吧?Bullet(Cocos2dx)之创建地形

看个例子

std::vector<float> points;
PhysicsHelper3D::loadRaw("monkey.raw", points);	// blender 猴头

_convexBody = _world->addConvexHull(&points[0], points.size() / 3, btVector3(-2, 0, 5), false, PhysicsMaterial3D(5.f, 0.5f, 0.5f, 0.f));
_convexBody = _world->addConvexHull(&points[0], points.size() / 3, btVector3(2, 0, 5), true, PhysicsMaterial3D(5.f, 0.5f, 0.5f, 0.f));

右不格式化,左为格式化

 

再来了解一下组合形状

简言之,将多个形状组合成一个图形,最上面的图片右方就是一个组合形状

可由多个不同的Primitive Shape组成

 

构造函数很简单

btCompoundShape(bool enableDynamicAabbTree = true);

默认开启动态树,优化

关键是增加碰撞形状

void	addChildShape(const btTransform& localTransform,btCollisionShape* shape);

localTransform形状相对偏移,shape碰撞形状。

相对偏移:加入最终btCompoundShape(0,0,0),相对偏移就是相对于(0,0,0)

得出要想获得组合形状就要自己创建btCollisionShape,然后添加到btCompoundShape

笔者做了简单的封装

struct PhysicsShapeInfo3D
{
	btCollisionShape* colShape;
	btTransform transform;

	PhysicsShapeInfo3D() {};

	PhysicsShapeInfo3D(btCollisionShape* shape, btTransform& trans)
		: colShape(shape), transform(trans)
	{}
};

提供简单的结构体来传递Collision Shapes属性

btRigidBody* PhysicsWorld3D::addCompound(std::vector<PhysicsShapeInfo3D>& shapeList, const btVector3& position, const PhysicsMaterial3D& material)
{
	btCompoundShape* shape = new btCompoundShape;
	
	for (auto& shapeInfo : shapeList)
	{
		shape->addChildShape(shapeInfo.transform, shapeInfo.colShape);
	}
	
	auto body = getBody(shape, position, material);
	_world->addRigidBody(body);
	return body;
}

std::vector<PhysicsShapeInfo3D>& shapeList 形状属性列表,来获取组合形状的刚体

 

来个例子

// 1

shape = new btBoxShape(btVector3(1.f, 1.f, 1.f));

shapeInfo.colShape = shape;

trans.setOrigin(btVector3(0, 1, 0));

shapeInfo.transform = trans;

_shapes.push_back(shapeInfo);

 

// 2

shape = new btSphereShape(1.f);

shapeInfo.colShape = shape;

trans.setOrigin(btVector3(0, 3, 0));

shapeInfo.transform = trans;

_shapes.push_back(shapeInfo);

 

_world->addCompound(_shapes, btVector3(0, 0, 5.f));

上例创建一个上为球体,下为长方体的组合形状

当然也可以通过球体和圆柱创建保龄球,也就可以写出一个保龄球的小游戏。

 源代码


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Cocos2d-x 是一个开源的跨平台游戏开发框架,支持 C++、Lua 和 JavaScript 等语言。以下是一个简单的飞机大战游戏的制作流程: 1. 创建一个新的 Cocos2d-x 项目,在命令行中使用以下命令: ``` cocos new MyGame -p com.your_company.mygame -l cpp -d /path/to/your/project ``` 其中,`MyGame` 是项目的名称,`com.your_company.mygame` 是项目的包名,`/path/to/your/project` 是项目的路径。 2. 在 `Classes` 文件夹下创建游戏场景类和游戏层类。游戏场景类负责管理游戏层,游戏层类负责绘制游戏界面和处理用户输入事件。可以参考以下代码: ```c++ class GameScene : public cocos2d::Scene { public: static cocos2d::Scene* createScene(); virtual bool init(); CREATE_FUNC(GameScene); }; class GameLayer : public cocos2d::Layer { public: virtual bool init(); void update(float delta); CREATE_FUNC(GameLayer); }; ``` 3. 在游戏层中添加背景图和玩家飞机。可以使用 `Sprite` 类来加载图片资源,并使用 `addChild()` 方法将其添加到层中。例如: ```c++ auto background = Sprite::create("background.png"); background->setPosition(visibleSize.width / 2, visibleSize.height / 2); addChild(background); player = Sprite::create("player.png"); player->setPosition(visibleSize.width / 2, player->getContentSize().height / 2); addChild(player); ``` 4. 实现玩家飞机的移动和射击功能。可以使用 `EventKeyboard` 类来监听键盘事件,并在 `update()` 方法中更新玩家飞机的位置和子弹的位置。例如: ```c++ void GameLayer::update(float delta) { // 移动玩家飞机 if (isKeyPressed(EventKeyboard::KeyCode::KEY_UP_ARROW)) { player->setPositionY(player->getPositionY() + 5.0f); } if (isKeyPressed(EventKeyboard::KeyCode::KEY_DOWN_ARROW)) { player->setPositionY(player->getPositionY() - 5.0f); } if (isKeyPressed(EventKeyboard::KeyCode::KEY_LEFT_ARROW)) { player->setPositionX(player->getPositionX() - 5.0f); } if (isKeyPressed(EventKeyboard::KeyCode::KEY_RIGHT_ARROW)) { player->setPositionX(player->getPositionX() + 5.0f); } // 发射子弹 if (isKeyPressed(EventKeyboard::KeyCode::KEY_SPACE)) { auto bullet = Sprite::create("bullet.png"); bullet->setPosition(player->getPositionX(), player->getPositionY() + player->getContentSize().height / 2); addChild(bullet); auto moveBy = MoveBy::create(1.0f, Vec2(0, visibleSize.height)); auto removeSelf = RemoveSelf::create(); auto sequence = Sequence::create(moveBy, removeSelf, nullptr); bullet->runAction(sequence); } } ``` 5. 添加敌机和碰撞检测功能。可以使用定时器来定期生成敌机,并使用 `Rect` 类来判断玩家飞机和子弹是否与敌机发生碰撞。例如: ```c++ void GameLayer::addEnemy(float delta) { auto enemy = Sprite::create("enemy.png"); float x = CCRANDOM_0_1() * visibleSize.width; enemy->setPosition(x, visibleSize.height + enemy->getContentSize().height / 2); addChild(enemy); auto moveBy = MoveBy::create(2.0f, Vec2(0, -visibleSize.height - enemy->getContentSize().height)); auto removeSelf = RemoveSelf::create(); auto sequence = Sequence::create(moveBy, removeSelf, nullptr); enemy->runAction(sequence); } void GameLayer::checkCollision() { for (auto enemy : enemies) { if (player->getBoundingBox().intersectsRect(enemy->getBoundingBox())) { // 碰撞处理 } for (auto bullet : bullets) { if (bullet->getBoundingBox().intersectsRect(enemy->getBoundingBox())) { // 碰撞处理 } } } } ``` 6. 最后,在游戏场景类的 `init()` 方法中添加游戏层、定时器和碰撞检测函数。例如: ```c++ bool GameScene::init() { if (!Scene::init()) { return false; } auto gameLayer = GameLayer::create(); addChild(gameLayer); schedule(schedule_selector(GameLayer::addEnemy), 1.0f); scheduleUpdate(); return true; } void GameLayer::update(float delta) { checkCollision(); } ``` 以上是一个简单的飞机大战游戏的制作流程,具体的实现细节还需要结合实际情况进行调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值