Box2dおよびLibgdx

良い一日。



少し前に、私はLibgdxの研究を開始しましたが、このフレームワークに関する記事はロシア語にはあまりないことがわかりました。 もちろん、私はロシア語の記事に出会ったが、それらは翻訳だけでした。 マニュアル。 開発者が作成物について話そうとした真実もありました。 しかし、ある記事ではすべてを把握することはできません;その結果、そのような記事ではすべてが混同されました。



LibGDXのさまざまな側面/部分について段階的に記事を書き始めたので、最終的にはだれでも自分のおもちゃの実行可能なバージョンを作成できます。 この記事では、LibGDXに存在するBox2Dについてお話したいと思います。

画像



Box2Dはリアルタイムの物理エンジンであり、2次元の物理オブジェクトで動作するように設計されています。 あなたがサイドビュー/プラットフォーマーである種のゲームを開発しているなら、これは完璧なソリューションです。

必要なクラスはすべてパッケージcom.badlogic.gdx.physics.box2dにあります。



基礎として、私は自分の記事の1つで説明したアーキテクチャを採用しました。 MVCパターンが使用されます。



ゲーム画面



GameScreenScreen、 InputProcessorを実装します。これにより、クリックなどのすべてのユーザーアクションを処理できます。



必要なクラスが作成されます:MyWorld、WorldController、WorldRenderer。

MyWorldの代わりに基礎として取り上げられたこの記事では、Worldを使用しています。 Box2Dパッケージの同名のクラスと競合しないように変更する必要がありました。



