I denne opplæringen vil vi utvide vår nettbaserte plattformsporingsleder slik at den kan takle tegn som tar opp mer enn en celle i rutenettet.
Hvis du ikke har lagt til enveisplattformstøtte til koden din ennå, anbefaler jeg at du gjør det, men det er ikke nødvendig for å følge denne opplæringen.
Du kan spille Unity demo, eller WebGL-versjonen (100 MB +), for å se det endelige resultatet i aksjon. Bruk WASD å flytte tegnet, venstre klikk på et sted for å finne en sti du kan følge for å komme dit, Høyreklikk en celle for å skifte bakken på det punktet, og Klikk og dra skyvekontrollene for å endre sine verdier.
Tegn i forskjellige størrelser må ta forskjellige baner - den oppdaterte algoritmen gjenkjenner dette.Banebryteren aksepterer posisjonen, bredden og høyden på tegnet som en inngang. Selv om bredde og høyde er enkle å tolke, må vi avklare hvilken blokk posisjonskoordinatene refererer til.
Stillingen som vi passerer, må være når det gjelder kartkoordinater, noe som betyr at vi igjen må omfatte noe unøyaktighet. Jeg bestemte meg for at det ville være fornuftig å få stillingen til å referere til den nederste venstre tegnflisen, da dette samsvarer med kartkoordinatsystemet.
Karakterens fliser posisjoner for en 3x3 karakter.Med det rydde opp, kan vi oppdatere stifinderen.
Først må vi sørge for at vår egendefinerte størrelse kan passe inn i destinasjonsstedet. Inntil dette punktet har vi bare sjekket en blokk for å gjøre dette, da det var den maksimale (og eneste) størrelsen på tegnet:
hvis (mGrid [end.x, end.y] == 0) returnere null;
Nå må vi imidlertid gjenta gjennom hver celle at tegnet ville okkupere hvis det stod i sluttposisjonen, og sjekke om noen av dem er en solid blokk. Hvis de er, så kan karakteren ikke selv stå der, så målet kan ikke nås.
For å gjøre dette, la oss først erklære en boolsk som vi vil sette til falsk
hvis tegnet er i en solid flis og ekte
ellers:
var inSolidTile = false;
Deretter vil vi iterere gjennom hver blokk av tegnet:
for (var w = 0; w < characterWidth; ++w) for (int h = 0; h < characterHeight; ++h)
Inne i denne sløyfen må vi sjekke om en bestemt blokk er solid; Hvis så, setter vi inSolidTile
til ekte
, og gå ut av sløyfen:
for (var w = 0; w < characterWidth; ++w) for (int h = 0; h < characterHeight; ++h) if (mGrid[end.x + w, end.y + h] == 0 || mGrid[end.x + w, end.y + h] == 0) inSolidTile = true; break; if (inSolidTile) break;
Men dette er ikke nok. Vurder følgende situasjon:
Grønne blokker: karakter; blå blokk: mål.Hvis vi skulle flytte tegnet slik at det er bunn-venstre blokk okkupert målet, så bunnen-Ikke sant blokk ville være fast i en solid blokk - så algoritmen ville tro at siden karakteren ikke passer til målstillingen, er det umulig å nå sluttpunktet. Selvfølgelig er det ikke sant; Vi bryr oss ikke om hvilken del av karakteren som når målet.
For å løse dette problemet vil vi flytte sluttpunktet til venstre, steg for trinn, opp til punktet der den opprinnelige målplasseringen ville matche bunnen-Ikke sant tegnblokk:
for (var i = 0; i < characterWidth; ++i) inSolidTile = false; for (var w = 0; w < characterWidth; ++w) for (var h = 0; h < characterHeight; ++h) if (mGrid[end.x + w, end.y + h] == 0 || mGrid[end.x + w, end.y + h] == 0) inSolidTile = true; break; if (inSolidTile) break; if (inSolidTile) end.x -= 1; else break;
Vær oppmerksom på at vi ikke bare skal sjekke nederste venstre og høyre hjørne, fordi følgende tilfelle kan oppstå:
Igjen, grønne blokker: karakter; blå blokk: mål.Her kan du se at hvis noen av de nederste hjørner okkuperer målplasseringen, vil tegnet fortsatt være i fast bakke på den andre siden. I dette tilfellet må vi matche bunnen-senter blokkere med målet.
Til slutt, hvis vi ikke finner noe sted der tegnet passer, kan vi også gå ut av algoritmen tidlig:
hvis (inSolidTile == true) returnerer null;
For å se om karakteren vår er på bakken, må vi sjekke om noen av tegnets bunn-fleste celler er rett over en solid flis.
La oss se på koden vi brukte for et 1x1 tegn:
hvis (mMap.IsGround (start.x, start.y - 1)) firstNode.JumpLength = 0; ellers firstNode.JumpLength = (short) (maxCharacterJumpHeight * 2);
Vi avgjør om startpunktet er på bakken ved å sjekke om flisen umiddelbart under startpunktet er en flis. For å oppdatere koden, gjør vi det enkelt under alle de nederste blokkene av tegnet.
Først, la oss erklære en boolsk som vil fortelle oss om tegnet starter på bakken. I utgangspunktet antar vi at det ikke gjør det:
bool starterOnGround = false;
Deretter vil vi gjenta gjennom alle de nederste tegnblokkene og sjekke om noen av dem er rett over en flis. Hvis så, så setter vi startsOnGround
til ekte
og gå ut av sløyfen:
for (int x = start.x; x < start.x + characterWidth; ++x) if (mMap.IsGround(x, start.y - 1)) startsOnGround = true; break;
Endelig setter vi hoppverdien avhengig av om tegnet startet på bakken:
hvis (starterOnGround) firstNode.JumpLength = 0; ellers firstNode.JumpLength = (short) (maxCharacterJumpHeight * 2);
Vi må også endre etterfølgerens grensekontroll, men her trenger vi ikke å sjekke alle fliser. Det er godt nok å sjekke kontur av tegnet - blokkene rundt kanten - fordi vi vet at forelderens stilling var bra.
La oss se hvordan vi sjekket etterfølgerens grenser tidligere:
hvis (mGrid [mNewLocationX, mNewLocationY] == 0) fortsette; hvis (mMap.IsGround (mNewLocationX, mNewLocationY - 1)) onGround = true; ellers hvis (mGrid [mNewLocationX, mNewLocationY + characterHeight] == 0) atCeiling = true;
Vi oppdaterer dette ved å sjekke om noen av konturblokkene er innenfor en solid blokk. Hvis noen av dem gjør det, kan karakteren ikke passe i posisjonen og etterfølgeren skal hoppes over.
Først, la oss iterere over alle de øverste og nederste blokkene av tegnet, og kontroller om de overlapper en solid flis på nettet vårt:
for (var w = 0; w < characterWidth; ++w) if (mGrid[mNewLocationX + w, mNewLocationY] == 0 || mGrid[mNewLocationX + w, mNewLocationY + characterHeight - 1] == 0) goto CHILDREN_LOOP_END;
De CHILDREN_LOOP_END
Etiketten fører til slutten av etterfølgerløkken; ved å bruke det, hopper vi over behovet først gå i stykker
ut av løkken og da Fortsette
til neste etterfølger i etterfølgerløkken.
Hvis noen av bunnblokkene ligger rett over en solid flis, må etterfølgeren være på bakken. Dette betyr at selv om det ikke er en fast flis direkte under selve etterfølgercellen, vil etterfølgeren fortsatt bli ansett som en På bakken
node, hvis karakteren er bred nok.
for (var w = 0; w < characterWidth; ++w) if (mGrid[mNewLocationX + w, mNewLocationY] == 0 || mGrid[mNewLocationX + w, mNewLocationY + characterHeight - 1] == 0) goto CHILDREN_LOOP_END; if (mMap.IsGround(mNewLocationX + w, mNewLocationY - 1)) onGround = true;
Hvis noen av flisene over karakteren er solide, så er tegnet i taket.
for (var w = 0; w < characterWidth; ++w) if (mGrid[mNewLocationX + w, mNewLocationY] == 0 || mGrid[mNewLocationX + w, mNewLocationY + characterHeight - 1] == 0) goto CHILDREN_LOOP_END; if (mMap.IsGround(mNewLocationX + w, mNewLocationY - 1)) onGround = true; if (mGrid[mNewLocationX + w, mNewLocationY + characterHeight] == 0) atCeiling = true;
Nå må vi bare sjekke at det ikke finnes noen faste blokker i tegnets venstre og høyre celler. Hvis det er, kan vi trygt hoppe over etterfølgeren, fordi vår karakter ikke passer til den aktuelle posisjonen:
for (var h = 1; h < characterHeight - 1; ++h) if (mGrid[mNewLocationX, mNewLocationY + h] == 0 || mGrid[mNewLocationX + characterWidth - 1, mNewLocationY + h] == 0) goto CHILDREN_LOOP_END;
Vi har fjernet en ganske betydelig begrensning fra algoritmen; nå har du mye mer frihet når det gjelder størrelsen på spillets tegn.
I neste veiledning i serien bruker vi vår algoritme til å finne en bot som kan følge selve stien. bare klikk på et sted, og det vil løpe og hoppe for å komme dit. Dette er veldig nyttig for NPCs!