| 版权声明:本文为博主原创文章,未经博主允许不得转载。
在很多的游戏设计中一般都会涉及和模拟到真实的物理世界。然而游戏中模拟真实世界的物理会很复杂。使用已经写好的物理引擎会用很大的帮助和便利。 Box2D的和Chipmunk是两个著名的物理引擎,在Cocos2d-x引擎中已经很好的集成它们。在Cocos2d-x v2的开发人员需要直接使用物理引擎。 Cocos2d-x提供了一个简单的类:CCPhysicsSprite,以帮助缩小使用差距,但对于其它元素的开发者需要直接调用物理引擎的API。
在Cocos2d-x V3+ 版本事情发生了变化。Cocos2d-x V3+ 版本集成了全新的物理引擎,基于Chipmunk的核心API。开发人员不再需要关心它的物理引擎将被用于或需要直接调用物理引擎的API。
这节我们将介绍一下Box2D物理引擎:
Box2D是一个用于游戏的2D刚体仿真库。Cocos2d-x程序员可以使用它在他们的游戏使物体的运动方式,使可信的游戏世界更互动。采用Box2D来模拟真实的物理世界的过程其实就是一句话:这个过程与上帝创建世界的过程类似,上帝首先创建了世界,然后在为世界指定了边界,否则万物就会掉到世界之外的混沌里去了,最后上帝在继续的创建了万物(这是网上的一句很适合形容Box2D创建物理世界的话)。
下面就根据上面的上帝创建世界的构成来创建游戏中的物理世界:
(1)、首先创建了世界(其实就是初始化物理世界,并且设置此物理世界的一些特性)
(2)、指定物理世界的边界
(3)、创建万物(即创建动态或者静态的Body)
(4)、当我们将上面的物理世界全部创建完之后,我们发现创建的刚体并不能运动。其实是我们没有更新刚体的位置。因此我们应该写一个定时器: update来时时更新Body的信息。
(5)、然后我们在初始化场景的init()函数中,启动定时器即可。下面解释上面的部分代码:
>.SetAllowSleeping():是否允许物体睡眠,休眠状态(true),可以提高物理世界中物体的处理效率,只在发生碰撞时才唤醒该对象。
>.gravity.Set(0.0f, -10.0f)是设置矢量值,其中(0.0f, -10.0f)表示只有重力作用物体,-10.0f表示沿着y轴向下,如果在x轴方向‘-’表示沿x轴向左。
>.world->SetContinuousPhysics(true):是开启连续物理测试,[开启连续物理测试,这是因为计算机只能把一段连续的时间分成许多离散的时间点,再对每个时间点之间的行为进行演算,如果时间点的分割不够细致,速度较快的两个物体碰撞时就可能会产生“穿透”现象,开启连续物理将启用特殊的算法来避免该现象。]。
代码 1:(动态物体自由落体运动)
HelloWorld.h
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
#include "Box2D\Box2D.h"
#define PTM_RATIO 32
class HelloWorld : public cocos2d::Layer
{
private:
b2World* world;
public:
static cocos2d::Scene* createScene();
virtual bool init();
virtual void update(float dt);
virtual bool onTouchBegan(cocos2d::Touch* touch, cocos2d::Event* event);
// implement the "static create()" method manually
CREATE_FUNC(HelloWorld);
void initPhysics();
void createland();
void addNewSprite(cocos2d::Vec2 point);
};
#endif // __HELLOWORLD_SCENE_H__
HelloWorld.cpp
#include "HelloWorldScene.h"
USING_NS_CC;
Scene* HelloWorld::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::create();
// 'layer' is an autorelease object
auto layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
//
// 1. super init first
if ( !Layer::init() )
{
return false;
}
auto visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
auto sprite = Sprite::create("HelloWorld.png");
sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
this->addChild(sprite, 0);
//初始化物理引擎
this->initPhysics();
this->createland();
setTouchEnabled(true);
setTouchMode(Touch::DispatchMode::ONE_BY_ONE);
//开始游戏循环
scheduleUpdate();
return true;
}
void HelloWorld::initPhysics()
{
//重力参数
b2Vec2 gravity;
gravity.Set(0.0f, -9.8f);
//创建世界
world = new b2World(gravity);
//是否允许物体休眠
world->SetAllowSleeping(true);
//开启连续的物理测试
world->SetContinuousPhysics(true);
}
void HelloWorld::addNewSprite(Vec2 point)
{
//创建物理引擎的精灵对象
auto sprite = Sprite::create("CloseNormal.png");
sprite->setPosition(Vec2(point.x, point.y));
this->addChild(sprite);
//刚体定义
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(point.x / PTM_RATIO,
point.y / PTM_RATIO);
b2Body* body = world->CreateBody(&bodyDef);
body->SetUserData(sprite);
//定义盒子
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(0.6, 0.6);
//夹具
b2FixtureDef fixtureDef;
//设置夹具的形状
fixtureDef.shape = &dynamicBox;
//设置密度
fixtureDef.density = 1.0f;
//设置摩擦系数
fixtureDef.friction = 0.3f;
//使用夹具固定形状到物体上
body->CreateFixture(&fixtureDef);
}
void HelloWorld::update(float dt)
{
world->Step(dt, 8, 1);
for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetUserData() != nullptr)
{
Sprite* sprite = (Sprite*)b->GetUserData();
sprite->setPosition(Vec2(b->GetPosition().x*PTM_RATIO,
b->GetPosition().y*PTM_RATIO));
sprite->setRotation(0);
}
}
}
bool HelloWorld::onTouchBegan(cocos2d::Touch* touch, cocos2d::Event* event)
{
Touch *t = touch;
Vec2 point = t->getLocationInView();
point = Director::getInstance()->convertToGL(point);
this->addNewSprite(point);
return false;
}
void HelloWorld::createland()
{
Size s = Director::getInstance()->getVisibleSize();
//地面物体定义
b2BodyDef groundBodyDef;
//左下角
groundBodyDef.position.Set(0, 0);
//创建地面物体
b2Body* groundBody = world->CreateBody(&groundBodyDef);
//定义一个有边的形状
b2EdgeShape groundBox;
//底部
groundBox.Set(b2Vec2(0, 0), b2Vec2(s.width / PTM_RATIO, 0));
//使用夹具固定形状到物体上
groundBody->CreateFixture(&groundBox, 0);
//顶部
groundBox.Set(b2Vec2(0, s.height / PTM_RATIO),
b2Vec2(s.width / PTM_RATIO, s.height / PTM_RATIO));
groundBody->CreateFixture(&groundBox, 0);
// 左边
groundBox.Set(b2Vec2(0, s.height / PTM_RATIO),
b2Vec2(0, 0));
groundBody->CreateFixture(&groundBox, 0);
// 右边
groundBox.Set(b2Vec2(s.width / PTM_RATIO, s.height / PTM_RATIO),
b2Vec2(s.width / PTM_RATIO, 0));
groundBody->CreateFixture(&groundBox, 0);
}
代码 2:(动态物体上升移动)
HelloWorld.h
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
#include "Box2D\Box2D.h"
#define PTM_RATIO 32
class HelloWorld : public cocos2d::Layer
{
protected:
b2World* world;
cocos2d::Sprite* sprite;
b2Body* body;
public:
static cocos2d::Scene* createScene();
virtual bool init();
virtual void update(float dt);
virtual bool onTouchBegan(cocos2d::Touch* touch, cocos2d::Event* event);
// implement the "static create()" method manually
CREATE_FUNC(HelloWorld);
void initPhysics();
void createLand();
void addNewSprite();
};
#endif // __HELLOWORLD_SCENE_H__
HelloWorld.cpp
#include "HelloWorldScene.h"
#include "SimpleAudioEngine.h"
USING_NS_CC;
Scene* HelloWorld::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::create();
// 'layer' is an autorelease object
auto layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
//
// 1. super init first
if ( !Layer::init() )
{
return false;
}
auto visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
auto sprite = Sprite::create("HelloWorld.png");
sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
this->addChild(sprite, 0);
//初始化物理引擎
this->initPhysics();
this->createLand();
addNewSprite();
setTouchEnabled(true);
setTouchMode(Touch::DispatchMode::ONE_BY_ONE);
//开始游戏循环
scheduleUpdate();
return true;
}
void HelloWorld::initPhysics()
{
//重力参数
b2Vec2 gravity;
gravity.Set(0.0f, -9.8f);
//创建世界
world = new b2World(gravity);
//是否允许物体休眠
world->SetAllowSleeping(true);
//开启连续的物理测试
world->SetContinuousPhysics(true);
}
void HelloWorld::addNewSprite()
{
//创建物理引擎的精灵对象
sprite = Sprite::create("CloseNormal.png");
sprite->setPosition((Director::getInstance()->getVisibleSize().width / 3) * 3,
Director::getInstance()->getVisibleSize().height);
this->addChild(sprite);
//刚体定义
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(sprite->getPosition().x / PTM_RATIO,
sprite->getPosition().y / PTM_RATIO);
body = world->CreateBody(&bodyDef);
body->SetUserData(sprite);
//定义2米见方的盒子
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(0.6, 0.6);
//夹具
b2FixtureDef fixtureDef;
//设置夹具的形状
fixtureDef.shape = &dynamicBox;
//设置密度
fixtureDef.density = 1.0f;
//设置摩擦系数
fixtureDef.friction = 0.3f;
//使用夹具固定形状到物体上
body->CreateFixture(&fixtureDef);
}
void HelloWorld::update(float dt)
{
world->Step(dt, 8, 1);
for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetUserData() != nullptr)
{
Sprite* sprite = (Sprite*)b->GetUserData();
sprite->setPosition(Vec2(b->GetPosition().x*PTM_RATIO,
b->GetPosition().y*PTM_RATIO));
sprite->setRotation(0);
}
}
}
bool HelloWorld::onTouchBegan(cocos2d::Touch* touch, cocos2d::Event* event)
{
//Touch *t = touch;
//Vec2 point = t->getLocationInView();
//point = Director::getInstance()->convertToGL(point);
//点击一次屏幕,小球上升5个像素点
body->SetLinearVelocity(b2Vec2(0, 5));
return false;
}
void HelloWorld::createLand()
{
Size s = Director::getInstance()->getVisibleSize();
//地面物体定义
b2BodyDef groundBodyDef;
//左下角
groundBodyDef.position.Set(0, 0);
//创建地面物体
b2Body* groundBody = world->CreateBody(&groundBodyDef);
//定义一个有边的形状
b2EdgeShape groundBox;
//底部
groundBox.Set(b2Vec2(0, 0), b2Vec2(s.width / PTM_RATIO, 0));
//使用夹具固定形状到物体上
groundBody->CreateFixture(&groundBox, 0);
//顶部
groundBox.Set(b2Vec2(0, s.height / PTM_RATIO),
b2Vec2(s.width / PTM_RATIO, s.height / PTM_RATIO));
groundBody->CreateFixture(&groundBox, 0);
// 左边
groundBox.Set(b2Vec2(0, s.height / PTM_RATIO),
b2Vec2(0, 0));
groundBody->CreateFixture(&groundBox, 0);
// 右边
groundBox.Set(b2Vec2(s.width / PTM_RATIO, s.height / PTM_RATIO),
b2Vec2(s.width / PTM_RATIO, 0));
groundBody->CreateFixture(&groundBox, 0);
}
代码 3:(漂浮物体横向移动)
HelloWorld.h
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
#include "Box2D\Box2D.h"
#define PTM_RATIO 32
class HelloWorld : public cocos2d::Layer
{
protected:
b2World* world;
cocos2d::Sprite* sprite;
b2Body* body;
public:
static cocos2d::Scene* createScene();
virtual bool init();
virtual void update(float dt);
virtual bool onTouchBegan(cocos2d::Touch* touch, cocos2d::Event* event);
// implement the "static create()" method manually
CREATE_FUNC(HelloWorld);
void initPhysics();
void createLand();
void addNewSprite();
};
#endif // __HELLOWORLD_SCENE_H__
HelloWorld.cpp
#include "HelloWorldScene.h"
#include "SimpleAudioEngine.h"
USING_NS_CC;
Scene* HelloWorld::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::create();
// 'layer' is an autorelease object
auto layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
//
// 1. super init first
if (!Layer::init())
{
return false;
}
auto visibleSize = Director::getInstance()->getVisibleSize();
auto origin = Director::getInstance()->getVisibleOrigin();
auto sprite = Sprite::create("HelloWorld.png");
sprite->setPosition(Vec2(visibleSize.width / 2 + origin.x, visibleSize.height / 2 + origin.y));
this->addChild(sprite, 0);
//初始化物理引擎
this->initPhysics();
this->createLand();
addNewSprite();
setTouchEnabled(true);
setTouchMode(Touch::DispatchMode::ONE_BY_ONE);
//开始游戏循环
scheduleUpdate();
return true;
}
void HelloWorld::initPhysics()
{
//重力参数
b2Vec2 gravity;
gravity.Set(0.0f, -9.8f);
//创建世界
world = new b2World(gravity);
//是否允许物体休眠
world->SetAllowSleeping(true);
//开启连续的物理测试
world->SetContinuousPhysics(true);
}
void HelloWorld::addNewSprite()
{
float offset = -rand() % 5;
auto visibleSize = Director::getInstance()->getVisibleSize();
auto origin = Director::getInstance()->getVisibleOrigin();
Sprite* down = Sprite::create("down.png");
down->setPosition(Vec2(visibleSize.width / 2, down->getContentSize().height));
down->setScaleY(0.5);
this->addChild(down);
b2BodyDef bodyDef;
bodyDef.position = b2Vec2(down->getPosition().x / PTM_RATIO, down->getPosition().y / PTM_RATIO);
bodyDef.type = b2_kinematicBody;
bodyDef.linearVelocity = b2Vec2(-2, 0);
b2Body* body = world->CreateBody(&bodyDef);
body->SetUserData(down);
b2PolygonShape shape1;
shape1.SetAsBox(down->getContentSize().width / 2 / PTM_RATIO, down->getContentSize().height / 2 / PTM_RATIO);
b2FixtureDef fixtureDef;
fixtureDef.shape = &shape1;
fixtureDef.density = 1.0f;
fixtureDef.friction = 1.0f;
body->CreateFixture(&fixtureDef);
Sprite* up = Sprite::create("up.png");
up->setPosition(Vec2(visibleSize.width / 2, visibleSize.height - up->getContentSize().height));
up->setScaleY(0.5);
this->addChild(up);
b2BodyDef upbodyDef;
upbodyDef.position=b2Vec2(up->getPosition().x / PTM_RATIO, up->getPosition().y / PTM_RATIO);
upbodyDef.type = b2_kinematicBody;
upbodyDef.linearVelocity = b2Vec2(-2, 0);
b2Body* body2 = world->CreateBody(&upbodyDef);
body2->SetUserData(up);
b2PolygonShape shape2;
shape2.SetAsBox(up->getContentSize().width / 2 / PTM_RATIO, up->getContentSize().height / 2 / PTM_RATIO);
b2FixtureDef fixtureDef2;
fixtureDef2.shape = &shape2;
body2->CreateFixture(&fixtureDef2);
}
void HelloWorld::update(float dt)
{
world->Step(dt, 8, 1);
for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetUserData() != nullptr)
{
Sprite* sprite = (Sprite*)b->GetUserData();
sprite->setPosition(Vec2(b->GetPosition().x*PTM_RATIO,
b->GetPosition().y*PTM_RATIO));
sprite->setRotation(0);
}
}
}
bool HelloWorld::onTouchBegan(cocos2d::Touch* touch, cocos2d::Event* event)
{
//Touch *t = touch;
//Vec2 point = t->getLocationInView();
//point = Director::getInstance()->convertToGL(point);
//点击一次屏幕,小球上升5个像素点
//body->SetLinearVelocity(b2Vec2(-5, 5));
addNewSprite();
return false;
}
void HelloWorld::createLand()
{
Size s = Director::getInstance()->getVisibleSize();
//地面物体定义
b2BodyDef groundBodyDef;
//左下角
groundBodyDef.position.Set(0, 0);
//创建地面物体
b2Body* groundBody = world->CreateBody(&groundBodyDef);
//定义一个有边的形状
b2EdgeShape groundBox;
//底部
groundBox.Set(b2Vec2(0, 0), b2Vec2(s.width / PTM_RATIO, 0));
//使用夹具固定形状到物体上
groundBody->CreateFixture(&groundBox, 0);
//顶部
groundBox.Set(b2Vec2(0, s.height / PTM_RATIO),
b2Vec2(s.width / PTM_RATIO, s.height / PTM_RATIO));
groundBody->CreateFixture(&groundBox, 0);
// 左边
groundBox.Set(b2Vec2(0, s.height / PTM_RATIO),
b2Vec2(0, 0));
groundBody->CreateFixture(&groundBox, 0);
// 右边
groundBox.Set(b2Vec2(s.width / PTM_RATIO, s.height / PTM_RATIO),
b2Vec2(s.width / PTM_RATIO, 0));
groundBody->CreateFixture(&groundBox, 0);
}