Deze tutorial gaat ervan uit dat je al een basis voor een game hebt waarop je verder kunt bouwen. Een loginscherm laten zien, het "speler object" en het "andere speler object" komen niet aan bod. Als je nog niet zover bent, in de beginnen met GameMaker Server tutorial komen die onderwerpen wel aan bod.
Deze post zal uit twee delen bestaan. In het eerste deel loop ik langs alle ideeën en theorieën die je moet weten om instanties te kunnen synchroniseren. Het tweede deel gaat over hoe je alle theorie in een game toepast.
In deze tutorial gebruik ik het woord "client" (Engels) een aantal keer. Een client is een instantie van de game, verbonden met de server. Als er bijvoorbeeld 3 spelers met de server zijn verbonden, zijn er 3 clients met de server verbonden. Als een speler beweegt is die beweging van één client naar de andere clients verzonden.
Er zijn 3 verschillende synchronisatietypes: One-time, Extended en Full synchronisatie. Ieder type kan lokaal of niet-lokaal zijn.
One-time synchronisatie synchroniseert de instantie exact één keer. Wanneer je de instantie destroyt of een variabele aanpast wordt dat niet gesynchroniseerd.
Extended synchronisatie synchroniseert de instantie twee keer: Een keer bij het aanroepen van gms_instance_sync(...), en een keer zodra de instantie gedestroyt wordt. Dit type synchronisatie synchroniseert geen veranderingen in waardes van variabelen.
Full synchronisatie synchroniseert de instantie bij het aanroepen van gms_instance_sync(...), als een variabele verandert en zodra de instantie vernietigd is. Dit type synchroniatie lijkt erg op hoe het spelersobject gesynchroniseerd wordt. Instanties worden naar andere spelers verzonden zodra ze de room binnenkomen.
Lokale instance synchronisatie zal de instantie alleen synchroniseren als het dicht bij de speler is. Het probeert te voorspellen welke instanties door de speler gezien worden, en synchroniseert alleen die instanties. Bij full synchronisatie wordt de instantie altijd gesynchroniseerd, maar veranderingen in positie of van variabelen worden alleen verzonden als de speler dicht bij genoeg is.
Of de speler die instantie kan zien wordt bepaald door de "Sync-afstand." Deze afstand kan op de site worden ingesteld, onder Ontwikkelaar -> Game-instelllingen -> Sync-afstand. Een goede gok voor hoe hoog de Synch-afstand moet zijn is door de room_width of de room_height (de hoogste van de twee) te vermenigvukdigen met 1.5.
Elk type synchronisatie heeft een eigen constante:
One-time synchronisatie: is_onetime
Extended synchronisatie: is_extended
Full synchronisatie: is_full
Standaard zijn alledrie de constanten niet lokaal. Om een type synchronisatie lokaal te maken moet de constante bitwise ge-xor'ed worden met isc_local. (uitleg over bitwise operatoren hoort niet echt in dee tutorial. Als je er meer over wilt leren kun je goede tutorials op het internet vinden). Bijvoorbeeld:
Bij het maken van een multiplayergame is het belangrijk om het dataverbruik tot een minimum te houden. Instance synchronisatie kan veel data gebruiken. Daarom is het belangrijk dat je het juiste type synchronisatie kiest voor het juiste type instantie.
Om te bepalen wat voor type synchronisatie je nodig hebt, is het handig om te bedenken of het moment waarop een instantie wordt vernietigd, en hoe en wanneer de instantie beweegt en variabeles veranderen voorspelbaar is.
Bijvoorbeeld: Een kogel wordt gedestroyt wanneer het een muur aanraakt. Omdat de botsing met de muur op alle clients op dezelfde tijd gebeurt, is het niet nodig om het vernietigen van de instantie te synchroniseren.
Aan de andere kant, als een object wordt vernietigd doordat bijvoorbeeld de speler er op klikt, dan is dat niet voorspelbaar. Niet alle spelers zullen op hetzelfde moment op het object klikken. Voor de andere clients is er dus geen manier om te weten of een instantie is vernietgd, zonder expliciet naar de andere clients te verzenden dat de instantie vernietigd is.
Hetzelfde geld voor variabelen. Als de waarde van een variabele voorspelbaar is, hoeft het niet verzonden te worden.
De punten hierboven kun je in drie vragen stellen:
"Is het mogelijk om te weten wat de waarde van een variabele is en zal zijn zonder de variabele te verzenden?"
"Is het mogelijk om te weten wat de positie is en zal zijn van een instantie zonder de positie te verzenden?"
"Is het mogelijk om te weten wanneer een object wordt vernietigd, zonder het te verzenden?"
Ieder type synchronisatie heeft een andere combinatie van eigenschappen:
One-time synchronisatie: positie voorspelbaar, destroy voorspelbaar, variabelen voorspelbaar.
Extended synchronisatie: positie voorspelbaar, destroy onvoorspelbaar, variabelen voorspelbaar.
Full synchronisatie: positie onvoorspelbaar, destroy onvoorspelbaar, variabelen onvoorspelbaar.
Merk op dat niet alle mogelijke combinaties een eigen synchronisatietype hebben. De 3 types zijn de meest voorkomende. Het gebeurt niet vaak dat de waarde van een variabele niet te voorspellen is, maar de positie wel.
Dat is alles wat je moet weten om écht te kunnen beginnen. Houd er rekening mee dat je meestal One-time en Extended synchronisatie kan gebruiken. Full synchonisatie gebruikt veel CPU kracht en data in verhouding met de andere opties.
Nu je weet wat de verschillende types synchronisatie zijn en wanneer je ze het beste kunt gebruiken, is het tijd om dat toe te passen op een game. In dit deel van de tutorial maak ik gebruik van de game die de beginnen met GameMaker Server tutorial omschrijft.
Zorg ervoor dat je de Sync-afstand op de site hebt ingesteld.
Ik ga een voorbeeld geven van twee van de drie types synchronisatie:
One-time instance synchronisatie
Full instance synchronisatie
Extended instance synchronisatie lijkt erg op One-time synchronisatie.
Maak een nieuw object, en noem het "obj_bullet" en geeft het een sprite.
Open obj_player, en voeg een Global Mouse Left Pressed event toe.
Voeg het volgende stuk code toe:
In GM:Studio zul je meer werk moeten doen als je een instantie variabele wilt synchroniseren. In de GM8 code hierboven is de variabele die we willen synchoniseren ("damage") als extra argument opgegeven bij het aanroepen van gms_instance_sync(...). Intern gebruikt de extensie variable_local_get om dan de waarde van die variabele op te vragen. In GM:Studio is variable_local_get verwijderd.
GameMaker Server heeft een speciale functie die gebruikt kan worden om de variabele en de waarde in te stellen voor instance synchronisatie, gms_instance_sync_var_add(...). Voeg het volgende stuk code toe aan het Global Mouse Left Pressed event:
Ongeacht welk type instance synchronisatie je gebruikt, de variabelen worden maar een keer naar de andere clients verzonden. De variabelen x, y, speed en direction worden altijd verzonden.
Zodra de instantie gemaakt is op de andere clients wordt het user defined 12 event aangeroepen.
Je zult ook handmatig de gesynchroniseerde variabelen moeten instellen, omdat de extensie intern variabele_local_set gebruikt, die in GM:Studio ook verwijderd is. Open obj_bullet en voeg de volgende code toe aan het Event User 12 event:
variable_map is een variabele die een ds_map bevat met alle gesynchroniseerde variabeles. variable_map is verwijderd nadat het Event user 12 is uitgevoerd.
De speed en direction variabelen (en alle aangepaste variabelen / de variabele_map variable) zijn nog niet ingesteld in het create event van obj_bullet. Je moet het User Defined 12 event gebruiken als je gebruik wilt maken van de gesynchroniseerde variabelen.
Op dit moment is je game is een staat waarop je het kunt uitvoeren. Als je dat wilt, kun je nu je game testen.
Als je meer wilt oefenen, probeer dan eens muren toe te voegen aan de game. Voeg een stuk code toe dat obj_bullet vernietigd zodra het een muur raakt. (wanneer obj_bullet de muur raakt is voorspelbaar).
Probeer de speler health te geven, en laat de kogels damage doen. Houd er rekening mee dat obj_other_player alleen een voorstelling van het obj_player object op een andere client is. Voeg alleen een collision event met het obj_player object toe. Om te controleren wie de bullet heeft afgevuurd kun je gebruik maken van de speciale owner variabele. Deze variabele is voor alle objecten ingesteld na het aanroepen van gms_instance_sync(..).
Maak een object en noem het obj_npc. Geef het, voor deze tutorial, een helemaal witte sprite. We zullen dat laten gebruiken om met image_blend de NPC een willekeurige kleur te geven.
In het create event:
Stel alarm[0] in op 1 seconde
Stel de speed in op 4
Voeg deze code aan het alarm[0] event toe:
Roep randomize() aan in het GMS object of een ander initialisatie-object om er zeker van te zijn dat de NPC's iedere keer een andere kant op bewegen.
Destroy de instantie zodra het obj_bullet aanraakt.
Maak een object obj_npc_spawner. Zorg dat het obj_npc iedere 3 seconden spawnt, totdat er 5 obj_npc's rondlopen:
Create:
Alarm[0]:
Als je de opdracht zou krijgen om de bovenstaande code te veranderen zodrat het de instanties ook goed synchroniseert, is je eerste reactie waarschijnlijk om gms_instance_sync aan te roepen. Alhoewel dat werkt, zorgt het ook voor een probleem: Als er 3 spelers online zijn, zal iedere client iedere 3 seconden een NPC spawnen. Dat betekent dat 3 NPC's iedere 3 seconden worden gespawnt. Als er 5 spelers online zijn, dan spawnen 5 NPC's iedere 3 seconden.
Om de spawnsnelheid hetzelfde te houden, moet maar een client instanties spawnen. Om te bepalen welke client dat is kun je de master player gebruiken. De master player is een door de server aangewezen speler, en verandert automatisch als een speler uitlogt of andere spelers inloggen.
Pas de code in alarm[0] aan:
Omdat image_blend een ingebouwde variabele is, kan de waarde automatisch door de extensie worden opgevraagd. Je hoeft daarom gms_instance_sync_var_add(...) in dit geval niet aan te roepen.
Merk op dat het alarm iedere 3 seconden wordt uitgevoerd, ook als de speler niet de master player is. Op deze manier is er geen code nodig om van master player te wisselen als de oude master player uitlogt. Het alarm van de nieuwe master player loopt dan al. De gms_self_ismaster()-aanroep wordt op iedere client iedere 3 seconden uitgevoerd om er zeker van te zijn dat als de master player disconnect een andere client het werk makkelijk kan overnemen.
Het NPC object zelf moet ook aangepast worden. Als iedere client zou proberen de direction iedere seconde te veranderen, zou het snel een rommel worden. Het obj_npc object zou in meerdere richtingen tegelijk moeten bewegen, omdat iedere client een andere willekeurige direction kiest. Daarom kan een instantie maar door een client tegelijk bestuurd worden. Een bepaalde client is "eigenaar" (owner) van een instantie. Je kunt de functies gms_instance_is_owner(id) en gms_instance_get_owner(id) gebruiken om te controleren of de client eigenaar is van een instantie en wie de eigenaar van de instantie is.
In tegenstelling tot de master player veranderen instanties niet automatisch van eigenaar. Iedere instantie moet aan een andere speler "gegeven" worden door gms_instance_handover(...) of gms_instance_handover_all() aan te roepen. De server besluit vervolgens aan wie de instantie gegeven gaat worden. Dat betekent dat als je wilt dat instanties niet vernietigd worden als je uitlogt of naar een andere room gaat, je de instanties aan een andere speler moet geven in deze events:
Room end
Game end
Bewerk het alarm[0] event van obj_npc:
Voeg deze code toe om de image_blend variabele te updaten als de client niet de eigenaar van de instantie is:
Zorg er als laatste voor dat de instanties aan andere spelers worden gegeven als de eigenaar van de instantie van room verandert of uitlogt:
Game End & Room End event:
Het is handig om een Outside Room event toe te voegen aan obj_npc, en een actie "wrap in both directions" toe te voegen om de NPC's op het scherm te houden. Voeg een instantie van obj_npc_spawner toe aan rm_play.
Dat is het!Eindelijk. Deze tutorial is veel langer geworden dan ik van plan was, maar ik kreeg het niet voor elkaar om de tutorial korter te maken zonder een deel weg te laten. Laat me weten wat je van deze tutorial vind! Welk onderwerp wil je als volgende tutoial zien?