Collision¶
Collisioniga alustamine¶
1. Eeldame, et meil on valmis:
Mängukaart
Kaardi collision layer
Kaart on laetud
TmxMapLoader mapLoader = new TmxMapLoader();
TiledMap map = mapLoader.load("maps/level1.tmx"); // Laeb Tiled kaardi failist
2. Siit saab jätkata vähemalt kahel erineval viisil:
Esimeseks võimaluseks, ning mõistlikuks otsuseks oleks kasutada LibGDX-is Box2D füüsikaliidest, mille abil saab hallata:
Collisioneid
Mängija liikumist
Gravitatsiooni (nt platformerites) jne.
Teiseks võimaluseks oleks kirjutada kõik nullist ise, mis on oluliselt keerulisem ja aeganõudvam. (seda ma siin ei käsitle)
3. Alustame Box2D lisamisega
Box2D lisamine build.gradle failidesse
build.gradle juurkaustas (see kontrollib kõiki mooduleid) – seda ei pea tavaliselt muutma.
core/build.gradle – siia lisame Box2D API, mis on vajalik kogu mängu loogika jaoks.
dependencies {
implementation group: 'com.esotericsoftware', name: 'kryonet', version: '2.22.0-RC1'
api "com.badlogicgames.gdx:gdx-box2d:$gdxVersion"
}
desktop/build.gradle – siia lisame Box2D native library (natives-desktop), et see töötaks lauaarvutites.
project(":desktop") {
apply plugin: "java-library"
dependencies {
implementation project(":core") // Desktop kasutab Core moodulit
api "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-desktop"
}
}
Selle jaoks tuleb build.gradle
faili vajalik dependency lisada
implementation "com.badlogicgames.gdx:gdx-box2d:1.10.0"
(vajadusel GPT aitab)
4. Järgmiseks täiendame veel oma GameScreen
klassi konstruktorit:
private final World world;
private final Box2DDebugRenderer b2dr;
this.world = new World(new Vector2(0, 0), true);
this.b2dr = new Box2DDebugRenderer();
World
klass määratleb Box2D maailma, kus toimub füüsika simuleerimine.World
muutujas onVector 2
gravitatsiooni määramiseks ningdoSleep
mis säästab arvutusvõimsust staatiliste objektide puhul.
Box2D füüsikamootoris on kolm peamist kehatüüpi, mis käituvad erinevalt:
Staatiline (StaticBody): ei liigu ega reageeri jõududele
Kasutatakse seinte, põrandate, platvormide jms jaoks.
Ei mõjuta dünaamilisi kehasid otseselt, aga neile võib vastu põrgata.
Dünaamiline (DynamicBody) – füüsikaseadustele alluv keha
Reageerib gravitatsioonile, impulssidele, jõududele ja kokkupõrgetele.
Kasutatakse tegelaste, kastide, pallide jms jaoks.
Kineetiline (KinematicBody) – juhitav, kuid ei reageeri füüsikale
Liigutatakse käsitsi setLinearVelocity või setTransform abil.
Ei mõjuta dünaamilisi kehasid (aga neile võib vastu põrgata).
Mida doSleep teeb? Kui doSleep on tõsi, siis Box2D ei arvuta füüsikat kehadelt, mis ei liigu.
Selle maailma sees on kehad (body
) ja objektid (fixture
). Fixture
kinnitatakse kehale, andes sellele kuju, võimaldades teiste kehadega kokku põrgata jne.
Box2DDebugRenderer
renderdab kehasid ja objekte. (See on testimiseks mõeldud.)
5. Mängu kehade ja objektide lisamiseks loo uus klass, näiteks ** ``Box2DWorldGenerator``, **, ja anna sinna sisse GameScreen
klass.
Mina teen seda oma GameScreenis
BodyDef bdef = new BodyDef();
PolygonShape shape = new PolygonShape();
FixtureDef fdef = new FixtureDef();
Body body;
for (MapObject object : map.getLayers().get(2).getObjects().getByType(RectangleMapObject.class)) {
Rectangle rectangle = ((RectangleMapObject) object).getRectangle();
bdef.type = BodyDef.BodyType.StaticBody;
bdef.position.set((rectangle.getX() + rectangle.getWidth() / 2) / Demo.PPM, (rectangle.getY() + rectangle.getHeight() / 2) / Demo.PPM);
body = world.createBody(bdef);
shape.setAsBox(rectangle.getWidth() / 2 / Demo.PPM, rectangle.getHeight() / 2 / Demo.PPM);
fdef.shape = shape;
body.createFixture(fdef);
}
BodyDefi
kasutatakse Box2D-s keha omaduste määratlemiseks.See sisaldab atribuute nagu keha tüüp (staatiline, kineetiline, dünaamiline), asend, kaal, kiirus jne.
PolygonShape
on Box2D klass, mida kasutatakse keha külge kinnitatud objekti kuju määratlemiseks.FixtureDefi
kasutatakse objekti omaduste (nt kuju, tiheduse, hõõrdumise jne) määratlemiseks. See “kinnitatakse” keha külge.
Omadus |
BodyDef (Keha määratlus) |
FixtureDef (Kokkupõrgete määratlus) |
---|---|---|
Mida määrab? |
Keha üldised füüsikalised omadused |
Kokkupõrgete ja füüsika omadused |
Olulised parameetrid |
|
|
Kas see lisatakse kehale? |
Kasutatakse |
Lisatakse kehale |
For loopi abil loome kehasid objektidest, mis on varem Tiled abil kaardile lisatud.
(Hiljem saad lisada teise sarnase tsükli ka teiste kujundite jaoks, näiteks ümmarguste kehade loomiseks.)
bdef.type = BodyDef.BodyType.StaticBody
: määrab keha tüübiksStaticBody
, mis ei liigu ega reageeri jõududele.
Seejärel määrame keha asukoha ristküliku keskpunkti ja shape
saab kuju, mis on pool ristküliku laiusest ja pikkusest. Määrame objekti kujuks shape’i
.
6. Lisaks peame render()
meetodisse lisama read:
this.b2dr.render(world, camera.combined);
this.b2dr.setDrawBodies(true);
this.b2dr.setDrawBodies(true);
:
Renderdab meie objektidele ümber jooned, et saaksime paremini vajadusel
debug-ida
.Kui sulgudesse panna
false
, saame jooned peita.
Mängu jooksutamisel võiksid nüüd ruudukujuliste objektide ümber olla sellised jooned:

