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());
}
これは、manage update メソッドまたは update ハンドラで記述する必要がある別のコードです。
ボールが他のものと相互作用していない場合は、SetLinearVelocity を使用しても問題なく、力/インパルス (C++) を計算するよりも簡単です。
b2Vec2 currentDirection = body->GetLinearVelocity();
currentDirection.Normalize();
body->SetLinearVelocity( 5 * currentDirection );
注意点として、これはボールが自由に動いているときにのみ行う必要があります。壁や世界の何かに跳ね返っている最中であれば、問題が発生します。したがって、これを行う前に、何かに触れていないことを確認する必要があります。GetContactList() が null を返す場合、ボディは何にも触れていません。
この調整は、すべての時間ステップで行うのに十分安価である必要がありますが、速度は何かに再び当たるまで変化しないため、バウンスが終了した後に 1 回だけ行うこともできます。コンタクト リスナーを使用し、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());
}