Ontwikkeling van een stapsgewijze strategie. Stap voor stap een strategie ontwikkelen. Voor zelfstandig werken

themapark:
We hebben nodig:
spriet bouwen
sprite-menu
SPRITES VAN VERSCHILLENDE KNOPPEN ZOALS:
srite met de inscriptie (constructie, constructie, build, enz.)
een venster dat verschijnt
bouwtekening,
1) de rest voegen we zelf toe
2) het woord namaak - door mijzelf bedacht, omdat we zullen het moeten vervalsen om overeen te komen met onze bron)
IILaten we beginnen:
1) maak alles wat in paragraaf I staat, behalve 1)
Laten we een globale variabele maken die geld heet, en een beginbedrag instellen
We zullen ook een muis- en toetsenbordobject maken
Laten we een tekst maken, deze info noemen, een altijd-gebeurtenis maken en er een actie in maken:
selecteer info in de actie selectie selecteer tekst instellen in de tekst schrijf dit:
"geld: " &(global("geld".
2) een menu toevoegen, de hoofdtaak van het menu is niet om te interfereren, maar om de speler te helpen navigeren (hoe kan het interfereren? - het is gemakkelijk als je het midden in het spel plaatst voordat je het menu maakt, laten we). maak een nieuwe laag, die we menu zullen noemen, in zijn verhoudingen (instellingen, opties) in het weergave-item dat we schrijven:


we zullen er een sprite aan toevoegen en de afbeelding nemen van het menu dat in de pre-productiematerialen zat (punt I) en ons menu op een afgelegen plek plaatsen waar het niet interfereert, maar zichtbaar zal zijn op het scherm
Laten we ook een knop plaatsen die is gemaakt van voorafwerkingsmaterialen (punt I) met de inscriptie BUILD (of iets dergelijks)
laten we het op het menu zetten
Ga nu naar de Gebeurtenisblad-editor
maak een evenement aan (#blah blah blah# - dit is mijn bericht (uitleg) aan jou alleen in plaats van blah blah blah zal er mijn commentaar voor je zijn; >> - actie; ll - verdeling van vensters bijvoorbeeld:

muis en toetsenbord ll op object geklikt ll links geklikt om bezwaar te maken #uw menuknop met de inscriptie BUILD (of iets dergelijks)##de rest later (zie 3e punt)#
3)nu het moeilijkste deel(Ik heb dit in twee punten opgesplitst, zodat het niet zo ingewikkeld zou zijn),
maak een sprite van pre-afwerkingsmaterialen “een venster dat zal verschijnen”
dan maken we een lege sprite genaamd p1, verplaatsen het venster van het scherm en plaatsen p1 op de plaats waar je venster zou moeten verschijnen als je op de build-knop drukt (of iets dergelijks)
geweldig! Ga nu naar de evenementenbladeditor
Laten we de onvoltooide gebeurtenis tot het einde opschrijven:
Tekst ll set tekst ll bla-bla-bla)
muis en toetsenbord ll op object geklikt ll links geklikt om object te maken #uw menuknop met het label BUILD (of iets dergelijks)#>>
4)Het tweede deel van het moeilijkste deel:
laten we een sprite maken waarin de afbeelding van het gebouw (materialen voor de afwerking) wordt getekend, laten we deze h1 noemen
laten we een lege sprite maken, noem deze p2, plaats deze nu op de plaats waar het venster zou moeten openen,
Laten we een sprite maken, ook een venster (voorafwerkingsmaterialen), in het venster zullen we prachtig de naam van het gebouw, de kosten en beschrijving ervan schrijven (optioneel) en het i1 noemen
laten we nog een lege sprite maken met de naam p3, deze naast p2 plaatsen, alleen zo dat hij alleen p2 raakt met de linkerbovenhoek
Laten we nu verschillende evenementen aanmaken, maar eerst maken we van de vorige gebeurtenis één nieuwe actie:
muis en toetsenbord ll op object geklikt ll links geklikt om object te maken #de knop van uw menu met de inscriptie BUILD (of iets dergelijks)#>> systeem ll maakt object relatief aan object ll #uw venster# #laagnummer onder het naammenu# # X ;Y-niet veranderen# naar object p1
>>systeem ll maak object relatief aan object ll #uw tweede venster# #laagnummer onder het naammenu# #X;Y-niet veranderen# naar object p2
We moeten hem ook een evenement teruggeven:
kopieer de gebeurtenis en keer deze om
nieuw evenement
muis&toetsenbord ll staat boven object ll h1>>systeem ll maak object relatief aan object ll i1 #laagnummer onder het naammenu# #X;Y-niet veranderen# naar object p3
Laten we een sprite maken met een gebouw (gebruik preproductiematerialen) en noemen het huis
Laten we een venster maken waarin onze gebouwen verschijnen wanneer ze in het menu worden geselecteerd, noem het rlo
evenementen:
muis&toetsenbord ll op object geklikt ll links geklikt naar h1>>systeem ll maken naar object relatief aan object ll huis #laagnummer onder het naammenu# #X;Y-niet veranderen# naar object rlo
>> systeem ll aftrekken van waarde ll #hoeveelheid geld die tijdens de bouw moet worden weggenomen#
Nu was het onmogelijk om een ​​evenement op te bouwen
Ik zal je mijn vroegere verbodsmethode vertellen (als ik klaar ben met schrijven, zal ik een andere methode onderzoeken die me blauw maakte toen ik me de game-themaparkwereld herinnerde)
evenementen:
huis ll bij botsing met een ander object ll tegen huis
>>huis zal vernietigen
>> systeem ll aftrekken van waarde ll - #verdubbel het geldbedrag dat tijdens de bouw is weggenomen##let op dat je moet zetten - aantal#
eigenlijk alles.
III wat ik wil zeggen:

