Box2d 中快速移动的物体有时会相互穿过

发布于 2024-10-24 01:09:27 字数 1989 浏览 1 评论 0 原文

我知道 Box2d 世界中快速移动的物体会产生隧道效应并相互穿过。解决方案是将尸体定义为子弹。我这样做了,但身体有时仍然会相互交叉,特别是如果相遇点不完全朝向中间并且身体在交叉时部分重叠。有什么解决办法吗?

这就是我制作所有物体的方式:

redBall = [CCSprite spriteWithFile:@"red-ball" rect:CGRectMake(0, 0, 34, 34)];
redBall.tag = 1;
[self addChild:redBall];
ballBodyDef.type = b2_dynamicBody;
ballBodyDef.position.Set((winSize.width/2)/PTM_RATIO, redBall.position.y/PTM_RATIO);
ballBodyDef.userData = redBall;

ballBodyDef.bullet = true;
_ballBody = _world->CreateBody(&ballBodyDef);

// Create circle shape
b2CircleShape circle;
circle.m_radius = 17.0/PTM_RATIO;

// Create shape definition and add to body
b2FixtureDef ballShapeDef;
ballShapeDef.shape = &circle;
ballShapeDef.density = 0.2f;
ballShapeDef.friction = 0.0f;
ballShapeDef.restitution = 1.0f;
_ballFixture = _ballBody->CreateFixture(&ballShapeDef);

我在 TouchesEnd 中移动这个球,如下所示:

- (void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

    UITouch *myTouch = [touches anyObject];
    CGPoint location = [myTouch locationInView:[myTouch view]];
    location = [[CCDirector sharedDirector] convertToGL:location];          

    CGPoint shootVector = ccpSub(location, striker.position);
    CGFloat shootAngle = ccpToAngle(shootVector);
    CGPoint normalizeShootVector = ccpNormalize(shootVector);

    float x1 = - cos(shootAngle);
    float y1 = - sin(shootAngle);

    int power = 0;
    float dist =ccpDistance(location, redBall.position);
    if (dist >= 200) 
        power = 20;
    else if (dist >= 100)
        power = 10;
    else if (dist >=75)
        power = 7;
    else if (dist >= 60)
        power = 4;
    else if (dist >= 50)
        power = 3;
    else if (dist >= 35)
        power = 2;
    else
        power = 1;

    b2Vec2 force = b2Vec2(x1*power, y1*power);
    _ballBody->ApplyLinearImpulse(force,ballBodyDef.position);      
}

这只是计算触摸点到球的距离,根据距离找到施加在球上的力量,然后将球移动到触碰。这个球会与任何其他挡住它的球发生碰撞。

I know that fast moving bodies in Box2d world cause tunneling effect and pass through each other. Solution is to define the bodies as bullets. I did that but bodies sometimes still cross each other especially if encounter point is not exactly towards middle and bodies partially overlap while crossing. Any solution?

This is how I am making all the bodies:

redBall = [CCSprite spriteWithFile:@"red-ball" rect:CGRectMake(0, 0, 34, 34)];
redBall.tag = 1;
[self addChild:redBall];
ballBodyDef.type = b2_dynamicBody;
ballBodyDef.position.Set((winSize.width/2)/PTM_RATIO, redBall.position.y/PTM_RATIO);
ballBodyDef.userData = redBall;

ballBodyDef.bullet = true;
_ballBody = _world->CreateBody(&ballBodyDef);

// Create circle shape
b2CircleShape circle;
circle.m_radius = 17.0/PTM_RATIO;

// Create shape definition and add to body
b2FixtureDef ballShapeDef;
ballShapeDef.shape = &circle;
ballShapeDef.density = 0.2f;
ballShapeDef.friction = 0.0f;
ballShapeDef.restitution = 1.0f;
_ballFixture = _ballBody->CreateFixture(&ballShapeDef);

I am moving this ball in TouchesEnd as:

