注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

风的驿站

一徐清风,半指烛光,觥筹已净,只余茶香。残卷一章,妙趣非常,忽闻帘响,愿闻其详?

 
 
 

日志

 
 
关于我

喜欢写生 编程 音乐 设计 喜欢把自己的想法变成实实在在的东西 喜欢安静的做一些事情 CSDN博客:http://blog.csdn.net/qwertyupoiuytr

网易考拉推荐

【原创】使用Box2d实现物体在液体中的漂浮效果(一)  

2014-08-25 13:32:37|  分类: Box2D |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

今天我们来学习使用Box2d物理引擎制作物体在液体中的漂浮效果,没有新的技术,只是综合运用了以前博文中介绍过的技术。

注:如果使用Box2d flash的版本,可以直接使用buoyancycontroller来实现本文制作的效果。

首先我们来分析一下大体思路:
首先需要在场景中创建一个液体区域(以下简称液体,我们不做液体流动的模拟,只是创建一个矩形的区域),由于需要检测物体与液体的接触(Contact),所以液体应该也是一个b2Body对象,只不过液体不检测碰撞,故我们将其设置为传感器(Sensor)。

要实现物体与液体的接触检测,我们需要创建一个继承自b2ContactListener的子类,重写BeginContactEndContact方法,在BeginContactEndContact中对物体的速度进行调整(因为液体具有一定的阻力和粘稠度)。

物体在液体中还会受到浮力的作用,浮力的大小与物体浸入液体的体积有关,而要计算物体浸入液体的体积,就要用到射线投射的相关知识与多边形面积计算的相关知识了。

好了,下面我们开始来制作,首先还是看一下运行效果截图:

使用Box2d实现物体在液体中的漂浮效果(一) - 远行的风 - 风的驿站

 

首先我们以cocos2d iOS with Box2d为模板创建工程(Box2d版本为2.3.1),接着在HelloWorldLayer中添加下面的方法创建液体区域(也就是上面图片中的下半部分绿色区域):

-(void) createWater {

    CGSize winSize = [[CCDirector sharedDirector] winSize];

    float height = winSize.height;

    float width = winSize.width;

   

    b2BodyDef bodyDef;

    bodyDef.type = b2_staticBody;

    bodyDef.position.Set(0, 0);

    b2Body* body = world->CreateBody(&bodyDef);

   

    b2FixtureDef fixtureDef;

    fixtureDef.isSensor = true;

   

    b2PolygonShape* shape = new b2PolygonShape();

    shape->SetAsBox(width / PTM_RATIO, height * 0.4f / PTM_RATIO);

   

    fixtureDef.shape = shape;

    body->CreateFixture(&fixtureDef);

}

该方法在场景中添加了一个静态的矩形物体,将其设置为传感器。

initPhysics方法中调用该方法即可。

接着我们修改一下addNewSpriteAtPosition方法:

-(void) addNewSpriteAtPosition:(CGPoint)p

{

      b2BodyDef bodyDef;

      bodyDef.type = b2_dynamicBody;

      bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);

      b2Body *body = world->CreateBody(&bodyDef);

     

      b2PolygonShape dynamicBox;

    float width = CCRANDOM_0_1() * 1.5f + 0.5f;

    float height = CCRANDOM_0_1() * 1.0f + 0.5f;

      dynamicBox.SetAsBox(width, height);

     

      b2FixtureDef fixtureDef;

      fixtureDef.shape = &dynamicBox;    

      fixtureDef.density = 0.7f;

      fixtureDef.friction = 0.3f;

      body->CreateFixture(&fixtureDef);

}

修改前的方法会创建一个带有纹理贴图的大小为1平方米的盒子,盒子密度为1,修改后,我们去掉盒子的贴图纹理,并且使用随机数将盒子的长宽修改为随机值,并将盒子的密度修改为0.7f(这里我们液体密度是1,为了使物体漂浮,需要盒子密度小于液体密度)。

