Korjatavate asjade kaardile lisamine ja integreerimine¶
Korjatavad asjad on paljudes mängudes mingi asja eelduseks (näiteks paremate punktide saamiseks) või vajalik mängu võitmiseks.
Siin vaatamegi, kuidas lisada korjatavad asjad kaardile, kuidas mängija saaks neid üles korjata ja samal ajal oleks kõik ka serveriga sünkroonis.
Server¶
1. Esiteks me peaksime serveris hoidma mingit listi kõigist kaardil olevatest korjatavatest asjadest.
Iga korjatav asi võiks olla ka eraldi klass, sest nii on lihtsam seda hallata.
2. Teiseks peaks igal korjataval asjal olema mingi unikaalne id.
Nii kui me uue korjatava asja klassi loome, lisatakse eelmisele unikaalsele id-le +1.
Samuti, kui me kasutame erinevaid korjatavaid asju (näiteks meil on viite erinevat tüüpi relvasid), siis peaks olema ka igal korjataval asjal identifikaator selle jaoks, mis tüüpi see asi on.
public class DroppedItem {
public static int nextId;
private static int getAndIncrementNextId() {
return ++nextId;
}
public enum ItemType {
AR1,
AR2,
PISTOL,
SHOTGUN,
SNIPER,
GRENADE_LAUNCHER
}
private final int id;
private ItemType itemType;
private boolean isTaken = false;
private float x;
private float y;
public DroppedItem(ItemType itemType, float x, float y) {
this.id = getAndIncrementNextId();
this.itemType = itemType;
this.x = x;
this.y = y;
}
public float getY() {
return y;
}
public float getX() {
return x;
}
public int getId() {
return id;
}
public int getItemType() {
return itemType;
}
public void take() {
isTaken = true;
}
public boolean isTaken() {
return isTaken;
}
}
Näiteks selline klass:
Anname kaasa
ItemType
enumi
, mis identifitseerib, millise korjatava asjaga on tegemist.x
jay
koordinaadid, ehk need koordinaadid, kus see korjatav asi parajasti kaardil asetseb.
3. Järgnevalt võiksime kuhugi klassi, kus meil serveris mängu loogika toimub, lisada listi DroppedItem
klassidest.
public ArrayList<DroppedItem> droppedItems = new ArrayList<DroppedItem>();
4. Nüüd peaksime need korjatavad asjad kuidagi ka genereerima.
Selle jaoks võiks olla eraldi meetod seal klassis kus toimub mängu loogika:
private void generateDroppedItems() {
DroppedItem.ItemType[] items = DroppedItem.ItemType.values();
Random rand = new Random();
for (int i = 0; i < 20; i++) {
int randomIndex = rand.nextInt(items.length);
DroppedItem.ItemType randomItem = items[randomIndex];
int randomX = rand.nextInt(mapWidth);
int randomY = rand.nextInt(mapHeight);
droppedItems.add(new DroppedItem(randomItem, randomX, randomY));
}
}
5. Nüüd peaksime need asjad kuidagi ka kõikidele mängus olevatele mängijatele edasi saatma.
Selleks peaksime looma klassid, mida saaks Kryoneti abiga klientidele saata.
Näiteks loome kaks eraldi klassi, kus lisame funktsiooni OnSendDroppedItems
, et saada
massiiv kõigist hetkel kaardil olevatest korjatavatest asjades ja
registreerime need Kryoneti.
kryo.register(droppedItem.class);
kryo.register(OnSendDroppedItems.class);
kryo.register(java.util.ArrayList.class);
public static class DroppedItem {
public int id;
public float x;
public float y;
public int itemType;
}
public static class OnSendDroppedItems {
ArrayList<DroppedItem> items;
}
Ja saatmine võiks toimuda näiteks siis, kui mäng algab (saadame selle igale kliendile):
Network.OnSendDroppedItems sendDrops = new Network.OnSendDroppedItems();
sendDrops.items = droppedItems;
player.sendTCP(sendDrops);
Klient¶
Vaatame nüüd, kuidas kliendis:
Seda infot vastu võtta.
Infot kaardile renderdada.
Teeme ka selle valmis, et kui üks mängijatest mingi asja üles korjab, siis saadetakse ka see kõigile mängijatele edasi.
6. Lisame kliendi poolele (näiteks klassi, kus teie mängu loogika toimub) listi:
public ArrayList<DroppedItem> weapons = new ArrayList<>();
7. Edasi loome DroppedItem
klassi:
public class DroppedItem extends Sprite{
private int gunType = 0;
public static final String[] weaponSpriteList = {"Assault-rifle-1.png", "Assault-rifle-2.png", "Pistol-1.png", "Shotgun-1.png", "Sniper-rifle-3-scoped.png", "grenade-launcher-grey.png"};
private enum Types {
AR1, AR2, PISTOL, SHOTGUN, SNIPER, GRENADE_LAUNCHER
}
private final float x;
private final float y;
public final int id;
private boolean isUsed = false;
private Sprite sprite;
public DroppedItem(int gunType, float x, float y, int id) {
super(new Sprite(new Texture(Weapon.weaponSpriteList[gunType])));
this.gunType = gunType;
this.x = x;
this.y = y;
this.id = id;
}
public boolean isWeaponBeingUsed() {
return isUsed;
}
public void useWeapon() {
isUsed = true;
}
public int getGunType() {
return this.gunType;
}
public void setWeaponFree() {
isUsed = false;
}
public void update(float delta) {
setX(this.x);
setY(this.y);
}
public void draw(Batch batch) {
this.update(Gdx.graphics.getDeltaTime());
super.draw(batch);
}
}
8. Võtame serverilt saadud maha visatud asjade klassi paketi kliendis vastu:
if (object instanceof Network.OnSendDroppedItems) {
final Network.OnSendDroppedItems mov = (Network.OnSendDroppedItems) object;
Gdx.app.postRunnable(new Runnable() {
@Override
public void run() {
play.addDroppedItems(mov.items);
}
});
return;
}
9. Ja kutsume välja meetodi, kus lisame need kõik kliendis maha visatud asjade listi:
public void addDroppedItem (ArrayList<Network.droppedItem> items) {
for(Network.droppedItem item : items) {
weapons.add(new DroppedItem(item.gunType, item.x, item.y, item.id));
}
}
10. Nüüd saame kirjutada meie mängu render
loogika meetodisse selle,
et rendertataks ka kõik maha visatud asjad:
private void renderWeapons() {
for (DroppedItem weapon: weapons) {
weapon.setPosition(weapon.getX(), weapon.getY());
weapon.draw(renderer.getBatch());
}
}
11. Edasi peaksime tegema selle, et kui mängija soovib maha visatud asju üles korjata.
Selleks võiks meil olla eraldi meetod render
meetodis.
Teeme nii, et kui mängija vajutab maha
visatud asja peal olles klahvi E
, siis see ese korjatakse üles ja selle
kohta saadetakse serverile teave:
private void checkWeaponPickUp(float playerLeftX, float playerRightX, float playerTopY, float playerBottomY) {
for (DroppedItem weapon : weapons) {
float weaponCenterX = weapon.getX() + 32;
float weaponCenterY = weapon.getY() + 32;
if (weaponCenterX < playerRightX + 20 && weaponCenterX > playerLeftX - 20 && weaponCenterY > playerBottomY + 5 && weaponCenterY < playerTopY + 20) {
if (Gdx.input.isKeyJustPressed(Input.Keys.E)) {
if (this.player.inventory.size() < 2) {
sendTake(weapon);
break;
}
}
}
}
}
Mängijal peaks olema mingisugune inventory
.
Eelmises näites saab mängijal inventorys
korraga olla vaid 2 asja.
12. Lisame Player
klassi inventory
, kus on salvestatud üles korjatud asjade item id
-d listis:
public ArrayList<Integer> inventory = new ArrayList<>();
Server-klient suhe¶
13. Realiseerime meetodi sendTake
ja paketi OnTake
:
public void sendTake(DroppedItem weapon) {
Network.OnTake takeAction = new Network.OnTake();
takeAction.id = weapon.id;
online.sendObj(takeAction);
}
public static class OnTake {
public int id;
}
Saadame OnTake
paketi serverisse.
14. Nüüd võtame selle serveris vastu ning:
Saadame kõikidele mängijatele, et selle
id
-ga asi korjati üles.Saadame ka sellele mängijale, kes üles korjas, et
inventory
-sse lisatakse see asi.
Serveri poolel:
if (object instanceof Network.OnTake) {
Network.OnTake onTake = (Network.OnTake) object;
sendEveryone(onTake, player.getID(), gameId);
Network.OnInventoryAdd invAdd = new Network.OnInventoryAdd();
invAdd.itemId = game.droppedItems.get(onTake.id).getItemId();
invAdd.count = 1;
player.sendTCP(invAdd);
}
public static class OnInventoryAdd {
public int itemId;
public int count;
}
15. Saadame sama paketi OnTake
kõigile klientidele edasi. Lisame ka
paketi OnInventoryAdd
, et saaksime lisada selle asja inventorysse
sellele, kes saatis:
if (object instanceof Network.OnTake) {
final Network.OnTake mov = (Network.OnTake) object;
Gdx.app.postRunnable(new Runnable() {
@Override
public void run() {
play.removeDroppedItem(mov.id);
}
});
return;
}
public void removeDroppedItem(int id) {
for(DroppedItem weapon : weapons) {
if (weapon.id == id) {
weapons.remove(weapon);
break;
}
}
}
if (object instanceof Network.OnInventoryAdd) {
final Network.OnInventoryAdd mov = (Network.OnInventoryAdd) object;
Gdx.app.postRunnable(new Runnable() {
@Override
public void run() {
play.player.addInventoryItem(mov.itemId, mov.count);
}
});
return;
}
public void addInventoryItem(int itemId, int count) {
if (this.inventory.size() < 2) {
this.inventory.add(itemId);
}
}
Mõistagi tuleks lisada mitmed checkid, kas mängija üldse saab seda üles korjata (kas ta on sellele piisavalt lähedal, ei kasuta mingit cheati selle korjamise/saamise jaoks).
Mängija inventory
-t võiks hoida pigem serveris aga see ei ole selle kursuse raames pigem probleemiks.