- (void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

    UITouch *myTouch = [touches anyObject];
    CGPoint location = [myTouch locationInView:[myTouch view]];
    location = [[CCDirector sharedDirector] convertToGL:location];          

    CGPoint shootVector = ccpSub(location, striker.position);
    CGFloat shootAngle = ccpToAngle(shootVector);
    CGPoint normalizeShootVector = ccpNormalize(shootVector);

    float x1 = - cos(shootAngle);
    float y1 = - sin(shootAngle);

    int power = 0;
    float dist =ccpDistance(location, redBall.position);
    if (dist >= 200) 
        power = 20;
    else if (dist >= 100)
        power = 10;
    else if (dist >=75)
        power = 7;
    else if (dist >= 60)
        power = 4;
    else if (dist >= 50)
        power = 3;
    else if (dist >= 35)
        power = 2;
    else
        power = 1;

    b2Vec2 force = b2Vec2(x1*power, y1*power);
    _ballBody->ApplyLinearImpulse(force,ballBodyDef.position);      
}

It's simply the calculation of distance of touch point from the ball, finding power to apply on ball according to the distance and moving the ball in the direction of touch. And this ball collides with any other ball that comes in it's way.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

煞人兵器 2024-10-31 01:09:27

让我进一步详细说明达菲莫的回答。

还记得 CCLayer 的 tick 方法包含以下代码吗?:

int32 velocityIterations = 8;
int32 positionIterations = 1;

world->Step(dt, velocityIterations, positionIterations);

两个 int32 变量告诉 box2D 应该执行多少次迭代(即传递)来施加力、检测碰撞等。根据 box2D手册,增加这些值会以性能为代价提高模拟的准确性,反之亦然,减少这些值。因此,我建议您调整这些值,尤其是positionIterations,直到您对结果满意为止。

编辑:

这是另一个建议。再次记住 tick 方法的调用速率与 fps 相同,即每秒最多 60 帧?这意味着 b2World::Step 函数以 1/60 秒的间隔进行离散模拟,因此如果快速移动的物体所用时间少于该时间,则它可以设法穿过另一个物体。因此,要解决这个问题,您需要提高离散模拟的频率,假设达到每秒 180 步。但问题是如何? Cocos2D-iPhone 为每一帧调用 tick 方法,增加帧速率(如果可能的话)会降低性能并浪费所有处理能力。

以下是在不更改帧速率的情况下通过在同一周期内多次调用 b2World::Step 函数来实现此目的的方法:

int32 velocityIterations = 8;
int32 positionIterations = 1;
uint substeps = 3;
float32 subdt = dt / substeps;

for (uint i = 0; i < substeps; i++) {
    world->Step(subdt, velocityIterations, positionIterations);

    // do your physics-related stuff inside here but leave any sprites manipulation outside this loop
}

Let me elaborate further on duffymo's answer.

Remember the CCLayer's tick method contains the following codes?:

int32 velocityIterations = 8;
int32 positionIterations = 1;

world->Step(dt, velocityIterations, positionIterations);

The two int32 variables tell box2D how many iterations (i.e. passes) it should do to apply forces, detect collisions etc. According to box2D manual, increasing these values improves the accuracy of the simulation at the cost of performance, and vice versa for decreasing these values. So I would suggest you tweak these values, especially the positionIterations, until you are satisfied with the result.

EDIT:

Here is another suggestion. Remember again that the tick method is being called at the same rate as the fps, which is at most 60 per second? That means the b2World::Step function is doing discrete simulation at 1/60 second intervals, so a fast moving body manage to pass through another body if it takes less than that amount of time. So to solve this, you need to increase the frequency of the discrete simulation, let's say to 180 steps per second. But the question is how? Cocos2D-iPhone calls the tick method for every frame, and increasing the framerate (if it's even possible) will reduce the performance and waste all the processing power.

Here's how you can do it without changing the framerate, by calling the b2World::Step function a couple of times within the same tick:

int32 velocityIterations = 8;
int32 positionIterations = 1;
uint substeps = 3;
float32 subdt = dt / substeps;

for (uint i = 0; i < substeps; i++) {
    world->Step(subdt, velocityIterations, positionIterations);

    // do your physics-related stuff inside here but leave any sprites manipulation outside this loop
}
心凉怎暖 2024-10-31 01:09:27

您需要改进渗透检测:提高空间或时间或两者的灵敏度。

You need to refine your penetration detection: increase the sensitivity in either space or time or both.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文