Instanties synchroniseren

Voordat je start

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.

Verschillende synchronisatietypes

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:

is_onetime | isc_local

is_extended | isc_local

is_full | isc_local

Wanneer welk type gebruiken

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.

Instance synchronisatie toevoegen aan een game

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.

Shooting (One-time synchronization)

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.

GM8 / GM8.1:

Voeg het volgende stuk code toe:

i = instance_create(x, y,obj_bullet);
i.speed = 8;
i.direction = point_direction(x, y,mouse_x, mouse_y);
i.damage = random_range(30,50);
gms_instance_sync(i, is_onetime | isc_local, “damage”);

GM:Studio:

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:

i = instance_create(x, y,obj_bullet);
i.speed = 8;
i.direction = point_direction(x, y,mouse_x, mouse_y);
i.damage = random_range(3050);

gms_instance_sync_var_add(“damage”,i.damage);
gms_instance_sync(i, is_onetime | isc_local);

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.

GM:Studio:

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:

damage =ds_map_find_value(variable_map, “damage”);

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(..).

Vijanden spawnen & synchroniseren (Full synchronisatie)

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:

direction= choose(090180270);
image_blend =irandom(c_white);
alarm[0] = room_speed;

GM:Studio:

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] = room_speed * 3;

Alarm[0]:

alarm[0] = room_speed *3;
if(instance_number(obj_npc) < 5)
{
 instance_create(x, y, obj_npc);
}

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:

alarm[0] = room_speed *3;
if(gms_self_ismaster())
{
 if(instance_number(obj_npc)< 5)
 {
 var i;
 i = instance_create(x,y, obj_npc);
 gms_instance_sync(i, is_full | isc_local,"image_blend");
 }
}

GM:Studio:

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:

if(gms_instance_is_owner(id))
{
 image_blend = irandom(c_white);
 gms_instance_set(id,“image_blend”, image_blend);
 direction = choose(0,90180270)
}
alarm[0] = room_speed;

Voeg deze code toe om de image_blend variabele te updaten als de client niet de eigenaar van de instantie is:

if(!gms_instance_is_owner(id))
{
 image_blend = gms_instance_get(id, "image_blend");
}

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:

if(gms_instance_is_owner(id))
{
 gms_instance_handover(id);
}

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?

Reacties (62)

Laatste bericht op 17 Jan 2020

Maartey op 7 Jun 2015, 11:45:14
Hoi kan iemand mij helpen? Ik ben bezig met een topdown shooter en ik wil graag de kogels synchroniseren(one-time-synchronisatie) Maar hierboven word het aangegeven met de muis alleen in mijn shooter moet je schieten met de pijltjestoetsen. Dit is de code van de KeyboardLeft event:

if canShoot and ammo>0
{
ammo-=1;
canShoot = false;
alarm[0] = 15;
action_create_object_motion(obj_bullet,x,y,9,180);}
Maartey op 7 Jun 2015, 14:23:53
XD... Ik ben vergeten mijn probleem er bij te zetten... Het probleem is dat ik echt geen idee heb hoe ik het moet doen met de pijltjestoetsen.
Size43 (Beheerder) op 13 Jun 2015, 11:55:36
Sorry voor mijn late reactie, ik heb het erg druk. Als je de instantie synchroniseert nadat je de speed & direction hebt ingesteld worden deze correct meegezonden:

var i;
i = instance_create(obj_bullet, x, y);
i.speed = 9;
i.direction = 180;
gms_instance_sync(is_onetime | isc_local, i);


