Sissejuhatus

Saate järgida neid samme koos selle videoga (igal sammul on ka timestamp).

1. Looge uus projekt GitLabis ja kloonige see IntelliJ-sse.

  • Projekti loomisel looge kaust client (game kaust videos) ja pärast LibGDX seadistamist looge server moodul.

Klient: tegeleb kasutajaliidese ja mänguloogikaga, see osa mängust töötab mängija seadmel.

Server: haldab mitme kliendi vahelist suhtlust, võimaldades mitmikmängu suhtlust.

Klient suhtleb serveriga, et vahetada teavet mängumaailma ja mängija tegevuste kohta.

Alustamine LibGDX-iga

(timestamp) Alustamine LibGDX-iga:

2. Looge LibGDX-projekt, luues IntelliJ mänguprojektis kausta libs.

  • Laadige alla LibGDX Project Setup Tool.

  • Avage IntelliJ projektis terminal.

  • Navigeerige kausta libs (kaust, kuhu installisite .jar faili) (käsu cd libs abil).

  • Kasutage java -jar gdx-liftoff-x.x.x.x.jar, et avada Project Setup.
    • "x"-ide asemele pange versioon failist, mis alla tõmbasite (näiteks gdx-liftoff-1.13.1.1.jar).

3. Konfigureerige oma projekti üksikasjad seadistusaknas:

  • Project name: Nimetage projekt vastavalt oma mängule.

  • Package name: Paketi nimeks valige ee.taltech.[oma mängu nimi].

  • Game class: Nimetage oma mänguklass vastavalt oma mängule.

  • Output folder: Väljundkausta sisestage oma client kausta tee.

  • Supported Platforms: Valige Desktop toetatud platvormiks.

  • Official Extensions: Valige mõni kasulik ametlik laiendus (nt Bullet, box2d, AI).

LibGDX-is on laiendused kas teegid või moodulid, mille abil saab lisada erinevaid lisafunktsioone.

  • Bullet: Pakub täiustatud 3D-füüsika- ja simulatsioonivõimalusi (collision detection, soft body dynamics, rigid body dynamics jne).

  • box2d: Sarnane Bulletile, aga 2D.

  • AI: Lihtsustab AI programmeerimist. Vajalik, kui tahta oma mängu lisada intelligentseid NPC-d (enemies, allies jne).

4. Pärast genereerimist avage client kaust uues IntelliJ aknas.

  • Projekti ehitamiseks kasutage: Tasks > build > build

  • Mängu käivitamiseks kasutage: Tasks > other > run

Serveri seadistamine

(timestamp) Serveri seadistamine:

5. Loo server moodul Gradle ehitussüsteemi jaoks ja kasuta Gradle DSL-ina Groovy't.
  • (New -> Module)

  • vali Build system alt Gradle

  • Gradle DSL alt vali Groovy.

  • Ava server moodul uues IntelliJ aknas.

6. Ava server moodul uues IntelliJ aknas.

  • Projekti ehitamiseks kasutage Tasks > build > build

  • Serveri käivitamiseks kasutage Tasks > other > run

Gradle: Automatiseerib sõltuvuste (dependencies) allalaadimist, uuendamist, leidmist.

Groovy: Gradle build scripts kirjutamiseks.

Serveri loogika rakendamine Kryonet’iga

(timestamp) Serveri loogika rakendamine Kryonet'iga:

7. Loo server klass, lisades:

  • Uus directory src\main\java,

  • Luues package ee.taltech.game.server,

  • Lisades uue Java klassi serveri loogika jaoks (nt GameServer).

Külastage Kryoneti GitHubi lehte rakenduse üksikasjade kohta.

Vaadake Kryonet’i kasutamise näiteid.

Lisa Kryonet’i dependency Gradle’ile

(timestamp) Lisa Kryonet’i dependency Gradle’ile:

