Raster-gebaseerd Pad Vinden
In deze tutorial maken we een simulatie voor padvinden op basis van een 2-dimensionaal array grid. Dit zal ons de basisideeën geven voor het creëren van padvindactiviteiten in onze games. We zullen het grid visualiseren met lege vierkante objecten als tegels en een rond object als onze avatar die kan bewegen binnen dat gridgebied op commando.
I. Het opzetten van de objecten
We zullen slechts vier objecten in ons project gebruiken. Onze groene avatar, een blauwe tegel, een witte blokkade, en het gridlabel voor het opzetten en visualiseren van ons grid.
Avatar
Onze avatar is een leeg object met een x- en y-schaal van 50%. Zorg ervoor dat de botsing is ingesteld op een ronde vorm, zodat het eruitziet zoals in de afbeelding hieronder. Dit object zal drie vooraf gedefinieerde attributen hebben: on tile, previous tile, en target tile. Stel de standaardwaarden van deze attributen in op nul, zodat ze geen null-waarde hebben wanneer we ze later proberen op te halen.
Tegelobject
Maak een ander leeg object aan dat zal worden gebruikt om de tegels in ons grid te visualiseren. De schaal is ook 50% en heeft slechts één vooraf gedefinieerd attribuut: on tile. Stel deze waarde ook in op nul.
Blokkadeobject
Maak een ander leeg object aan dat zal dienen als blokkades in ons grid. Dit is een wit leeg object met een schaal van 40%. Onze avatar zal niet in staat zijn om over een tegel heen te bewegen die een blokkade heeft.
Gridlabel
Dit is gewoon een label dat de gedragingen heeft die het opzetten en visualiseren van ons grid mogelijk maken. Het heeft vooraf gedefinieerde attributen: start x met een waarde van 4 en start y met een waarde van 3. Deze waarden zijn offsets die zullen worden gebruikt als referenties bij het spawnen van onze tegels om ons grid netjes in het midden te positioneren.
Avatar/Tegel vooraf gedefinieerde attributen:
We definiëren een tegel hier als een 2-grootte array die een x- en y-waarde heeft op basis van ons grid.
- on tile - dit is de tegelwaarde waarop ons object zich momenteel in ons grid bevindt
- previous tile - een tegel die eerder is doorkruist door onze avatar
- target tile - de tegel waar onze avatar naartoe zal gaan
II. Het opzetten van het grid
In het gridlabel definiëren we onze 2D-array en het spawnen van ons grid. Er zijn 2 hoofdpunten in dit object: het instellen van de array en het visualiseren van de array. Laten we eerst het set-array-bundel doen.
Stel de array in - bundel
Eerst maken we een lege array met de naam 'Grid'. Deze array bevat onze 2-dimensionale array. We hebben een box-container genaamd 'x row count' met een waarde van 24 en een andere lege array genaamd 'y row' die 18 lege waarden heeft.
Het eerste gedrag in onze bundel is een loop met een herhalingsaantal van 'x row count'. Voor elke stap van deze loop voegen we een lege y-row toe aan de 'Grid-array'. Dit zal een array binnen een array maken met een dimensie van 24x18.
Opmerking: we hebben een inactieve opmerking aan het begin van onze bundel verbonden om de automatische uitvoering van dit root-gedrag te negeren. Een root-gedrag is een gedrag dat niet wordt geactiveerd op basis van een gebeurtenis en openlijk in de interface is geplaatst.
Visualiseer het grid - bundel
Het belangrijkste dat we hier gaan doen, is elk waarde van onze 'Grid-array' doorlopen en een tegelobject spawnen. We zullen ook een voorwaarde toevoegen aan onze loop die het spawnen van een witte blokkade bovenop het tegelobject mogelijk maakt.
Eerst halen we de array-telling van onze 'y-row array', de 'start x'-attribuutwaarde van dit object en de 'start y'.
Maak een loop die een herhaling heeft van 'x row count' en voor elke stap van deze loop voegen we de indexwaarde van die loop en de 'start x'-attribuutwaarde toe. Dit resulteert in een absolute x-positie die we later zullen gebruiken om onze gespawnde tegels te positioneren. Vervolgens is er de 'y-row loop' die een herhaling heeft van 'y row count'. Voor elke stap van deze loop voegen we de indexwaarde van die loop en de 'start y'-attribuutwaarde toe, wat nu resulteert in de absolute y-positie.
Na het toevoegen van de waarden onder de 'y-row loop' is het tijd om het blauwe object te spawnen. Stel het aantal actieve objecten ervan in op 999, omdat we er een heleboel nodig hebben voor ons grid, en ook met een duur van 0. Verplaats dat gespawnde object naar een x-punt met een waarde van de x-loop 'toegevoegde waarden', en het y-punt met de waarde van de y-loop 'toegevoegde waarden'.
Vervolgens voegen we een array toe met 2 lege waarden genaamd 'On tile' die we alleen als een array-placeholder zullen gebruiken. Na het verplaatsen van het tegelobject, is het tijd om de 'On tile'-array te wijzigen door de waarde op index 0 te vervangen door de waarde van de 'x-row loop'. Voeg daarna een andere array-modificatie toe die de waarde op index 1 vervangt door de 'y-row index'. Dit zijn de x- en y-coördinaten van ons grid die het object zal hebben als een 'on tile'-attribuut.
Nu stellen we het attribuut van het gespawnde object in met een dynamische sleutel, 'on tile', en met een waarde van 'On tile - array'.
Ondertussen, onder de y-row loop, is er de voorwaarde voor het spawnen van het witte blokkadeobject. We zullen een kans van 2 op 5 hebben om deze bundel uit te voeren, wat zal bijdragen aan de randomisatie van ons grid. Vervolgens markeren we een waarde van 1 voor de x- en y-coördinaat daarvan in onze 'Grid'-array, wat ons vertelt dat die coördinaat een gespawnde blokkade heeft.
Eerst moet er een willekeurig nummer van 1 tot 5 zijn. Als dat nummer kleiner dan of gelijk aan 2 is, spawnen we de blokkade. Stel het aantal actieve objecten in op 999 en verplaats dat object naar hetzelfde punt als de blauwe tegel die we eerder hebben gespawnd, ook met een duur van 0.
Vervolgens moeten we de informatie opslaan dat deze coördinaat door een blokkade werd bezet, als deze werd gespawnd. We doen dit door die coördinaat te markeren met een waarde van 1. Eerst moeten we de waarde op index 'x-row loop' van de 'Grid'-array ophalen. Deze waarde geeft ons een array van een 'y-row'. We zullen deze 'y-row' wijzigen door de waarde op index 'y-row loop' te vervangen door de waarde 1, wat de gemarkeerde waarde is. Omdat deze waarde-modificatie alleen van toepassing is op die waarde en niet op de 'Grid' zelf, moeten we de 'Grid'-array nog steeds aanpassen. Vervang de waarde op index 'x-row loop' van de 'Grid'-array door de waarde van 'modify y-row - array'.
Eenmaal gespeeld, zou het tot deze soort vorm moeten resulteren.
III. Op Tile Druk
Om met het grid zelf te kunnen interageren, hebben we een bundelgedrag nodig telkens wanneer we op een blauwe tegel drukken. De eerste druk resulteert in de verschijning van onze avatar in ons grid, en elke volgende druk na dat evenement zal ons padvinden starten.
In het blauwe object is een gedrag bundel die 3 hoofdpunten heeft: het resetten van de vorige tegel van onze avatar, het instellen van zijn on tile/zijn target tile, en dan een kleuranimatie instellen om het aanraakevenement op het scherm te visualiseren.
Eerst moeten we het 'on tile'-attribuut van het tegelobject ophalen. Stel het 'previous tile'-attribuut van onze avatar in op 0. Onze avatar wordt ingesteld zodat deze een 'previous tile' vermijdt, daarom resetten we de 'previous tile' bij elke tegel-pers.
Vervolgens moeten we het 'on tile'-attribuut van onze avatar ophalen. De eerste voorwaarde is of de waarde van 'on tile' gelijk is aan nul. Dit betekent dat als de avatar nog niet is verschenen, dit waar zal zijn. Als het waar is, stellen we het 'on tile'-attribuut in op de waarde van het 'on tile'-attribuut van het tegelobject. Vervolgens verplaatsen we de positie van onze avatar door de positie van het tegelobject op te halen, waarbij we zijn x- en y-waarden naar dat punt wijzen met een duur van 0.
Als de waarde van 'on tile' niet gelijk is aan nul, dan stellen we het 'target tile'-attribuut van onze avatar in op de waarde van het 'on tile'-attribuut van het tegelobject. Dit betekent dat onze avatar zich al in ons grid bevindt, en dan kunnen we de gedrag bundel 'find path' uitvoeren. We hebben deze gedrag echter nog niet, maar we zullen dat opnieuw oplossen nadat we het latere deel van deze tutorial voor de 'find path'-bundel hebben gedaan.
Nu we onze avatar goed in ons grid hebben ingesteld, hoeven we alleen dit aanraakevenement te visualiseren. Eerst hebben we een root-gedrag 'get color' om zijn oorspronkelijke kleur te krijgen. Vervolgens, na het aanraakevenement, stellen we de kleur van het tegelobject in op zwart met een duur van 0, en stellen we de kleur daarna weer in op zijn oorspronkelijke waarde met een duur van 0,2.
Drukken op een blauwe tegel zou dit resultaat moeten opleveren.
IV. Voer de Find Path-bundel uit
Voor het laatste deel van onze tutorial zullen we het algoritme implementeren dat ons in staat stelt om de dichtstbijzijnde tegel te vinden die onze avatar kan nemen om zijn bestemming te bereiken. Deze bundel zal herhaaldelijk worden uitgevoerd voor elke tegelstap die onze avatar op ons grid verplaatst.
We zetten het algoritme op binnen ons avatarobject en dit zal drie hoofdpunten hebben: het vinden van aangrenzende tegels, het vinden van de dichtstbijzijnde aangrenzende tegel en de beweging naar die aangrenzende tegel.
Het vinden van de aangrenzende tegels
In dit deel zullen we alle aangrenzende tegels verzamelen die onze avatar kan doorkruisen. We zullen de aangrenzende tegels in vier richtingen controleren en zien of die tegels een blokkade hebben of dat ze eerder zijn bewandeld.
Eerst voegen we onze 'Adjacent tiles' array toe. Een 'Direction' array met 2 lege waarden, en de vier richtingsarrays die elk 2 waarden hebben: N (0,1), S (0,-1), E (1,0), W( -1,0).
Het eerste gedrag in onze bundel is om onze aangrenzende tegels te wissen. Omdat we deze bundel herhaaldelijk zullen uitvoeren, moeten we onze verzamelde 'aangrenzende tegels' elke keer resetten. De volgende vier gedragingen zijn aanpassingen aan de 'Direction'-array. We stellen de array van de 'Direction'-array vier keer in met de vier richtingsarrays, en voeren vervolgens de bundel hieronder uit. We doen dit om een loop te creëren met verschillende waarde van de 'Direction'-array elke keer.
Nu we de richting hebben, hoeven we alleen hun x- en y-waarden te combineren met het 'on tile'-attribuut van onze avatar. Dan kunnen we onze doelwaarde in de 'Grid'-array krijgen om te weten of die tegel was gemarkeerd met een blokkade.
'Direction x' is de waarde van de 'Direction'-array op index 0, 'get on tile' x is de waarde van 'get on tile' op index 0. Target x is de toevoeging van 'direction x' en 'get on tile x'. Hetzelfde geldt voor y-waarden, maar met een doelindex van 1.
'Target y row' is de arraywaarde van de 'Grid'-array op index 'Target x'. En dan is de 'Target index in y row' de waarde van de 'Target y row'-array op index van 'Target y'. Dat is de coördinaat in ons grid die ons zal vertellen of het is gemarkeerd met een blokkade.
Als de waarde in 'Target index in y row' gelijk is aan 1, dan is die doeltegel gemarkeerd als geblokkeerd. Maar als de waarde niet gelijk is aan 1, kunnen we doorgaan met onze bundel.
Aangezien onze Target x en y onze voorwaarde zijn gepasseerd, hebben we een nieuwe lege tegelarray toegevoegd met de naam 'New adjacent tile'. Dit zal de waarden van Target x en y opslaan als een tegelarray. We zullen de waarde van Target x op index 0 van 'New adjacent tile' instellen en Target y op index 1.
Haal nu het 'previous tile'-attribuut van onze avatar op. Als de 'previous tile' niet gelijk is aan de 'new adjacent tile', betekent dit dat de nieuwe tegel niet eerder is doorkruist. Dan kunnen we veilig de 'New Adjacent tile' aan de 'Adjacent tiles'-array toevoegen door deze toe te voegen. We zullen de waarde van 'previous tile' later instellen nadat we onze avatar met succes naar een andere tegel hebben verplaatst.
"Je kunt deze bundel uitbreiden door een 8-richtingarrays te maken, waarmee je avatar naar diagonale richtingen kan bewegen."
Het vinden van de dichtstbijzijnde aangrenzende tegel
Nu we onze betrouwbare aangrenzende tegels hebben verzameld, hoeven we alleen de aangrenzende tegel te vinden die het dichtst bij de doeltegel van onze avatar ligt. We zullen de berekening doen met behulp van een afstandsformule die wordt geleverd door de x- en y-waarden van onze doeltegel en de aangrenzende tegels.
Er zijn twee hoofdpunt in deze bundel: de initialisatie van variabelen voor berekening en de berekening van tegelafstanden binnen een loop.
a. Initialisatie van variabelen
Eerst halen we de array-telling van onze aangrenzende tegels op. Vervolgens krijgen we het 'target tile'-attribuut van onze avatar en halen we de x- en y-waarden ervoor op. We voegen 2 nieuwe box-containers toe: de 'Min distance' en de 'Target tile index'. We stellen de initiële waarde van 'Min distance' in op 9999, en de waarde van 'Target tile index' op 0. De min distance is ingesteld op een hoog nummer omdat we de kortste afstand vanaf het begin nodig hebben. De target tile index is onze referentie-index in de 'Adjacent tiles'-array die de kortste afstand tot onze 'target tile' heeft.
Ons belangrijkste doel hier is om de index van de tegel binnen onze 'Adjacent tiles'-array te verkrijgen die het dichtst bij onze 'target tile' ligt.
b. Berekening van tegelafstanden
In deze bundel zullen we de berekening doen binnen een loop die we voor elke aangrenzende tegel zullen doen. De loop herhaalt zich door 'adjacent tiles count' en we zullen de tegelwaarde van de 'Adjacent tiles'-array verkrijgen met behulp van de index van die loop.
Haal de x- en y-waarden van die aangrenzende tegel op en vervolgens zullen we de afstand tussen onze 'target tile' en de 'adjacent tile' krijgen met behulp van deze formule:
afstand = sqrt((x2 - x1)^2+(y2 - y1)^2)
x1 = Doel x
x2 = Aangrenzende x
y1 = Doel y
y2 = Aangrenzende y
We trekken eerst hun x- en y-waarden van elkaar af, vermenigvuldigen ze met zichzelf om hun exponentiële waarden te krijgen, tellen die waarden op, en nemen vervolgens de vierkantswortel van de toegevoegde waarden om de afstandswaarde te krijgen.
Als de waarde van de 'Distance' kleiner is dan de waarde van de box-container 'Min distance', stellen we de waarde van de 'target tile index' in op de huidige index van de loop, wat de index is van onze opgehaalde tegel binnen de 'Adjacent tiles'-array. We stellen ook de waarde van de box-container 'Min distance' in op de 'Distance'-waarde zodat we deze kunnen vergelijken met de volgende tegel in onze loop.
Na het einde van de loop hebben we elke aangrenzende tegel met elkaar vergeleken en hebben we de indexwaarde van de aangrenzende tegel die het dichtst bij onze 'target tile' ligt verkregen.
Beweging naar de aangrenzende tegel
In deze laatste bundel van onze tutorial zullen we onze avatar naar de dichtstbijzijnde aangrenzende tegel verplaatsen en de hele 'Find Path'-bundel opnieuw uitvoeren als we nog niet naar onze 'target tile' zijn verplaatst.
De eerste stap is om te controleren of onze 'Adjacent tiles count' groter is dan 0. Dit om ervoor te zorgen dat we alleen verplaatsen als we ten minste één betrouwbare aangrenzende tegel hebben opgehaald.
Vervolgens 'Haal de dichtstbijzijnde tegel' op door onze waarde van 'Target tile index' uit de vorige bundel te gebruiken om de arraywaarde van de 'Adjacent tiles'-array te krijgen.
Om het exacte punt te verkrijgen waar onze avatar naartoe zal bewegen, hebben we de x- en y-tegelwaarden van die tegel nodig, en moeten we ook de 'start x'- en 'start y'-attribuutwaarden van ons 'Grid'-label ophalen. We voegen dan de x-waarde van onze 'nearest tile' en de 'start x'-waarde toe, en hetzelfde geldt voor de y-waarden. Deze toegevoegde waarden zijn onze doel x- en y-punten waar onze avatar naartoe zal bewegen.
Voordat we naar dat punt verhuizen, stellen we de 'previous tile' van onze avatar in op de huidige waarde van zijn 'on tile'-attribuut. We kunnen deze 'on tile'-waarde uit de vorige bundels halen. We verplaatsen vervolgens onze avatar naar onze doelpunten, met een duur van 0,2.
Na de voltooiing van het gedrag 'Beweeg naar punt', stellen we nu de waarde van het 'on tile'-attribuut van onze avatar in op de arraywaarde van 'Get nearest tile'. Als de arraywaarde van 'Get nearest tile' niet gelijk is aan onze 'target tile', voeren we de 'Find Path'-bundel uit, en herhalen we zo de hele bundel opnieuw. De waarde van het 'target tile'-attribuut kan uit de vorige bundel worden opgehaald.
Door op een blauwe tegel te drukken en na de verschijning van onze avatar in het grid, zal de 'Find Path'-bundel worden uitgevoerd. Dit zal de beweging van de avatar van tegel naar tegel activeren.
Conclusie
In deze tutorial hebben we geleerd om een object padvindactiviteit te maken met behulp van een array die we de 'Grid' hebben genoemd, die wordt gebruikt om informatie op te slaan over of die tegel bezet was of niet. Je kunt dit idee uitbreiden door object-id's als informatieopslag te gebruiken in plaats van alleen een cijfer voor onze 'Grid'-array. Dit zal ons meer betrouwbare informatie geven die we kunnen gebruiken bij het padvinden. Bijvoorbeeld, we kunnen de attributen van die object-id verkrijgen om te weten of het levend is of niet, of het ons zal toestaan om op zijn tegel te stappen of niet.
Deze tutorial heeft alleen de basisideeën achter padvinden besproken. Een nadeel hiervan is dat het niet het kortste pad mogelijk naar een doeltegel creëert, en alleen zijn huidige aangrenzende tegel controleert. Dit kan resulteren in een bewegingslus die nergens heen leidt.
Dit kan echter worden opgelost door ons padvindalgoritme uit te breiden. We kunnen dit doen door opnieuw te itereren op onze opgehaalde aangrenzende tegel om ook zijn aangrenzende tegels te verkrijgen totdat we onze doeltegel vinden. Elke gevonden dichtstbijzijnde aangrenzende tegel wordt aan een lijst toegevoegd. Om een oneindige lus te voorkomen, voeg een voorwaarde toe dat als die aangrenzende tegel al is gecontroleerd, die tegel niet aan onze lijst wordt toegevoegd. Voeg ook een maximale limiet toe aan de iteratie-aantal om een oneindige lus te voorkomen die wordt veroorzaakt door de doeltegel als deze te ver weg is of niet mogelijk te vinden. Zodra die iteratie de doeltegel vindt, verlaten we die iteratie en beginnen we de avatar van tegel naar tegel te verplaatsen met behulp van de tegellijst.