Ik was erg overstuur door de ineenstorting van mijn spel. Het was een strategiespel en dit artikel is samengesteld volgens het schema. Ik vraag je om niet te veel kritiek te leveren, schreef ik al heel lang, als je spraakfouten vindt, schrijf dan en ik zal ze corrigeren
en ook hier is de broncode voor je kijkplezier, kijk, in principe is alles hetzelfde als hier geschreven, alleen dit was een demoversie van het spel, het belangrijkste is om niets te veranderen, anders zal het fouten bevatten !
gebruik het, experimenteer, controleer het, doe wat je wilt, het is allemaal goed voor mij

KaMiKaZa:
Alle “systeem”-expressies moeten worden opgenomen in de “Code”-tag.
Dan denk ik dat het beter zal gaan.
Het lijkt mij ook dat screenshots hier geen pijn zouden doen. En ook de broncode, voor beginners.

themapark:
Ik weet niet hoe ik screenshots van gebeurtenissen moet maken.

Nou, dat is niet nodig.

ikmnp:
thema_park, er is een speciale knop op het toetsenbord - PrintScreen

themapark:
Ik weet dat sommige mensen het gewoon anders doen. Bovendien heeft iedereen zijn eigen spriet
en als ik al deze sprites opzet, zullen maar weinig mensen het begrijpen.
Nou ja, misschien geeft iemand een plusje? Geen wonder dat ik het moeilijk had?

Burlachenko:
Om zo’n les voor iedereen interessant te maken, moet deze dienovereenkomstig worden opgemaakt, maar hier “hoe dan ook.”
En toch, als je wilt, beetje bij beetje, als je tijd hebt, ‘verfraai’ het dan alsjeblieft.

themapark:
Oké, ik kom thuis van school en maak me klaar.
PS. toegevoegde bron

Serega Lebedev:

iamnp, waar gaan deze screenshots later naartoe?

KaMiKaZa:

Naar het klembord.
Ga naar een willekeurige teksteditor en voer de bewerking "Plakken" uit, of druk op Ctrl+V.

Vorming van strategie
Amateurs kun je niet vertrouwen:
hun plannen kunnen onverwachts lukken,
en niemand is er klaar voor.

(A.Cuningham)

In de twee voorgaande nummers hebben we geleerd eenvoudige 2D-spellen maken, bestuur sprites, blader door het spelscherm, volg botsingen van spelobjecten, bouw een interface (knoppen, muis, toetsenbord, tekstgebieden) en werk in volledig scherm en in venstermodi. Dit alles werd gedaan aan de hand van een arcadespel als voorbeeld.

