Zabawy z LibGDX BOX2D libgdx tutorial.

Zabawy z LibGDX BOX2D ciąg dalszy. Pobawiłem się trochę BodyDef i Joint. Na prostym przykładzie można się fajnie pobawić fizyką :-). Poprzez zmianę parametrów takich jak density, friction, restitution czy gravity możemy w grze modelować zachowanie poszczególnych aktorów/elementów tym samym uzyskiwać efekt podobny do tego ze świata realnego.

Efekt działania poniższego kodu można zobaczyć Tutaj
Lewy prawy klawisz pozwala na przesuwanie skrzynek i równoważenie ciężaru.

Co ciekawe LibGDX różnie sobie radzi z kodem tzn. ten sam kod działający na desktop’ie poprawnie wymagał pewnych korekt przy kompilacji na html.

Można stworzyć grę wykorzystując Rectangle posługujemy się tu jednak prostymi elementami i każdy obiekt w grze jest prostokątem o określonych wymiarach i pozycji. LibGDX zapewnia proste metody dzięki, którym można programować kolizje. Tak powstał Ninja Fighter. Nie mniej dopiero wykorzystanie PolygonShape z silnika Box2D pozwala na pełną magię. PolygonShape w tandemie z FixtureDef pozwala nie tylko na zabawy z kolizjami ale na definiowanie zachowania poszczególnych elementów podczas ruchu i kolizji. Kolejnym udogodnieniem w pracy z PolygonShape jest możliwość definiowania różnych kształtów co przy większym dopracowaniu szczegółów będzie nieodzowne. Trudno bowiem wyobrazić sobie dobrą graficznie grę opartą o poruszające się prostokąty :-).

Poprzez definiowanie grawitacji, gęstości obiektów, sprężystości uzyskuje się ciekawe efekty. Warto w poniższym kodzie pobawić się tymi parametrami.

Warto też wspomnieć, a właściwie od tego trzeba byłoby zacząć, że BodyDef pozwala zdefiniować rodzaj obiektu, aktora. Wyróżnia przy tym 3 typy Dynamic, Static i KineticBody.

Wszędzie tam gdzie potrzebujemy stałe obiekty nie reagujące na fizykę np. ziemia, ściana etc. użyjemy StaticBody.

DynamicBody to będzie nasz główny aktor np. piłka, samochód etc. wszystko co będzie w ruchu, będzie spadać, uderzać etc.

KinematicBody to coś pośredniego tzn. nie reaguje na siły ale ma możliwość ruchu np. ruchome platformy, mosty etc.

Jak widać w poniższym kodzie testowałem różne warianty do czego zachęcam. Nie mniej w finalnej wersji ustawiłem mocowanie na StaticBody pomimo iż pierwotnie ten obiekt testowałem jako KinematicBody dla zabawy.

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.CircleShape;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.physics.box2d.joints.DistanceJointDef;