Arvatavasti on sul olemas juba mingi klass mängija jaoks.
7. Mängija klassi võiksid nüüd teha järgnevad muudatused:
Uued muutujad
b2body
(klassBody
).World
Konstruktor võiks olla midagi sellist (lisaks olemasolevale loogikale):
public Player(World world) {
this.world = world;
definePlayer();
}
8. Uus meetod definePlayer()
.
Teeme midagi sarnast, mis enne objektide genereerimisega:
private void definePlayer() {
BodyDef bdef = new BodyDef();
bdef.position.set(32 / Demo.PPM, 32 / Demo.PPM);
bdef.type = BodyDef.BodyType.DynamicBody;
b2body = world.createBody(bdef);
FixtureDef fdef = new FixtureDef();
CircleShape shape = new CircleShape();
shape.setRadius(10 / Demo.PPM);
fdef.shape = shape;
b2body.createFixture(fdef);
}
Näed, et igal pool on arvulised väärtused jagatud muutujaga PPM
.
PPM
ehk pixels per meter
on konstant, mille võiksid defineerida oma mängu
põhiklassis või eraldi konstantidele pühendatud klassis. (väärtus võiks olla nt 100).
Kuna Box2D engine kasutab meetreid, siis peame skaleerima oma mängu kasutama väiksemaid suurusi, vastasel juhul liiguvad objektid väga aeglaselt.
Näiteks oleks meie b2body
raadius ilma PPMita
10 meetrit.
9. Nüüd navigeeri uuesti GameScreen
klassi ja jaga seal ka läbi PPMiga
järgnevad väärtused:
this.viewport = new FitViewport(Gdx.graphics.getWidth() / Demo.PPM,
Gdx.graphics.getHeight()/ Demo.PPM, camera);
this.renderer = new OrthogonalTiledMapRenderer(map, 1 / Demo.PPM);
this.bdef.position.set((rectangle.getX() + rectangle.getWidth() / 2) /
Demo.PPM, (rectangle.getY() + rectangle.getHeight() / 2) / Demo.PPM);
this.shape.setAsBox(rectangle.getWidth() / 2 / Demo.PPM,
this.rectangle.getHeight() / 2 / Demo.PPM);
Kui sul seda veel pole, siis võiksid luua mängijale liikumisloogika.
Kui sul see juba on, siis võiksid seda muuta nii, et liigutad b2body
,
rakendades sellele applyLinearImpulse
meetodit.
playerBody.applyLinearImpulse(impulse, playerBody.getWorldCenter(), true);
applyLinearImpulse(impulse, point, wake)
– rakendab liikumisjõu kehale (b2body):
- impulse – määrab jõu vektori (kiirus ja suund), mida rakendatakse kehasse.
- point – punkt, kus jõud rakendatakse (siin on kasutatud keha keskmist punkti playerBody.getWorldCenter()).
- wake – määrab, kas keha peaks pärast jõu rakendamist olema ärkvel (kas keha hakkab liikumiseks arvutama järgmise sammu või mitte).
10. Nüüd peame lisama update()
meetodisse:
this.player.b2body.setLinearVelocity(Vector2.Zero);
sest vastasel juhul hõljub mängija keha mööda maailma ringi ning ta reageerib kasutaja sisendile vaevumärgatavalt.
11. Samuti lisame rea:
this.world.step(1/60f, 6, 2);
See määrab kui kiiresti Su mängus igasuguseid kokkupõrkeid jms handlitakse. Selle väärtusi pole otseselt muuta vaja.
Nüüd jooksuta jälle oma mängu ning:
Peaksid kaardil nägema ringi, millega saad ringi liikuda.
Ring peaks
collide'ima
objektidega, mille enne genereerisime.
