box2d 粗略的模拟水浮力

准备工作,首先修改 box2d 的 b2Body 类,

增加一个public 修饰的 bool 类型标识变量 m_isInWater,用于标识物体当前是否在水中。

然后,切到 box2d 的 b2World 类,在 CreateBody 方法的尾部添加一行初始化标识变量的代码:

b2Body* b2World::CreateBody(const b2BodyDef* def) { b2Assert(IsLocked() == false); if (IsLocked()) { return NULL; } void* mem = m_blockAllocator.Allocate(sizeof(b2Body)); b2Body* b = new (mem) b2Body(def, this); // Add to world doubly linked list. b->m_prev = NULL; b->m_next = m_bodyList; if (m_bodyList) { m_bodyList->m_prev = b; } m_bodyList = b; ++m_bodyCount; /** Added By Bruce Yang on 2011.11.25.12.49~ */ b->m_isCuttable = true; // body 默认设置为可被切割的~ b->m_isBalloon = false; // body 默认设置为非气球~ b->m_isInWater = false; // body 默认设置不在水中(即使出生就在水中也不要紧,contactListener会立即做相应设置)~ return b; }contactListener 的相关代码:

// // MyContactListener.m // GameScene // // Created by Bruce Yang on 2/18/10. // Copyright (c) 2012 EricGameStudio. All rights reserved. // #import "MyContactListener.h" MyContactListener::MyContactListener() : _contacts() { } MyContactListener::~MyContactListener() { } /** * 处理物体落水受到浮力的情况~ * 在刚入水的时候,物体的速度将骤减,然后受到一个持续向上的浮力,直到物体脱离水区域为止~ * 本来在 x 轴方向是不应该受到浮力的影响的,但考虑到水的粘滞性,x 轴方向的速度收缩为原来的 0.8 倍~ * 还有就是,角速度的大小也会受到液体粘滞性的影响,因此角速度也要做相应的处理~ */ void addBuoyancyTag(MyContact contact) { float ySpeedDecreaseFactor = 0.3f; float xSpeedDecreaseFactor = 0.7f; float rSpeedDecreaseFactor = 0.7f; if(contact.fixtureA == [BYSingle getInstance].buoyancy) { // 1.y方向的速度在入水的临界点骤减为原来的 0.3 倍~ b2Body* body = contact.fixtureB->GetBody(); b2Vec2 oldSpeed = body->GetLinearVelocity(); body->SetLinearVelocity(b2Vec2(oldSpeed.x * xSpeedDecreaseFactor, oldSpeed.y * ySpeedDecreaseFactor)); body->SetAngularVelocity(body->GetAngularVelocity() * rSpeedDecreaseFactor); // 2.标识为 “受水浮力影响” 状态~ body->m_isInWater = true; } if(contact.fixtureB == [BYSingle getInstance].buoyancy) { b2Body* body = contact.fixtureA->GetBody(); b2Vec2 oldSpeed = body->GetLinearVelocity(); body->SetLinearVelocity(b2Vec2(oldSpeed.x * xSpeedDecreaseFactor, oldSpeed.y * ySpeedDecreaseFactor)); body->SetAngularVelocity(body->GetAngularVelocity() * rSpeedDecreaseFactor); body->m_isInWater = true; } } // 移除 “受水浮力影响” 标识(切割水中物体的时候会出错,蛋疼)~ void removeBuoyancyTag(MyContact contact) { if(contact.fixtureA == [BYSingle getInstance].buoyancy) { contact.fixtureB->GetBody()->m_isInWater = false; } if(contact.fixtureB == [BYSingle getInstance].buoyancy) { contact.fixtureA->GetBody()->m_isInWater = false; } } void MyContactListener::BeginContact(b2Contact* contact) { MyContact myContact = { contact->GetFixtureA(), contact->GetFixtureB() }; // added by Bruce Yang on 2012.04.13.13.34~ addBuoyancyTag(myContact); _contacts.push_back(myContact); } void MyContactListener::EndContact(b2Contact* contact) { MyContact myContact = { contact->GetFixtureA(), contact->GetFixtureB() }; // added by Bruce Yang on 2012.04.13.13.34~ removeBuoyancyTag(myContact); vector<MyContact>::iterator pos; pos = find(_contacts.begin(), _contacts.end(), myContact); if (pos != _contacts.end()) { _contacts.erase(pos); } } void MyContactListener::PreSolve(b2Contact* contact, const b2Manifold* oldManifold) { } void MyContactListener::PostSolve(b2Contact* contact, const b2ContactImpulse* impulse) { } 主场景的tick方法:

-(void) tick: (ccTime) dt { int32 velocityIterations = 8; int32 positionIterations = 1; _world->Step(dt, velocityIterations, positionIterations); /** Iterate over the bodies in the physics world */ for (b2Body* b = _world->GetBodyList(); b; b = b->GetNext()) { if(b->GetUserData() != NULL) { // Synchronize the AtlasSprites position and rotation with the corresponding(相应的) body CCSprite *actor = (CCSprite*)b->GetUserData(); CGPoint po = CGPointMake(b->GetPosition().x*PTM_RATIO, b->GetPosition().y*PTM_RATIO); if(![MGameScene isPositionOutOfBounds:po]) { // 如果没有越界的话,实时更新~ actor.position = po; actor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle()); // 给接触水区域的物体施加持续向上的浮力~ if(b->m_isInWater) { float absG = fabs(_world->GetGravity().y); float mass = b->GetMass(); float density = 0; if(b->GetFixtureListCount() != 0) { density = b->GetFixtureList()->GetDensity(); } else { density = 1.2f; } float volumn = mass / density; // mass = rho * volumn,水的密度是1.0, // 但为了使密度为 1.0 的冰块不致于很慢的沉入水中,将水的密度调为 0.9 以减小浮力~ float waterMass = 0.9f * volumn; b->ApplyForceToCenter(b2Vec2(0, waterMass * absG)); } } else { if(actor.tag != TAG_STATE_READY_TO_BE_REMOVED) { [actor removeFromParentAndCleanup:YES]; } [MGameScene byDestroyBody:b]; // 对于越界的 body,一律销毁之~ } } } }值得一提的是:

我开始是将表示变量放在 CCSprite 类里面的,这样会出现一个问题:

contactListener 当物体脱离水区域调用 removeBuoyancyTag() 方法的时候

如果在水中将该 body 切割的话(切割会将旧 body 销毁,并生成两份新的 body),从 body 里面取出 CCSprite 对象会报出错误,

后来在我将 isInWater 这个标识放到 b2Body 里面后,干掉了这个bug(没做细致的测试,不过试了蛮多次,没发现出问题)~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值