public class MyGdxGame extends ApplicationAdapter {

World world;
Body bodyPad, bodyPadLeg, weightR, weightL;
BodyDef kinematicBodyDef, kinematicBodyDefMocowanie;

static final float WORLD_TO_BOX = 0.01f;
Box2DDebugRenderer box2DDebugRenderer;
OrthographicCamera camera;
float posX;
float posY;
float powerX, powerY;
float maxRight;
float maxTop;

@Override
public void create() {
world = new World(new Vector2(0, -10), true);
camera = new OrthographicCamera();

posX = ConvertToBox(300);
posY = ConvertToBox(200);
powerY = 0;

maxRight = ConvertToBox(Gdx.graphics.getWidth());
maxTop = ConvertToBox(Gdx.graphics.getHeight());
camera.setToOrtho(false, maxRight, maxTop);
box2DDebugRenderer = new Box2DDebugRenderer();

//Body static np. ground
BodyDef groundBodyDef = new BodyDef();
groundBodyDef.position.set(new Vector2(0, 0));

Body groundBody = world.createBody(groundBodyDef);
PolygonShape groundBox = new PolygonShape();
groundBox.setAsBox(maxRight, 0.5f);
groundBody.createFixture(groundBox, 0.0f);
groundBox.dispose();

//Body dynamic ball

BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.DynamicBody;
bodyDef.position.set(posX-ConvertToBox(10), ConvertToBox(250));
bodyDef.fixedRotation = false;

Body body = world.createBody(bodyDef);

CircleShape circle = new CircleShape();
circle.setRadius(ConvertToBox(20f));

FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = circle;
fixtureDef.density = 0.9f;
fixtureDef.friction = 0.9f;
fixtureDef.restitution = 0.5f;

Fixture fixture = body.createFixture(fixtureDef);
circle.dispose();

//Body dynamic pochylnia

kinematicBodyDef = new BodyDef();
kinematicBodyDef.type = BodyDef.BodyType.DynamicBody;
kinematicBodyDef.gravityScale = 0.0f;
kinematicBodyDef.fixedRotation = false;
kinematicBodyDef.position.set(posX, posY);

bodyPad = world.createBody(kinematicBodyDef);

PolygonShape box = new PolygonShape();
box.setAsBox(ConvertToBox(200), ConvertToBox(5));

FixtureDef fixtureDefKin = new FixtureDef();
fixtureDefKin.shape = box;
fixtureDefKin.density = ConvertToBox(10.5f);
fixtureDefKin.friction = ConvertToBox(1f);
fixtureDefKin.restitution = ConvertToBox(10f);

Fixture fixturek = bodyPad.createFixture(fixtureDefKin);
box.dispose();

//Body mocowanie static

kinematicBodyDefMocowanie = new BodyDef();
kinematicBodyDefMocowanie.type = BodyDef.BodyType.StaticBody;
kinematicBodyDefMocowanie.gravityScale = 0.5f;
kinematicBodyDefMocowanie.fixedRotation = false;
kinematicBodyDefMocowanie.position.set(posX, posY);

bodyPadLeg = world.createBody(kinematicBodyDefMocowanie);

CircleShape mocowanie = new CircleShape();
mocowanie.setRadius(ConvertToBox(1f));

FixtureDef fixtureDefKinLeg = new FixtureDef();

mocowanie.dispose();

//join

DistanceJointDef distanceJointDef = new DistanceJointDef();
distanceJointDef.bodyA = bodyPad;
distanceJointDef.bodyB = bodyPadLeg;
distanceJointDef.length = ConvertToBox(1);

world.createJoint(distanceJointDef);

//Weight
BodyDef weightBodyDef;
weightBodyDef = new BodyDef();
weightBodyDef.type = BodyDef.BodyType.DynamicBody;
weightBodyDef.gravityScale = 0.8f;
weightBodyDef.fixedRotation = true;
weightBodyDef.position.set(posX+ConvertToBox(160), posY+ConvertToBox(50));

weightR = world.createBody(weightBodyDef);

PolygonShape weightPolygon = new PolygonShape();
weightPolygon.setAsBox(ConvertToBox(10), ConvertToBox(10));

FixtureDef fixtureWeight = new FixtureDef();
fixtureWeight.shape = weightPolygon;
fixtureWeight.density = 10.5f;
fixtureWeight.friction = 0.4f;
fixtureWeight.restitution = 0.3f;

Fixture fixturekWeightt = weightR.createFixture(fixtureWeight);

weightPolygon.dispose();

//Weight
BodyDef weightBodyDefLeft;
weightBodyDefLeft = new BodyDef();
weightBodyDefLeft.type = BodyDef.BodyType.DynamicBody;
weightBodyDefLeft.gravityScale = 0.8f;
weightBodyDefLeft.fixedRotation = true;
weightBodyDefLeft.position.set(posX - ConvertToBox(160), posY + ConvertToBox(50));

weightL = world.createBody(weightBodyDefLeft);

PolygonShape weightPolygonL = new PolygonShape();
weightPolygonL.setAsBox(ConvertToBox(10), ConvertToBox(10));

FixtureDef fixtureWeightL = new FixtureDef();
fixtureWeightL.shape = weightPolygonL;
fixtureWeightL.density = 10.5f;
fixtureWeightL.friction = 0.4f;
fixtureWeightL.restitution = 0.3f;

Fixture fixturekWeighttL = weightL.createFixture(fixtureWeightL);

weightPolygonL.dispose();

}

float ConvertToBox(float x) {
return x * WORLD_TO_BOX;
}

@Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
box2DDebugRenderer.render(world, camera.combined);
world.step(1 / 45f, 6, 2);
bodyPad.setLinearVelocity(powerX, powerY);

input();

}

public void input() {

if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) {
weightL.setLinearVelocity(ConvertToBox(10),0);
weightR.setLinearVelocity(ConvertToBox(10), 0);
}
if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {
weightL.setLinearVelocity(-ConvertToBox(10),0);
weightR.setLinearVelocity(-ConvertToBox(10), 0);

}

}
}

Na koniec warto wspomnieć o DistanceJoint w skrócie pozwala na łączenie obiektów. W powyższym przykładzie pochylnia jest połączona ze statycznym ciałem czyli mocowaniem. Warto pobawić się parametrem

distanceJointDef.length = ConvertToBox(1);

Co bardziej zobrazuje o co chodzi. Nie mniej bez Joint’ów trudno byłoby stworzyć np. samochód :-).
LibGDX BOX2D to potężny tandem i dopiero uchylam sobie do niego drzwi ale zabawa jest przednia. Nie mniej kod powyższy po wygenerowaniu waży 30MB, przypuszczam, że coś takiego w językach web’owych byłoby lżejsze 🙂

Facebooktwittergoogle_plusredditpinterestlinkedinmail

Dodaj komentarz