Je hoeft dan geen instance sync meer in het create event van obj_bullet te hebben, waar ik vermoed dat je 'em nu hebt :)
Maartey op 25 Jun 2015, 19:00:18
XD Ik denk dat we beide laat reageren.
Maar bedankt het werkt nu!
Speace_Gamer_ op 17 Jan 2020, 15:45:40
Ik ben nu een game aan het maken maar het doet heel raar mbt het syncen naar de server. Hij doet nu automatisch mijn player duplicaten, andere instanties willen ook niet meewerken. Ik heb de tutorial al meerdere keren gelzen en de examples ook goed doorgenomen maar ikkan er niet uitkomen. Kan iemand mij helpen???
Size43 (Beheerder) op 17 Jan 2020, 19:10:54
Aangezien je al een apart topic hebt aangemaakt lijkt me het handiger om daar verder te gaan:https://gamemakerserver.com/nl/forum/board/1/view/710/
Possopher op 10 Apr 2017, 13:04:51
Hey, besloot weer een poging te wagen in gm studio maar heb een korte vraag: als je een gesynchroniseerde variable hebt ingesteld met "ds_map_find_value" in user defined, moet je voor die variable dan ook nog "gms_instance_set" gebruiken? wat is precies het verschil tussen beide?
Size43 (Beheerder) op 16 Apr 2017, 16:32:22
gms_instance_set is voor het aanpassen van is_full insanties nadat deze aangemaakt zijn.

De waardes die je met ds_map_find_value in het user defined 12 event instelt worden eenmalig bij het createn van de instantie ingesteld.

ds_map_find_value in het user defined 12 gaat dus samen met gms_instance_sync_var_add, en gms_instance_set gaat samen met gms_instance_get.
Possopher op 29 May 2017, 19:40:20
Ok thanks. Ik heb nu vrijwel alles werkend alleen nu ik ook volledig gesynchroniseerde instances heb krijg ik na een paar rooms in te gaan opeens een "out of memory" melding gevolgd door een crash. Geen error message van de gms extensie gewoon een popup bericht van gamemaker zelf. Het viel me op dat ik hem ook krijg als ik een paar keer heen en weer ga tussen dezelfde 2 rooms wat raar is omdat er dan qua gesynchroniseerde instanties geen cpu bij zou moeten komen.

Heb jij een idee waar dit probleem vandaan zou kunnen komen? Misschien ergens een leak in het synchroniseren ofzo? Tot nu toe krijg ik de crash steeds op room start (na al een paar rooms bezocht te hebben). Want als het goed is stoppen full sync instanties bij het verlaten van de room automatisch met synchroniseren toch of moet ik dat ergens handmatig afbreken?
sorry dat ik je met problemen blijf lastig vallen haha als het goed is is dit de laatste!!
Size43 (Beheerder) op 6 Jun 2017, 15:26:27
Krijg je de error ook als je in een keer heel veel instanties synchroniseert? Of alleen als de room switch ertussen zit?
Possopher op 11 Jun 2017, 18:09:55
Heb het probleem inmiddels opgelost. had te maken met de spawners van de instanties die bleven synchroniseren als de owner niet duidelijk was aangegeven
sylvia2963 op 23 Nov 2015, 22:47:46
ik heb een vraag.
hoe komt het dat de syncronisatie traag verloopt ?
ik gebruik nu gms_optimize_variables(true,true); (Forgeio heeft me hierbij al goed geholpen nogmaals bedankt hiervoor )
maar toch lijkt er lag op te zitten. kan iemand uitleggen waardoor dit komt?
PaPdeveloper op 3 Jul 2015, 22:49:03
Hoi Size43!

Het gaat de goede kant op met onze game, alleen hebben wij een nieuwe bug ontdekt die wij zelf niet kunnen oplossen:


___________________________________________
############################################################################################
ERROR in
action number 1
of Step Event0
for object GMS:

Instance to LinkSync was not found.
at gml_Script_XServer_error
############################################################################################
--------------------------------------------------------------------------------------------
stack frame is
gml_Script_XServer_error (line 0)
gml_Script_gms_step
gml_Object_GMS_Step_0