8. Ava build.gradle fail ja navigeeri Kryoneti Maveni Repository, et leida sobiv Gradle’i dependency teave.

  • Lisa Kryonet’i dependency oma projekti, muutes build.gradle faili.

  • Ehita projekt lisatud sõltuvusega kasutades Tasks > build > build

Kryonet’i rakendamine Serveri klassis

(timestamp) Kryonet’i rakendamine Serveri klassis:

9. Impordi com.esotericsoftware.kryonet.Server oma serveri klassi.

10. Lisage oma serveri klassi Server instance:

  • private Server server = new Server();

11. Konstruktoris initsialiseerige server ja käsitlege vigu järgmise koodiga:

server = new Server();
server.start();
try {
    server.bind(8080, 8081);
} catch (IOException e) {
    throw new RuntimeException(e);
}
  • server.start() algatab serveri ja valmistab selle ette ühenduste käsitlemiseks.

  • server.bind(8080, 8081) seob serveri TCP pordile 8080 ja UDP pordile 8081, try-catch plokk aitab vigu käsitleda.

12. Lisage oma serveri klassi põhimeetod vastavalt oma serveri klassi nimele:

public static void main(String[] args) { GameServer gameServer = new GameServer(); }

(timestamp) 13. Järgmine asi, mida lisada oma konstruktorisse, on Listener.

  • Kryonet'i listener: vastutab erinevate sündmuste ja sõnumite käsitlemise eest Kryonet’i serveri käivitamise ajal. Kood, mis lisab listener, et printida vastuvõetud objekte, on järgmine:

server.addListener(new Listener() {
    public void received(Connection connection, Object object) {
        System.out.print(object);
    }
});

Klient

(timestamp) Klient:

LibGDX on genereerinud meie projekti kliendi osa, mis koosneb kahest moodulist:

  • Desktop moodul võimaldab lihtsat viisi mängu testida ja arendada töölauakeskkonnas.

  • Core moodul on koht, kuhu saate panna ühise koodi, mis hõlmab tavaliselt mängu loogikat või ressursside laadimist.

Vaadake näidisklienti GitHubis.

Kryonet’i seadistamine kliendi jaoks

(timestamp) Kryonet’i seadistamine kliendi jaoks:

14. Peate lisama Kryonet’i implementation Gradle’i dependency ka kliendi jaoks:

  • Navigeeri client -> core -> build.gradle

  • Navigeeri Kryoneti Maveni Repository, et leida sobiv Gradle’i dependency teave.

  • Ehita projekt lisatud sõltuvusega kasutades Tasks > build > build

Kliendi mänguklass

15. Nüüd avage oma kliendi mänguklass (client -> core -> src).

LibGDX genereeris mõned meetodid mängu loomiseks:

  • create meetodit kutsutakse üks kord, kui rakendus luuakse esmakordselt. Siin initsialiseeritakse tavaliselt ressursid, seatakse mängumaailm ja tehakse muud seadistamise ülesanded. render meetod on koht, kus värskendatakse mänguloogikat, käsitsetakse sisendit ja joonistatakse graafikat.

  • dispose meetod kutsutakse rakenduse sulgemisel või hävitamisel. Siin tuleks vabastada kõik ressursid, näiteks tekstuurid, helid või muud varad, et vabastada mälu ja vältida ressursilekkeid.

Kliendi connection serveriga

(timestamp) Kliendi connection serveriga:

16. Impordi Kryonet oma kliendi mänguklassi:

  • import com.esotericsoftware.kryonet.Client;

17. Loo create meetodis Kryonet’i klient:

client = new Client();
client.start();

Sõnumi saatmiseks serverile mängu alguses kasutage create meetodis:

  • client.sendTCP("Start");

selleks, et saata sõna “Start” serverile.

Testige render meetodit, kirjutades sinna:

  • client.sendTCP("test");

18. Connect serveriga järgmise koodiga create meetodis:

try {
    client.connect(5000, "localhost", 8080, 8081);
} catch (IOException e) {
    throw new RuntimeException(e);
}

