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(没做细致的测试,不过试了蛮多次,没发现出问题)~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
Box2D和Cocos2D是两个游戏开发框架。Box2D是一个用于物理模拟的开源库,可以模拟刚体的运动和碰撞等物理效果。Cocos2D是一个用于2D游戏开发的跨平台框架,提供了丰富的图形渲染和用户交互功能。\[1\] 在使用Cocos2D和Box2D进行游戏开发时,你可以利用Box2D来处理游戏中的物理效果,比如重力、碰撞和刚体运动等。Cocos2D提供了与Box2D的集成,使得开发者可以方便地在Cocos2D中使用Box2D的功能。你可以通过创建物理世界、添加刚体和设置碰撞检测等来实现游戏中的物理效果。\[2\] 如果你刚刚接触Cocos2D和Box2D,建议你先学习Cocos2D和Box2D的基础知识,然后再深入学习如何在Cocos2D中使用Box2D。你可以参考一些入门教程,比如《Cocos2D入门》和《Box2D入门》。如果你对OpenGL ES 2.0和自定义Cocos2D 2.X着色器等背景知识感到困惑,你可以查阅相关教程来获取更多的帮助。\[3\] #### 引用[.reference_title] - *1* *3* [如何使用Box2D和Cocos2D制作一款像Fruit Ninja一样的游戏-第1部分](https://blog.csdn.net/kaka626/article/details/9397825)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [用Box2D和cocos2d-x制作弹弓类游戏](https://blog.csdn.net/qq55008307/article/details/8090839)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值