Deze keer gaan we van arcadespellen naar een meer “serieus” genre: strategieën. Hier zullen we een hele reeks nieuwe mechanismen onder de knie moeten krijgen, maar ook hier zal niets ingewikkelds zijn. In dit artikel wij Laten we de structuur van een turn-based strategie bestuderen(en ook realtime strategie(het is nog eenvoudiger om het te doen met LKI-Creator) en we zullen een spel als voorbeeld maken, echter alleen ontworpen voor meerdere gebruikers modus (en ook kaart-editor voor haar). We zullen de modus voor één speler behandelen in het volgende nummer van onze column - gewijd aan basisprincipes van kunstmatige intelligentie.

Omdat dit al de derde les is, zullen we niet in detail treden alle voorbeeldcode - gelukkig is veel precies hetzelfde gedaan als de twee voorgaande keren. Ter referentie is er een voorbeeldprogramma (er staan ​​​​veel opmerkingen in) en eerdere artikelen.

Welnu, je kunt de materialen van onze eerdere lessen vinden op onze CD, in de speciaal voor dit doel gecreëerde sectie 'Doe het zelf-spel'.

Verklaring van het probleem

Laten we een strategiespel schrijven dat bestaat uit een gevecht tussen twee fantasielegers. Het doel van de strijd is om er meerdere te vangen obelisken, op de kaart geplaatst. Voor de strijd zetten we onze troepen in, bestaande uit 6 zwaardvechters, 4 boogschutters, 2 ridders, 2 magiërs en 1 spook, binnen het ons toegewezen gebied. Naast hen zijn er neutrale op de kaart draken.

Kenmerken van strijders
Vechter Beweging Hits Bereik Schade Bescherming Mogelijkheden
Zwaardvechter4 8 1 7 2 -
Boogschutter4 5 7 5 1 -
Ridder3 15 1 9 4 Genezing, ridderslag
Magiër3 12 5 6 0 Vuurbol
Spook4 7 2 5 5 Regeneratie
Draak6 30 2 12 5 Vlucht

De kenmerken van de jagers worden in de tabel weergegeven. Behandeling- dit is het recht om een ​​naburige krijger (behalve een geest) één keer per gevecht tot volledige gezondheid te genezen. Ridderslag- het recht om één keer per spel drievoudige schade toe te brengen. Vuurbol- de aanval van de magiër verwijdert niet alleen hitpoints van het directe doelwit, maar ook van omliggende vierkanten. Regeneratie- herstel van 1 treffer per beurt. Vlucht- het recht om over obstakels heen te bewegen.

Het spel wordt gespeeld in de multiplayer-modus, in de Hot Seat-versie (speel vanaf één computer, draait één voor één). Nadat de spelers aan de beurt zijn, zijn neutrale draken aan de beurt en vallen ze elke vijand binnen een straal van 7 cellen aan.

Het feest eindigt wanneer een partij meer dan de helft van de obelisken op de kaart verovert of volledig sterft.

De kaart werd aanvankelijk ingesteld in de kaarteditor. Er zijn obelisken, draken en obstakels (objecten waardoor je niet kunt bewegen of aanvallen).

Voorbereiden op werk

Voordat we beginnen, moeten we het pakket opnieuw installeren LKI-Schepper. Feit is dat er, vergeleken met de vorige keer, veel veranderingen en toevoegingen aan zijn gedaan.

(Dat hoop ik Delphi je hebt het al geïnstalleerd; zo niet, lees dan de aanbevelingen over dit onderwerp in ons vorige artikel - in het juninummer van het tijdschrift of op de cd van dit nummer of op de website.)

Dit is belangrijk: de vorige versie van LKI-Creator had enkele compatibiliteitsproblemen met nieuwe versies van Delphi. In deze versie zijn ze geëlimineerd.

Neem het bestand met programmateksten en afbeeldingen van onze cd (sectie “Doe-het-zelf-spellen”) en pak het uit in de projectmap.

Nu kunt u de benodigde bestanden downloaden vanaf hier .

We zouden drie submappen moeten hebben. Eén - Units - slaat DirectX-bibliotheken en modules van het LKI-Creator-pakket op. In een ander - Project - gaan we aan de slag; De foto's die we nodig hebben en de vorige versie van ons arcadespel worden daar van tevoren geplaatst. In de derde - Escort - een kant-en-klaar programma waar we in moeten slagen.

Laten we nu LKI-Creator installeren (opnieuw installeren). Open in het Delphi-menu het onderdeel Component en selecteer Component installeren. Als u dit pakket al hebt geïnstalleerd, blijf dan op het tabblad In bestaand pakket, ga anders naar het tabblad In nieuw pakket en vul de lege regels in zoals weergegeven in de afbeelding (in de bovenste regel is de eenvoudigste manier om de LKI2dEngine te selecteren. pas-bestand met behulp van de knop Bladeren en onderaan gewoon LKI opschrijven). Klik vervolgens op OK en selecteer Installeren. Je zou nu het LKI-tabblad in het bovenste paneel van Delphi moeten zien.

Nu hoeft u alleen nog maar ons project te uploaden. In het menu Bestand selecteert u Openen en opent u het bestand Project\Obelisk.dpr…

Waar is de kaart, Billy? We hebben een kaart nodig!

Voordat we echter op de grote dingen ingaan, moeten we nog wat meer aan de grafische engine werken.

In Star Escort, ons vorige project, had de ‘kaart’ geen betekenis: de sterren werden willekeurig geplaatst en hadden nergens invloed op, en de positie van andere objecten werd rechtstreeks in de code gespecificeerd of door toeval bepaald. Dit is niet voor elk project geschikt. Dit betekent dat het tijd is om iets toe te voegen aan onze engine gebiedskaart.

U kunt waarschijnlijk al raden hoe het eruit zal zien: we plaatsen een kaartobject in het projectvenster en registreren het vervolgens in het pand Kaart onze motor.

Zo is het... maar we hebben meer dan één kaartklasse. Laten we het eens nader bekijken...

Soorten kaarten

De kaart bestaat uit iets landschap En voorwerpen erop geïnstalleerd. Het landschap wordt meestal (maar niet altijd) in zogenaamde cellen verdeeld tegels- tegels.

Zoals we weten uit een cursus geometrie op school, kan een vlak worden bedekt zonder gaten of overlappingen met regelmatige veelhoeken van drie typen: driehoek (gelijkzijdig), vierkant, zeshoek. Driehoekige velden zijn niet bijzonder handig, daarom worden vaker vierkante cellen of zeshoeken gebruikt.

In zekere zin is het leven met vierkanten eenvoudiger: als we een tweedimensionale reeks cellen hebben, is het meteen duidelijk hoe we de cellen naast een bepaalde cel kunnen vinden. Dit zijn +1 en -1 voor elk van de twee indices. Bij zeshoeken is alles iets ingewikkelder... maar het zeshoekige bord heeft een zeer waardevolle eigenschap: alle richtingen zijn hetzelfde. Bij een vierkant raster is dit niet het geval: de diagonalen verschillen aanzienlijk van de horizontale en verticale lijnen. Daarom kunnen zeshoeken voor serieuze strategische berekeningen beter zijn dan vierkanten.

Er zijn ook niet-betegelde kaarten. LKI-Creator ondersteunt twee typen: grafiek en patchwork.

Een grafische kaart is een kaart waarop slechts een paar belangrijke punten betekenis hebben, plus misschien speciale gebieden (bijvoorbeeld onbegaanbare gebieden), en de rest is slechts een patroon dat geen speleffect heeft. Dit is hoe sterrenkaarten vaak worden gemaakt, zoals in bijvoorbeeld Master of Orion: sterren en zwarte gaten zijn sleutelpunten, de rest is achtergrond. In deze modus maken ze soms wereldkaarten, bijvoorbeeld voor een rollenspel.

De patchwork-kaart is verdeeld in gebieden en binnen het gebied zijn alle punten hetzelfde; je kunt niet langs het “patchwork” bewegen. Dit is goed voor mondiale strategieën, waarbij een provincie de minimale territoriumeenheid is.

Voorbeelden van kaarten uit verschillende spellen, met vermelding van het type, staan ​​op de foto's.

De meerderheid dus tweedimensionaal kaarten (driedimensionaal - een speciaal artikel) kunnen in vier klassen worden verdeeld:

  • Rechthoekig- TLKIRectMap. Dit is een betegelde kaart, de cellen zijn vierkanten. Zo'n kaart bijvoorbeeld in Civilization III.
  • Zeshoekig- TLKIHexMap. Betegelde kaart met zeshoekige cellen. Wordt in veel wargames gebruikt, en niet alleen: zo werd bijvoorbeeld traditioneel de Heroes of Might & Magic-gevechtskaart gemaakt.

    Deze twee soorten kaarten zijn afstammelingen van de algemene klasse TLKITileMap.

  • Grafovaja- TLKIGraphMap. Deze kaart heeft achtergrond (achtergrondeigenschap) en de belangrijkste punten die erop zijn gemarkeerd, zijn statische objecten. De positie van andere objecten op deze kaart wordt uitgedrukt door gewone coördinaten (zoals een ruimteschip in de interstellaire ruimte) of door verwijzing naar een object (hetzelfde schip in een baan om een ​​planeet). Dit zijn de kaarten Master of Orion, Arcanum (wereldwijd), enzovoort.
  • Lapwerk- TLKI Clusterkaart. Het heeft een achtergrondeigenschap, zoals grafiek één, en een tweede eigenschap: masker, die bepaalt welk punt tot welke regio behoort, en de eigenschap Grenzen, die de verbindingen tussen de “snippers” definieert. Dit is hoe kaarten zijn gerangschikt, bijvoorbeeld in Medieval: Total War of Victoria.

Dit is belangrijk: mapklassen worden niet beschreven in de LKI2dEngine-module, maar in LKI2dMap.

Kantelhoeken

Maar als je denkt dat dit de mogelijkheden van LKI-Creator voor het weergeven van kaarten uitput, dan vergis je je volledig.

De kaart kan worden gepresenteerd bovenaanzicht of isometrisch- kijk vanuit een hoek ten opzichte van de verticaal. De kaart van Civilization III of Heroes of Might & Magic IV is bijvoorbeeld isometrisch, maar Civilization I heeft een bovenaanzicht.

Meestal wordt isometrie gebruikt voor naast elkaar geplaatste kaarten, terwijl grafiekkaarten worden gebruikt met een bovenaanzicht, aangezien de schaal van grafiekkaarten meestal kleiner is. Maar er zijn uitzonderingen: in Medieval: Total War is er bijvoorbeeld een lappendeken isometrische kaart.

De kaarteigenschap is verantwoordelijk voor isometrie Isometrisch en twee parameters die de hoek bepalen waaronder onze camera kijkt: Phi En Theta.

De eerste is verantwoordelijk voor de rotatie van de kaart ten opzichte van de verticale as: als u deze bijvoorbeeld instelt op 45 graden (gemeten in graden), wordt de cel van het rechthoekige raster met een opwaartse hoek georiënteerd, zoals in Beschaving. Bij Phi=0 zal een van de zijden van de cel horizontaal zijn.

De tweede regelt de kanteling van de camera ten opzichte van de verticaal. Voor het gemak wordt het gegeven als de verhouding tussen horizontale en verticale lengte-eenheden. Laten we zeggen dat als we willen dat onze cel in de hoogte half zo breed wordt getekend als in de breedte, we Theta op 2 moeten zetten.

Bij een tegelkaart mogen we deze hoeken niet willekeurig kiezen: 3D hebben we immers (nog) niet. Ze zijn rechtstreeks afhankelijk van de parameters van de tegels. Als we bijvoorbeeld een ruitvormige as hebben met een opwaartse hoek, en de verticale as is half zo groot als de horizontale as, dan moeten we de parameters 45 en 2 instellen.

Maar grafiek- en patchwork-kaarten geven je het recht om deze parameters naar eigen inzicht toe te wijzen (en ze desgewenst zelfs tijdens het proces te wijzigen), maar je moet je hier niet door laten meeslepen - naast het feit dat dergelijke beurten een tijdje duren veel tijd, ze zien er ook niet erg cool uit. En vergeet niet dat als je kaart artistiek is, met afbeeldingen, inscripties, enz., ze ermee zullen meedraaien... Over het algemeen is het soms gemakkelijker om een ​​lappendekenkaart te tekenen, rekening houdend met de vereiste rotatie - gelukkig, afstanden spelen daarbij vaak geen enkele rol.

Gewrichten

Lappendekenkaart, bovenaanzicht.

Tegelkaarten hebben nog een ander probleem: het verbinden van tegels. Het wordt bestuurd door de parameter TegelBorderStijl. Meestal dit tegelRecht, een modus waarin de tegels eenvoudig in elkaar passen zonder randeffecten, of tegelBorder, waarin lijnen worden getrokken die de ene tegel van de andere scheiden - de grenzen van de cellen (vergeet in het laatste geval niet om te definiëren kleur roosters in parameter TegelBorderKleur).

Maar er is een sluwere optie wanneer identieke tegels zonder veranderingen naast elkaar liggen en verschillende worden gebruikt met behulp van een speciale "overgangstegel". Dit wordt meestal gedaan als de kaart voornamelijk bestaat uit uitgestrekte gebieden van één type gebied, bijvoorbeeld grote groene gebieden, en een individuele cel niet belangrijk is en niet door de speler mag worden opgemerkt. Dit is de Heroes of Might Magic-kaart. Maar als elke cel afzonderlijk wordt verwerkt, zoals in Civilization, dan is deze methode niet geschikt en is het beter om de cellen duidelijk van elkaar te scheiden. “Fused”-technologie (ook wel masker) wordt opgegeven door de TileBorderStyle-waarde gelijk aan tegelGemaskerd. We zullen een andere keer over hun structuur praten - dit is een nogal complex onderwerp.

Tegel

Kaartelement - klasseobject TLKITiel- heeft een eenvoudige structuur. Het bevat in eerste instantie: coördinaten, de sprite die hem tekent, de tegeltypecode (die bepaalt wat we hier hebben - een heuvel, woestijn, weg, zee?) en crosscountry-vaardigheid (dit is relevant in de meeste games). De laatste is het aantal verplaatsingseenheden dat wordt besteed aan het verplaatsen door deze tegel land ploeg. Voor onbegaanbare tegels is dit een negatief getal.

Een andere parameter - Objecten, een lijst met objecten op deze tegel (type TLKIGameObject).

Om erachter te komen op welke cel is geklikt, heeft de kaart een methode MuisTegel(x,y) retourneert de geselecteerde tegel.

Tegelmethoden omvatten Is buurman(Tegel, afstand). Deze functie retourneert 'true' als de tegel zich niet meer dan afstandscellen van de gegeven tegel bevindt (standaard is deze parameter gelijk aan één, dat wil zeggen dat als u eenvoudig IsNeighbour(Tile) schrijft, de functie 'true' retourneert voor de tegel die er direct naast ligt. voor de gegeven tegel. Voor vierkante rasters worden de tegels die diagonaal grenzen ook als “buren” beschouwd.

Functies Eerste Buurman En Volgende buurman worden gebruikt om alle cellen naast een gegeven te controleren. De eerste wijst naar een buurcel, en de tweede kan alleen worden gebeld nadat de eerste is gebeld, en geeft de volgende buren één voor één door.

Opsomming van buren

// Schade aan een cel veroorzaken

procedure TObeliskTile.Damage(dmg: geheel getal);

als(Objecten. Aantal > 0) En// Misschien wel

// niet meer dan één object per cel

(Object.ID > 0) // Passieve objecten

// niet beschadigd

Dec(Objecten.Hits,

// Trek automatisch de bescherming tegen schade af

Max(0,dmg-(Objecten als TObeliskGameObject).Verdediging);

als Objecten.Hitsthen Die; // Wij verwijderen de doden

// Vuurbal aanval

procedure TObeliskTile.Vuurbal;

var Buurman: TObeliskTile;

Buurman:= EersteBuur als TObeliskTile;

Buurtschade(6);

Buurman:= VolgendeBuur als TObeliskTile;

tot Buurman = nul; // Tot de buren op zijn

Een voorbeeld staat in de zijbalk 'Buurten opsommen'. Deze procedure berekent de vuurbal die een cel en al zijn buren raakt.

Dit is interessant: voor haar werk het maakt helemaal niets uit, we hebben een zeshoekig rooster of een vierkant rooster.

Vaak hebben we nog een aantal andere parameters nodig, en meestal is dat de klasse tegels waaruit de kaart bestaat afstammeling TLKITiel. Dus in het voorbeeld wordt TObeliskTile overgenomen van TLKITile.

Dit is belangrijk: Als we een betegelde kaart naar ons spelscherm brengen, beginnen de coördinaten, evenals de afstandsgerelateerde TLKIGameObject-methoden, standaard de afstand in tegels te meten in plaats van in punten. De coördinaten van knoppen, pictogrammen, enz. blijven gemeten in pixels! Maar deze modus kan worden uitgeschakeld - dit kan handig zijn voor realtime strategieën.

Een kaart selecteren

Laten we dus beginnen met een rechthoekig rooster (TLKIRectMap), een isometrische afbeelding (hoekparameters 0, 1,5). Laat het raster tekenen (tileBorder-stijl). Laten we de engine vertellen dat deze specifieke kaart moet worden weergegeven. Tot nu toe zijn alle noodzakelijke acties voltooid zonder ook maar één regel code te schrijven.

Deze operaties moeten worden uitgevoerd naar engine-initialisatie, evenals lettertypedeclaratie.

We zullen de figuren, zoals voorheen, als sprites declareren.

Kaarteditor

Lappendekenkaart, isometrisch.

Er zijn hier nogal wat moeilijkheden. Exact dezelfde engine, dezelfde tegeldeclaraties... De interface, zoals het selecteren van een tegel, laden/opslaan, enz., kan eenvoudig worden gedaan met behulp van standaard Delphi-tools: niemand dwingt ons om deze naar volledig schermmodus te schakelen. We zullen dit hier niet analyseren - alles staat in het voorbeeldbestand. De voorbeeldcode gebruikt bewust de eenvoudigste methode; Indien gewenst kunt u bijvoorbeeld het objectenpalet en het tegelpalet grafisch maken.

De editor heeft slechts twee functies die ons onbekend zijn. De eerste is vrij eenvoudig: het is een nieuwe muisfunctie die speciaal is ontworpen voor tegelkaarten. Functie TLKIRectMap.SelectTile geeft een aanwijzer terug naar de exacte tegel waarop is geklikt, zodat we de klik gemakkelijk kunnen afhandelen.

Maar het tweede nieuwe product verdient een zorgvuldiger overweging.

Er zijn feitelijk veel manieren om gegevens in een bestand op te slaan en er gegevens uit te lezen. We kozen voor de methode die in het bestand is gecodeerd Kanonbasis. Cannon is een hulpmiddel voor het lezen en schrijven van afstammelingenobjecten TCannonObject met typecontrole en enkele andere functies.

Laten we eens kijken naar de code ("Schrijf kaart").

Een kaart opnemen

procedure TObeliskMap.Opslaan;

var i,j: geheel getal;

InitSave(FNaam);

WriteStr(kaartnaam);

Write(Map.Width, GrootteOf(Map.Width));

Write(kaarthoogte, groottevan(kaarthoogte));

voor ik:=0 naar Kaartbreedte-1 Doen

voor j:=0 naar Kaart. Hoogte-1 Doen

Write(Map.Tiles.Code, SizeOf(geheel getal);

Hier is hoe het werkt. Eerst moet u het bestand openen via een speciale procedure InitOpslaan, waarvan de enige parameter de bestandsnaam is. Vervolgens slaan we de header op voor typecontrole met behulp van een speciale procedure Schrijfkoptekst. Vervolgens schrijven we alles op wat we nodig hebben volgens de procedure SchrijfStr voor tekenreeksen, en voor alle andere velden - Schrijven(de tweede parameter is de grootte van de geschreven gegevens in bytes). U kunt indien nodig uw eigen procedures voor objectvelden schrijven Redden met een koprecord. Ten slotte sluiten we het bestand met de procedure FinSave.

Alle objecten die een eigen header hebben, moeten afzonderlijk worden gedeclareerd. In de sectie Initialisatie module (optionele sectie die erna komt Uitvoering, die opdrachten bevat die helemaal aan het begin moeten worden uitgevoerd, bij het starten van het programma), moet u bijvoorbeeld de volgende regel schrijven:

RegisterGebruikersnaam(tpMap, "TObeliskMap");

TpMap is een constante die u ook moet declareren. Vergelijk het met bijvoorbeeld 1. En wijs in de constructor van het TObeliskMap-object de waarde van deze constante toe aan de parameter TypeID.

Waarom al deze ophef? Naast typematching krijgt u één heel belangrijk voordeel.

Als het bestandsformaat verandert, bijvoorbeeld door de toevoeging van nieuwe velden, hoeft u geen “converters” te schrijven die de oude bestanden naar nieuwe converteren. Uw code zal ze automatisch lezen.

Deze code initialiseert het nieuwe veld automatisch als leeg als het niet in het bestand is opgeslagen. En u kunt een bestand schrijven door simpelweg de regel WriteStr(Name) helemaal aan het einde toe te voegen.

Opmerking: Als u nog steeds niet begrijpt waar dit proces voor is, hoeft u zich geen zorgen te maken. U kunt meer conventionele opname- en opslagmethoden gebruiken. Maar bij echt grootschalige gameprojecten biedt dit pad aanzienlijke voordelen.

Laten we spelen

Allereerst moeten we een nieuwe klasse maken die is afgeleid van TLKIGameObject. We zullen de eigenschappen van de oude missen. In de nieuwe klasse moet je velden toevoegen voor basiskenmerken: bereik, beweging, enzovoort.

Dit is belangrijk: Onze oude snelheidsparameter blijft bij ons, maar deze geeft de snelheid aan van het stuk dat over het scherm beweegt, en niet de afstand die het per beurt zal afleggen. Als we een real-time strategie zouden maken, zouden we geen nieuwe parameter nodig hebben, maar anders zullen we die moeten introduceren.

Op ons scherm zullen we TLKIButton-knoppen toepassen in de vorm van boogschutters, zwaardvechters, magiërs, geesten, ridders.

Eerst hebben we de regeling. Laten we de plaatsingszone voor de ene kant definiëren als de bovenste drie “lijnen” van de kaart, voor de andere kant als de onderste drie “lijnen”.

De code werkt als volgt. Wanneer je op een van de knoppen drukt, wordt de installatie van de bijbehorende jager geactiveerd; Als u op een leeg vierkant in het plaatsingsgebied klikt, wordt het figuur daar geplaatst en wordt de knop uitgeschakeld. Zodra alle knoppen zijn uitgeschakeld, wordt de zet overgedragen aan de vijand.

Aan het begin van elke nieuwe zet worden alle knoppen weer ingeschakeld: dit wordt gedaan zodat het voor een persoon gemakkelijker is om op te merken op wie hij nog niet lijkt. Dienovereenkomstig wordt door op de knop te klikken het figuur geselecteerd en zodra er een zet wordt gedaan, verdwijnt de knop. Een andere knop - "Beëindigen" - verschijnt pas na de plaatsingsfase.

De vorige keer hebben we al bewerkingen uitgevoerd om interface-elementen in en uit te schakelen, dus we zullen deze bewerking niet in detail analyseren - kijk naar de voorbeeldcode.

Verplaatsing van de figuur

// Als de geselecteerde cel bezet is door de vijand, vallen we aan,

// als we vrij zijn, verplaatsen we ons, als we bezig zijn met onze eigen dingen

// of een obstakel - negeer de klik

Tegel:= Map.MouseTile(MouseX, MuisY);

als(Tegel = nul)// Klik buiten het spelvenster

Dan Uitgang;

// Bewegen

als(Tegel.Objects.Count = 0)

En(Afstand(Zelf)

en niet Verplaatst Dan

// Laten we kijken of we daar kunnen komen

als niet HasWay (tegel) Dan Uitgang;

MoveObj(ID, Tegel.x, Tegel.y);

// Het spel is turn-based: beweeg onmiddellijk

Verplaatst:= waar;

//

als Aangevallen Dan

Icon.IsVisible:= vals;

// Aanval

als(Tegel.Objecten.Aantal > 0)

En(Afstand(Zelf)

en niet Aangevallen Dan

Obj:= Tegel.Objecten;

// Wij vallen alleen vijanden aan

als Obj.Zijde = Zijkant Dan Uitgang;

Obj.Schade(dmg);

Aangevallen:= waar;

// Als de verplaatsing voltooid is, verwijdert u het pictogram

als Verplaatst Dan

Icon.IsVisible:= vals;

De zet wordt als volgt verwerkt (zie “Stukzet”). De aangeklikte cel bevindt zich. Als er een vijand op staat en deze zich binnen bereik bevindt, wordt hij geschaad; als het stuk leeg is en binnen bereik, beweegt het stuk (als het bezet is, maar niet door een vijand, wordt de klik genegeerd). .

Toen beide partijen naderden, kwamen de draken in actie. Ze werken heel eenvoudig: ze selecteren de dichtstbijzijnde niet-draak die zich binnen 7 velden van hen bevindt en vallen aan. Zie de Dragon Actions-code.

Drakenacties

// Tegels controleren binnen 7 vierkanten van de draak

voor ik:= Max(0, x - 7) naar Min(Max.grootte, x + 7) Doen

voor j:= Max(0, y - 7) naar Min(Max.grootte, y + 7) Doen

if (Map.Tiles.Objects.Count > 0) En

(Kaart.Tiles.Objects.Code>1)

// 0 - obstakelcode, 1 - draak

Dan beginnen

// Een punt selecteren om te verplaatsen

als x=ik Dan bijl:=ik

anders als x>ik Dan bijl:=i+2

anders bijl:= i-2;

als y=j Dan ja:=j

anders als y>j Dan ja:= j+2

anders ja:= j-2;

MoveObj(NO, bijl, ay);

// Laten we aanvallen

Kaart.Tegels.Schade(12);

// De cyclus doorbreken: niet meer dan één aanval

// elke draak per ronde

Ten slotte hoeft u alleen nog maar te controleren of meer dan de helft van de obelisken bezet zijn door troepen van één kant. Als dat zo is, stop dan het spel en roep de winnaar uit!


We hebben dus een strategiespel. Voor volledig geluk ontbreekt echter in de eerste plaats kunstmatige intelligentie, die het mogelijk zal maken om het spel een modus voor één speler te geven (we beschouwen niet de eenvoudigste procedure voor het besturen van draken). Dat is wat we de volgende keer zullen doen. Tot over een maand!

In toekomstige nummers

In de volgende kwesties zullen we het hebben over:

  • deeltjessystemen voor het weergeven van rook, vonken, enz.;
  • werken met transparantie;
  • driedimensionale motoren;
  • AI-basisprincipes;
  • het debuggen van het programma;
  • het maken van een spelplan en script,
  • het schrijven van een ontwerpdocument;
  • spelbalans;
  • nadenken over spelkarakters en hun lijnen;
  • werken met Photoshop en 3D-pakketten;
  • animaties;
  • muziek en stemacteren;
  • en nog veel meer.

Het is heel goed mogelijk om te leren hoe je dit allemaal met je eigen handen kunt doen. Dit zul je snel zien.

Schrijf ons...

Voor degenen die denken dat het pakket met iets kan worden aangevuld: vergeet ten eerste niet dat er vandaag nog niet de definitieve versie van het pakket op onze schijf staat, maar alleen degene die de functies implementeert die in onze artikelen worden beschreven. Misschien zijn sommige van uw ideeën al geïmplementeerd en wachten ze op hun beurt (zie de kadertekst 'In toekomstige nummers'). En in ieder geval: probeer bij het aanbieden van een idee te rechtvaardigen waarom uw voorstel voor veel spellen tegelijk nuttig is, en niet alleen voor uw specifieke spel.

Voor zelfstandig werken

Terwijl u wacht op het volgende nummer, kunt u aan uw eigen project werken, of proberen dit te verbeteren. Hier zijn enkele ideeën die u zelf kunt implementeren:

  • verdeel obstakelobjecten in vernietigbare (bomen en struiken) en onverwoestbare (stenen), en zorg ervoor dat vuurballen en de adem van de draak de vegetatie verbranden;
  • maak putten (bruine cel) of een vuur dat meerdere beurten brandt (rode cel) op de plaats waar de brandaanval werd geactiveerd;
  • laat zwaardvechters en ridders hun buren bedekken, waardoor ze +1 krijgen voor de verdediging;
  • maak de beweging van figuren op het scherm soepel.

Wat als het in realtime is?

Het is niet moeilijker om een ​​real-time strategiespel te maken als je spelers verschillende inputmiddelen geeft. De eenvoudigste manier om dit te doen is via het netwerk - we zullen hierover praten in een van de komende nummers. Daarnaast zijn de volgende wijzigingen nodig:

  • geen veld nodig Spelsnelheid in de klas TObeliskObject- gebruik Snelheid van de basisengine (de bewegingssnelheid over het scherm is gelijk aan de spelsnelheid);
  • berekening van afstanden met gehele getallen is uitgeschakeld;
  • de code voor de beweging van de figuur wordt herschreven, rekening houdend met het feit dat het nodig is om een ​​traject rond obstakels te tekenen;
  • De knop "einde zet" is verwijderd.

Dat is alles. Ga je het zelf proberen?

Het is onwaarschijnlijk dat ik in dit artikel iets onbekends zal bespreken. Alle berekeningen zijn eenvoudig en begrijpelijk voor iedereen die weet wat Ajax is. Ik heb al een artikel geschreven over het combineren van een client met een server in realtime games (). In dit artikel behandel ik dezelfde problemen als die van toepassing zijn op turn-based games.

Dus wat is turn-based spel? De volgende definitie is te vinden op Wikipedia turn-based strategie - is een genre van computerspellen waarvan het belangrijkste kenmerk is dat spelers om beurten zetten doen, in tegenstelling tot realtime strategie. Ik zou deze definitie een beetje vereenvoudigen:

  • Turn-based strategie - is een turn-based strategiespel.
  • Strategie spel - Dit is een spelgenre waarin planning en strategisch denken de sleutel zijn tot het behalen van de overwinning..
  • Turn-based spel - is een spelgenre waarvan het belangrijkste kenmerk is dat spelers om beurten bewegingen maken.
Turn-based games zijn onder meer:
  • Turn-based strategieën
  • Kaartspellen
  • Bordspellen (schaken, go, monopolie, etc.)
Ik merk op dat turn-based games minder beperkingen opleggen aan de complexiteit van het interactieprotocol vergeleken met real-time games. Namelijk, reactietijd op een bepaalde gebeurtenis speelt geen sleutelrol. De speler krijgt doorgaans 10 seconden de tijd om een ​​beslissing te nemen. Zelfs als de ping gigantisch is, bijvoorbeeld 3 seconden, heeft de speler nog 7 seconden om na te denken. Bovendien kan de ping springen en springen, maar dat kan ons helemaal niets schelen (in realtime games doodt deze situatie vrijwel elk protocol).

Normaal gesproken neemt (in 95% van de turn-based games) precies één speler op een bepaald moment de beslissing. Het aantal verzoeken waarop wij adequaat moeten reageren, neemt daardoor af.

Daarom zullen we ons bij het maken van een protocol vooral richten op het gemak van de implementatie en ondersteuning ervan. Hierdoor kunnen we in minder tijd meer winst maken.

Bovenstaande redenering gaat uit van de 2 maanden durende ontwikkeling van een bepaald kaartspel.

Slimme of domme klant?

Laten we eerst beslissen hoe 'slim' onze klant kan zijn. Ik twijfel of het de moeite waard is om de applicatielogica (spelregels) op de client te dupliceren. Uiteraard moet de server slim zijn om mogelijke hacking van de applicatie te voorkomen. Maar is het de moeite waard om de klant bedrijfslogica te leren?

Dit hangt direct af van hoeveel de volledige hoeveelheid gegevens over de staat van je spel weegt. Als deze hoeveelheid data groot is, het lang duurt om deze op de server te verzamelen en naar de client wordt overgebracht, dan is het zinvol om een ​​deel van de logica op de client te implementeren om de server te ontlasten. In Civilization staat de gebruikte geheugenmeter bijvoorbeeld altijd buiten de hitlijsten. Kun je iets soortgelijks maken, waarbij alleen de gebruikersinterface op de client overblijft?

Aan de andere kant: hoe slimmer de klant, hoe duurder de game-ontwikkeling zal zijn. Ik wil opmerken dat de ontwikkelingstijd van de server op geen enkele manier afhankelijk is van de eruditie van de klant. Zelfs als de client super-duper-mega slim is, zal de server, als de gebruiker het browservenster opnieuw wil laden, alle gegevens over het spel moeten verzamelen en verzamelen om deze naar de client over te dragen. A la "Een opgeslagen spel laden". Conclusie: Een smart client kan een applicatie versnellen, maar zal altijd extra middelen nodig hebben om de applicatie te ontwikkelen.

Ik stel het volgende voor test:

1. Staat het kanaalvolume dit toe?

Schat het gemiddelde gewicht van de volledige hoeveelheid gamestatusgegevens. Vermenigvuldig vervolgens met het gemiddelde aantal verzoeken aan de server per seconde. Als het ontvangen aantal de capaciteit van het uitgaande datakanaal overschrijdt, is de domme klant onaanvaardbaar. Als dit aantal groter is dan 20% van het uitgaande kanaal, moet je nadenken of het zal werken?

2. Is het arbeidsintensief?

Schat de complexiteit van het algoritme voor het verzamelen van gamegegevens (in fracties van een seconde). Houd hierbij rekening met alle vragen aan de database. Vermenigvuldig vervolgens met het gemiddelde aantal verzoeken aan de server per seconde. Als de tijd langer is dan één seconde, is een domme klant onaanvaardbaar. Als dit aantal groter is dan 200 ms, moet u zich afvragen of dit zal duren?

Voortzetting: