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

news/2024/7/20 21:56:03 标签: box2d, IOS, 浮力, 液体


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

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

首先我们来分析一下大体思路:

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

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

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

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


 

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

-(void)createWater {

   CGSize winSize = [[CCDirectorsharedDirector] 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 = newb2PolygonShape();

   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"

 

classMyContactListener : public b2ContactListener {

public:

   void BeginContact(b2Contact* contact);

   void EndContact(b2Contact* contact);

};

实现如下:

#import"MyContactListener.h"

#import"FloatingObjectData.h"

 

voidMyContactListener::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);

}

 

voidMyContactListener::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类,这个类使我们创建的用来记录浸入液体的物体的状态的类,声明如下:

@interfaceFloatingObjectData : NSObject

@property BOOLisUnderWater;

@propertyfloat volumnUnderWater;

@end

实现:

#import"FloatingObjectData.h"

 

@implementationFloatingObjectData

 

@synthesizeisUnderWater;

@synthesizevolumnUnderWater;

 

-(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中重载的两个方法来处理碰撞了。

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



http://www.niftyadmin.cn/n/878454.html

相关文章

免费好用的文字转语音工具

自媒体可能是现在最火的东西,自媒体中最火的又是短视频,各家短视频平台都有简单好用的剪辑软件方便新手能轻松上手,但是语音这块的话却没有很好的解决办法,大部分需要真人出声或是拙劣的 AI 配音,但是像我这种普通话不…

使用Box2d实现物体在液体中的漂浮效果(二)

我们继续来制作物体在液体中的漂浮效果。 我们来考虑物体和液体的三种位置关系: 1. 物体完全离开液体 2. 物体一部分浸入液体 3. 物体完全浸入液体。 针对这三种位置关系,我们有下面三种结论: 1. …

当前可用的喜马拉雅专辑下载器

之前分享过不少下载喜马拉雅音频或专辑的工具,但是都一一失效了,找和分享都赶不上失效的快,索性就停了一段时间。但是老用车机和手机听歌也不是那么回事,多多少少还是想听点故事评书什么的,所以今天就又花了点时间&…

连接池配置_HikariCP被号称为性能最好的Java数据库连接池,如何配置使用?

公众号关注 “GitHub今日热榜”设为 “星标”,带你挖掘更多开发神器!HiKariCP是数据库连接池的一个后起之秀,号称性能最好,可以完美地PK掉其他连接池。为何要使用HiKariCP?这要先从BoneCP说起:什么&#xf…

教你使用Box2d制作用蜡笔手绘物体的效果(一)

首先推荐一款好玩儿的物理益智游戏“CrayonPhysics”,中文名叫“蜡笔物理学”(当然不是做广告哈哈),游戏中我们通过手工绘制各种各样的“物体”来让一个红色的小球吃掉星星。整个游戏都是蜡笔画的风格&am…

免费45天WPS稻壳会员领取

昨天在微信公众号“十点神器”分享了一个WPS专业版的永久密钥和使用方法,想到大家可能有需求使用稻壳会员,今天就找了一下一些免费的稻壳会员,还真的找到了一个45天的免费稻壳会员领取链接,好东西当然要分享。 稻壳会员能干啥&am…

栅格图像 转_这项AI图像描摹,90%的人都不知道!学会就是出图大佬

我们在寻找场地信息时,经常会找到一些专项地图,但是其非矢量的性质导致我们无法对其编辑;有时候从地图网站中下载地图,下载的图标和图像均无法更改,今天就告诉大家一个非矢量图像转矢量的方法。可能有的同学已经知道图…

教你使用Box2d制作用蜡笔手绘物体的效果(二)

我们继续来制作蜡笔手绘物体的效果。上一篇中我们完成了刚体绘制,这一部分我们来完成蜡笔纹理。 先来看一下最终的效果: (如果觉得蜡笔的纹理不够好的话,可以精加工一个自己的蜡笔纹理) 下面…