Hoe om Inskrywingswette in Jou Spel te Implementeer en te Gebruik
Afrikaans (Afrikaans) translation by Aisyah Arrafah (you can also view the original English article)
'N Spel word gewoonlik gemaak van verskeie verskillende entiteite wat met mekaar kommunikeer. Hierdie interaksies is geneig om baie dinamies en sterk geassosieer te wees met die spel. Hierdie handleiding dek die konsep en implementering van boodskapwyse stelsels wat entiteitinteraksies kan verenig, sodat jou kode hanteerbaar en hanteerbaar kan word, aangesien dit in kompleksiteit ontwikkel.
Inleiding
'N Bom kan met karakters reageer deur op te blaas en skade te veroorsaak, mediese kits kan 'n entiteit genees, slotte kan deure oopmaak, ensovoorts. In game interaksies is eindeloos, maar hoe kan ons speletjiekode hanteerbaar hou terwyl al die interaksies hanteer word? Hoe maak ons seker dat die kode kan verander en voortgaan om te werk wanneer nuwe en onverwagte interaksies verskyn?

Wanneer die interaksie bygevoeg word (veral die onverwagte), sal jou kode meer en meer lyk. 'N Naïef aansoek sal jou vinnig vra om vrae soos:
"Dit is entiteit A, so ek moet die skade metode skade ()
daarop aanspreek, of? Is dit skadeByItem ()
? Miskien is hierdie metode van skadeByWeapon ()
korrek?"
Stel jou voor die slegte gemors wat versprei na al jou spel entiteite, omdat hulle almal op verskillende en vreemde maniere met mekaar kommunikeer. Gelukkig is daar 'n beter, eenvoudiger en meer hanteerbare manier om dit te doen.
Boodskap Wachtrij
Voer die boodskap queue. Die basiese idee agter hierdie konsep is om alle spelinteraksie as 'n kommunikasiestelsel toe te pas (wat vandag nog in gebruik is): die uitruil van boodskappe. Mense kommunikeer deur middel van boodskappe letters) vir eeue, want dit is 'n effektiewe en eenvoudige stelsel.
In ons werklike posdiens kan die inhoud van elke boodskap anders wees, maar die manier waarop hulle fisies gestuur en ontvang word, bly dieselfde. Die sender stuur die inligting in die koevert en sy adres na die bestemming. Doelwitte kan antwoord (of nie) deur dieselfde meganisme te volg, net om die "van/na" veld op die koevert te verander.