Deze error verschijnt op willekeurige momenten en als je op 'ignore' drukt kan je weer gewoon verder, maar het moet natuurlijk wel opgelost worden! Kan jij ons hierbij helpen? Alvast bedankt :)!
Size43 (Beheerder) op 7 Jul 2015, 15:03:09
Die error komt als tegen de tijd dat er op een client de instantie aangemaakt wordt de gesynchroniseerde instantie al weer vernietigd is. (De instantie kan niet "gelinkt" worden aan een syncID). Vernietig je misschien een instantie die je met is_full synchoniseert binnen een paar steps weer?
PaPdeveloper op 10 Jul 2015, 13:19:21
O ja, ik zie het! Het gebeurt inderdaad als iemand een pickupje maakt en hem meteen weer op pakt. Bedankt!
PaPdeveloper op 24 May 2015, 11:12:38
Hoi Size43,
In onze game worden er soms pakketjes gespawnt die een speler kan open maken. Uit zo'n pakketje komt dan een pickup van een wapen/attachment. Ik heb gemaakt dat die pickupjes met extended worden gesynchroniseerd, maar als een speler zo'n pickup op pakt wordt het die pickup bij andere spelers niet verwijderd. Dus de extended synchronisatie werkt hier dus hetzelfde als onetime synchronisatie en dit klopt natuurlijk niet!
Size43 (Beheerder) op 24 May 2015, 16:13:27
Dat lijkt me inderdaad een bug. Gebeurt dit ook als de speler die de pickup heeft gecreated de pickup weer destroyt?
PaPdeveloper op 25 May 2015, 10:18:00
Ja dan doet hij het ook niet.
Size43 (Beheerder) op 26 May 2015, 10:59:45
Zou je me een gmk/gmz kunnen mailen waarin dit gebeurt (gamemakerserver@outlook.com), zodat ik het probleem kan oplossen?
PaPdeveloper op 5 Jun 2015, 19:46:46
Sorry voor deze late reactie! We hebben een simpel gmk'tje naar je toe gestuurd waarbij te zien is dat het niet werkt. Heel erg bedankt alvast!
Size43 (Beheerder) op 13 Jun 2015, 11:52:23
Bedankt voor het mailtje. Ik ben op dit moment nog erg druk, dus ik heb nog geen tijd gehad om ernaar te kijken. Zodra ik over een week ofzo iets meer tijd heb zal ik ernaar krijken, en een update releasen.
PaPdeveloper op 14 Jun 2015, 21:56:29
Geen probleem! Ik ben al lang blij dat je ernaar wilt kijken!
Size43 (Beheerder) op 20 Jun 2015, 16:26:25
Hey,

Ik heb de bug gevonden en opgelost. Vandaag of morgen release ik 1.8.6, die zal deze fix ook bevatten :)

Size43
PaPdeveloper op 23 Jun 2015, 14:42:15
Heel erg bedankt :)! Moeten we de update ergens downloaden of gaat dat vanzelf?
Size43 (Beheerder) op 23 Jun 2015, 20:04:23
Ja, op de startpagina vind je de downloadlink :)
PaPdeveloper op 24 Jun 2015, 18:09:08
Het werkt al meteen! Heel erg bedankt!
TomPostmus op 29 Apr 2015, 13:52:24
Hallo Size43,

Ik heb een generator gemaakt die een level genereert. De generator maakt stukjes grond aan en synchroniseert die dan met onetime_sync. Als er meerdere clients in een room zitten moet het natuurlijk niet zo zijn dat elke client een level maakt, want dan gaat alles helemaal door elkaar. Hierom heb ik voor de generatorcodes een if gms_self_ismaster() code gedaan. Maar hierdoor krijg ik een nieuw probleem: de generator genereert een level aan het begin van de room, als er dan een client later bij komt zijn die stukjes grond bij hem dus nog niet gemaakt, dus valt hij gewoon naar beneden. Hoe moet ik dit fixen? Alvast bedankt!
Size43 (Beheerder) op 30 Apr 2015, 11:06:19
Instanties worden alleen op de server onthouden als je ze synchroniseert met full instance sync. Wat je kunt doen is óf zodra een nieuwe speler een room binnenkomt alle instanties naar die speler verzenden met P2P messages, óf je kunt de instanties in de room opslaan in de gameINI en andere clients die weer uit laten lezen.
TomPostmus op 28 Apr 2015, 17:56:07
Hallo Size43,

