Laten we eerlijk zijn:we spelen allemaal graag games, vooral op onze computers. Totdat internet wijdverbreid werd, speelden de meesten van ons alleen computerspellen, meestal tegen AI-tegenstanders. Het was leuk, maar zodra je je realiseerde hoe de gameplay-mechanica werkte, verloor de game het grootste deel van zijn magie.
De ontwikkeling van het internet verplaatste games online. Nu kunnen we tegen menselijke tegenstanders spelen en onze vaardigheden testen tegen die van hen. Nooit meer uit het hoofd spelen!
Toen kwamen er massale multiplayer online (MMO)-spellen op en veranderde alles. Duizenden spelers bevonden zich in dezelfde game-universums, wedijverden om middelen, onderhandelen, handelen en vechten. Om dergelijke spellen mogelijk te maken, was een databasestructuur nodig waarin alle relevante informatie kon worden opgeslagen.
In dit artikel zullen we een model ontwerpen dat de meest voorkomende elementen uit MMO-games bevat. We zullen bespreken hoe het te gebruiken, de beperkingen en de mogelijke verbeteringen.
Een inleiding tot datamodellen voor MMO-games
Er zijn tegenwoordig tal van zeer populaire MMO-spellen en ze omvatten allerlei scenario's. Ik zal me hier concentreren op strategiespellen zoals Ogame , Travian , Sparta :War of Empires en Imperia Online . Deze spellen gaan meer over plannen, bouwen en strategiseren, en minder over directe actie.
MMO-games spelen zich af in verschillende universums, zijn visueel verschillend en gebruiken min of meer verschillende gameplay-opties. Toch zijn sommige ideeën hetzelfde. Spelers strijden om locaties, vechten ervoor en vormen allianties met (en tegen) andere spelers. Ze bouwen structuren, verzamelen middelen en onderzoeken technologieën. Ze bouwen eenheden (zoals krijgers, tanks, handelaren, enz.) en gebruiken deze om te handelen met bondgenoten of om met tegenstanders te vechten. Dat alles moet in onze database worden ondersteund.
We kunnen deze spellen zien als online bordspellen met veel geïndexeerde vierkanten. Aan elk vierkant kunnen veel verschillende acties zijn gekoppeld; sommige acties zullen meerdere vierkanten bevatten - b.v. wanneer we eenheden of middelen van de ene naar de andere locatie verplaatsen.
De database is verdeeld in vijf hoofdgebieden:
Players / Users
Alliances
Locations and Structures
Research and Resources
Units
De overige zeven niet-gegroepeerde tabellen hebben betrekking op eenheden en beschrijven de positie van eenheden en bewegingen in het spel. We zullen elk van deze gebieden veel gedetailleerder bekijken, te beginnen met Spelers en Allianties .
Spelers en allianties
Zonder twijfel zijn spelers het belangrijkste onderdeel van elk spel.
De player
tabel bevat een lijst van alle geregistreerde spelers die deelnemen aan een spelinstantie. We slaan de gebruikersnamen, wachtwoorden en schermnamen van de spelers op. Deze worden opgeslagen in de user_name
, password
, en nickname
respectievelijk attributen.
Nieuwe gebruikers moeten tijdens de registratie een e-mailadres opgeven. Er wordt een bevestigingscode gegenereerd en naar hen verzonden, waarop ze zullen antwoorden. We updaten de confirmation_date
attribuut wanneer de gebruiker zijn e-mailadres verifieert. Deze tabel heeft dus drie unieke sleutels:user_name
, nickname
en email
.
Elke keer dat een gebruiker inlogt, wordt een nieuw record toegevoegd in de login_history
tafel. Alle attributen in deze tabel spreken voor zich. De logout_time
is specifiek. Het kan NULL zijn wanneer de huidige sessie van de gebruiker actief is of wanneer gebruikers het spel afsluiten (zonder uit te loggen) vanwege technische problemen. In de login_data
kenmerk, slaan we inloggegevens op, zoals de geografische locatie van een speler, het IP-adres en het apparaat en de browser die ze gebruiken.
De meeste MMO-spellen laten ons samenwerken met andere spelers. Een van de standaard vormen van samenwerking tussen spelers is de alliantie. Spelers delen hun in-game "privégegevens" (online status, plannen, locatie van hun steden en kolonies, enz.) met anderen om te profiteren van geallieerde acties en voor de pure lol ervan.
De alliance
tabel bevat basisinformatie over spelallianties. Elk heeft een unieke alliance_name
die we zullen opslaan. We hebben ook een veld, date_founded
, die opslaat wanneer de alliantie werd opgericht. Als een alliantie wordt ontbonden, slaan we die informatie op in de date_disbanded
attribuut.
Het alliance_member
tabel relateert spelers met allianties. Spelers kunnen meer dan eens lid worden en dezelfde alliantie verlaten. Hierdoor is de player_id
– alliance_id
paar is geen unieke sleutel. We bewaren informatie over wanneer een speler lid wordt van de alliantie en wanneer (indien) ze vertrekken in de date_from
en date_to
velden. De membership_type_id
attribuut is een verwijzing naar het membership_type
woordenboek; het slaat het huidige niveau van spelersrechten in de alliantie op.
De rechten van spelers in een alliantie kunnen in de loop van de tijd veranderen. De membership_actions
, membership_type
en actions_allowed
tabellen definiëren samen alle mogelijke rechten voor alliantieleden. Dit model staat spelers niet toe om hun eigen niveaus van rechten in een alliantie te definiëren, maar dat zou gemakkelijk genoeg kunnen worden bereikt door nieuwe records toe te voegen in het membership_type
woordenboek en het opslaan van informatie over aan welke allianties ze gerelateerd zijn.
Samenvattend:de waarden die in deze tabellen zijn opgeslagen, worden door ons gedefinieerd tijdens de eerste configuratie; ze veranderen alleen als we nieuwe opties introduceren.
De membership_history
table slaat alle gegevens op met betrekking tot de rollen of rechten van spelers binnen een alliantie, inclusief het bereik wanneer deze rechten geldig waren. (Hij kan bijvoorbeeld een maand "beginners"-rechten hebben en vanaf dat moment "volledig lidmaatschap".) De date_to
attribuut is NULLable omdat de huidige actieve rechten nog niet zijn beëindigd.
De membership_actions
woordenboek bevat een lijst van alle acties die spelers in een alliantie kunnen ondernemen. Elke actie heeft zijn eigen action_name
en spellogica is rond deze namen gebouwd. We kunnen waarden verwachten zoals “bekijk ledenlijst” , “bekijk de status van leden” en “bericht verzenden” hier.
Het membership_type
woordenboek bevat de unieke namen van de actiegroepen die in het spel worden gebruikt. De actions_allowed
tabel wijst acties toe aan lidmaatschapstypen. Elke actie kan slechts één keer aan een type worden toegewezen. Daarom is de membership_action
- membership_type
paar vormt de unieke sleutel voor deze tafel.
Locaties en structuren
Spellocaties zijn gebieden waar spelers middelen verzamelen en structuren en eenheden bouwen. Sommige games hebben een vooraf gedefinieerd bereik van mogelijke locaties, terwijl andere gebruikers hun eigen locaties kunnen definiëren.
In een 3D-ruimte kunnen locaties worden gedefinieerd met [x:y:z]-coördinaten. Als een game een vooraf gedefinieerd bereik heeft, is het mogelijk dat spelers geen locatie buiten het bereik [0:1000] voor alle drie de assen mogen gebruiken, dus we zijn beperkt tot een 1000 * 1000 * 1000 ruimte.
Aan de andere kant willen we misschien spelers toestaan om de exacte coördinaten van hun nieuwe locatie in te voeren - b.v. [1001:2073:4] – en we willen dat de game het voor hen verwerkt.
We houden een lijst bij van alle locaties die worden gebruikt in een instantie van onze game op de location
tafel. Elke locatie heeft zijn eigen naam, maar de namen zijn niet uniek. Aan de andere kant, de coordinates
attribuut mag alleen unieke waarden bevatten. Locatiecoördinaten worden opgeslagen als tekstwaarden, dus we kunnen coördinaten voor 3D-games opslaan als [112:72:235]. Coördinaten voor 2D-spellen kunnen worden opgeslagen als <1102:98>.
In sommige spellen hebben locaties een aantal vierkanten die worden gebruikt om gebouwen of eenheden te huisvesten. We houden die informatie in de dimension
attribuut, dat een tekstveld is. Een dimensie kan eenvoudig een aantal vierkanten in een 2D- of 3D-raster zijn. De player_id
attribuut slaat informatie op over de huidige eigenaar van die locatie. Het kan NULL zijn wanneer locaties vooraf zijn gedefinieerd en spelers strijden om ze te bezetten.
De structure
tabel bevat een lijst van alle structuren die we op verschillende spellocaties kunnen bouwen. Structuren vertegenwoordigen verbeteringen die ons in staat stellen betere eenheden te produceren, nieuwe soorten onderzoek uit te voeren, meer middelen te produceren, enz. Elke structuur die in het spel wordt gebruikt, heeft zijn eigen unieke structure_name
. Enkele mogelijke structure_name
waarden zijn "boerderij", "ertsmijn", "zonne-installatie" en "onderzoekscentrum".
We kunnen verwachten dat elke structuur meerdere keren wordt geüpgraded, dus we slaan ook informatie op over het huidige niveau. Elke upgrade verbetert de output van structuren, dus het produceert meer middelen of stelt ons in staat om nieuwe functies in het spel te gebruiken. We kunnen het maximale upgradeniveau niet van tevoren weten, dus we zullen alle niveaugerelateerde zaken (kosten, upgradetijd en productie) met formules definiëren. Alle formules die in de database zijn opgeslagen, vormen de kern van de spelmechanica en hun aanpassing is cruciaal voor de spelbalans en de gameplay in het algemeen.
Dat is ook het geval met de upgrade_time_formula
attribuut. Een voorbeeldwaarde voor dit veld is “
In de meeste gevallen zijn er vereisten waaraan moet worden voldaan voordat spelers bepaalde acties ondernemen. Misschien moeten we een bepaalde hoeveelheid onderzoek voltooien voordat we nieuwe structuren kunnen bouwen of omgekeerd. We slaan het onderzoeksniveau dat nodig is om structuren te bouwen op in de prerequisite_research
tafel. Relaties en het structuurniveau dat nodig is om verschillende onderzoeken te starten, worden bijgehouden in de prerequisite_structure
tafel. In beide tabellen zijn de refererende sleutels research_id
en structure_id
zijn gekoppeld om een unieke sleutel te vormen. De level_required
attribuut is de enige waarde.
Deze twee tabellen, prerequisite_research
en prerequisite_structure
, vormen ook de kern van het spel.
Voor elke structuur zullen we een lijst met vereisten definiëren:andere structuren en hun minimumniveaus die spelers moeten hebben om te beginnen met bouwen. We slaan deze gegevens op in de structure_required
tafel. Hier, structure_id
vertegenwoordigt de structuur die we willen bouwen; structure_required_id
is een verwijzing naar de vereiste structuur(en), en level
is het vereiste niveau.
De structure_built
tabel slaat informatie op over de huidige structuurniveaus op een bepaalde locatie. De upgrade_ongoing
kenmerk wordt alleen ingesteld als er momenteel een upgrade gaande is, terwijl de upgrade_end_time
attribuut zal een tijdstempel bevatten zodra de upgrade is voltooid.
De structure_formula
tabel heeft betrekking op structuren en middelen. Het externe sleutelpaar van deze tabel vormt zijn unieke sleutel. Deze tabel heeft ook twee tekstattributen met formules met upgrade_time_formula
. We hebben ze nodig omdat we de middelen moeten definiëren die worden besteed aan het bouwen van elke structuur. We moeten ook de productie van hulpbronnen definiëren na de upgrade, als de structuur enige hulpbronnen genereert (d.w.z. ertsmijn zal
Onderzoek en bronnen
Onderzoek (of technologieën) in games zijn meestal vereist voor het creëren van andere functies. Zonder bepaalde niveaus van onderzoek kunnen er geen nieuwe structuren of eenheidstypes worden gebouwd. Onderzoek kan ook zijn eigen vereisten hebben. Een van de meest voorkomende is het niveau van een bepaalde structuur, meestal een "onderzoekslab" genoemd. Of misschien moeten spelers een bepaald niveau van onderzoek voltooien voordat ze nieuw onderzoek kunnen starten. Al deze vereisten worden in deze sectie behandeld. Hieronder kunnen we het datamodel voor Onderzoek en Middelen vinden:
Het research
tabel bevat een lijst van alle mogelijke onderzoeksacties in ons spel. Het gebruikt dezelfde logica als de structure
tafel. De research_name
attribuut is de unieke sleutel van de tabel, terwijl de upgrade_time_formula
veld bevat een tekstweergave van de formule voor onderzoekstijdvereisten, met upgrade_formula
opgeslagen in de research_formula
tafel.
Net als bij structuren, zullen we de lijst met alle andere onderzoeken en hun niveaus definiëren die moeten worden voltooid voordat we een ander type onderzoek kunnen starten. We slaan deze gegevens op in de research_required
tabel, waar research_id
staat voor het gewenste onderzoek; research_required_id
is een verwijzing naar het vooronderzoek, en level
is het vereiste niveau.
Onderzoek is gerelateerd aan individuele spelers, en voor elke speler – onderzoek ch-paar moeten we het huidige onderzoeksniveau van een speler en eventuele lopende upgradestatussen opslaan. We slaan deze informatie op met behulp van het research_level
tabel op dezelfde manier waarop we de structure_built
tafel.
Hulpbronnen zoals hout, erts, edelstenen en energie worden gewonnen of verzameld en later gebruikt om structuren en andere verbeteringen te bouwen. We slaan een lijst op met alle bronnen in de game in de resource
woordenboek. Het enige kenmerk hier is de resource_name
veld, en het is ook de unieke sleutel van de tabel.
Om de huidige hoeveelheid bronnen op elke locatie bij te houden, gebruiken we de resources_on_location
tafel. Nogmaals, een buitenlands sleutelpaar (resource_id
en location_id
) vormt de unieke sleutel van de tabel, terwijl het number
attribuut slaat de huidige resourcewaarden op.
Eenheden en bewegingen
Middelen worden gebruikt om eenheden te produceren. Eenheden kunnen worden gebruikt om grondstoffen te vervoeren, andere spelers aan te vallen of in het algemeen te plunderen en te verbranden.
De lijst met eenheidstypes die in onze game worden gebruikt, is opgeslagen in de unit
woordenboek met slechts één waarde, unit_name
; dat attribuut is de unieke sleutel van deze tabel. Enkele veelvoorkomende speleenheden zijn "zwaardvechter", "gevechtskruiser", "griffin", "straaljager", "tank", enz.
We moeten elke eenheid beschrijven met specifieke kenmerken. Een lijst met alle mogelijke kenmerken wordt opgeslagen in de characteristic
woordenboek. De characteristic_name
veld een unieke waarde bevat. Waarden in dit veld kunnen zijn:"aanval", "verdediging" en "hitpunten". We zullen kenmerken toewijzen aan eenheden met behulp van de unit_characteristic
relatie. Het externe sleutelpaar van unit_id
en characteristic_id
vormen de unieke sleutel van de tafel. We zullen slechts één attribuut gebruiken, value
, om de gewenste waarde op te slaan.
De research_unit
tabel bevat een lijst van alle onderzoeksactiviteiten die moeten worden afgerond voordat we kunnen beginnen met de productie van een bepaald type eenheid. De unit_cost
tabel definieert de middelen die nodig zijn om een enkele eenheid te produceren. Beide tabellen hebben unieke sleutels die zijn samengesteld uit het externe sleutelpaar (research_id
of resources_id
gecombineerd met unit_id
) en één waardeveld (cost
en level_required
).
En nu het leuke gedeelte. Productie is leuk, maar eenheden verplaatsen en actie ondernemen is nog beter. We hebben de unit
tabel, maar we houden het hier omdat het verband houdt met andere tabellen.
Ofwel eenheden zijn gestationeerd op een locatie of ze verplaatsen zich tussen locaties. Het toevoegen van de player_id
veld bepaalt wie de eigenaar is van de locatie of de groep die zich tussen de locaties verplaatst.
Als eenheden net op de opgegeven locatie zijn gestationeerd, slaan we die locatie op en het aantal eenheden dat daar is gestationeerd. Hiervoor gebruiken we de units_on_location
tafel.
Als eenheden niet gestationeerd zijn, verplaatsen ze zich. We moeten hun vertrekpunt en hun bestemming opslaan. Daarnaast moeten we mogelijke acties tijdens bewegingen definiëren. Al dergelijke acties worden opgeslagen in de movement_type
woordenboek. De type_name
kenmerk is uniek terwijl de allows_wait
attribuut bepaalt of een actie wachten op het bestemmingspunt toestaat.
We kunnen een enkel type eenheid verplaatsen, maar in bijna alle gevallen zullen we veel eenheden van verschillende typen eenheden verplaatsen. Die groep deelt gemeenschappelijke gegevens en we slaan ze op in de group_movement
tafel. In deze tabel definiëren we de volgende items:
- de speler die die actie heeft gestart
- het actietype
- het startpunt
- het bestemmingspunt
- de
arrival_time
op de bestemming - de
return_time
naar het startpunt - de
wait_time
op de bestemming
De return_time
attribuut kan NULL zijn als dit een enkele reis is, en wait_time
wordt bepaald door de speler. Eenheden die tot een groep behoren, worden gedefinieerd door waarden die zijn opgeslagen in de units_in_group
tafel. Het externe sleutelpaar van units_id
en group_moving_id
vormt de unieke sleutel van de tafel. Het aantal eenheden van hetzelfde type binnen een groep wordt gedefinieerd in het number
attribuut.
Elke beweging kan middelen van de ene locatie naar de andere transporteren. Daarom definiëren we een veel-op-veel-relatie tussen de group_movement
en de resources
tafels. Naast de primaire en externe sleutels, is de resources_in_group
tabel bevat alleen het number
attribuut. In dit veld wordt de hoeveelheid middelen opgeslagen die spelers van het startpunt naar hun bestemming verplaatsen.
In de meeste gevallen kunnen spelers anderen bellen om mee te doen aan hun avontuur. Om dat te ondersteunen gebruiken we twee tabellen:allied_movement
en allied_groups
. Eén speler zal een gezamenlijke actie ondernemen en dat zal een nieuw record creëren in de allied_movement
tafel. Alle groepen eenheden die deelnemen aan een geallieerde actie worden gedefinieerd door waarden die zijn opgeslagen in de allied_groups
tafel. Elke groep kan slechts één keer aan een geallieerde actie worden toegewezen, dus buitenlandse sleutels vormen de unieke sleutel van deze tabel.
Dit model geeft ons de basisstructuur die nodig is om een MMO-strategiespel te bouwen. Het bevat de belangrijkste spelfuncties:locaties, structuren, middelen, onderzoek en eenheden. Het brengt ze ook met elkaar in verband, laat ons vereisten definiëren in de database en slaat ook de meeste spellogica op in de database.
Nadat deze tabellen zijn gevuld, is de meeste spellogica gedefinieerd en verwachten we niet dat er nieuwe waarden worden toegevoegd. Bijna elke tabel heeft een unieke sleutelwaarde, ofwel een functienaam of een extern sleutelpaar. Door de kenmerken van eenheden en productie-/kostenformules te wijzigen, kunnen we de spelbalans in de databaselaag wijzigen.
Hoe zou je dit model veranderen? Wat vind je leuk en wat zou je anders doen? Vertel het ons in het commentaargedeelte!