Sfat rapid creați mișcarea inamică netedă cu mișcare sinusoidală

În acest sfat rapid, vă voi arăta cum să utilizați sinus funcția de a oferi obiectelor dvs. de joc o mișcare de mișcare în spate și înapoi - nu mai zgâriate zig-zags unde vrăjmașii voștri plutiți par să se aplece împotriva unui perete invizibil!


Exemple

În primul rând, permiteți-mi să vă arăt un fel de mișcare netedă în spate și în spate. (Grafica este din pachetul nostru complet gratuit de împușcături.

Acest dușman se mișcă în sus și în jos, tragând gloanțe la intervale regulate:

Acest dușman țese pe ecran:

Ambele tipuri de mișcări sunt utile pentru jocuri de tip "shoot-up". Observați cât de netedă și treptată a mișcării se simte - nu mișcări bruște, nici un "ticălos" în timp ce inamicul își schimbă direcția. Acest lucru este în contrast cu ...


Abordarea naivă

O primă încercare comună de a crea o mișcare înapoi și înapoi este de a face ceva de genul:

 var goingUp = false; Funcția rulează la fiecare câteva milisecunde. // vedeți: http://gamedev.tutsplus.com/articles/glossary/quick-tip-what-is-the-game-loop/funcția gameLoop () if (ufo.y> = bottomOfRange) goingUp = true ;  altfel dacă (ufo.y <= topOfRange)  goingUp = false;  if (goingUp)  ufo.y -= ufo.ySpeed;  else  ufo.y += ufo.ySpeed;  ufo.x += ufo.xSpeed; 

Practic, acest lucru îi spune vrăjmașului să se deplaseze la o rată constantă (adică același număr de pixeli de fiecare dată) până când atinge punctul cel mai de jos din intervalul permis, apoi să se deplaseze la aceeași rată constantă până când ajunge la cel mai înalt punct din intervalul său permis, de peste și peste din nou.

Inamicul poate fi făcut să se deplaseze orizontal prin setarea lui xSpeed la un număr diferit de zero: un număr negativ o face să se deplaseze la stânga și un număr pozitiv o face să se miște bine.

Aceste exemple arată cum arată acest tip de mișcare. În primul rând, fără mișcare orizontală:

Acum, cu mișcare orizontală:

Ea atinge scopul de a se deplasa înainte și înapoi, dar cu siguranță nu este la fel de netedă ca exemplul nostru anterior.


Cauza

Motivul acestei mișcări greoaie este că viteza verticală a inamicului face o schimbare bruscă uriașă - chiar dacă valoarea lui ufo.ySpeed ramâne acelasi.

Presupune ufo.ySpeed este 10. Pe drum, inamicul se mișcă în sus la 10px / bifați (pixeli pe bifați, unde "bifați" este lungimea unei bucle de joc). Odată ce inamicul ajunge în partea de sus, acesta inversează direcția și se mișcă dintr-o dată la 10px / bifați în jos. Trecerea de la + 10px / bifați la -10px / bifați este o diferență de 20px / bifați, și asta e atât de vizibil.

Atunci când cauza este explicată astfel, soluția pare evidentă: încetinește inamicul în apropierea punctelor cele mai înalte și inferioare! În acest fel, schimbarea vitezei sale nu va fi atât de mare atunci când inversează direcția.

O primă încercare ar putea arăta astfel:

 var goingUp = false; var movingSlowly = false; Funcția rulează la fiecare câteva milisecunde. // vedeți: http://gamedev.tutsplus.com/articles/glossary/quick-tip-what-is-the-game-loop/funcția gameLoop () if (ufo.y> = bottomOfRange) goingUp = true ;  altfel dacă (ufo.y <= topOfRange)  goingUp = false;  if (ufo.y <= bottomOfRange + 100)  movingSlowly = true;  else if (ufo.y >= topOfRange - 100) movingSlowly = adevărat;  altceva movingSlowly = false;  dacă (se deplasează încet) if (goingUp) ufo.y - = ufo.ySpeed ​​/ 2;  altfel ufo.y + = ufo.ySpeed ​​/ 2;  altceva if (goingUp) ufo.y - = ufo.ySpeed;  altceva ufo.y + = ufo.ySpeed;  ufo.x + = ufo.xSpeed; 

Acest cod este dezordonat, dar tu ai ideea: dacă inamicul se află în limita maximă sau inferioară de 100px, se mișcă la jumătate din viteza sa normală.

Acest lucru funcționează, deși nu este perfect. Inamicul va avea în continuare un "salt" în viteză când va schimba direcția, dar cel puțin nu va fi la fel de vizibil. Cu toate acestea, inamicul va avea acum sari suplimentare în viteză atunci când se mișcă din ritmul său regulat la viteza mai lentă! Dang.

Noi ar putea fixați acest lucru prin împărțirea intervalului în secțiuni mai mici sau făcând viteza mai mult decât distanța exactă de la inamic la limitele sale ... dar există o cale mai ușoară.


Miscarea sinusoidala

Gândiți-vă la un model de tren care merge pe o pistă perfect circulară. Trenul își schimbă în mod constant direcția, dar totuși se mișcă într-un ritm constant, fără "sărituri".

Acum, imaginați-vă un perete de o parte a traseului circular și o lumină puternică puternică pe partea opusă (astfel încât pista și trenul se află între cele două). Trenul va arunca o umbră pe perete. Dar, desigur, acea umbră nu se va mișca într-un cerc, deoarece peretele este plat: se va mișca înainte și înapoi, pe o linie dreaptă, dar totuși cu mișcarea fără tren fără tren a trenului!

Asta e exact ceea ce vrem. Și, din fericire, există o funcție care ne va da: sinus funcţie. Acest animat GIF de pe Wikipedia demonstrează:


Imagine de la Wikimedia Commons. Mulțumesc, Lucas!

Linia roșie este curba y = sin (x). Asa de, sin (0,5 x pi) este 1, sin (pi) este 0 și așa mai departe.

Este puțin incomod că pi (π) este unitatea de bază folosită pentru această funcție, dar putem gestiona. Putem să o folosim așa:

 var numberOfTicks = 0; funcția gameLoop () numberOfTicks ++; ufo.y = păcat (numberOfTicks * pi); ufo.x + = ufo.xSpeed; 

Vezi ce se întâmplă aici? După o singură bifare, ufo.y va fi setat la sin (1 x pi), care este 0. După două căpușe, ufo.y va fi setat la sin (2 x pi), care este… 0, din nou. Oh. Rezistă.

 var numberOfTicks = 0; funcția gameLoop () numberOfTicks ++; ufo.y = păcat (numberOfTicks * 0.5 * pi); ufo.x + = ufo.xSpeed; 

Acum, după o singură bifă, ufo.y va fi setat la sin (0,5 x pi), care este 1. După două căpușe, ufo.y va fi setat la sin (1 x pi), care este 0. După trei căpușe, ufo.y va fi setat la sin (1,5 x pi), care este -1, si asa mai departe. (Funcția sinusoidală se repetă, deci sin (a) == sin (a + (2 * pi)), întotdeauna - nu trebuie să vă faceți griji pentru a vă asigura că acest lucru A este sub un anumit număr!)

Evident, mergând de la 1 la 0 la -1 și așa mai departe nu este ceea ce vrem. În primul rând, dorim ca valorile limită să fie altceva decât atunci 1 și -1. E ușor - noi înmulțim întregul păcat funcție de limita maximă dorită:

 var numberOfTicks = 0; funcția gameLoop () numberOfTicks ++; ufo.y = 250 * păcat (numberOfTicks * 0.5 * pi); ufo.x + = ufo.xSpeed; 

Acum va veni dușmanul y = +250 la y = -250. Dacă vrem să treacă 100 la 600, putem adăuga un extra 350 la această valoare (din moment ce 250 + 350 = 600 și -250 + 350 = 100):

 var numberOfTicks = 0; funcția gameLoop () numberOfTicks ++; ufo.y = (250 * sin (numărOfTicks * 0,5 * pi)) + 350; ufo.x + = ufo.xSpeed; 

Dar valoarea încă scade 100 la 350 la 600, deoarece sin (numereOfTicks * 0.5 * pi) este încă sărind de la -1 la 0 la 1.

Dar, da, știm de ce asta se întâmplă: este pentru că valoarea lui numberOfTicks * 0.5 * pi este sărind de la 0,5 * pi la 1 * pi la 1,5 * pi. Uitați-vă din nou la GIF dacă nu vedeți de ce ar fi cauzat acest lucru:

Deci, tot ce trebuie să facem este să alegem un decalaj diferit între numărul pe care îl alocăm păcat() funcție, în loc de numberOfTicks * 0.5 * pi. Dacă doriți ca mișcarea din spate și înapoi să dureze de zece ori mai mult, utilizați-o numberOfTicks * 0,5 * pi / 10. Dacă doriți să dureze de 25 de ori mai mult, utilizați numberOfTicks * 0.5 * pi / 25, si asa mai departe.

Puteți folosi această regulă pentru a face ca mișcarea să dureze exact cât timp doriți. Dacă bucla de joc rulează o dată la fiecare 25 de milisecunde (de 40 de ori pe secundă), atunci puteți utiliza numberOfTicks * 0.5 * pi / 40 pentru a face inamicul să se deplaseze de la centru la vârf exact o dată pe secundă, sau numberOfTicks * 0,5 * pi / (40 * 2) pentru al face să treacă de la început la fund exact o dată pe secundă.

Desigur, puteți să uitați de toate acestea și să experimentați numere diferite pentru a vedea ce este bine. Acest demo folosește păcat (numberOfTicks / 50), și îmi place rezultatul:

Experimentați-vă și distrați-vă!