修改完成后我们尝试在场景中点击添加盒子,发现盒子全部“沉到”液体底部,没有任何效果。

我们继续添加一个类MyContactListener,继承自b2ContactListener,类声明如下:

#import "Box2D.h"

 

class MyContactListener : public b2ContactListener {

public:

    void BeginContact(b2Contact* contact);

    void EndContact(b2Contact* contact);

};

实现如下:

#import "MyContactListener.h"

#import "FloatingObjectData.h"

 

void MyContactListener::BeginContact(b2Contact* contact) {

    //获取接触的两个fixture

    b2Fixture* fixtureA = contact->GetFixtureA();

    b2Fixture* fixtureB = contact->GetFixtureB();

   

    //由于场景中只有一个传感器(即液体区域),因此如果碰撞的两个物体都不是传感器,则

    //不是物体与液体的相互作用,忽略这次接触

    if (!fixtureA->IsSensor() && !fixtureB->IsSensor()) {

        return;

    }

   

    //获取与液体接触的物体对象

    b2Body* body = fixtureA->IsSensor() ? fixtureB->GetBody() : fixtureA->GetBody();

    //物体浸入液体之前的速度

    b2Vec2 bodyVelocity = body->GetLinearVelocity();

    //物体浸入液体之后由于受到液体的作用,将其速度降低

    body->SetLinearVelocity(b2Vec2(bodyVelocity.x * 0.7f, bodyVelocity.y * 0.3f));

    //同时降低物体的角速度

    body->SetAngularVelocity(body->GetAngularVelocity() * 0.7);

   

    //设置物体的UserData,标记其已经浸入液体

    FloatingObjectData* objectData = [[FloatingObjectData alloc] init];

    objectData.isUnderWater = true;

    body->SetUserData(objectData);

}

 

void MyContactListener::EndContact(b2Contact* contact) {

    b2Fixture* fixtureA = contact->GetFixtureA();

    b2Fixture* fixtureB = contact->GetFixtureB();

    if (!fixtureA->IsSensor() && !fixtureB->IsSensor()) {

        return;

    }

    b2Body* body = fixtureA->IsSensor() ? fixtureB->GetBody() : fixtureA->GetBody();

   

    //设置物体的UserData,标记其已经离开液体

    FloatingObjectData* objectData = (FloatingObjectData*)body->GetUserData();

    if (objectData != nil) {

        objectData.isUnderWater = false;

    }

}

代码中添加了详细的注释,不做过多解释了。代码里面我们用到了一个FloatingObjectData类,这个类使我们创建的用来记录浸入液体的物体的状态的类,声明如下:

@interface FloatingObjectData : NSObject

@property BOOL isUnderWater;

@property float volumnUnderWater;

@end

实现:

#import "FloatingObjectData.h"

 

@implementation FloatingObjectData

 

@synthesize isUnderWater;

@synthesize volumnUnderWater;

 

-(id)init {

    if (self = [super init]) {

        isUnderWater = false;

        volumnUnderWater = 0;

    }

   

    return self;

}

 

@end

比较简单,定义了两个属性,isUnderWater用来标记物体是否在液体中,volumnUnderWater用来记录物体浸入液体的体积。

上面的代码添加好之后,我们在initPhysics方法中添加下面的初始化代码:

MyContactListener* myContactListener = new MyContactListener();

world->SetContactListener(myContactListener);

这样world就会使用我们定义好的ContactListener中重载的两个方法来处理碰撞了。

现在再次运行程序,我们发现添加物体的时候,当物体落入水中,能够感觉到物体受到阻力导致速度减慢了一点,但是物体仍然不能够漂浮。关于物体浮力效果的制作部分我们留到下一篇教程中完成。


使用Box2d实现物体在液体中的漂浮效果(二)
  评论这张
 
阅读(211)| 评论(2)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017