I denne serien av opplæringsprogrammer viser jeg deg hvordan du lager en Geometry Wars-inspirert tvillingpokerskytespill, med neongrafik, galne partikkeleffekter og fantastisk musikk, for iOS med C ++ og OpenGL ES 2.0. I denne delen legger vi til eksplosjoner og visuell flair.
I serien så langt har vi satt opp spillingen og lagt til virtuelle gamepad-kontroller. Deretter legger vi til partikkeleffekter.
Partikkeleffekter opprettes ved å lage et stort antall små partikler. De er svært allsidige og kan brukes til å legge til flair til nesten hvilket som helst spill. I Shape Blaster vil vi lage eksplosjoner ved hjelp av partikkeleffekter. Vi vil også bruke partikkeleffekter til å skape avgassbrann for spillers skip, og for å legge til visuell flair til de svarte hullene. I tillegg vil vi se på hvordan partikler samhandler med tyngdekraften fra de svarte hullene.
Frem til nå har du sannsynligvis bygget og kjørt Shape Blaster ved å bruke alle standardinnstillingene debug
bygg av prosjektet. Selv om dette er greit og bra når du feilsøker koden, slår feilsøking av de fleste hastigheter og matteoptimaliseringer som kan gjøres, samt at du aktiverer alle påstander i koden.
Faktisk, hvis du kjører koden i feilsøkingsmodus herfra, vil du legge merke til at bildesatsen begynner å falle dramatisk. Dette skyldes at vi målretter mot en enhet som har en redusert mengde RAM, CPU-klokkeslett og mindre 3D-maskinvare sammenlignet med en stasjonær datamaskin eller en bærbar datamaskin.
Så på dette tidspunktet kan du eventuelt deaktivere feilsøking, og slå på "utgivelsesmodus". Utgivelsesmodus gir oss full kompilator og matteoptimalisering, samt fjerning av ubrukt feilsøkingskode og påstander.
Når du har åpnet prosjektet, velger du Produkt Meny, Scheme, deretter Rediger skjema ... .
Det følgende dialogvinduet åpnes. Velge Løpe på venstre side av dialogboksen, og fra Bygg konfigurasjon, endre popup-elementet fra debug til utgivelse.
Du vil legge merke til hastighetsgevinstene umiddelbart. Prosessen er lett omvendt hvis du trenger å feilsøke programmet igjen: bare velg debug i stedet for utgivelse og du er ferdig.
Tips: Vær oppmerksom på at eventuelle ordningsendringer som dette krever en fullstendig kompilering av programmet.
Vi starter med å opprette en ParticleManager
klasse som vil lagre, oppdatere og tegne alle partiklene. Vi gjør denne klassen generell nok til at den enkelt kan gjenbrukes i andre prosjekter, men vil fortsatt kreve noen tilpasning fra prosjekt til prosjekt. Å beholde ParticleManager
så generelt som mulig, vil det ikke være ansvarlig for hvordan partiklene ser ut eller beveger seg; Vi håndterer det andre steder.
Partikler har en tendens til å bli skapt og ødelagt raskt og i store mengder. Vi vil bruke et objektbasseng for å unngå å skape store mengder søppel. Dette betyr at vi vil tildele et stort antall partikler oppe foran, og deretter fortsette å bruke de samme partiklene.
Vi vil også gjøre ParticleManager
ha en fast kapasitet. Dette vil forenkle det og bidra til at vi ikke overskrider ytelses- eller minnebegrensninger ved å lage for mange partikler. Når maksimalt antall partikler overskrides, begynner vi å erstatte de eldste partiklene med nye. Vi skal lage ParticleManager
en generisk klasse. Dette vil tillate oss å lagre egendefinert tilstandsinformasjon for partiklene uten å kode det hardt inn iParticleManager
seg selv.
Vi lager også en partikkel~~POS=TRUNC
klasse:
klasse partikkel offentlig: ParticleState mState; tColor4f mColor; tVector2f mPosition; tVector2f mScale; tTexture * mTexture; float mOrientation; float mDuration; flyte mPercentLife; offentlig: Partikkel (): mScale (1,1), mPercentLife (1.0f) ;
De partikkel~~POS=TRUNC
Klassen har all den informasjonen som kreves for å vise en partikkel og forvalte levetiden. ParticleState
er det å holde noen ytterligere data vi kan trenge for våre partikler. Hvilke data som trengs vil variere avhengig av ønskede partikkeleffekter; det kan brukes til å lagre hastighet, akselerasjon, rotasjonshastighet eller alt annet du trenger.
For å hjelpe med å håndtere partiklene, trenger vi en klasse som fungerer som et sirkulært matrise, noe som betyr at indekser som normalt ikke er begrenset, vil i stedet vikle rundt til begynnelsen av matrisen. Dette vil gjøre det enkelt å erstatte de eldste partiklene først hvis vi går tom for plass til nye partikler i vårt utvalg. For dette legger vi til følgende som en nestet klasse i ParticleManager
:
klasse CircularParticleArray beskyttet: std :: vectormList; size_t mStart; size_t mCount; offentlig: CircularParticleArray (int kapasitet) mList.resize ((size_t) kapasitet); size_t getStart () return mStart; void setStart (size_t verdi) mStart = verdi% mList.size (); size_t getCount () return mCount; tomt setCount (size_t verdi) mCount = verdi; size_t getCapacity () return mList.size (); Partikkel og operatør [] (const size_t i) return mList [(mStart + i)% mList.size ()]; const Partikkel og operatør [] (const size_t i) const return mList [(mStart + i)% mList.size ()]; ;
Vi kan sette mSTART
medlem for å justere hvor indeks null i vår CircularParticleArray
tilsvarer i den underliggende gruppen, og mCount
vil bli brukt til å spore hvor mange aktive partikler som er i listen. Vi vil sikre at partikkelen ved indeks null er alltid den eldste partikkelen. Hvis vi erstatter den eldste partikkelen med en ny, vil vi bare øke mSTART
, som i det vesentlige roterer den sirkulære gruppen.
Nå som vi har våre hjelperklasser, kan vi begynne å fylle ut ParticleManager
klasse. Vi trenger en ny medlemsvariabel, og en konstruktør.
CircularParticleArray mParticleList; ParticleManager :: ParticleManager (int kapasitet): mParticleList (kapasitet)
Vi lager mParticleList
og fyll den med tomme partikler. Konstruktøren er det eneste stedet hvor ParticleManager
tildeler minne.
Deretter legger vi til createParticle ()
metode som skaper en ny partikkel ved hjelp av neste ubrukt partikkel i bassenget, eller den eldste partikkelen hvis det ikke er ubrukte partikler.
void ParticleManager :: createParticle (tTexture * tekstur, const tVector2f og posisjon, const tColor4f & tint, flytens varighet, const tVector2f og skala, const ParticleState & state, float theta) size_t index; hvis (mParticleList.getCount () == mParticleList.getCapacity ()) index = 0; mParticleList.setStart (mParticleList.getStart () + 1); ellers index = mParticleList.getCount (); mParticleList.setCount (mParticleList.getCount () + 1); Partikkel & ref = mParticleList [indeks]; ref.mTexture = tekstur; ref.mPosition = posisjon; ref.mColor = tint; ref.mDuration = varighet; ref.mPercentLife = 1.0f; ref.mScale = skala; ref.mOrientation = theta; ref.mState = state;
Partikler kan ødelegges når som helst. Vi må fjerne disse partiklene samtidig som de andre partiklene forblir i samme rekkefølge. Vi kan gjøre dette ved å iterere gjennom listen over partikler mens vi holder styr på hvor mange er blitt ødelagt. Når vi går, beveger vi hver aktiv partikkel foran alle de ødelagte partiklene ved å bytte den med den første ødelagte partikkelen. Når alle ødelagte partiklene er på slutten av listen, deaktiverer vi dem ved å sette listen mCount
variabel til antall aktive partikler. Ødelagte partikler forblir i den underliggende gruppen, men vil ikke bli oppdatert eller tegnet.
ParticleManager :: oppdatering ()
håndterer oppdatering av hver partikkel og fjerning av ødelagte partikler fra listen:
void ParticleManager :: update () size_t removalCount = 0; for (size_t i = 0; i < mParticleList.getCount(); i++) Particle& ref = mParticleList[i]; ref.mState.updateParticle(ref); ref.mPercentLife -= 1.0f / ref.mDuration; Swap(mParticleList, i - removalCount, i); if (ref.mPercentLife < 0) removalCount++; mParticleList.setCount(mParticleList.getCount() - removalCount); void ParticleManager::Swap(typename ParticleManager::CircularParticleArray& list, size_t index1, size_t index2) const Particle temp = list[index1]; list[index1] = list[index2]; list[index2] = temp;
Den siste tingen å implementere i ParticleManager
tegner partiklene
void ParticleManager :: draw (tSpriteBatch * spriteBatch) for (size_t i = 0; i < mParticleList.getCount(); i++) Particle particle = mParticleList[(size_t)i]; tPoint2f origin = particle.mTexture->getSurfaceSize () / 2; spriteBatch-> draw (2, particle.mTexture, tPoint2f ((int) partikkel.mPosition.x, (int) partikkel.mPosition.y), tOptional(), partikkel.m.Kolor, partikkel.m.orientasjon, opprinnelse, partikkel.mSkala);
Den neste tingen å gjøre er å lage en tilpasset klasse eller struktur for å tilpasse hvordan partiklene vil se i Shape Blaster. Det vil være flere forskjellige typer partikler i Shape Blaster som oppfører seg litt annerledes, så vi begynner med å lage en enum
for partikkeltypen. Vi trenger også variabler for partikkelens hastighet og innledende lengde.
klasse ParticleState public: enum ParticleType kNone = 0, kEnemy, kBullet, kIgnoreGravity; offentlig: tVector2f mVelocity; PartikkelType mType; float mLengthMultiplier; offentlig: ParticleState (); ParticleState (const tVector2f & hastighet, ParticleType type, float lengthMultiplier = 1.0f); ParticleState getRandom (float minVel, float maxVel); ugyldig oppdateringPartikkel (partikkel og partikkel); ;
Nå er vi klare til å skrive partikkelen Oppdater()
metode. Det er en god ide å gjøre denne metoden rask, siden det må bli kalt for et stort antall partikler.
Vi begynner enkelt. La oss legge til følgende metode til ParticleState
:
void ParticleState :: updateParticle (Particle & particle) tVector2f vel = particle.mState.mVelocity; partikkel.mposisjon + = vel; particle.mOrientation = Extensions :: toAngle (vel); // denormaliserte flyter forårsaker betydelige ytelsesproblemer hvis (fabs (vel.x) + fabs (vel.y) < 0.00000000001f) vel = tVector2f(0,0); vel *= 0.97f; // Particles gradually slow down particle.mState.mVelocity = vel;
Vi kommer tilbake og forbedrer denne metoden på et øyeblikk. La oss først lage partikkeleffekter, slik at vi faktisk kan teste ut endringene våre.
I GameRoot
, erklære en ny ParticleManager
og ring det Oppdater()
og tegne()
metoder:
// i GameRoot beskyttet: ParticleManager mParticleManager; offentlig: ParticleManager * getParticleManager () return & mParticleManager; // i GameRoots konstruktør GameRoot :: GameRoot (): mParticleManager (1024 * 20), mViewportSize (800, 600), mSpriteBatch (NULL) // i GameRoot :: onRedrawView () mParticleManager.update (); mParticleManager.draw (mSpriteBatch);
Også, vi vil erklære en ny forekomst av tTexture
klasse i Kunst
klassen kalles mLineParticle
for partikkelens tekstur. Vi laster det som om vi gjør det andre spillets sprites:
// I kunstens konstruktør mLineParticle = new tTexture (tSurface ("laser.png"));
La oss nå få fiender til å eksplodere. Vi vil endre Enemy :: wasShot ()
metode som følger:
void Enemy :: wasShot () mIsExpired = true; for (int i = 0; i < 120; i++) float speed = 18.0f * (1.0f - 1 / Extensions::nextFloat(1, 10)); ParticleState state(Extensions::nextVector2(speed, speed), ParticleState::kEnemy, 1); tColor4f color(0.56f, 0.93f, 0.56f, 1.0f); GameRoot::getInstance()->getParticleManager () -> createParticle (Art :: getInstance () -> getLineParticle (), mPosition, farge, 190, 1.5f, state);
Dette skaper 120 partikler som skyter utover med forskjellige hastigheter i alle retninger. Den tilfeldige hastigheten vektes slik at partikler er mer sannsynlig å bevege seg nær maksimalhastigheten. Dette vil føre til at flere partikler kommer til å ligge i eksplosjonens kant etter hvert som den ekspanderer. Partiklene varer 190 bokser, eller litt over tre sekunder.
Du kan nå kjøre spillet og se på fiender eksplodere. Imidlertid er det fortsatt noen forbedringer som skal gjøres for partikkeleffekter.
Det første problemet er at partiklene forsvinner brått når deres varighet går ut. Det ville være bedre hvis de kunne jevne ut, men la oss gå litt lenger enn dette og få partiklene til å lyse lysere når de beveger seg raskt. Også, det ser fint ut hvis vi forlenger fortflyttende partikler og forkorter sakte bevegelige seg.
Endre ParticleState.UpdateParticle ()
metode som følger (endringer er uthevet).
void ParticleState :: updateParticle (Particle & particle) tVector2f vel = particle.mState.mVelocity; partikkel.mposisjon + = vel; particle.mOrientation = Extensions :: toAngle (vel); flytehastighet = vel.length (); flyte alfa = tMath :: min (1.0f, tMath :: min (partikkel.mPercentLife * 2, hastighet * 1.0f)); alfa * = alfa; partikkel.mColor.a = alfa; partikkel.mScale.x = partikkel.mState.mLengthMultiplier * tMath :: min (tMath :: min (1.0f, 0.2f * hastighet + 0.1f), alfa); // denormaliserte flyter forårsaker betydelige ytelsesproblemer hvis (fabs (vel.x) + fabs (vel.y) < 0.00000000001f) vel = tVector2f(0,0); vel *= 0.97f; // Particles gradually slow down particle.mState.mVelocity = vel;
Eksplosjonene ser mye bedre ut nå, men de er alle i samme farge.
Monokromatiske eksplosjoner er en god start, men kan vi gjøre det bedre?Vi kan gi dem mer variasjon ved å velge tilfeldig farger. En metode for å produsere tilfeldige farger er å velge de røde, blå og grønne komponentene tilfeldig, men dette vil produsere mange kjedelige farger, og vi vil at partiklene våre har et neonlysutseende. Vi kan ha mer kontroll over våre farger ved å spesifisere dem i HSV-fargeplassen. HSV står for fargetone, metning og verdi. Vi ønsker å velge farger med et tilfeldig fargetone, men en fast metning og verdi. Vi trenger en hjelperfunksjon som kan produsere en farge fra HSV-verdier.
tColor4f ColorUtil :: HSVToColor (float h, float s, float v) hvis (h == 0 && s == 0) return tColor4f (v, v, v, 1.0f); flyte c = s * v; float x = c * (1 - abs (int32_t (h)% 2 - 1)); flyte m = v - c; hvis (h < 1) return tColor4f(c + m, x + m, m, 1.0f); else if (h < 2) return tColor4f(x + m, c + m, m, 1.0f); else if (h < 3) return tColor4f(m, c + m, x + m, 1.0f); else if (h < 4) return tColor4f(m, x + m, c + m, 1.0f); else if (h < 5) return tColor4f(x + m, m, c + m, 1.0f); else return tColor4f(c + m, m, x + m, 1.0f);
Nå kan vi endre Enemy :: wasShot ()
å bruke tilfeldige farger. For å gjøre eksplosjonsfargen mindre ensformig, velger vi to nærliggende nøkkelfarger for hver eksplosjon og interpolerer lineært mellom dem med en tilfeldig mengde for hver partikkel:
void Enemy :: wasShot () mIsExpired = true; float hue1 = Extensions :: nextFloat (0, 6); float hue2 = fmodf (hue1 + Extensions :: nextFloat (0, 2), 6.0f); tColor4f color1 = ColorUtil :: HSVToColor (hue1, 0.5f, 1); tColor4f color2 = ColorUtil :: HSVToColor (hue2, 0.5f, 1); for (int i = 0; i < 120; i++) float speed = 18.0f * (1.0f - 1 / Extensions::nextFloat(1, 10)); ParticleState state(Extensions::nextVector2(speed, speed), ParticleState::kEnemy, 1); tColor4f color = Extensions::colorLerp(color1, color2, Extensions::nextFloat(0, 1)); GameRoot::getInstance()->getParticleManager () -> createParticle (Art :: getInstance () -> getLineParticle (), mPosition, farge, 190, 1.5f, state);
Eksplosjonene skal se ut som animasjonen nedenfor:
Du kan leke med fargegenerasjonen som passer dine preferanser. En alternativ teknikk som fungerer bra, er å hånd velge en rekke fargemønstre for eksplosjoner og velg tilfeldig fra dine forhåndsvalgte fargeskjemaer.
Vi kan også få kulene til å eksplodere når de kommer til kanten av skjermen. Vi vil i hovedsak gjøre det samme som vi gjorde for fiendtlige eksplosjoner.
La oss endre Bullet :: oppdatering ()
som følger:
hvis (! tRectf (0, 0, GameRoot :: getInstance () -> getViewportSize ()) inneholder (tPoint2f ((int32_t) mPosition.x, (int32_t) mPosition.y))) mIsExpired = true; for (int i = 0; i < 30; i++) GameRoot::getInstance()->getParticleManager () -> createParticle (Art :: getInstance () -> getLineParticle (), mPosition, tColor4f (0.67f, 0.85f, 0.90f, 1), 50, 1, ParticleState (Extensions :: nextVector2 (0, 9) , ParticleState :: kBullet, 1));
Du kan legge merke til at det å gi partiklene en tilfeldig retning er søppelaktig, fordi minst halvparten av partiklene umiddelbart vil gå utenfor skjermen (mer hvis kulen eksploderer i et hjørne). Vi kunne gjøre litt ekstra arbeid for å sikre at partikler bare gis hastigheter motsatt veggen de står overfor. I stedet skjønner vi imidlertid fra Geometry Wars og la alle partikler sprette av veggene, så noen partikler som går utenom skjermen vil bli spilt tilbake.
Legg til følgende linjer til ParticleState.UpdateParticle ()
hvor som helst mellom de første og siste linjene:
tVector2f pos = partikkel.mposisjon; int bredde = (int) GameRoot :: getInstance () -> getViewportSize (). bredde; int høyde = (int) GameRoot :: getInstance () -> getViewportSize (). høyde; // kolliderer med kantene på skjermen hvis (pos.x < 0) vel.x = (float)fabs(vel.x); else if (pos.x > bredde) vel.x = (float) -fabs (vel.x); hvis (pos.y < 0) vel.y = (float)fabs(vel.y); else if (pos.y > høyde) vel.y = (float) -fabs (vel.y);
Vi får en virkelig stor eksplosjon når spilleren blir drept. endre PlayerShip :: drepe ()
som så:
void PlayerShip :: kill () PlayerStatus :: getInstance () -> removeLife (); mFramesUntilRespawn = PlayerStatus :: getInstance () -> getIsGameOver ()? 300: 120; tColor4f eksplosjonColor = tColor4f (0,8f, 0,8f, 0,4f, 1,0f); for (int i = 0; i < 1200; i++) float speed = 18.0f * (1.0f - 1 / Extensions::nextFloat(1.0f, 10.0f)); tColor4f color = Extensions::colorLerp(tColor4f(1,1,1,1), explosionColor, Extensions::nextFloat(0, 1)); ParticleState state(Extensions::nextVector2(speed, speed), ParticleState::kNone, 1); GameRoot::getInstance()->getParticleManager () -> createParticle (Art :: getInstance () -> getLineParticle (), mPosition, farge, 190, 1.5f, state);
Dette ligner fiendens eksplosjoner, men vi bruker flere partikler og bruker alltid samme fargevalg. Partikkeltypen er også satt til ParticleState :: kNone
.
I demonstrasjonen sakter partikler fra fiendeeksplosjoner raskere enn partikler fra spillerens skip eksploderer. Dette gjør spillerens eksplosjon siste litt lenger og ser litt mer episk ut.
Nå som vi har partikkeleffekter, la vi gå tilbake til de svarte hullene og få dem til å samhandle med partikler.
Svarte hull bør påvirke partikler i tillegg til andre enheter, så vi må endre ParticleState :: updateParticle ()
. La oss legge til følgende linjer:
hvis (particle.mState.mType! = kIgnoreGravity) for (std :: list:: iterator j = EntityManager :: getInstance () -> mBlackHoles.begin (); j! = EntityManager :: getInstance () -> mBlackHoles.end (); j ++) tVector2f dPos = (* j) -> getPosition () - pos; flyteavstand = dPos.length (); tVector2f n = dPos / avstand; vel + = 10000.0f * n / (avstand * avstand + 10000.0f); // legge tangentiell akselerasjon for nærliggende partikler hvis (avstand < 400) vel += 45.0f * tVector2f(n.y, -n.x) / (distance + 100.0f);
Her, n
er enheten vektoren peker mot det svarte hullet. Den attraktive kraften er en modifisert versjon av den inverse firkantfunksjonen:
avstand ^ 2 + 10.000
; Dette medfører at den attraktive kraften nærmer seg en maksimumsverdi i stedet for å tippe mot uendelig ettersom avstanden blir svært liten. avstand ^ 2
blir mye større enn 10.000. Derfor legger du til 10.000 til avstand ^ 2
har en svært liten effekt, og funksjonen tilnærmer en normal inversfirkantfunksjon.vel + = n
Tips: Å rotere en vektor, V
, 90 ° med urviseren, ta (V.Y, -V.X)
. På samme måte, for å rotere den 90 ° mot urviseren, ta (-V.Y, V.X)
.
Et svart hull vil produsere to typer partikler. Først vil det periodisk sprøyte ut partikler som vil bane rundt det. For det andre, når et svart hull er skutt, vil det sprøyte ut spesielle partikler som ikke påvirkes av dens tyngdekraft.
Legg til følgende kode i Blackhole :: WasShot ()
metode:
float hue = fmodf (3.0f / 1000.0f * tTimer :: getTimeMS (), 6); tColor4f color = ColorUtil :: HSVToColor (fargetone, 0.25f, 1); const int numParticles = 150; float startOffset = Extensions :: nextFloat (0, tMath :: PI * 2.0f / numParticles); for (int i = 0; i < numParticles; i++) tVector2f sprayVel = MathUtil::fromPolar(tMath::PI * 2.0f * i / numParticles + startOffset, Extensions::nextFloat(8, 16)); tVector2f pos = mPosition + 2.0f * sprayVel; ParticleState state(sprayVel, ParticleState::kIgnoreGravity, 1.0f); GameRoot::getInstance()->getParticleManager () -> createParticle (Art :: getInstance () -> getLineParticle (), pos, farge, 90, 1.5f, state);
Dette fungerer i det meste på samme måte som de andre partikkeleksplosjonene. En forskjell er at vi velger fargen på fargen basert på totalt forløpt spilletid. Hvis du skyter det svarte hullet flere ganger i rask rekkefølge, vil du se fargen på eksplosjonene gradvis rotere. Dette ser mindre rotete ut enn å bruke tilfeldig farger, samtidig som det tillater variasjon.
For den orbitende partikkelsprøyten må vi legge til en variabel til Svart hull
klasse for å spore den retningen som vi for tiden sprøyter partikler på:
beskyttet: int mHitPoints; flyte mSprayAngle; BlackHole :: BlackHole (const tVector2f & posisjon): mSprayAngle (0) ...
Nå legger vi til følgende i Blackhole :: oppdatering ()
metode.
// De svarte hullene sprayer noen baneformede partikler. Sprøyten skifter av og på hvert kvart sekund. hvis ((tTimer :: getTimeMS () / 250)% 2 == 0) tVector2f sprayVel = MathUtil :: fromPolar (mSprayAngle, Extensions :: nextFloat (12, 15)); tColor4f color = ColorUtil :: HSVToColor (5, 0,5f, 0,8f); tVector2f pos = mPosisjon + 2.0f * tVector2f (sprayVel.y, -sprayVel.x) + Extensions :: nextVector2 (4, 8); ParticleState State (sprayVel, ParticleState :: kEnemy, 1.0f); GameRoot :: getInstance () -> getParticleManager () -> createParticle (Art :: getInstance () -> getLineParticle (), pos, farge, 190, 1.5f, state); // roter sprayretningen mSprayAngle - = tMath :: PI * 2.0f / 50.0f;
Dette vil føre til at de svarte hullene spruter sprut av lilla partikler som danner en ring som kretser rundt det svarte hullet, slik som:
Som diktert av lovene i geometrisk-neon fysikk, driver spillers skip seg ved å jette en strøm av brennende partikler ut av eksosrøret. Med vår partikkelmotor på plass, er denne effekten lett å lage og legger til visuell flair til skipets bevegelse.
Når skipet beveger seg, oppretter vi tre strømmer av partikler: en senterstrøm som brenner rett ut på skipets bakside, og to sidestrømmer med vinkler som svinger frem og tilbake i forhold til skipet. De to sidestrømmene svinger i motsatt retning for å lage et kryssovergangsmønster. Sidestrømmene har en rødere farge, mens senterstrømmen har en varmere gul-hvit farge. Animasjonen nedenfor viser effekten:
For å få brannen til å lyse mer lyst, vil vi få skipet avgir ytterligere partikler som ser slik ut:
Disse partiklene blir tonet og blandet med de vanlige partiklene. Koden for hele effekten er vist nedenfor:
void PlayerShip :: MakeExhaustFire () if (mVelocity.lengthSquared ()> 0.1f) mOrientation = Extensions :: toAngle (mVelocity); float cosA = cosf (mOrientation); float sinA = sinf (mOrientation); tMatrix2x2f rot (tVector2f (cosA, sinA), tVector2f (-sinA, cosA)); flyte t = tTimer :: getTimeMS () / 1000.0f; tVector2f baseVel = Extensions :: scaleTo (mVelocity, -3); tVector2f perpVel = tVector2f (baseVel.y, -baseVel.x) * (0,6f * (float) sinf (t * 10.0f)); tColor4f sideColor (0,78f, 0,15f, 0,04f, 1); tColor4f midColor (1,0f, 0,73f, 0,12f, 1); tVector2f pos = mPosisjon + rot * tVector2f (-25, 0); // posisjon av skipets eksosrør. const float alfa = 0,7f; // middelpartikkelstrøm tVector2f velMid = baseVel + Extensions :: nextVector2 (0, 1); GameRoot :: getInstance () -> getParticleManager () -> createParticle (Art :: getInstance () -> getLineParticle (), pos, tColor4f (1,1,1,1) * alfa, 60.0f, tVector2f (0,5f, 1), ParticleState (velMid, ParticleState :: kEnemy)); GameRoot :: getInstance () -> getParticleManager () -> createParticle (Art :: getInstance () -> getGlow (), pos, midColor * alfa, 60.0f, tVector2f (0.5f, 1), ParticleState (velMid, ParticleState: : kEnemy)); // sidepartikkelstrømmer tVector2f vel1 = baseVel + perpVel + Extensions :: nextVector2 (0, 0.3f); tVector2f vel2 = baseVel - perpVel + Extensions :: nextVector2 (0, 0.3f); GameRoot :: getInstance () -> getParticleManager () -> createParticle (Art :: getInstance () -> getLineParticle (), pos, tColor4f (1,1,1,1) * alfa, 60.0f, tVector2f (0,5f, 1), ParticleState (vel1, ParticleState :: kEnemy)); GameRoot :: getInstance () -> getParticleManager () -> createParticle (Art :: getInstance () -> getLineParticle (), pos, tColor4f (1,1,1,1) * alfa, 60.0f, tVector2f (0,5f, 1), ParticleState (vel2, ParticleState :: kEnemy)); GameRoot :: getInstance () -> getParticleManager () -> createParticle (Art :: getInstance () -> getGlow (), pos, sideColor * alfa, 60.0f, tVector2f (0.5f, 1), ParticleState (vel1, ParticleState: : kEnemy)); GameRoot :: getInstance () -> getParticleManager () -> createParticle (Art :: getInstance () -> getGlow (), pos, sideColor * alfa, 60.0f, tVector2f (0.5f, 1), ParticleState (vel2, ParticleState: : kEnemy));
Det er ikke noe snø som skjer i denne koden. Vi bruker en sinusfunksjon til å produsere svivelvirkningen i sidestrømmene ved å variere sidelengshastigheten over tid. For hver strøm genererer vi to overlappende partikler per ramme: en halvtransparent, hvit LineParticle
, og en farget glødpartikkel bak den. AnropMakeExhaustFire ()
ved slutten av PlayerShip.Update ()
, umiddelbart før du setter fartøyets hastighet til null.
Med alle disse partikkeleffekter begynner Shape Blaster å se ganske kul ut. I den siste delen av denne serien vil vi legge til en ekstra fantastisk effekt: det klingende bakgrunnsnettet.