Ik heb een schietspelletje gemaakt. Je kunt elkaars wapens al zien. Als iemand zijn wapen reload, zien de anderen dat heel schokkerig gaan. Ze zien dan soms het wapentje van subimage veranderen. Ik heb gedaan dat de otherplayer objecten hun wapens drawen. Ze krijgen dus de image_angle en de sprite_index van de speler die zij moeten voorstellen en die gebruiken die variables in een draw_sprite_ext code. Kan ik ervoor zorgen dat het variable sprite_index preciezer verzonden wordt? Alvast bedankt!

Tom
TomPostmus op 29 Apr 2015, 22:01:06
Ik ben vergeten te melden dat de room_speed 120 bij mijn game is. Ik weet niet of dit uit maakt maar misschien dat dat je helpt na te denken wat er fout zou kunnen zijn!
Size43 (Beheerder) op 30 Apr 2015, 11:04:13
Met 120 fps is het zo goed als onmogelijk om de image_index vloeiend te simuleren. Alle spelers zouden dan een ping van minder dan 10ms moeten hebben. Wat handiger is om te doen is de image_index niet synchroniseren, en op iedere client apart de animatie af te spelen. Als de animatie niet op de frame nauwkeurig is met de andere clients is dat niet zo'n groot probleem.
Possopher op 6 Apr 2015, 01:29:01
Hey, ik ben al een stuk verder met het synchroniseren maar ik loop nog steeds bij een aantal dingen vast, vooral variables die ook effect hebben op andere spelers.
Ik probeer bijvoorbeeld de player een soort explosie te laten creeren die damage doet bij alle andere players,
maar ik heb geen idee waar ik al die variables moet plaatsen, wanneer ik player of other_player moet gebruiken enz. Hier is ongeveer wat ik probeer te doen maar het werkt nu natuurlijk voor geen meter

//player maakt de explosie
i = instance_create(x,y,obj_explosion)
gms_instance_sync(i, is_onetime | isc_local,"targets")

//targets is de variable van de explosie, en zijn alle players die erdoor geraakt worden.
//ik weet dus niet of ik die bij de explosie object in user defined of gewoon in create moet doen
targets = place_meeting(x, y, obj_player)

//en dan de damage die het verricht (moet ik dit ook nog syncen als de explosie bij alle clients al gemaakt is?)
if !(gms_instance_is_owner(id))
{
with targets
hp -= 90
}
Size43 (Beheerder) op 6 Apr 2015, 20:06:26
Lijkt me dat je die variable targets op iedere client apart kunt berekenen? Dat scheelt weer een variabele verzenden :)

Verder kun je ook niet zomaar instance ids verzenden naar andere clients, omdat instance ids niet bij iedere client hetzelfde zijn. In dat geval zou je die objecten ook moeten synchroniseren.
Possopher op 10 Apr 2015, 20:52:22
Ok, was ook nog niet de uiteindelijke code, maar meer om een idee te geven. bedoel je dat ik elk individueel target voor een collision met de aanval moet laten checken, of dat ik de target variable gewoon in de create van de aanval moet doen en niet moet syncen? ik probeer namelijk zo min mogelijk collision checks voor mijn players te doen aangezien de game nogal veel aanvallen enz heeft.