kus:

  • 5000 on aegumisaeg, mille jooksul klient ootab connectioni loomist.

  • Localhost on serveri hostinimi või IP-aadress.

  • 8080 ja 8081 on TCP ja UDP portide numbrid, millele klient connectib.

19. Pärast sessiooni lõppu sulgege klient dispose meetodis:

client.close();
try {
    client.dispose();
} catch (IOException e) {
    throw new RuntimeException(e);
}

20. Käivitage oma server (kui pole veel käivitatud) ja käivitage oma klient connectioni testimiseks.

  • Peaksite nägema pidevat sõnumit “test”.

Liikumise lisamine kliendile

(timestamp) Liikumise lisamine kliendile:

21. Nüüd lisame näiteks juba genereeritud LibGDX-assetile liikumise:

Render meetodis kasutatakse sprite’i renderdamiseks SpriteBatchi.

  • begin() ja end() meetodid määratlevad partii ulatuse.

  • draw() meetodit kasutatakse iga individuaalse sprite’i jaoks.

Näeme, et batch.draw(img, 0, 0);:

  • Esimene 0 on img sprite’i x-koordinaat ja teine 0 on y-koordinaat.

Sprite’i liigutamiseks peame olema võimelised muutma neid koordinaate.

22. Lisage klassile privaatsed int muutujad:

  • private int x = 0, y = 0;

23. Render meetodis pange batch.draw(img, 0, 0); asemele batch.draw(img, x, y);

Liikumise käsitlemine

(timestamp) Liikumise käsitlemine:

24. Render meetodisse lisage järgmine kood, et muuta liikumist vastavalt nupulevajutusele:

if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) {
    x -= 10;
}
if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {
    x += 10;
}

Nüüd muutub x-koordinaat. Saate klientmängu käivitada selle testimiseks.

Nüüd laseme mängijate koordinaadid serverile saata.

25. Rakendage meetod mängija koordinaatide saatmiseks serverile:

  • private void sendPositionInfoServer() { client.sendUDP(x + " " + y);}

26. Kutsuge seda iga kord pärast koordinaatide muutmist, nii et see oleks näiteks:

if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) {
    x -= 10;
    sendPositionInfoServer();
}
if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {
    x += 10;
    sendPositionInfoServer();
}

ja nüüd iga kord, kui liigute, saadab see teie koordinaatide teabe serverile.

N-mängijate connection

(timestamp) N-mängijate connection:

27. Mitmete eksemplaride lubamine:

  • Mine Run / Debug Configurations

  • Vali sealt Edit Configurations...

  • Modify options alt vali Allow multiple instances

Mitmete eksemplaride lubamine (Allow multiple instances) tähendab seda, et saate oma arvutis panna käima mitu mängu korraga. Ilma selleta saate jooksutada vaid ühte korraga.

(timestamp) Serveri listeneri korral kasutage,

  • server.sendToAllUDP(object);

et saata teavet kõigile klientidele.

(timestamp) 28. Kliendi create meetodisse lisage listener, et vastu võtta andmeid serverilt:

client.addListener(new Listener.ThreadedListener(new Listener() {
    @Override
    public void received(Connection connection, Object object) {
        System.out.print("received: " + object);
    }
}));

Nüüd saadab server teavet igale connected kliendile.

(timestamp) Klientide eristamiseks kasutage serveris connectionide ID-sid ja haldage mänguobjekte ID-de ja koordinaatidega.

28. Serveri klassi lisage,

  • private Map<Integer, String> gameObjects = new HashMap<>();

et jälgida mänguobjekte.

29. Nüüd lisage see kood serveri listeneri received meetodi sisse:

if (object instanceof String) {
    gameObjects.put(connection.getID(), (String) object);
}
server.sendToAllUDP(gameObjects.entrySet().stream().map(entry -> entry.getKey() + ":" + entry.getValue()).collect(Collectors.joining("|")));

