Bullet(cocos2dx)学习制作桌球游戏之前期准备

使用cocos2dx结合bullet设计一款简陋的桌球游戏,就是为了回顾前期学过的bullet

首先要把桌球游戏需要的基本资源准备好,15个球,1个白球,1张台球桌,球杆可有可无。

先看看目前实现的效果



至于这张台球桌的模型,我是随便设计一下


当然这个模型只是为了加载raw文件(静态网格数据),为了使模型的贴图显示出来,

我使用Blender直接创建了一个Plane,然后将台球桌的贴图贴在Plane上,于是就能以假乱真的

实现一个台球桌


在游戏开始前,初始化物理环境,加载一张台球桌,设置相应的物理属性,按规则摆放好台球。

 

1.设置重力为(0.f, -9.8f, 0.f),以模拟真实的物理环境。

_world = PhysicsWorld3D::createWithDebug(this, btVector3(0.f, -9.8f, 0.f));

2.加载台球桌

 首先将台球桌的贴图模型加载进游戏,设置相应的位置,

 然后加载台球桌的物理网格,还记得PhysicisMesh3D吗,并调整好位置,

 当然比较不好设置的就是台球桌的物理属性,什么摩擦系数啊,弹性系数啊,滚动摩擦系数啊

void HelloWorld::initTable()
{
	auto tableSprite = Sprite3D::create("ball/table.c3b");
	this->addChild(tableSprite);
	tableSprite->setPosition3D(Vec3(0.f, -0.57f, 0.f));
	tableSprite->setCameraMask(2);

	_tableMesh = PhysicsMesh3D::constuct("table.raw");
	_world->addTriangleMesh(_tableMesh, btVector3(0, -0.57f, 0), PhysicsMaterial3D(0.f, 0.5f, 0.2f, 0.2f));
}

3.摆放台球

对于15球来说摆放的顺序是这样的,在网上找的规则



黑8放在第三行的中间位置,白色的为全色球,黑色的为花色球。

 

可以这样设想,只要定义一个数组存放每个位置的球号就行了,

BALLS_NUMBER[0] = 1;

 

BALLS_NUMBER[1] = 2;

BALLS_NUMBER[2] = 9;

 

BALLS_NUMBER[3] = 10;

BALLS_NUMBER[4] = 8;

BALLS_NUMBER[5] = 3;

 

BALLS_NUMBER[6] = 4;

BALLS_NUMBER[7] = 11;

BALLS_NUMBER[8] = 5;

BALLS_NUMBER[9] = 12;

BALLS_NUMBER[10] = 13;

BALLS_NUMBER[11] = 6;

BALLS_NUMBER[12] = 14;

BALLS_NUMBER[13] = 15;

BALLS_NUMBER[14] = 7;

 

球号是我自己按照规则随便放的。

 

下面就是如果将这些球放好,

假设每个球半径为0.57f,球都在Y坐标为0的位置,那么关键就是如何确定每个球的

X,Z.台球摆放好无论多少行都是个等边三角形,以3行为例


先放置第一个球,以后每一行的第一个球都是按照蓝色箭头的方向放置,假设上为Z,右为X

那么第二行第一个球就是(ball[1].posX + ball.radius, ball[1].posZ + 3*ball.radius)

设方向向量为dir(ball.radius, 0, 3*ball.radius);

ball[2].pos = ball[1].pos + dir

第三行就是ball[3].pos = ball[1].pos + dir * 2;

一次类推ball[n].pos = ball[1].pos + dir * (n-1);

对于同一行的第k个球就是同一行的第一个球.pos.x - 2 * radius;

Sprite3D* ballSprite;
	btRigidBody* ballBody;

	int curNumber = -1;
	float offsetZ = -7.f;
	Vec3 ballPos;
	const Vec3 dir = Vec3(-0.57f, 0.f, -0.987269f);

	for (int i=0; i<5; ++i)
	{
		ballPos = dir * i;
		ballPos.x += -1.14f;
		ballPos.z += offsetZ;
		for (int j=0; j<=i; ++j)
		{
			curNumber++;		// 第几个球
			ballPos.x += 1.14f;  // 每行第k个都是上一个球的X+ 2 * radius

			ballSprite = Sprite3D::create("ball/ball.c3b", StringUtils::format("ball/ball_%d.png", BALLS_NUMBER[curNumber]));
			this->addChild(ballSprite);
			ballSprite->setPosition3D(ballPos);
			ballSprite->setCameraMask(2);

			ballBody = _world->addSphere(0.57f, btVector3(ballPos.x, ballPos.y, ballPos.z), PhysicsMaterial3D(4.2f, 0.2f, 0.9f, 0.15f));
			ballBody->setUserPointer(ballSprite);

			_balls.push_back(ballBody);
		}
	}

看上面代码

Sprite3D::create("ball/ball.c3b",
StringUtils::format("ball/ball_%d.png", BALLS_NUMBER[curNumber]));

根据提前的设计加载相应的球号。

 

ballPos = dir * i;

ballPos.x += -1.14f;

ballPos.z += offsetZ;

设置每行第一个球的位置

 

最后就是加载白球,白球要特别独立出来

// white ball
	ballSprite = Sprite3D::create("ball/ball.c3b", "ball/ball_white.png");
	this->addChild(ballSprite);
	ballSprite->setPosition3D(Vec3(0.f, 0.f, 5.f));
	ballSprite->setCameraMask(2);
	_whiteBallBody = _world->addSphere(0.57f, btVector3(0.f, 0.f, 5.f), PhysicsMaterial3D(4.2f, 0.2f, 0.9f, 0.15f));
	_whiteBallBody->setUserPointer(ballSprite);

4.更新物理世界

_world->update(delta);

	float m[16];
	for (auto ballBody : _balls)
	{
		ballBody->getWorldTransform().getOpenGLMatrix(m);
		static_cast<Sprite3D*>(ballBody->getUserPointer())->setNodeToParentTransform(Mat4(m));
	}

	_whiteBallBody->getWorldTransform().getOpenGLMatrix(m);
	static_cast<Sprite3D*>(_whiteBallBody->getUserPointer())->setNodeToParentTransform(Mat4(m));

每一帧都去更新实际上是很浪费资源的,当所有的球都不动时,其实没必要更新,但是只有不到20个球,

性能不会影响,当游戏中出现大量的物体时,就要重载btMotionState,这个以后讨论。

5.测试一下

当点击屏幕是给白球施加一个冲量,记住一定要先唤醒物体,不然不会有效果的

_whiteBallBody->setActivationState(ACTIVE_TAG);

_whiteBallBody->applyCentralImpulse(btVector3(0.f, 0.f, -60.5f));

 

总结:

不是美工,模型什么的设计很费劲,贴图都是网上找的。

台球桌,台球的物理属性,调整麻烦,目前调整的还不好

对于添加的Sprite3D一定要设置CameraMask不然是不会被看到的。

添加光照,使物体具有立体感

// light
	auto light = SpotLight::create(Vec3(0, -1.f, 0.f), Vec3(0.f, 0.f, 0.f), Color3B::WHITE, 0.f, 0.5f, 1000.f);
	light->setPosition3D(Vec3(0.f, 100.f, 0.f));
	this->addChild(light);
	light->setCameraMask(2);

源码下载

Bullet库的设置方法请参考http://blog.csdn.net/ctxdecs/article/details/42045099

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
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(); } ``` 以上是一个简单的飞机大战游戏制作流程,具体的实现细节还需要结合实际情况进行调整。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值