maar als ik een van deze manieren zou gebruiken, zou je dan een voorbeeld kunnen geven hoe ik het creeren van de aanval + de damage kan synchroniseren, waarbij de aanval onderscheid maakt tussen de 'caster' en andere spelers? dit gedeelte was me nl nog niet helemaal duidelijk.
Size43 (Beheerder) op 10 Apr 2015, 21:14:19
Ik zou de target gewoon in de create doen en niet syncen, tenzij je natuurlijk een specifieke player wilt raken. Dan moet je de player_id meesturen, en op iedere client controleren of de playerID overeenkomt.

Iedere instantie die je synchroniseert krijgt automatisch een variabele "owner". Die variabele bestaat vanaf het User Defined 12 event. (dat is ná het create event, maar voor elk ander event). Om te kijken of een bepaalde speler het object heeft aangemaakt kun je dus die variabele vergelijken met de playerID. Dat is voor obj_player gms_self_playerid() en voor obj_other_player de variabele player_id.

Damage kun je gewoon als variabele synchroniseren. In GM8 wordt de variabele automatisch ingesteld, maar in GM:Studio moet je die nog zelf uitlezen:

GM:Studio: In het User Defined Event 12:
damage = ds_map_find_value(variable_map, "damage")
Zie ook deze tutorial voor meer info.
Possopher op 12 Apr 2015, 14:54:28
Ok thanks het werkt nu. ik bekeek het veel te ingewikkeld :P
Nog 2 laatste dingen: allereerst werk ik met een soort combat systeem waarbij de bullet van de player naar een soort target box vliegt, die ontstaat wanneer je op een andere speler klikt. Alleen als speler A die box op een andere plek heeft, en speler B schiet een bullet naar hem, dan gaat de kogel op het scherm van A naar zijn eigen target box, terwijl hij eigenlijk naar de target box van B moet. Deze target box is gewoon 1 object en wordt niet gesynct, dus je kunt alleen je eigen target box zien.
Ik probeer nu dus dmv die owner variable te zorgen dat de bullet naar de box van de owner moet, ook wanneer de speler niet de owner is maar ik weet niet hoe je dit moet aangeven.
Possopher op 12 Apr 2015, 14:55:34
Als tweede heb ik een probleem wanneer 1 van de spelers een room verlaat en weer terugkomt. om een of andere reden worden dan de variables van de npcs niet meer herkend en word ik overspoeld met errors. Ik denk dat dit ermee te maken heeft dat mijn rooms persistent zijn. De npcs zijn gebonden aan de master player en hebben full sync. Als ik de master weghaal krijg ik het nog steeds dus het probleem zit in de synchronisatie. nogmaals bedankt voor de hulp
Size43 (Beheerder) op 12 Apr 2015, 21:02:58
1 - Kun je de positie van de box met de gesynchroniseerde instantie meesturen? Dus dat je een box_x en een box_y variabele hebt die naar de juiste positie wijzen.

Als de box 'vast' zit op een speler, kun je ook alleen de playerID meezenden, en vervolgens zo de positie van de andere speler opvragen:
pos_x = gms_other_get(speler_target, "x")


2 - Wat je kunt doen is zodra de room eindigt gms_instance_handover_all aanroepen om de NPCs aan een andere speler 'te geven'. Het is niet mogelijk om gesynchroniseerde instanties persistent te houden.
Possopher op 17 Apr 2015, 13:55:33
Het targeten met de box is inmiddels met wat omweggetjes gelukt, maar met de npcs (en eigenlijk alle instances met full synchronisatie) gaat nog steeds wat mis als ik de room verlaat. De errors zijn inmiddels weg, maar als ik weer terugkom in de room wordt elke instantie nog een keer aangemaakt, maar alleen een weergave ervan volgens mij, zonder events. Waarschijnlijk doe ik gewoon iets fout in de basics. Hier zijn de codes die ik gebruik:

//Creation code voor de room
if gms_self_ismaster() and
!instance_exists(obj_NPCparent)
{
//NPCs
instance_create(2144,1472,obj_npc1)
instance_create(2400,3136,obj_npc2)
enz
}
(ik dacht misschien werkt dit zodat ze maar 1x worden aangemaakt maar was niet het geval)

//NPC parent's create:
gms_instance_sync(id,is_full)

//NPC parent's step:
if (radius <= 500) and (gms_instance_is_owner(id))
{
lopen en dergelijke
}

//Game end, room end
if(!gms_instance_is_owner(id))
gms_instance_handover_all()
Size43 (Beheerder) op 18 Apr 2015, 11:50:31
Waarschijnlijk checkt de creation code voor de room te vroeg of de instantie al bestaat. GameMaker Server controleert namelijk pas in het step event of er van room is veranderd, vraagt dan aan de server om de juiste instanties te sturen, en kan daarna pas alle instanties in de room weer aanmaken. Als je die code in een alarm event zet met een delay erop zou het wel moeten werken.

Dat het alleen de weergave van de instantie is komt waarschijnlijk doordat de server owner van de instanties werd omdat er niemand anders in de room was, en de server vervolgens niet weer een speler owner maaktte zodra die in een room kwam. Dat zou nu moeten zijn gefixt.
Possopher op 18 Apr 2015, 18:02:06
ahh ik was een heel belangrijk ding vergeten te melden; mijn rooms zijn persistent.
Dus het gedeelte in de creation code waar de npcs worden gemaakt wordt alleen in het begin aangeroepen als er nog geen npcs bestaan, als ik vervolgens de room verlaat en weer terugkom niet meer omdat de npcs er nog zijn.

Is het op een of andere manier dan mogelijk om het gebeuren van de persistent rooms iets te vertragen, of het syncen van de npcs juist iets eerder aan te roepen? Anders kan ik wel geen persistent rooms doen en zegmaar van elke npc hun laatste locatie onthouden, en ze daar weer te plaatsen aan het begin van de room maar dat is weer zo'n gedoe ;(
Possopher op 18 Apr 2015, 18:17:24
ow zie dat ik dit in een eerste post al wel gezegd had. Dan zou het met instance_handover toch al moeten werken? Dat de npcs door de room worden aangemaakt ipv ze gewoon in de room te plaatsen had ik trouwens later gedaan, als ze wel al in de room zitten had ik hetzelfde probleem.
Size43 (Beheerder) op 18 Apr 2015, 20:41:29
Het punt is dat de extensie de instanties die volgens de server in de room staan weer gaat aanmaken. Daar is weinig aan te doen. Je zult dus de instanties die al in de room stonden moeten destroyen, zodat GameMaker Server dan de juiste weer kan aanmaken.

Overigens, als je full instance sync gebruikt wordt de positie van de NPCs toch al onthouden door de server? Dan heb je helemaal geen persistent rooms meer nodig.
Possopher op 19 Apr 2015, 11:40:18
Ok thanks ik snap hem :P Ik heb nu in de room_end (create werkte niet) van elke npc gedaan dat ze zichzelf destroyen zodra er meerdere instances van zichzelf aanwezig zijn.