See kood saadab iga connected kliendi ID ja koordinaadid kõigile klientidele.

Teiste klientide nähtavaks muutmine

(timestamp) Teiste klientide nähtavaks muutmine:

Teise näitena teiste klientide nähtavaks muutmiseks kasutame LibGDX-i vara “drop.png”.

30. Kliendi klassi:

  • Lisa Texture opponentImg;

  • Initsialiseeri tekstuur create meetodis: opponentImg = new Texture("drop.png");

Disconnectioni käsitlemine

(timestamp) Disconnectioni käsitlemine:

31. Serveri listeneri korral ülekirjutage disconnected meetod ja eemaldage connected mängijad:

@Override
public void disconnected(Connection connection) {
    gameObjects.remove(connection.getID());
}

32. Muutke serveri edastusjoont serveri listeneri korral:

  • server.sendToAllUDP(gameObjects.entrySet().stream().map(entry -> entry.getKey() + ":" + entry.getValue()).collect(Collectors.joining("|")));

saab

  • server.sendToAllUDP(gameObjects);

Selgitus: Algne koodirida muudab enne saatmist andmed string formaati, et neid oleks lihtsam lugeda.

Uus koodirida saadab koju objekti ilma töötlemiseta, mis võimaldab paindlikumalt kasutada edastatud andmeid kliendi poolel.

Saadetud mänguobjektide käsitlemine kliendil

(timestamp) Saadetud mänguobjektide käsitlemine kliendil:

33. Loo vastaste SpriteBatchide jaoks kaardistus:

  • private Map<Integer, SpriteBatch> opponentBatches = new HashMap<>();

34. Lisage kaart vastu võetud mänguobjektide salvestamiseks kliendile:

  • private Map<Integer, String> receivedGameObjects = new HashMap<>();

35. Render meetodis käitle vastaste renderdamist järgmise koodiga:

for (Map.Entry<Integer, String> entry : receivedGameObjects.entrySet()) {
    int id = entry.getKey();
    if (id == clientId) continue;

    if (!opponentBatches.containsKey(id)) {
        opponentBatches.put(id, new SpriteBatch());
    }

    SpriteBatch b = opponentBatches.get(id);
    String[] coordinates = entry.getValue().split(",");
    b.begin();
    b.draw(opponentImg, Integer.parseInt(coordinates[0]), Integer.parseInt(coordinates[1]));
    b.end();
}

if (receivedGameObjects != null) {
    receivedGameObjects = null;
}

36. Kliendi klassis, lisage muutuja klient ID salvestamiseks:

  • private int clientId;

37. Määrake kliendi ID create meetodis:

  • clientId = client.getID();

38. Kliendi listeneri received meetodis salvestage saadud andmed receivedGameObjects:

if (object instanceof Map) {
    receivedGameObjects = (Map<Integer, String>) object;
}

Kryo registreerimine

(timestamp) Kryo registreerimine:

Veenduge, et Kryo registreerimine oleks nii serveris kui ka kliendis.

See tuleks teha võrguklassis nii serveris kui ka kliendis (näide), kuid me teeme selle põhiklassis näitena.

NB! Palun kasutage oma projektis selleks võrguklassi.

Kryo registreerimist kasutatakse kõigi klasside jaoks, kuna see teavitab Kryo serialiseerimise raamistikku klassidest, mis võivad võrgu kaudu saata.

39. Loo meetodis create HashMap’i registreerimiseks:

Kryo kryo = client.getKryo();
kryo.register(HashMap.class);

40. Serveri konstruktoris registreerige HashMap:

Kryo kryo = server.getKryo();
kryo.register(HashMap.class);

Lõplik kaustade/failide hierarhia

Järgmine osa videost käsitleb serveri JAR-faili loomist ja selle üleslaadimist TalTech’i serverisse. Te ei pea seda hetkel tegema, kuna see toimub semestri teises pooles.