Deur die idee op jou spel toe te pas, kan alle interaksies tussen entiteite as boodskappe beskou word. As 'n spel entiteit wil omgaan met 'n ander (of 'n groep van hulle), al wat jou hoef te doen is 'n boodskap te stuur. Doelstellings sal hanteer of reageer op boodskappe gebaseer op hul inhoud en wie die sender is.
In hierdie benadering word kommunikasie tussen spelentiteite 'n eenheid. Alle entiteite kan boodskappe stuur en ontvang. Maak nie saak hoe kompleks of vreemd die interaksie of boodskap is nie, die kommunikasiekanale bly altyd dieselfde.
In die volgende afdeling, sal ek verduidelik hoe jou hierdie boodskap wachtrijbenadering in jou spel kan toepas.
Ontwerp van 'n Koevert (Boodskap)
Kom ons begin met die ontwerp van die koevert, wat die mees basiese element in die boodskapkousstelsel is.
'N Omhulsel kan geteken word soos in die foto hieronder:

Die eerste twee velde (sender
en bestemming
) is verwysings na geskep instansies en entiteite wat onderskeidelik hierdie boodskap ontvang. Deur hierdie velde te gebruik, kan die sender en ontvanger weet waar die boodskap gestuur word en waar dit vandaan kom.
Twee ander velde (tipes
en data
) werk saam om te verseker dat boodskappe korrek hanteer word. Die tipe
kolom beskryf waaroor hierdie boodskap handel. As die tipe byvoorbeeld "skade"
is, sal die ontvanger hierdie boodskap hanteer as 'n bevel om sy gesondheidspunte te verminder; As die tipe "nastreef"
, sal die ontvanger dit beskou as 'n opdrag om iets te volg - en so aan.
Die data
veld verbind direk met die tipe
kolom. Ontmoet vordering vorigeeld, soos die boodskaptipe "skade"
, sal die data
veld 'n nommer bevat - sê, 10
- wat die hoeveelheid skade wat die ontvanger moet toepas op die gesondheidspunte beskryf. As die boodskapstype "nastreef"
, sal die data
'n voorwerp bevat waarin die teiken verduidelik word wat nagestreef moet word.
Die data
veld kan enige inligting bevat, wat die koevert 'n veelsydige kommunikasiemiddel maak. Enigiets kan in die veld geplaas word: heelgetalle, boeie, snare en selfs ander voorwerpe. Die basiese reël is dat die ontvanger moet weet wat in die data
veld is, gebaseer op wat in die tipe
veld is.
Al die teorie kan vertaal word in 'n baie eenvoudige klas genaamd Boodskap
. Dit bevat vier eienskappe, een vir elke veld:
Message = function (to, from, type, data) { // Properties this.to = to; // a reference to the entity that will receive this message this.from = from; // a reference to the entity that sent this message this.type = type; // the type of this message this.data = data; // the content/data of this message };
Byvoorbeeld, dit word gebruik, indien entiteit A
wil stuur "skade"
boodskap aan entiteit B
, al wat hy moet doen is om 'n voorwerp uit die klas te modelleer Skade
, stel die eiendom to
na B
, stel die eiendom van
na homself (entiteit A
), stel die tipe
na "skade"
en stel die data
uiteindelik tot 'n sekere getal (10
, byvoorbeeld):
// Instantiate the two entities var entityA = new Entity(); var entityB = new Entity(); // Create a message to entityB, from entityA, // with type "damage" and data/value 10. var msg = new Message(); msg.to = entityB; msg.from = entityA; msg.type = "damage"; msg.data = 10; // You can also instantiate the message directly // passing the information it requires, like this: var msg = new Message(entityB, entityA, "damage", 10);
Noudat ons 'n manier het om 'n boodskap te skep, is dit tyd om te dink aan die klas wat dit sal stoor en aflewer.
Implementeer 'n Waglys
Die klas wat verantwoordelik is vir die stoor en aflewering van die boodskappe sal genoem word MessageQueue
. Dit sal as poskantoor werk: alle boodskappe word aan hierdie klas oorhandig, wat verseker dat hulle na hul bestemming gestuur sal word.
Vir nou sal, die MessageQueuem
klas 'n baie eenvoudige struktuur hê:
/** * This class is responsible for receiving messages and * dispatching them to the destination. */ MessageQueue = function () { this.messages = []; // list of messages to be dispatched }; // Add a new message to the queue. The message must be an // instance of the class Message. MessageQueue.prototype.add = function(message) { this.messages.push(message); };
Eeiendom boodskap
is skikking. Dit sal al die boodskappe stoor wat deur MessageQueue
gestuur sal word. Die metode voeg ()
aanvaar voorwerpe uit die Boodskap
klas as 'n parameter, en voeg daardie voorwerp by die boodskaplys.
Hier is hoe ons vorige voorbeeld van entiteit A
boodskappe entiteit B
oor skade sou werk met die MessageQueue
klas:
// Instantiate the two entities and the message queue var entityA = new Entity(); var entityB = new Entity(); var messageQueue = new MessageQueue(); // Create a message to entityB, from entityA, // with type "damage" and data/value 10. var msg = new Message(entityB, entityA, "damage", 10); // Add the message to the queue messageQueue.add(msg);
Ons het nou 'n manier om boodskappe in 'n tou te skep en op te slaan. Dis tyd dat hulle hul bestemming bereik.
Boodskappe Lewer
Om die MessageQueue
klas daadwerklik 'n boodskap te stuur, moet ons eers bepaal hoe die entiteit die boodskap sal hanteer en ontvang. Die maklikste manier is om 'n metode te voeg met die naam onMessage ()
aan enige entiteit wat boodskappe kan ontvang:
/** * This class describes a generic entity. */ Entity = function () { // Initialize anything here, e.g. Phaser stuff }; // This method is invoked by the MessageQueue // when there is a message to this entity. Entity.prototype.onMessage = function(message) { // Handle new message here };
Die MessageQueue
klas sal die onMessage ()
metode van elke entiteit wat die boodskap moet ontvang bel. Die parameters wat aan die metode oorgedra word is boodskappe wat deur die tou stelsel gestuur word (en aanvaar deur bestemming).
Die MessageQueue
klas sal 'n boodskap in sy tou gelyktydig in die aflewering()
stuur:
/** * This class is responsible for receiving messages and * dispatching them to the destination. */ MessageQueue = function () { this.messages = []; // list of messages to be dispatched }; MessageQueue.prototype.add = function(message) { this.messages.push(message); }; // Dispatch all messages in the queue to their destination. MessageQueue.prototype.dispatch = function() { var i, entity, msg; // Iterave over the list of messages for(i = 0; this.messages.length; i++) { // Get the message of the current iteration msg = this.messages[i]; // Is it valid? if(msg) { // Fetch the entity that should receive this message // (the one in the 'to' field) entity = msg.to; // If that entity exists, deliver the message. if(entity) { entity.onMessage(msg); } // Delete the message from the queue this.messages.splice(i, 1); i--; } } };
Hierdie metode herhaal alle boodskappe in die tou en vir elke boodskap, veld om
gebruik te word om 'n verwysing na die ontvanger te kry. Die onMessage()
metode van die ontvanger word dan aangeskakel, met die huidige boodskap as 'n parameter, en die gelewer boodskap word dan uit die MessageQueue
lys verwyder. Hierdie proses word herhaal totdat alle boodskappe gestuur word.
Die gebruik van Boodskap Queuing
Dit is tyd om al die besonderhede van hierdie implementering saam te sien. Kom ons gebruik ons boodskapwinkelstelsel in 'n baie eenvoudige demo wat bestaan uit verskeie mobiele entiteite wat met mekaar kommunikeer. Ter wille van eenvoud sal ons met drie entiteite werk: Geneser
, Hardloper
en Jagter
.
Die Runner
het 'n gesondheidsbalk en beweeg lukraak. Healer
sal genees Runner
wat verbyloop; daarbenewens die Hunter
sal skade in die omgewing veroorsaak Runner
. Alle interaksies sal hanteer word deur gebruik te maak van die boodskapkoeistelsel.
Voeg Boodskap wagwoord by
Kom ons begin deur die PlayState
te skep wat 'n lys van entiteite (genesers, hardlopers en jagters) bevat en 'n voorbeeld van die MessageQueue
klas:
var PlayState = function() { var entities; // list of entities in the game var messageQueue; // the message queue (dispatcher) this.create = function() { // Initialize the message queue messageQueue = new MessageQueue(); // Create a group of entities. entities = this.game.add.group(); }; this.update = function() { // Make all messages in the message queue // reach their destination. messageQueue.dispatch(); }; };
In die speletjie-lus, verteenwoordig deur die update()
metode, word die metode van die wagwoord se aflewering()
gestuur, sodat alle boodskappe aan die einde van elke spelraam gelewer word.
Voeg die Runner
Die Runner
klas het die volgende struktuur:
/** * This class describes an entity that just * wanders around. */ Runner = function () { // initialize Phaser stuff here... }; // Invoked by the game on each frame Runner.prototype.update = function() { // Make things move here... } // This method is invoked by the message queue // to make the runner deal with incoming messages. Runner.prototype.onMessage = function(message) { var amount; // Check the message type so it's possible to // decide if this message should be ignored or not. if(message.type == "damage") { // The message is about damage. // We must decrease our health points. The amount of // this decrease was informed by the message sender // in the 'data' field. amount = message.data; this.addHealth(-amount); } else if (message.type == "heal") { // The message is about healing. // We must increase our health points. Again the amount of // health points to increase was informed by the message sender // in the 'data' field. amount = message.data; this.addHealth(amount); } else { // Here we deal with messages we are not able to process. // Probably just ignore them :) } };
Die belangrikste deel is die onMessage()
metode, wat deur die boodskapwachtrij genoem word wanneer daar 'n nuwe boodskap vir hierdie geval is. Soos vroeër verduidelik, word die tipe
veld in die boodskap gebruik om te besluit wat hierdie kommunikasie is.
Gebaseer op die boodskaptipe, word die korrekte aksie geneem: as die boodskaptipe "wanfunksie"
is, verminder gesondheidspunte; As die boodskap type "herstel"
, verhoog gesondheid punte. Die aantal gesondheidspunte wat verhoog of verminder word, word deur die sender in die data
veld van die boodskap gedefinieer.
In die PlayState,
voeg ons 'n paar hardlopers by die lys van entiteite:
var PlayState = function() { // (...) this.create = function() { // (...) // Add runners for(i = 0; i < 4; i++) { entities.add(new Runner(this.game, this.game.world.width * Math.random(), this.game.world.height * Math.random())); } }; // (...) };
Die gevolg is dat vier hardlopers willekeurig beweeg:
Voeg die Hunter
Die Hunter
klas het die volgende struktuur:
/** * This class describes an entity that just * wanders around hurting the runners that pass by. */ Hunter = function (game, x, y) { // initialize Phaser stuff here }; // Check if the entity is valid, is a runner, and is within the attack range. Hunter.prototype.canEntityBeAttacked = function(entity) { return entity && entity != this && (entity instanceof Runner) && !(entity instanceof Hunter) && entity.position.distance(this.position) <= 150; }; // Invoked by the game during the game loop. Hunter.prototype.update = function() { var entities, i, size, entity, msg; // Get a list of entities entities = this.getPlayState().getEntities(); for(i = 0, size = entities.length; i < size; i++) { entity = entities.getChildAt(i); // Is this entity a runner and is it close? if(this.canEntityBeAttacked(entity)) { // Yeah, so it's time to cause some damage! msg = new Message(entity, this, "damage", 2); // Send the message away! this.getMessageQueue().add(msg); // or just entity.onMessage(msg); if you want to bypass the message queue for some reasong. } } }; // Get a reference to the game's PlayState Hunter.prototype.getPlayState = function() { return this.game.state.states[this.game.state.current]; }; // Get a reference to the game's message queue. Hunter.prototype.getMessageQueue = function() { return this.getPlayState().getMessageQueue(); };
Die jagters sal ook beweeg, maar dit sal skade aan al die naaste hardlopers veroorsaak. Hierdie gedrag word geïmplementeer in die update()
metode, waar alle speletjie entiteite gekontroleer word en hardlopers boodskappe oor die skade sal stuur.
Die skadeboodskap word soos volg geskep:
msg = new Message(entity, this, "damage", 2);
Die boodskap bevat inligting oor die bestemming (die entiteit, in hierdie geval, wat die entiteit
geanaliseer is in die huidige iterasie), die sender (dit
, verteenwoordig die aanvallende jagter), die boodskap tipe ("skade"
) en die hoeveelheid skade (2
, dit is toegeskryf aan die boodskap data
veld).
Die boodskap word dan na die bestemming gestuur via die opdrag this.getMessageQueue(). Voeg (msg)
, wat die nuutgeskepte boodskap by die boodskapwachtrij voeg.
Ten slotte voeg ons die Hunter
by die lys van entiteite in die PlayState
:
var PlayState = function() { // (...) this.create = function() { // (...) // Add hunter at position (20, 30) entities.add(new Hunter(this.game, 20, 30)); }; // (...) };
Die gevolg is dat sommige hardlopers rondbeweeg, boodskappe van jagters ontvang terwyl hulle mekaar nader:
Ek het 'n vlieënde koevert bygevoeg as 'n visuele hulpmiddel om te help wys wat aangaan.
Voeg die Healer
Die Healer
klas het die volgende struktuur:
/** * This class describes an entity that is * able to heal any runner that passes nearby. */ Healer = function (game, x, y) { // Initializer Phaser stuff here }; Healer.prototype.update = function() { var entities, i, size, entity, msg; // The the list of entities in the game entities = this.getPlayState().getEntities(); for(i = 0, size = entities.length; i < size; i++) { entity = entities.getChildAt(i); // Is it a valid entity? if(entity) { // Check if the entity is within the healing radius if(this.isEntityWithinReach(entity)) { // The entity can be healed! // First of all, create a new message regaring the healing msg = new Message(entity, this, "heal", 2); // Send the message away! this.getMessageQueue().add(msg); // or just entity.onMessage(msg); if you want to bypass the message queue for some reasong. } } } }; // Check if the entity is neither a healer nor a hunter and is within the healing radius. Healer.prototype.isEntityWithinReach = function(entity) { return !(entity instanceof Healer) && !(entity instanceof Hunter) && entity.position.distance(this.position) <= 200; }; // Get a reference to the game's PlayState Healer.prototype.getPlayState = function() { return this.game.state.states[this.game.state.current]; }; // Get a reference to the game's message queue. Healer.prototype.getMessageQueue = function() { return this.getPlayState().getMessageQueue(); };
Die kode en sy struktuur is baie soortgelyk aan die Hunter
klas, behalwe vir sommige verskille. Soortgelyk aan die jagter se aansoek, die iteration update()
metode om die lys van in game entiteite op te gradeer, stuur boodskappe aan alle entiteite binne sy genesingsreeks:
msg = new Message(entity, this, "heal", 2);
Die boodskap het ook 'n bestemming (entiteit
), 'n sender (dit
, is die geneser wat die aksie uitvoer), 'n boodskaptipe ("genees"
) en die aantal genesingspunte (2
toegeken in die data
veld van die boodskap) .
Ons het bygevoeg Healer
na die lys van innerlike entiteite Playstate
net soos ons gedoen het Hunter
en die resultaat is 'n toneel met hardlopers, jagters en genesers:
En dis dit! Ons het drie verskillende entiteite wat met mekaar kommunikeer deur boodskappe uit te ruil.
Bespreking Oor Buigsaamheid
Hierdie boodskap wachtrij stelsel is 'n veelsydige manier om in-game interaksies te bestuur. Interaksie word gedoen deur 'n geïntegreerde kommunikasiekanaal en het 'n enkele koppelvlak wat maklik om te gebruik en implementeer.
Aangesien jou spel in kompleksiteit groei, kan nuwe interaksies nodig wees. Sommige van hulle kan heeltemal onverwags wees, so jou sal kode moet aanpas om dit te hanteer. As jou 'n boodskaptoewysstelsel gebruik, is dit nodig om iewers nuwe boodskappe by te voeg en elders te hanteer.
Verbeel jou byvoorbeeld dat jou wil skep Hunter
hang uit met die Healer
; jou moet net maak die Hunter
boodskappe met nuwe interaksies te stuur - byvoorbeeld, "flee"
-en verseker dat die Healer
kan dit in onMessage
metode hanteer:
// In the Hunter class: Hunter.prototype.someMethod = function() { // Get a reference to a nearby healer var healer = this.getNearbyHealer(); // Create message about fleeing a place var place = {x: 30, y: 40}; var msg = new Message(entity, this, "flee", place); // Send the message away! this.getMessageQueue().add(msg); }; // In the Healer class: Healer.prototype.onMessage = function(message) { if(message.type == "flee") { // Get the place to flee from the data field in the message var place = message.data; // Use the place information flee(place.x, place.y); } };
Hoekom nie Net Boodskappe Direk Stuur nie?
Terwyl die uitruil van boodskappe tussen entiteite nuttig kan wees, kan jy wonder hoekom MessageQueue
tog nodig is. Kan jy nie net die ontvanger metode opMessage()
noem in plaas daarvan om op MessageQueue
te staatmaak, soos in die kode hieronder nie?
Hunter.prototype.someMethod = function() { // Get a reference to a nearby healer var healer = this.getNearbyHealer(); // Create message about fleeing a place var place = {x: 30, y: 40}; var msg = new Message(entity, this, "flee", place); // Bypass the MessageQueue and directly deliver // the message to the healer. healer.onMessage(msg); };
Jou kan sekerlik so 'n boodskapstelsel implementeer, maar die gebruik van MessageQueue
het verskeie voordele.
Byvoorbeeld, deur sentrale boodskappe te sentreer, kan jou 'n paar koel kenmerke soos vertraagde boodskappe, die vermoë om boodskappe aan entiteitgroepe te stuur en visuele ontfoutingsinligting (soos die vlieënde koeverte wat in hierdie handleiding gebruik word) gebruik.
Daar is ruimte vir kreatiwiteit in die MessageQueue
klas, tot jou en jou spelbehoeftes.
Gevolgtrekking
Hantering van interaksies tussen spelentiteite wat die boodskaptoewysstelsel gebruik, is 'n manier om jou kode georganiseer en gereed te hou vir die toekoms. Nuwe interaksies kan maklik en vinnig bygevoeg word, selfs jou mees komplekse idees, solank dit as boodskappe opgesom word.
Soos bespreek in die handleiding, kan u die gebruik van sentrale boodskaptoewys ignoreer en slegs boodskappe direk aan entiteite stuur. U kan ook kommunikasie sentraliseer deur gebruik te maak van die stuur (MessageQueue
klas in ons geval) om in die toekoms plek te maak vir nuwe funksies, soos hangende boodskappe.
Ek hoop jou vind hierdie benadering nuttig en het dit bygevoeg aan jou spelontwikkelaar se spelgordel. Hierdie metode kan lyk as 'n oormaat vir klein projekte, maar dit sal sekerlik jou hoofpyn op die lang duur vir groter speletjies red.
Subscribe below and we’ll send you a weekly email summary of all new Game Development tutorials. Never miss out on learning about the next big thing.
Update me weeklyEnvato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post