@Override public void show() { //  ,        ,      MyWorld.CAMERA_WIDTH = MyWorld.CAMERA_HEIGHT* Gdx.graphics.getWidth()/Gdx.graphics.getHeight(); world = new MyWorld(); renderer = new WorldRenderer(world, MyWorld.CAMERA_WIDTH, MyWorld.CAMERA_HEIGHT,true); controller = new WorldController(world); Gdx.input.setInputProcessor(this); //     }
      
      







当然、touchDown、touchUpなどのすべての処理メソッドをオーバーライドする必要があります。



たとえば、touchDown、touchUpを再定義して、キャラクターが左右に移動してジャンプできるようにします。



 private void ChangeNavigation(int x, int y){ controller.resetWay(); //   ,  ,     () if(height-y > world.getPlayer().getPosition().y * renderer.ppuY) controller.upPressed(); //   ,  ,     if ( x< world.getPlayer().getPosition().x * renderer.ppuX) controller.leftPressed(); //   ,  ,     if (x> (world.getPlayer().getPosition().x )* renderer.ppuX) controller.rightPressed(); } @Override public boolean touchDown(int x, int y, int pointer, int button) { if (!Gdx.app.getType().equals(ApplicationType.Android)) return false; ChangeNavigation(x,y); return true; } @Override public boolean touchUp(int x, int y, int pointer, int button) { if (!Gdx.app.getType().equals(ApplicationType.Android)) return false; controller.resetWay(); return true; } @Override public boolean touchDragged(int x, int y, int pointer) { ChangeNavigation(x,y); return false; }
      
      







GameScreenでは、ユーザーアクションとコントローラーアラームが処理されています。 ここで重要なメソッドはrender()



。これは特定の間隔で起動し、ロジックを処理してオブジェクトを描画します。



 @Override public void render(float delta) { Gdx.gl.glClearColor(0, 0, 0, 232F/255); Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT); controller.update(delta); renderer.render(delta); }
      
      







マイワールド





MyWorldはオブジェクトの単なるコンテナになります。 また、パッケージのWorldクラスのインスタンスへのリンクも含まれます。 オブジェクトを定義する必要があります。



 World world; // Player player; //  Array<MovingPlatform> platforms = new Array<MovingPlatform>(); // public MovingPlatform groundedPlatform = null;
      
      







さて、オブジェクトを作成します。



 public MyWorld(){ width = 30; height = 8; //   ,    () world = new World(new Vector2(0, -20), true); createWorld(); } private void createWorld(){ BodyDef def = new BodyDef(); def.type = BodyType.DynamicBody; Body boxP = world.createBody(def); player = new Player(boxP); player.getBody().setTransform(3.0f, 4.0f, 0); player.getBody().setFixedRotation(true); Body box = createBox(BodyType.StaticBody, 1, 1, 0); for(int i = 0; i < 10; i++) { box = createBox(BodyType.DynamicBody, (float)Math.random(), (float)Math.random(), 13); box.setTransform((float)Math.random() * 10f - (float)Math.random() * 10f, (float)Math.random() * 10 + 6, 0); } platforms.add(new MovingPlatform(world, 3F, 3, 1,0.25F, 2, 0, 2)); for(int i=1;i<width; ++i){ Body boxGround = createBox(BodyType.StaticBody, 1F, 1F, 0); boxGround.setTransform(i,0,0); } } private Body createBox(BodyType type, float width, float height, float density) { BodyDef def = new BodyDef(); def.type = type; Body box = world.createBody(def); PolygonShape poly = new PolygonShape(); poly.setAsBox(width, height); box.createFixture(poly, density); poly.dispose(); return box; }
      
      







BodyDef



ソリッドに関するすべてのデータを含むボディ定義。 この定義をさまざまなオブジェクトに再利用できます。



BodyType



ボディのタイプ。 次の3つのいずれかが可能です。

静的-質量ゼロ、速度ゼロ、プログラムでのみ移動できます。

キネマティック-ゼロ質量、非ゼロ速度、シフト可能;

動的-正の質量、非ゼロ速度、シフト可能。



staticは、移動する必要のないオブジェクトに使用する必要があります。 例として:床、壁。



Body



、実際には、ボディ自体。

Body



オブジェクトは、 setTransform()



メソッドを使用して移動および回転できます。



プレイヤー



Playerクラスはキャラクターです:



 public class Player { final static float MAX_VELOCITY = 3f; public final static float SPEED = 5f; public final static float SIZE = 0.8f; public Fixture playerPhysicsFixture; public Fixture playerSensorFixture; Body box; public Player(Body b){ box = b; PolygonShape poly = new PolygonShape(); poly.setAsBox(0.4f, 0.4f); playerPhysicsFixture = box.createFixture(poly, 3); poly.dispose(); CircleShape circle = new CircleShape(); circle.setRadius(0.41f); circle.setPosition(new Vector2(0, 0f)); playerSensorFixture = box.createFixture(circle, 1); setFriction(10F); circle.dispose(); box.setBullet(true); } public Body getBody(){ return box; } //    public void setFriction(float f){ playerSensorFixture.setFriction(f); playerPhysicsFixture.setFriction(f); } public Vector2 getPosition(){ return box.getPosition(); } public Vector2 getVelocity() { return velocity; } Vector2 velocity = new Vector2(); public void update(float delta) { Vector2 vel = box.getLinearVelocity(); velocity.y = vel.y; box.setLinearVelocity(velocity); if(isJump) {box.applyLinearImpulse(0, 8, box.getPosition().x, box.getPosition().y); isJump = false;} } boolean isJump = false; public void jump(){ isJump = true; } public void resetVelocity(){ getVelocity().x =0; getVelocity().y =0; } }
      
      







この例では、Box2Dでキネマティックペアからオブジェクトを作成できることを示しました。 この場合、プレーヤーは接着された長方形と円で構成されます。



原則として、位置の明らかな変化まで、さまざまな方法でオブジェクトを移動できます。 しかし、Box2Dには、このための2つの特別な方法があります。

applyLinearImpulse()



-オブジェクトの運動量を設定します。 現実の世界と同様に、オブジェクトを押すと、衝動に応じて、摩擦により衝動が消えるまで体が動きます。

setLinearVelocity()



-移動速度を設定します。 パルスとは異なり、速度は変更するまで一定です。



私は個人的にsetLinearVelocity()



を使用してX軸に沿って移動します。 手押し車に指を当てている間、ペルシャ人が動き、離すとキャラクターが止まります。 applyLinearImpulse()



を使用して水平方向に移動する場合、画面から指を離した後もしばらくの間、キャラクターは移動し続けます。

原則として、時々 applyLinearImpulse()



applyLinearImpulse()



左右に移動できapplyLinearImpulse()



。 例として、キャラクターが滑りやすい表面を移動する場合(例として、氷)。



box.setBullet(true);



は、衝突を検出するときにボディが弾丸と見なされることを示します。



移動プラットフォーム



MovingPlatform



は、動くプラットフォームです。



 public class MovingPlatform { Body platform; Vector2 pos = new Vector2(); Vector2 dir = new Vector2(); Vector2 vel = new Vector2(); float maxDist = 0; float width; //Vector2 l = new Vector2(); public MovingPlatform(World world, float x, float y, float width, float height, float dx, float dy, float maxDist) { platform = createBox(world, BodyType.KinematicBody, width, height, 1); this.width = width; pos.x = x; pos.y = y; dir.x = dx; dir.y = dy; vel.x = dx; vel.y = dy; this.maxDist = maxDist; platform.setTransform(pos, 0); platform.getFixtureList().get(0).setUserData("p"); platform.setUserData(this); } public void resume(){ dir.x= vel.x; dir.y = vel.y; } public void pause(){ vel.x = dir.x; vel.y = dir.y; dir.x = 0; dir.y = 0; platform.setLinearVelocity(dir); } public void update(float deltaTime) { if(dir.x < 0 && platform .getPosition().x < pos.x-maxDist) { platform .getPosition().x =pos.x; dir.mul(-1); } if(dir.x > 0 && platform .getPosition().x> pos.x+maxDist) { platform .getPosition().x =pos.x+maxDist; dir.mul(-1); } platform.setLinearVelocity(dir); } private Body createBox(World world,BodyType type, float width, float height, float density) { BodyDef def = new BodyDef(); def.type = type; Body box = world.createBody(def); PolygonShape poly = new PolygonShape(); poly.setAsBox(width/2, height/2); Fixture f = box.createFixture(poly, density); poly.dispose(); return box; } }
      
      





プラットフォームは元の位置から一定の距離を移動します。



ワールドコントローラー



WorldController



では、 update()



メソッドのユーザーアクションに応じてロジックが更新されます。



 boolean grounded ; public void update(float delta) { Array<MovingPlatform> platforms = world.getPlatforms(); //   for(int i = 0; i < platforms.size; i++) { MovingPlatform platform = platforms.get(i); platform.update(Math.max(1/60.0f, delta)); } grounded = isPlayerGrounded(Gdx.graphics.getDeltaTime()); //  processInput(); //   world.getPlayer().update(delta); }
      
      







ユーザーのクリックを処理し、新しい移動方向を示す必要があります。

  //            private void processInput() { Player player = world.getPlayer(); //    if (keys.get(Keys.LEFT)) player.getVelocity().x =- Player.SPEED; //    if (keys.get(Keys.RIGHT)) player.getVelocity().x = Player.SPEED; //   if (keys.get(Keys.UP)) //    if(grounded) // player.jump(); //    if(!grounded) //  world.getPlayer().setFriction(0f); //   else{ if(keys.get(Keys.RIGHT) || keys.get(Keys.LEFT)) world.getPlayer().setFriction(0.2f); else world.getPlayer().setFriction(100f); } }
      
      







キャラクターが地面にいるかどうかをチェックするメソッドを記述するだけです。 これを行うには、オブジェクトのすべての連絡先のリストを取得する必要があります。 その中で、キャラクターの連絡先を選択します。 連絡先の中にキャラクターの下にあるオブジェクトがある場合、それは固体表面上にあります。

  private boolean isPlayerGrounded(float deltaTime) { world.groundedPlatform = null; List<Contact> contactList = world.getWorld().getContactList(); for(int i = 0; i < contactList.size(); i++) { Contact contact = contactList.get(i); if(contact.isTouching() && (contact.getFixtureA() == world.getPlayer().playerSensorFixture || contact.getFixtureB() == world.getPlayer().playerSensorFixture)) { Vector2 pos = world.getPlayer().getPosition(); WorldManifold manifold = contact.getWorldManifold(); boolean below = true; for(int j = 0; j < manifold.getNumberOfContactPoints(); j++) { below &= (manifold.getPoints()[j].y < pos.y - 0.4f); } if(below) { if(contact.getFixtureA().getUserData() != null && contact.getFixtureA().getUserData().equals("p")) { world.groundedPlatform = (MovingPlatform)contact.getFixtureA().getBody().getUserData(); } if(contact.getFixtureB().getUserData() != null && contact.getFixtureB().getUserData().equals("p")) { world.groundedPlatform = (MovingPlatform)contact.getFixtureB().getBody().getUserData(); } return true; } return false; } } return false; }
      
      







キャラクターの位置に応じて、摩擦の力が変化します。 摩擦がゼロの場合、キャラクターはプラットフォームに沿って移動しませんが、スライドするように所定の位置に残ります。





より多くの摩擦を設定する必要があると仮定することは論理的です。 私は外国のマニュアルを見ました...この方法は常に機能するとは限りません。



この場合、あなたは他の方法で行くことができます:キャラクターではなく接触で摩擦力を変更します。 つまり、isPlayerGroundedメソッドに次のように記述します。

 if (!keys.get(Keys.LEFT) && !keys.get(Keys.RIGHT)) contact.setFriction(200F); else contact.setFriction(0F);
      
      





次に、キャラクターが立っている場合、多くの摩擦のためにプラットフォームとともに移動します。 キャラクターを動かす必要がある場合は、摩擦を減らします。







実際にはすべて。 キャラクターは左右に移動したり、ジャンプしたりできます。 ランダムに生成されたブロックを移動することもできます。



これで、オブジェクトをレンダリングするだけで済みます。



ワールドレンダラー



WorldRenderer



クラスでは、 WorldRenderer



WorldRenderer



ます。

 public class WorldRenderer { Box2DDebugRenderer renderer; public static float CAMERA_WIDTH = 10f; public static float CAMERA_HEIGHT = 15f; public float ppuX; public float ppuY; MyWorld world; public OrthographicCamera cam; public WorldRenderer(MyWorld world, float w, float h, boolean debug) { renderer = new Box2DDebugRenderer(); this.world = world; CAMERA_WIDTH = w; CAMERA_HEIGHT = h; ppuX = (float)Gdx.graphics.getWidth() / CAMERA_WIDTH; ppuY = (float)Gdx.graphics.getHeight() / CAMERA_HEIGHT; this.cam = new OrthographicCamera(CAMERA_WIDTH, CAMERA_HEIGHT); SetCamera(CAMERA_WIDTH / 2f, CAMERA_HEIGHT / 2f); } public void SetCamera(float x, float y){ this.cam.position.set(x, y,0); this.cam.update(); } public void dispose(){ world.dispose(); } public void render(float delta) { renderer.render(world.getWorld(), cam.combined); world.getWorld().step(delta, 4, 4); } }
      
      







render()



メソッドでのレンダリング自体が発生します。 この例は、デバッグモードでBox2DDebugRendererを描画してオブジェクトの境界を表示するために使用されます。 アプリケーションの最終バージョンでは、 render()



メソッドで、すべてのオブジェクトのスプライトを出力する必要があります。



world.getWorld().step(delta, 4, 4);



物理学を更新する頻度を示します。 ほとんどの場合、描画よりも頻繁に物理学を更新する必要はないと思います。

PS私自身、Box2Dの研究を始めたばかりなので、どんなヒントや批判も喜んでいます。



All Articles