Bou 'n Peer-to-Peer Multiplayer Networked Game
Speel multispeler speletjies is altyd pret. In plaas daarvan om AI-beheerde teenstanders te klop, moet die speler strategieë ondervind wat deur 'n ander mens geskep word. Hierdie handleiding bied die implementering van 'n multiplayer-speletjie wat oor die netwerk gespeel word deur 'n nie-gesaghebbende peer-to-peer (P2P) benadering.
Translation service is temporarily unavailable Jy moet 'n basiese begrip hê van netwerkkommunikasie.
U kan die finale kode aflaai van die GitHub-repo of die zip-bron lêers.
Voorskou van Finale Uitslae
Netwerk demo. Kontrole: arrow of WASD om skuif, Space om skiet, B of bomme versprei.
Kuns van Remastered Tyrian Graphics, Iron Plague en Hard Vacuum deur Daniel Cook (Lost Garden).
Multiplayer-speletjies wat oor die netwerk gespeel word, kan geïmplementeer word deur verskeie verskillende benaderings, wat in twee groepe ingedeel kan word: gesaghebbend en onwettig.
In 'n gesaghebbende groep is die algemeenste benadering die kliënt-bediener-argitektuur, waar die sentrale entiteit (gesaghebbende bediener) die hele spel beheer. Elke kliënt wat aan die bediener koppel, ontvang voortdurend data, plaaslik geskepde voorstellings van spelstatus. Dis soos om TV te kyk.
Magtige implementering gebruik kliënt-bediener argitektuur.
As die kliënt 'n aksie uitvoer, soos om van een punt na 'n ander te beweeg, word die inligting na die bediener gestuur. Die bediener kontroleer of die inligting korrek is en dateer dan die status van die spel op. Afterwardsusebarkan-inligting kan volgens hul spelstatus opdateer.
In 'n nie-gesaghebbende groep, daar is geen sentrale entiteit nie en elke vennoot (spel) beheer die status van die spel. In die peer-to-peer (P2P) -benadering stuur eweknieë data aan alle ander eweknieë en ontvang data van hulle, met dien verstande dat die inligting betroubaar en korrek is (vry van bedrog):
Nie-gesaghebbende implementering met behulp van P2P-argitektuur.
In hierdie handleiding bied ek 'n multiplayer-speletjie-implementering aan wat oor 'n netwerk gespeel word deur 'n nie-gesaghebbende P2P benadering. Hierdie wedstryd is 'n deathmatch arena waar elke speler 'n skip beheer wat bomme kan afvuur en laat val.
Ek sal fokus op die kommunikasie en sinkronisering van eweknie lande. Speletjies en netwerkkodes word soveel as moontlik opgesom om dit eenvoudig te maak.
Wenk: 'n gesaghebbende benadering is veiliger teen bedrog, want die bediener beheer die status van die spel heeltemal en kan enige verdagte boodskappe ignoreer, soos die entiteit wat sê dat dit 200 pixels beweeg as slegs 10 beweeg.
Definisie van Nie-Gesaghebbende Speletjie
'N Ongemagtigde multiplayer-speletjie het nie 'n sentrale entiteit om die status van die spel te beheer nie. Elke eweknie moet dus die status van sy of haar eie spel beheer, enige veranderinge en belangrike aksies aan ander kommunikeer. As gevolg hiervan sien spelers twee scenario's gelyktydig: die skip beweeg volgens die insette en simuleer alle ander vaartuie wat deur die teenstander beheer word:
Speler se skip word plaaslik beheer. Skip teenstanders word gesimuleer gebaseer op netwerk kommunikasie.
Beweging en optrede van die spelers skip word gelei deur plaaslike insette, sodat die speler spel status is onmiddellik opgedateer. Vir die beweging van alle ander vaartuie, moet spelers netwerkboodskappe van elke teenstander ontvang waar hulle weet waar hul skip beweeg.
Die boodskappe neem tyd om deur die netwerk van een rekenaar na 'n ander te reis, so wanneer die speler inligting ontvang wat sê dat die opponerende skip daar in (x, y), dit mag nie meer bestaan ​​nie - daarom word dit simulasie genoem:
Kommunikasievertragings wat deur die netwerk veroorsaak word.
Om die simulasie akkuraat te hou, is elke vennoot verantwoordelik vir die verspreiding van slegs inligting oor sy skip, nie ander nie. Dit is, as die spel vier spelers het- vertel A, B, C en D- speler A is die enigste een wat kan weet waar die skip is A, indien blootgestel, as 'n koeël geskiet word of 'n bom laat val, ensovoorts. Alle ander spelers sal 'n boodskap van A ontvang wat oor sy optrede inlig en hulle sal reageer, so as A's koeëls het skip C's, dan C sal 'n boodskap uitstuur wat vertel dat dit vernietig is.
As gevolg hiervan sal elke speler alle ander skepe (en hul aksies) sien volgens die ontvangde boodskappe. In 'n perfekte wêreld sal daar geen netwerkvertraging wees nie, so boodskappe sal onmiddellik gaan en simulasies sal baie presies wees.
Soos die latensie egter toeneem, word die simulasie onakkuraat. Byvoorbeeld, speler A skiet en sien Bullets hit ship B, maar niks het gebeur nie, dit is omdat sien A oor B vertraag word weens netwerkonderbrekings. Wanneer B eintlik A se koeëlboodskap ontvang het, was B in 'n ander posisie, so geen treffer is gepropageer nie.
Mapping Relevante Aksies
'N belangrike stap in die implementering van die spel en maak seker dat elke speler die presiese simulasie behoorlik kan sien sodat die identifikasie van die aksie gepas is. Die aksie het die huidige status van die spel verander, soos om van een punt na 'n ander te beweeg, 'n bom te laat val, ens.
In ons spel, is die belangrike aksies:
skiet (speler se skip het 'n koeël of 'n bom geskiet)
beweeg (speler se skip beweeg)
sterf (speler se skip is vernietig)
Aksie spelers tydens die spel.
Elke aksie moet oor die netwerk gestuur word. Dit is dus belangrik om 'n balans te vind tussen die aantal aksies en die grootte van die netwerkboodskap wat hulle sal kry. Hoe groter die boodskap (dws hoe meer data dit bevat), hoe langer neem dit om te vervoer, aangesien dit dalk meer as een netwerkpakket benodig.
Kort boodskappe vereis minder CPU tyd om te pak, stuur en uitpak. Klein netwerk boodskappe genereer ook meer boodskappe wat gelyktydig gestuur word, wat die deurvoer verhoog.
Sodra die betrokke aksie gekarteer is, is dit tyd om te produseer sonder gebruikerinvoer. Alhoewel dit 'n goeie sagteware-ingenieursbeginsel is, mag dit nie uit die oogpunt van multiplayer-speletjies duidelik wees nie.
As jy 'n voorbeeld van skiet van ons spel gebruik, as dit sterk verband hou met insetlogika, kan jy nie dieselfde skietkode gebruik in verskillende situasies nie:
Neem aksie onafhanklik.
As die skietkode byvoorbeeld geskei word van die insetlogika, is dit moontlik om dieselfde kode te gebruik om die speler se koeël en die teenstander se koeël te skiet (wanneer die netwerkboodskap aankom). Dit is om kodeherhaling te voorkom sodat hoofpyne nie gesny word nie.
Skip klasse in ons speletjies, byvoorbeeld, het nie multi-speler kode; dit is ten volle geskei. Dit beskryf 'n skip, of dit nou plaaslik is of nie. Die klas het egter verskeie metodes om die skip te manipuleer, soos rotate () en 'n setter om sy posisie te verander. As gevolg daarvan kan multiplayer-kodes die skip op dieselfde manier draai as wat die gebruikersinvoerkode doen. Die verskil is een is gebaseer op plaaslike insette, terwyl die ander gebaseer is op 'n netwerkboodskap.
Uitruil Data per Aksie
Nou dat alle relevante aksies gekarteer word, is dit tyd om boodskappe tussen vriende uit te ruil om 'n simulasie te skep. Voordat u enige data uitruil, moet die kommunikasieprotokol geformuleer word. Wat multiplayer-speletjie kommunikasie betref, kan protokolle gedefinieer word as 'n stel reëls wat beskryf hoe die boodskap gestruktureer is, sodat almal die boodskappe kan stuur, lees en verstaan.
Boodskappe wat in die spel verruil word, sal beskryf word as voorwerpe wat almal die sogenaamde verpligte eiendom bevat op (bedryfskode) Die op is gebruik om die boodskaptipe te identifiseer en die eienskappe van die boodskapvoorwerp aan te dui. Dit is die struktuur van alle boodskappe:
netwerk boodskap struktuur.
Die OP_DIE boodskap meld dat 'n skip vernietig is. Die x en y-eienskappe bevat die skip se ligging wanneer dit vernietig word.
Die OP_POSITION boodskap bevat die huidige ligging van 'n eweknie se skip. Die x en yeienskappe bevat skipkoördinate op die skerm, terwyl die hoek die huidige rotasiehoek van die vaartuig is.
Die OP_SHOT boodskap bepaal dat 'n skip iets skiet ('n koeël of 'n bom). Eiendom x en y bevat die plek van die skip wanneer dit afgedank word; eienskappe dx en dy dui die rigting van die vaartuig aan, om te verseker dat die koeël op alle eweknieë herhaal word met dieselfde hoek wat deur die skietkuns gebruik word wanneer dit geskiet word; en eiendom b definieer die tipe projektiel (koeël of bom).
Om die multiplayer kode te organiseer, skep ons 'n Multiplayer klas. Dit is verantwoordelik vir die stuur en ontvang van boodskappe, sowel as die opdatering van plaaslike skepe volgens ontvangde boodskappe om die huidige stand van die spel simulasie te weerspieël.
Die aanvanklike struktuur, wat slegs die boodskapkode bevat, is:
Stuur 'n Aksieboodskap
Vir enige voorheen gekarteerde relevante aksies moet netwerkboodskappe gestuur word, sodat alle kollegas van die aksie in kennis gestel sal word.
Aksie OP_DIE moet gestuur word wanneer die speler deur 'n koeël of 'n bomontploffing geraak word. Daar is reeds 'n metode in die spelkode wat die speler se skip vernietig wanneer dit getref word, so opgedateer om die inligting te versprei:
'N OP_POSITION aksie moet gestuur word elke keer as die speler sy huidige posisie verander Die multiplayer kode word ingespuit in die spelkode om daardie inligting te versprei, ook:
Ten slotte moet die optrede van OP_SHOT gestuur word elke keer as die speler iets vreet. Die boodskap wat gestuur word, bevat die tipe bullet wat afgevuur is, so elke eweknie sal die korrekte projektiel sien:
Sinkronisering Gebaseer op Geaccepteerde Data
Op hierdie punt, kan elke speler beheer en sien hul skip. Onder die kap, word die netwerk boodskappe gestuur op grond van relevante aksies. Die enigste ontbrekende deel is die toevoeging van die teenstander, sodat elke speler die ander skepe kan sien en met hulle kan kommunikeer.
In hierdie wedstryd word die skip gereël as 'n skikking. Die skikking het tot nou toe net een skip (speler). Om 'n simulasie vir alle ander spelers te skep, die Multiplayer klas sal verander word om nuwe skepe by die skikking by te voeg elke keer as 'n nuwe speler by die arena aansluit:
Boodskapuitruilingskodes verskaf outomaties unieke identifiseerders vir elke speler (user.id in die kode hierbo). Die identifikasie word gebruik deur multiplayer kode om 'n nuwe skip te skep wanneer 'n speler by die arena aansluit. So het elke skip 'n unieke identifikasie. Deur die outeur identifiseerder van elke ontvangde boodskap te gebruik, is dit moontlik om die skip in die skip se volgorde te vind.
Ten slotte, is dit tyd om die handgreepGetObject () by die Multiplayer klas by te voeg. Hierdie metode word genoem elke keer as 'n nuwe boodskap kom:
Wanneer nuwe boodskappe arriveer, metode handleGetObject () dipanggil dengan dua parameter: ID penulis (identifier unik) dan data pesan. Ontleding van boodskapdata, onttrek bedryfskode en dienooreenkomstig word alle ander eienskappe ook onttrek.
Deur gebruik te maak van onttrek data, reageer multiplayer kode alle aksies wat oor die netwerk ontvang word. As die OP_SHOT boodskap as voorbeeld gebruik word, is dit die stappe wat geneem is om die huidige spelstatus te verander:
Sien plaaslike skip geïdentifiseer deur userId.
Werk Ship se posisie en hoek volgens die data wat ontvang is.
Werk die rigting van die skip volgens die data wat ontvang is.
Roep spel metode wat verantwoordelik is vir die ontploffing van projektiele, vuurkoeëls of bomme af.
Soos voorheen genoem, word die skietkode geskei van die speler en die insetlogika, dus die projekte wat afgedank word, tree op soos deur plaaslike spelers ontslaan.
Mitigate Latency Issues
As die spel die entiteit uitsluitlik beweeg op grond van netwerkopdaterings, sal ontbrekende of hangende boodskappe veroorsaak dat die entiteit van een punt na 'n ander teleporteer. Dit kan versag word met plaaslike voorspellings.
Met behulp van interpolasie, word entiteitbewegings byvoorbeeld plaaslik geïnterpoleer vanaf een punt na 'n ander (albei aanvaar deur netwerkopdaterings). As gevolg hiervan, die entiteit sal glad tussen die punte beweeg. Ideaal, latensie moet nie die tyd duur wat dit verg om 'n entiteit van een punt na 'n ander te interpoleer nie.
Trik lain adalah ekstrapolasi, wat die entiteit plaaslik op sy huidige status beweeg. Dit veronderstel dat die entiteit nie die huidige roete sal verander nie, dus is dit veilig om dit in sy huidige rigting en snelheid te laat beweeg. As die latensie nie te hoog is nie, reproduceer ekstrapolasie die beweging wat die entiteit verwag, totdat die nuwe netwerkopdaterings aankom, wat 'n gladde bewegingspatroon tot gevolg het.
Ten spyte van die truuks, netwerk latency kan baie hoog en soms onbeheerbaar wees. Die maklikste benadering om dit uit te skakel is om die problematiese verhouding te verbreek. Die veiligste benadering hiervoor is om 'n time out te gebruik: as 'n kollega meer as die gegewe tyd neem om te antwoord, word dit ontkoppel.
Die skep van 'n multiplayer speletjie wat oor die netwerk gespeel word is 'n uitdagende en opwindende taak. Dit vereis 'n ander manier om na dinge te kyk, want alle relevante aksies moet deur alle eweknieë gestuur en weergegee word. As gevolg, alle spelers sien 'n simulasie van wat gebeur het, behalwe vir plaaslike skepe, wat geen netwerklaatigheid het nie.
Hierdie handleiding illustreer die implementering van multiplayer-speletjies deur gebruik te maak van 'n nie-gesaghebbende P2P benadering. Al die konsepte wat aangebied word, kan uitgebrei word om verskillende multiplayer meganismes te implementeer. Laat speletjie multiplayer begin!