I denne Quick Tip, vil jeg vise deg hvordan du bruker sinus
funksjon for å gi spillets objekter jevn frem og tilbake bevegelse - ikke mer harde zig-zags hvor dine flytende fiender synes å hoppe mot en usynlig vegg!
Først, la meg vise deg den slags glatte frem og tilbake bevegelse jeg mener. (Grafik er fra vår helt gratis shoot-'em-up sprite-pakke.)
Denne fienden beveger seg opp og ned, skyter kuler med jevne mellomrom som det går:
Denne fienden vever over skjermen:
Begge typer bevegelse er nyttige for shoot-opp-spill. Legg merke til hvor jevn og gradvis bevegelsen føles - ingen plutselige bevegelser, ingen "jerk" som fienden endrer retning. Det er i sterk kontrast til ...
Et vanlig første forsøk på å skape en frem og tilbake bevegelse er å gjøre noe slikt:
var goingUp = false; // Funksjonen går hvert par millisekunder. // Se: http://gamedev.tutsplus.com/articles/glossary/quick-tip-what-is-the-ame-loop/funksjon gameLoop () hvis (ufo.y> = bottomOfRange) goingUp = true ; annet hvis (ufo.y <= topOfRange) goingUp = false; if (goingUp) ufo.y -= ufo.ySpeed; else ufo.y += ufo.ySpeed; ufo.x += ufo.xSpeed;
I utgangspunktet forteller dette at fienden skal bevege seg ned med en konstant hastighet (dvs. det samme antall piksler hver gang) til den når det laveste punktet i det tillatte området, deretter å bevege seg opp med samme konstante hastighet til den når det høyeste punktet i det tillatte området, om og om igjen.
Fienden kan gjøres for å bevege seg horisontalt ved å sette sin xSpeed
til et annet tall enn null: et negativt tall gjør det flytte til venstre, og et positivt tall gjør det flytte til høyre.
Disse eksemplene viser hvordan denne typen bevegelse ser ut. Først uten horisontal bevegelse:
Nå, med horisontal bevegelse:
Det oppnår målet om å flytte frem og tilbake, men det er absolutt ikke så glatt som vårt tidligere eksempel.
Årsaken til denne humpete bevegelsen er at fiendens vertikale fart gjør en plutselig stor forandring - selv om verdien av ufo.ySpeed
forblir det samme.
Anta ufo.ySpeed
er 10
. På vei oppover beveger fienden oppover på 10px / tick (piksler per kryss, hvor en "tick" er lengden på en spillsløyfe). Når fienden når toppen, reverserer den retningen, og beveger seg plutselig med 10px / tick nedad. Skiftet fra + 10px / tick til -10px / tick er en forskjell på 20px / tick, og det er det som er så merkbart.
Når årsaken er stavet ut som dette, virker løsningen åpenbar: sakke fienden ned i nærheten av høyeste og laveste punkt! På den måten vil endringen i hastigheten ikke være så stor når den reverserer retning.
Et første forsøk på dette kan se slik ut:
var goingUp = false; var movingSlowly = false; // Funksjonen går hvert par millisekunder. // Se: http://gamedev.tutsplus.com/articles/glossary/quick-tip-what-is-the-ame-loop/funksjon gameLoop () hvis (ufo.y> = bottomOfRange) goingUp = true ; annet hvis (ufo.y <= topOfRange) goingUp = false; if (ufo.y <= bottomOfRange + 100) movingSlowly = true; else if (ufo.y >= topOfRange - 100) movingSlowly = true; else movingSlowly = false; hvis (movingSlowly) if (goingUp) ufo.y - = ufo.ySpeed / 2; ellers ufo.y + = ufo.ySpeed / 2; annet hvis (går opp) ufo.y - = ufo.ySpeed; ellers ufo.y + = ufo.ySpeed; ufo.x + = ufo.xSpeed;
Denne koden er rotete, men du får ideen: hvis fienden er innenfor 100 px av sine høyeste eller laveste grenser, beveger den seg ved halvparten av sin normale hastighet.
Dette fungerer, selv om det ikke er perfekt. Fienden vil fortsatt ha en "hopp" i fart når den endrer retning, men det vil i det minste ikke være så merkbar. Imidlertid vil fienden nå få flere hopp i fart når den beveger seg fra sitt vanlige tempo til den langsommere hastigheten! Dang.
Vi kunne fikse dette ved å dele området i mindre seksjoner, eller gjøre hastigheten litt flere av den nøyaktige avstanden fra fienden til grensene ... men det er en enklere måte.
Tenk på et modelltog som går rundt et perfekt sirkulært spor. Toget er i stadig skiftende retning, og likevel beveger det seg i et jevnt tempo, uten "hopp".
Forestill deg nå en vegg på den ene siden av det sirkulære sporet og et stort, sterkt lys på motsatt side (så sporet og toget er mellom de to). Toget vil kaste en skygge på veggen. Men selvfølgelig vil skyggen ikke bevege seg i en sirkel, fordi veggen er flat: den beveger seg frem og tilbake, i en rett linje, men likevel med den glatte, hoppfrie bevegelsen av toget!
Det er akkurat det vi vil ha. Og heldigvis er det en funksjon som vil gi den til oss: sinus funksjon. Denne animerte GIF fra Wikipedia demonstrerer:
Den røde linjen er kurven til y = sin (x)
. Så, synd (0,5 * pi)
er 1, sin (pi)
er 0, og så videre.
Det er litt ubeleilig at pi (π) er den grunnleggende enheten som brukes til denne funksjonen, men vi kan klare. Vi kan bruke det slik:
var numberOfTicks = 0; funksjon gameLoop () numberOfTicks ++; ufo.y = synd (numberOfTicks * pi); ufo.x + = ufo.xSpeed;
Se hva som skjer her? Etter en kryss, ufo.y
vil bli satt til synd (1 * pi)
, som er 0
. Etter to merker, ufo.y
vil bli satt til synd (2 * pi)
, som er… 0
, en gang til. Åh. Vent litt.
var numberOfTicks = 0; funksjon gameLoop () numberOfTicks ++; ufo.y = synd (numberOfTicks * 0.5 * pi); ufo.x + = ufo.xSpeed;
Nå, etter en tick, ufo.y
vil bli satt til synd (0,5 * pi)
, som er 1
. Etter to merker, ufo.y
vil bli satt til synd (1 * pi)
, som er 0
. Etter tre flått, ufo.y
vil bli satt til synd (1,5 * pi)
, som er -1
, og så videre. (Sinefunksjonen gjentar, så synd (a) == synd (a + (2 * pi))
, alltid - du trenger ikke å bekymre deg for å sørge for at en
er under et visst antall!)
Åpenbart går fra 1
til 0
til -1
og så videre er ikke det vi ønsker. Først vil vi at grenseværdiene skal være noe annet da 1
og -1
. Det er enkelt - vi multipliserer hele synd
Funksjon av ønsket maksimumsgrense:
var numberOfTicks = 0; funksjon gameLoop () numberOfTicks ++; ufo.y = 250 * sin (numberOfTicks * 0.5 * pi); ufo.x + = ufo.xSpeed;
Nå kommer fienden fra y = +250
til y = -250
. Hvis vi vil at den skal gå fra 100
til 600
, Vi kan bare legge til en ekstra 350
på denne verdien (siden 250 + 350 = 600
og -250 + 350 = 100
):
var numberOfTicks = 0; funksjon gameLoop () numberOfTicks ++; ufo.y = (250 * sin (numberOfTicks * 0.5 * pi)) + 350; ufo.x + = ufo.xSpeed;
Men verdien er fortsatt å hoppe fra 100
til 350
til 600
, fordi det synd (numberOfTicks * 0.5 * pi)
hopper fortsatt fra -1
til 0
til 1
.
Men, vi vet hvorfor det er skjer: det er fordi verdien av numberOfTicks * 0.5 * pi
hopper fra 0,5 * pi
til 1 * pi
til 1,5 * pi
. Se på GIF igjen hvis du ikke ser hvorfor det ville forårsake det:
Så alt vi trenger å gjøre er å velge et annet gap mellom tallet som vi legger inn i synd()
funksjon, i stedet for numberOfTicks * 0.5 * pi
. Hvis du vil at frem og tilbake bevegelsen skal ta ti ganger så lang tid, bruk numberOfTicks * 0.5 * pi / 10
. Hvis du vil at den skal ta 25 ganger så lang tid, bruk numberOfTicks * 0.5 * pi / 25
, og så videre.
Du kan bruke denne regelen til å gjøre bevegelsen sist akkurat så lenge du vil ha den til. Hvis spillsløyfen din går hver 25 millisekunder (40 ganger per sekund), kan du bruke numberOfTicks * 0.5 * pi / 40
for å få fienden til å flytte fra sentrum til toppen nøyaktig en gang per sekund, eller numberOfTicks * 0.5 * pi / (40 * 2)
for å få det til å bevege seg fra toppen til bunn nettopp en gang per sekund.
Selvfølgelig kan du bare glemme alt det og eksperimentere med forskjellige tall for å se hva som føles riktig. Denne demoen bruker synd (numberOfTicks / 50)
, og jeg liker resultatet:
Eksperimentere og ha det gøy!