我正在使用 AndEngine/Box2d 开发游戏。我有一个在屏幕上弹跳的球。我已经通过施加相反的力成功地使它忽略了重力,但是即使弹性设置为1,它也会在初始冲动后减速。基本上我想:
if(speed < a number ) 在运动方向上施加力或冲量(哪个更好?)
我该怎么做?
不幸的是,球正在与其他物体相互作用,因此设置速度不起作用,但我找到了解决方案!
使用力量和相当广泛的三角,我终于想出了:
private static class Ball extends Sprite {
Body body;
public Ball(final float pX, final float pY, final ITextureRegion pTextureRegion, final VertexBufferObjectManager pVertexBufferObjectManager) {
super(pX, pY, pTextureRegion, pVertexBufferObjectManager);
body = PhysicsFactory.createCircleBody(mPhysicsWorld, this, BodyType.DynamicBody, PhysicsFactory.createFixtureDef(0, 1, 0));
body.applyLinearImpulse(((float)5),(float) 5, body.getWorldCenter().x, body.getWorldCenter().y);
mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(this, body, true, true));
scene.attachChild(this);
}
@Override
protected void onManagedUpdate(final float pSecondsElapsed) {
body.applyForce(new Vector2(0,-SensorManager.GRAVITY_EARTH), new Vector2(body.getWorldCenter()));
float vx = body.getLinearVelocity().x, vy = body.getLinearVelocity().y, vr=(float) Math.sqrt(vx*vx+vy*vy);
float t= (float) Math.atan(Math.abs(vy/vx));
if(vr<destroyerVelocity){
if(vx>0&&vy<0)
body.applyForce((float) ((destroyerVelocity-vr)*Math.cos(t)*body.getMass()), (float) (-(destroyerVelocity-vr)*Math.sin(t)*body.getMass()), body.getWorldCenter().x, body.getWorldCenter().y);
else if(vx>0&&vy>0)
body.applyForce((float) ((destroyerVelocity-vr)*Math.cos(t)*body.getMass()), (float) ((destroyerVelocity-vr)*Math.sin(t)*body.getMass()), body.getWorldCenter().x, body.getWorldCenter().y);
else if(vx<0&&vy>0)
body.applyForce((float) (-(destroyerVelocity-vr)*Math.cos(t)*body.getMass()), (float) ((destroyerVelocity-vr)*Math.sin(t)*body.getMass()), body.getWorldCenter().x, body.getWorldCenter().y);
else if(vx<0&&vy<0)
body.applyForce((float) (-(destroyerVelocity-vr)*Math.cos(t)*body.getMass()), (float) (-(destroyerVelocity-vr)*Math.sin(t)*body.getMass()), body.getWorldCenter().x, body.getWorldCenter().y);
}
if(vr>destroyerVelocity){
if(vx>0&&vy<0)
body.applyForce((float) (-(vr-destroyerVelocity)*Math.cos(t)*body.getMass()), (float) ((vr-destroyerVelocity)*Math.sin(t)*body.getMass()), body.getWorldCenter().x, body.getWorldCenter().y);
else if(vx>0&&vy>0)
body.applyForce((float) (-(vr-destroyerVelocity)*Math.cos(t)*body.getMass()), (float) (-(vr-destroyerVelocity)*Math.sin(t)*body.getMass()), body.getWorldCenter().x, body.getWorldCenter().y);
else if(vx<0&&vy>0)
body.applyForce((float) ((vr-destroyerVelocity)*Math.cos(t)*body.getMass()), (float) (-(vr-destroyerVelocity)*Math.sin(t)*body.getMass()), body.getWorldCenter().x, body.getWorldCenter().y);
else if(vx<0&&vy<0)
body.applyForce((float) ((vr-destroyerVelocity)*Math.cos(t)*body.getMass()), (float) ((vr-destroyerVelocity)*Math.sin(t)*body.getMass()), body.getWorldCenter().x, body.getWorldCenter().y);
}
super.onManagedUpdate(pSecondsElapsed);
}
}
本质上,它的作用是创建一个身体并对其施加冲动以使其移动(由于某种原因比力好得多)。每次更新时,都会施加与重力相反的力,以使球保持在空中(基本上是浮动的)。弹性设置为 1,因此碰撞几乎是完全弹性的。现在是棘手的部分:我计算了 x 和 y 速度(分别为 vx 和 vy)并使用它们来计算合成速度(vr)和它们行进的角度(t)。
如果 vr 小于我想要的速度(destroyerVelocity),则施加一个力将其撞回驱逐舰速度。由于 F=mv/t 并且我只使用 t=1,所以在一个方向上施加的力等于所需速度 - 实际速度 * x/y(即 cos/sin)* 物体的质量。如果物体沿 x 正方向和 y 负方向行进,则施加 (x,-y) 的力,依此类推。
为了使速度尽可能接近驱逐舰的速度,如果恰好大于驱逐舰的速度,我必须向相反方向施加一个力。这是以相同的方式完成的,只是使用了相反的力量。
运行结果速度的日志语句(destroyerVelocity 为 7),它的内容如下:6.900001、6.9995001、7.00028、7.13005、...
虽然球有时会飙升到 15 或 20,但通常只需要 2 或 3 圈就可以在 7 的 +- .5 范围内,这对我来说已经足够了!希望这可以帮助其他寻找相同事物的人。
要在移动方向上施加连续的力,您可以使用以下代码。
Vector2 velocity = this.getBody().getLinearVelocity();
preX = velocity.x / (Math.abs(velocity.x));
preY = velocity.y / (Math.abs(velocity.y));
if (Math.abs(velocity.x) < 5) {
this.body.setLinearVelocity(preX * 5, velocity.y);
}
if (Math.abs(velocity.y) < 5) {
this.body.setLinearVelocity(velocity.x, preY * 5);
}
编辑 :
currentVelocity = bulletBody.getLinearVelocity();
if (currentVelocity.len() < speed
|| currentVelocity.len() > speed + 0.25f) {
velocityChange = Math.abs(speed
- currentVelocity.len());
currentVelocity.set(currentVelocity.x
* velocityChange, currentVelocity.y
* velocityChange);
bulletBody.applyForce(currentVelocity,
bulletBody.getWorldCenter());
}
这是您必须在管理更新方法或更新处理程序中编写的另一个代码。
如果球没有与其他任何东西相互作用,那么使用 SetLinearVelocity 应该没问题,它比计算力/脉冲 (C++) 更简单:
b2Vec2 currentDirection = body->GetLinearVelocity();
currentDirection.Normalize();
body->SetLinearVelocity( 5 * currentDirection );
需要注意的是,这只能在球自由运动时进行。如果它正好在从墙壁或世界上其他东西反弹的中间,这将导致问题。因此,在执行此操作之前,您需要确保它没有触及任何东西。如果 GetContactList() 返回 null 则主体没有接触任何东西。
这种调整应该足够便宜,可以在每个时间步中进行,但由于速度在再次击中某物之前不会改变,你甚至可以在反弹结束后只做一次。您可以使用接触侦听器并在 EndContact 回调中检查球是否仍在接触某物,如果没有,则在时间步结束后标记身体以进行调整。
我在我的更新功能中使用它来保持球的速度。
speed = ball.getLinearVelocity().length()
maxSpeed = 10;
if (speed > maxSpeed ){
ball.setLinearDamping(0.5);
}
else if (speed < maxSpeed) {
ball.setLinearDamping(0.0);
}
if (speed < maxSpeed ){
var currentVelocity = ball.getLinearVelocity();
currentVelocity.set(currentVelocity.x * 1.1, currentVelocity.y * 1.1);
ball.applyForce(currentVelocity,ball.getWorldCenter());
}