box2d 粗略的模拟水浮力

来源:互联网 发布:手机麻将透视软件 编辑:程序博客网 时间:2024/03/29 02:22

准备工作,首先修改 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(没做细致的测试,不过试了蛮多次,没发现出问题)~

原创粉丝点击