En zonder persistent zou het inderdaad ook kunnen, maar ik heb nog allerlei interactieve dingen in de room die ook onthouden moeten worden. Dus met persistent bespaar ik dan wat geheugen ipv al die dingen ook full sync te moeten geven
PaPdeveloper op 22 Mar 2015, 18:12:42
Ik heb nu elke actie van een wapen waarbij de sprite veranderd vervangen door de code: obj_speler.i.sprite_index = sprite_waarin_hij_veranderd. Als ik nu de reload toets van het wapen druk zie ik dat alleen het wapen van mijn speler gaat reloaden en niet die van een andere speler. Dat is goed, maar als een wapen van een speler reload zie je dat niet bij een andere speler. Dus als bijvoorbeeld een andere speler op de reload toets drukt zie ik hem niet reloaden, maar hij ziet zichzelf wel reloaden. Ik heb geprobeerd om bij het wapen voor de synchronisatie code(die staat bij het create event van de speler) gms_instance_synch_var_add("sprite_index", i.sprite_index) te doen, maar dit werkt natuurlijk niet. Kun jij mij helpen?
Size43 (Beheerder) op 22 Mar 2015, 19:20:15
Variables van een instantie worden niet automatisch gesynchroniseerd. Je zult zelf gms_instance_set en gms_instance_get moeten gebruiken om de variabele (sprite_index) in te stellen en op te vragen. :)
PaPdeveloper op 23 Mar 2015, 15:49:02
Het werkt!! Heel erg bedankt!! Weet je ook hoe je geluiden moet synchroniseren?
Size43 (Beheerder) op 23 Mar 2015, 19:45:51
Het makkelijkste is om gewoon het geluid op beide clients los af te spelen (bijvoorbeeld als iets een muur raakt ofzo). Dat er dan iets verschil zit in wanneer het geluid wordt afgespeeld merkt niemand.

Als dat niet lukt kun je een object maken dat het geluid afspeelt en dit synchroniseren.
PaPdeveloper op 20 Feb 2015, 16:09:42
Het synchroniseren werkt nu ongeveer. De spelers kunnen elkaars wapens zien, maar de spelers zien elkaars wapens in de richting van hunzelf. Ik heb geprobeerd om vóór de instance sync code de code: gms_instance_sync_var_add("image_angle", i.image_angle) toe te voegen, maar de spelers zien elkaars wapens gewoon nog in hun eigen richting. Weet jij hoe het wél moet? Bedankt!
PaPdeveloper op 20 Feb 2015, 16:09:09
Het synchroniseren werkt nu ongeveer. De spelers kunnen elkaars wapens zien, maar de spelers zien elkaars wapens in de richting van hunzelf. Ik heb geprobeerd om vóór de instance sync code de code: gms_instance_sync_var_add("image_angle", i.image_angle) toe te voegen, maar de spelers zien elkaars wapens gewoon nog in hun eigen richting. Weet jij hoe het wél moet? Bedankt!
PaPdeveloper op 20 Feb 2015, 16:03:20
Het werkt ongeveer nu. De spelers kunnen elkaars wapens zien, maar de image_angles van de wapens van alle spelers volgen de hele tijd de image_angle van één speler. En dat is de speler van het schermpje, die jij met dat schermpje bestuurt. Dus als ik een schermpje aanklik, zie ik de wapens van alle spelers in de richting van de speler die ik bestuur. En als ik dan een ander schermpje aanklik, zie ik alle wapens in de richting van de speler die ik dán bestuur. Ik heb geprobeerd om dit op te lossen door vóór het instance sync code de code: gms_instance_sync_var_add("image_angle", i.image_angle) te doen, maar de wapens stonden nog steeds in de zelfde richting als daarvoor. Weet jij hoe het 'wél moet? Bedankt!
Size43 (Beheerder) op 21 Feb 2015, 19:47:35
Je stelt waarschijnlijk de image_angle van de wapens in richting de muis. Overal waar je dat doet moet je nu een check doen of de de game de owner van het object is (gms_instance_is_owner). Enkel als dat zo is, stel je de image_angle in naar de muis.

Gebaseerd op wat je tot nu toe hebt gezegd, zou je ook dit kunnen doen:

Player object, step:
with obj_parent_wapens
{
    gms_self_set("wapen_sprite_index", sprite_index)
    gms_self_set("wapen_image_angle", image_angle)
}


Other player object, draw:
draw_self()
draw_sprite_ext(gms_other_get(player_id, "wapen_sprite_index"), image_index, x, y, image_xscale, image_yscale, gms_other_get(player_id, "wapen_image_angle"), image_blend, image_alpha);


Dan hoef je geen extra instanties te synchroniseren, en hoef je de wapen objecten niet aan te passen. :)
PaPdeveloper op 22 Feb 2015, 23:07:42
Heel erg bedankt! (en sorry nog voor al die reacties. Kun je die nog verwijderen:D?)
Size43 (Beheerder) op 23 Feb 2015, 17:13:09
Geen probleem :)

Het is niet mogelijk om comments te verwijderen of te editen. Als je je er heel erg aan ergert kun je de report link gebruiken, en dan kan ik ze verwijderen.
PaPdeveloper op 23 Feb 2015, 18:53:37
O van mij hoeft het niet persé, maar ik dacht dat jij je er misschien aan zou ergeren:).
Size43 (Beheerder) op 25 Feb 2015, 13:08:36
Ik heb geen echte admintools naast het kunnen verwijderen van gemeldde berichten. Als ik ieder bericht één voor één in de database zou gaan opzoeken zou dat veel te veel tijd kosten :)
PaPdeveloper op 1 Mar 2015, 19:53:21
Ik heb nu gms_instance_owner(id) geprobeerd, maar wat moet ik bij id invullen? Ik heb daar obj_speler.id ingevuld, maar dat werkt niet.
PaPdeveloper op 16 Feb 2015, 21:08:17
Is mijn reactie hiervoor verzonden? Ik zie hem hier namelijk niet staan.
PaPdeveloper op 16 Feb 2015, 21:14:19
Niet dus :D. Hier komt-ie nog een keer:

Het joinen werkt bij mijn game en de spelers kunnen elkaar nu wel zien. Maar de spelers hebben allebei wapens. Deze wapens zijn aparte objecten en zijn dus niet één met de speler. Ik heb geprobeerd om bij het Create event van de wapens gms_instance_synch(self, is_full | isc_local, image_angle, sprite_index) te zetten, maar de spelers kunnen elkaars wapens niet zien. Waarschijnlijk klopt hier dus helemaal niks van :D. Weet jij wat ik fout doe en hoe het wél moet? Alvast bedankt en verder een hele duidelijke uitleg hierboven!
Size43 (Beheerder) op 17 Feb 2015, 16:54:59
De reden waarom het synchroniseren niet werkt is waarschijnlijk omdat self in GameMaker -1 is, en niet de instance id. Je kunt daar dus beter de variabele id opgeven. Daarnaast moeten image_angle en sprite_index tussen quotes (als strings).

Maar wat je beter kunt doen is het wapentype/sprite/whatever als een variabele bij het spelersobject instellen, en vervolgens in het other player object die variabele opvragen en de sprite van het wapen tekenen.

Het wapen zit namelijk altijd vast aan de speler. Als je instance synchronisatie hiervoor gebruikt gaat de extensie twee keer dezelfde positie naar andere spelers verzenden. 1x de speler en 1x het wapen. Dat is natuurlijk zonde van alle data die daarmee verspilt wordt ;)
PaPdeveloper op 18 Feb 2015, 21:50:52
Maar ik maak dit spel met mijn broer en wij hebben al veel wapens en attachments voor die wapens gemaakt. Daarom zouden wij graag in het Create event van de parent van de wapens en attachments de synchronisatie willen hebben. Kan dit ook? Alvast bedankt!





(We hebben nu gebrobeerd in het Create event: gms_instance_synch(obj_speler.wapens,is_full | isc_local,"image_angle", "sprite_index" En bij obj_speler bij Step event: wapens = obj_parent_wapens)
Size43 (Beheerder) op 19 Feb 2015, 20:20:40
Aan je code te zien lijkt het alsof alle wapens alleen verschillen in sprite_index en image_angle. Klopt dat? Dan kun je het namelijk nog steeds zonder instanties te synchoniseren doen.

De rede waarom die instance sync niet werkt is omdat obj_speler.wapens geen instantie-ID is. Als je een object hebt dat een child van obj_parent_wapens is, kun je obj_parent_wapens.id gebruiken om de instantie-ID van het object te krijgen.