Lag en Neon Vector Shooter Med jME Particle Effects

Vi har kodet spill, lyd og brukergrensesnitt for vårt jMonkeyEngine-powered Geometry Wars-inspirerte spill, og nå kan vi vende oss til noen pene grafiske effekter og polere. I denne delen vil vi fokusere på partikkeleffekter (inkludert noen veldig fargerike eksplosjoner).


Oversikt

Her jobber vi over hele serien:


... og her er en video som viser av partikkeleffekter vi legger til i denne delen:


Det vil være forskjellige typer partikler så vel som forskjellige emittere:

  • Når fiender blir rammet, vil de dø i en fargerik eksplosjon.
  • Når spilleren dør, eksploderer skipet i en stor gylden eksplosjon.
  • Motoren til spilleren avgir en enkel partikkel brann effekt.
  • Kuler som rammer grensen på skjermen eksploderer.
  • Svarte hull utløser kontinuerlig lilla partikler (slik at de ser kulere ut).
  • Når et svart hull taper treffpunkter, utsender det en fargerik partikkelblast.

Foruten den siste partikkeltypen påvirkes alle partikler av tyngdekraften og suges inn i sorte hull. Så, når et svart hull suger i mange partikler om gangen, begynner det å gløder på grunn av alle partiklene - som ser ganske kult ut.

En annen effekt vi vil legge til er å gjøre partiklene våre større og derfor lysere jo raskere de er. Dette vil bety at en eksplosjon ser veldig lys og skinnende først, men fortaber raskt lysstyrken når partiklene sakter seg.

For å nå våre mål må vi legge til to nye klasser:

  • ParticleManager: Denne lederklassen vil ta vare på egenskapene for hver eksplosjonstype.
  • ParticleControl: Jeg tror du kan allerede gjette at denne klassen igjen kontrollerer oppførselen til partiklene våre.

La oss starte med den mest merkbare effekten: eksploderende fiender.


Enemy Explosions

Den første klassen vi trenger å implementere er ParticleManager klasse. Siden det er ansvarlig for gytepartikler, trenger det noen variabler, for eksempel guiNode, de particleNode og standardParticle.

Vi vil klone dette når vi trenger det, men ta en titt på grunnkoden:

 offentlig klasse ParticleManager private Node guiNode; Privat romlig standardPartikkel, glødPartikkel; privat node particleNode; privat tilfeldig rand; offentlig ParticleManager (Node guiNode, romlig standardParticle, romlig glødspartikkel) this.guiNode = guiNode; this.standardParticle = standardParticle; this.glowParticle = glowParticle; particleNode = ny Node ("partikler"); guiNode.attachChild (particleNode); rand = ny tilfeldig (); 

Integrere lederen i MonkeyBlasterMain er ikke så farlig. Vi erklærer det bare i begynnelsen og kaller konstruktøren i simpleInitApp ():

 particleManager = new ParticleManager (guiNode, getSpatial ("Laser"), getSpatial ("Glow"));

For å faktisk gjøre en fiende eksplodere, må vi ha den riktige metoden for å gjøre dette i ParticleManager:

 offentlig ugyldig fiendeExplosion (Vector3f posisjon) // init farger float hue1 = rand.nextFloat () * 6; float hue2 = (rand.nextFloat () * 2)% 6f; ColorRGBA color1 = hsvToColor (hue1, 0.5f, 1f); ColorRGBA color2 = hsvToColor (hue2, 0.5f, 1f); // lage 120 partikler for (int i = 0; i<120; i++)  Vector3f velocity = getRandomVelocity(250); Spatial particle = standardParticle.clone(); particle.setLocalTranslation(position); ColorRGBA color = new ColorRGBA(); color.interpolate(color1, color2, rand.nextFloat()*0.5f); particle.addControl(new ParticleControl(velocity,true,3100,color)); particleNode.attachChild(particle);  

Denne metoden er kort, men det gjør mye, så vi går gjennom det trinnvis.

Farger partiklene

For å gjøre partiklene mer interessante, tildeler vi tilfeldige farger til dem.

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 skal ha et "neon light" utseende.

Vi kan få mer kontroll over våre farger ved å spesifisere dem i HSV (fargetone, metning og verdi) fargeområde. Vi vil gjerne velge farger med et tilfeldig fargetone, men en fast metning og verdi for å få dem alle til å se lyse og skinnende, så vi trenger en hjelperfunksjon som kan produsere en farge fra HSV-verdier.

 offentlig ColorRGBA hsvToColor (float h, float s, float v) if (h == 0 && s == 0) returner ny ColorRGBA (v, v, v, 1);  flyte c = s * v; float x = c * (1 - Math.abs (h% 2 - 1)); flyte m = v - c; hvis (h < 1) return new ColorRGBA(c + m, x + m, m, 1);  else if (h < 2) return new ColorRGBA(x + m, c + m, m, 1);  else if (h < 3) return new ColorRGBA(m, c + m, x + m, 1);  else if (h < 4) return new ColorRGBA(m, x + m, c + m, 1);  else if (h < 5) return new ColorRGBA(x + m, m, c + m, 1);  else return new ColorRGBA(c + m, m, x + m, 1); 

Tips: Ikke bekymre deg for mye om hvordan denne funksjonen fungerer; bare forstå at det kan generere en RGBA-farge fra HSV-verdien. Metoden er utenfor omfanget og fokuset på denne opplæringen.

Hvorfor trenger vi to farger?

Nå tilbake til vår eksplosjonsmetode. Ta en titt på de uthevede linjene:

 offentlig ugyldig fiendeExplosion (Vector3f posisjon) // init farger float hue1 = rand.nextFloat () * 6; float hue2 = (rand.nextFloat () * 2)% 6f; ColorRGBA color1 = hsvToColor (hue1, 0.5f, 1f); ColorRGBA color2 = hsvToColor (hue2, 0.5f, 1f); // lage 120 partikler for (int i = 0; i<120; i++)  Vector3f velocity = getRandomVelocity(250); Spatial particle = standardParticle.clone(); particle.setLocalTranslation(position); ColorRGBA color = new ColorRGBA(); color.interpolate(color1, color2, rand.nextFloat()*0.5f); particle.addControl(new ParticleControl(velocity,true,3100,color)); particleNode.attachChild(particle);  

For å gjøre eksplosjonen mer fargerik, beregner vi to tilfeldige farger og interpolerer sluttpartikkelfargene tilfeldig for hver partikkel.

Gjøre partiklene flytte

Den neste tingen vi gjør er å beregne hastigheten for hver partikkel. Vi håndterer dette i en ekstra metode fordi vi vil at retningen skal være tilfeldig, men ikke hastigheten:

 privat Vector3f getRandomVelocity (float max) // generere Vector3f med tilfeldig retning Vector3f hastighet = ny Vector3f (rand.nextFloat () - 0.5f, rand.nextFloat () - 0.5f, 0) .normalizeLocal (); // bruk semi-tilfeldig partikkelhastighet float tilfeldig = rand.nextFloat () * 5 + 1; float particleSpeed ​​= max * (1f - 0,6f / tilfeldig); velocity.multLocal (particleSpeed); returhastighet; 

Først genererer vi en tilfeldig hastighetsvektor og normaliserer den. Deretter beregner vi en tilfeldig hastighet i området mellom 40% og 90% av max.

Nå tilbake til enemyExplosion () metode. Her er delen vi ikke har diskutert ennå:

 Romlig partikkel = standardPartikkel.klone (); particle.setLocalTranslation (posisjon); ColorRGBA color = new ColorRGBA (); color.interpolate (color1, color2, rand.nextFloat () * 0.5f); partikkel.addControl (ny ParticleControl (hastighet, 3100, farge)); particleNode.attachChild (partikkel);

Vi kloner standardParticle og sett oversettelsen til eksplosjonens opprinnelse. Deretter interpolerer vi partikkelfargene mellom de to tilfeldige segene (som nevnt ovenfor). Som du kan se, legger vi også til en ParticleControl som vil styre oppførselen til partikkelen. Til slutt må vi legge partikkelen til noden for at den skal vises.

Kontrollere partiklene

Nå som vår ParticleManager er ferdig, må vi implementere ParticleControl. Deler av koden vil se deg kjent ut:

 offentlig klasse ParticleControl utvider AbstractControl privat Vector3f hastighet; privat flyte levetid; privat lang spawntime; privat ColorRGBA-farge; offentlig ParticleControl (Vector3f-hastighet, flyt-levetid, ColorRGBA-farge) this.velocity = speed; this.lifespan = levetid; this.color = color; spawnTime = System.currentTimeMillis ();  @Override protected void controlUpdate (float tpf) // bevegelse spatial.move (hastighet.mult (tpf * 3f)); velocity.multLocal (1-3f * TPF); hvis (Math.abs (hastighet.x) + Math.abs (hastighet.y) < 0.001f)  velocity = Vector3f.ZERO;  // rotation if (velocity != Vector3f.ZERO)  spatial.rotateUpTo(velocity.normalize()); spatial.rotate(0,0,FastMath.PI/2f);  // scaling and alpha float speed = velocity.length(); long difTime = System.currentTimeMillis() - spawnTime; float percentLife = 1- difTime / lifespan; float alpha = lesserValue(1.5f,lesserValue(percentLife*2,speed)); alpha *= alpha; setAlpha(alpha); spatial.setLocalScale(0.3f+lesserValue(lesserValue(1.5f,0.02f*speed+0.1f),alpha)); spatial.scale(0.65f); // is particle expired? if (difTime > levetid) spatial.removeFromParent ();  @Override protected void controlRender (RenderManager rm, ViewPort vp)  privat float lesserValue (float a, float b) return a < b ? a : b;  private void setAlpha(float alpha)  color.set(color.r,color.g,color.b,alpha); Node spatialNode = (Node) spatial; Picture pic = (Picture) spatialNode.getChild(spatialNode.getName()); pic.getMaterial().setColor("Color",color);  

På toppen av klassen erklærer og initialiserer vi noen variabler; deres navn skal være selvforklarende nå. Hvis du ser på controlUpdate () du vil finne kjent kode: Vi beveger partikkelen med sin hastighet, sakke den litt ned og roter den i retning av hastigheten.
Hvis partikkelen er veldig treg, setter vi hastigheten til Vector3f.ZERO. Det er mye raskere å gjøre beregninger med null enn et svært lite tall, og forskjellen er ikke synlig uansett.

For å få en eksplosjon gå virkelig boom, Vi vil gjøre partikkelen større når den beveger seg raskt, som vanligvis er like etter eksplosjonen. På samme måte gjør vi partikkelen mindre og jevn transparent når den beveger seg veldig sakte eller når slutten av levetiden. For å gjøre det mer gjennomsiktig kaller vi en hjelpemetode, setAlpha (float alpha).

Tips: Hvis du ikke vet hvordan du får barns romlige plasser og setter materiale, kan du enten kopiere eller lime inn metoden eller se på SeekerControl eller WandererControl fra det andre kapitlet; det er forklart der.

Nå som vi har fullført ParticleControl, du kan starte spillet og se ... ingenting.
Vet du hva vi glemte?

Sette det sammen

Når en fiende dør, må vi ringe enemyExplosion () i ParticleManager, ellers vil ingenting skje! Se på MonkeyBlasterMain og se etter metoden handleCollisions (), dette er hvor fiender dør. Legg bare inn anropet i riktig linje:

 // ... annet hvis (enemyNode.getChild (i) .getName (). Equals ("Wanderer")) hud.addPoints (1);  particleManager.enemyExplosion (enemyNode.getChild (i) .getLocalTranslation ()); enemyNode.detachChildAt (i); bulletNode.detachChildAt (j); sound.explosion (); gå i stykker; // ... 

Og du må ikke glemme at det er en annen måte at fiender kan dø: når de blir sugd i sorte hull. Bare sett inn (nesten) samme linje noen linjer lenger nede når vi sjekker om kollisjoner med svart hull:

 if (checkCollision (enemyNode.getChild (j), blackHole)) particleManager.enemyExplosion (enemyNode.getChild (j) .getLocalTranslation ()); enemyNode.detachChildAt (j); 

Nå kan du endelig starte spillet og spille litt. Disse partiklene legger virkelig til atmosfæren, tror du ikke? Men la oss ikke stoppe med en effekt; det er mange flere å komme ...


Bullet Explosions

Når en kule rammer grensen på skjermen, vil vi få det til å eksplodere også.

Se på BulletControl. Det er allerede kode som kontrollerer om kulen er utenfor rammen på skjermen, så la oss utløse eksplosjonen der. For å gjøre det må vi erklære ParticleManager i BulletControl og send det i konstruktøren:

 offentlig BulletControl (Vector3f retning, int screenWidth, int screenHeight, ParticleManager particleManager) this.particleManager = particleManager;

Ikke glem at du må passere particleManager i MonkeyBlasterMain.

Vi setter inn anropet her:

 hvis (loc.x skjermbredde || loc.y> skjermhøyde) particleManager.bulletExplosion (loc); spatial.removeFromParent (); 

De bulletExplosion (Vector3f posisjon) Metoden er veldig lik den enemyExplosion (Vector3f posisjon) metode. De eneste forskjellene er at vi ikke vil få partiklene ganske like fort, og at vi bruker en fast farge (en lyseblå). Også, vi reduserer levetiden til partiklene.

 Offentlig tomrumsuttrykk (Vector3f-posisjon) for (int i = 0; i<30; i++)  Vector3f velocity = getRandomVelocity(175); Spatial particle = standardParticle.clone(); particle.setLocalTranslation(position); ColorRGBA color = new ColorRGBA(0.676f,0.844f,0.898f,1); particle.addControl(new ParticleControl(velocity, 1000, color)); particleNode.attachChild(particle);  

Siden vi har all nødvendig kode på plass, er det enkelt å legge til nye eksplosjoner, som du kan se. Før vi legger til en annen eksplosjon for spillernes død, legger vi til en ny funksjonalitet til ParticleControl.


Repelling partikler fra skjermgrensene

Når en kule rammer grensen på skjermen, er omtrent halvparten av partiklene ubrukelige. Disse partiklene vises aldri på skjermen fordi de flyr bort fra den. La oss endre det.

Vi vil nå snu hastigheten til hver partikkel som forlater skjermen rundt, slik at de blir "avstøtet" av grensene.

 Vector3f loc = spatial.getLocalTranslation (); hvis (lok.x < 0)  velocity.x = Math.abs(velocity.x);  else if (loc.x > skjermbredde) velocity.x = -Math.abs (hastighet.x);  hvis (loc.z < 0)  velocity.y = Math.abs(velocity.y);  else if (loc.y > skjermhøyde) hastighet.y = -Math.abs (hastighet.y); 

Vi inverterer ikke hele vektoren, bare den x eller y variabel (avhengig av grensen som ble rammet). Dette resulterer i en riktig avstøtende effekt, som et speil som reflekterer lys.

Tips: Du må ikke glemme å passere screenWidth og screenHeight fra MonkeyBlasterMain til ParticleManager og derfra til alle ParticleControl. Hvis du ikke bryr deg om ren kode så mye du kunne lage to statiske variabler i MonkeyBlasterMain og jobbe med dem.

Start spillet, og du vil merke at kuldeeksplosjoner ser mye lysere ut nå. Partikler fra fiendtlige eksplosjoner blir også avstøtet.


Spillereksplosjon

Når spilleren dør, vil vi ha en egentlig stor eksplosjon som dekker hele skjermen. Vi kaller metoden igjen, i killPlayer () i MonkeyBlasterMain.

 particleManager.playerExplosion (player.getLocalTranslation ());

Koden for playerExplosion er ganske mye det samme som før. Men denne gangen bruker vi to farger, hvite og gule, og interpolerer mellom dem. Vi setter hastigheten til 1000 og levetiden til 2800 millisekunder.

 offentlig tomt spillerExplosion (Vector3f posisjon) ColorRGBA color1 = ColorRGBA.White; ColorRGBA color2 = ColorRGBA.Yellow; for (int i = 0; i<1200; i++)  Vector3f velocity = getRandomVelocity(1000); Spatial particle = standardParticle.clone(); particle.setLocalTranslation(position); ColorRGBA color = new ColorRGBA(); color.interpolate(color1, color2, rand.nextFloat()); particle.addControl(new ParticleControl(velocity, 2800, color, screenWidth, screenHeight)); particleNode.attachChild(particle);  

Sugende partikler i svarte hull

Nå som vi har ganske få partikkeleffekter, la vi legge vekt på dem. Når de kommer nært nok til et svart hull, bør de suges inn - men dette er ikke sant for hver partikkel. Senere vil vi ha en type partikkel som suges inn og en type som ikke gjør det. Derfor må vi legge til et attributt til partiklene våre:

 particle.setUserData ("affectedByGravity", true);

Alle partikkeltyper som vi har opprettet til nå, skal suges inn med svarte hull, slik at du kan legge til denne linjen til hver metode der vi kaster partikler.

Nå til håndtering av tyngdekraften. Gå til handleGravity () i MonkeyBlasterMain -Det er her vi implementerte tyngdekraften i tredje del av serien.

Denne gangen vil vi ikke sjekke om en partikkel er innenfor rekkevidde av det svarte hullet, vi vil ganske enkelt bruke tyngdekraften til dem alle. Hvis en bestemt partikkel er langt borte, vil gravitasjonseffekten ikke være veldig sterk uansett.

Vi kontrollerer om partikkelen er påvirket av tyngdekraften, og hvis det er, bruker vi det:

 // sjekk partikler for (int j = 0; j 

Nå må vi utvide applyGravity () også:

 // ... annet hvis (target.getName (). Equals ("Laser") || target.getName (). Equals ("Glow")) target.getControl (ParticleControl.class) .applyGravity (gravity.mult 15000), avstand); 

Vi må sjekke målets navn for begge deler laser og Gløde, fordi det er to forskjellige typer partikler som vil ha samme oppførsel.

En annen ting å legge merke til er at vi ikke bare overfører den modifiserte gravitetsvektoren, men også avstanden til det svarte hullet. Dette er viktig i beregningen av kraften i applyGravity () i ParticleControl:

 Vector3f additionalVelocity = gravity.mult (1000f / (avstand * avstand + 10000f)); velocity.addLocal (additionalVelocity); hvis (avstand < 400)  additionalVelocity = new Vector3f(gravity.y, -gravity.x, 0).mult(3f / (distance + 100)); velocity.addLocal(additionalVelocity); 

Her, tyngde er enheten vektoren peker mot det svarte hullet. Den attraktive kraften er en modifisert versjon av den inverse kvadratfunksjonen.

Den første modifikasjonen er at nevnen er (avstand * avstand) + 10000-det vil si, den inneholder en avstandskvartet term. Dette får den attraktive kraften til å nærme seg en maksimumsverdi i stedet for å tippe mot uendelig ettersom avstanden blir svært liten.

Når avstanden blir større enn 100 piksler, (avstand * avstand) blir raskt mye større enn 10.000. Derfor legger du til 10.000 til (avstand * avstand) har en svært liten effekt, og funksjonen tilnærmer en normal inversfirkantfunksjon.

Men når avstanden er mye mindre enn 100 piksler, har avstanden en liten effekt på verdien av nevnen, og ligningen blir omtrent lik:

 vel + = n;

Den andre modifikasjonen vi har gjort er å legge en sidelengs komponent til hastigheten når partiklene kommer nær nok til det svarte hullet. Dette tjener to formål: For det første gjør partiklene spiral med urviseren inn mot det svarte hullet; For det andre når partiklene kommer nær nok, vil de nå likevekt og danne en glødende sirkel rundt det svarte hullet.

Tips: Å rotere en vektor, v, 90 ° med urviseren, ta (v.y, -v.x). På samme måte, for å rotere 90 ° mot urviseren, ta (-v.y, v.x).

Denne partikkel-effekten virker pen når du starter spillet og ser på det, og dette gjelder spesielt når det er mange eksplosjoner og partikler rundt. Men når det ikke er noen eksplosjoner, ser sorte hull ut litt kjedelig ut. Vi endrer det snart.


Sprøyte partikler ut av sorte hull

For å få sorte hull til å produsere partikler kontinuerlig, må vi ta en titt på controlUpdate (float tpf) metode i BlackHoleControl. Det er en hvis uttalelse som kontrollerer hvor svart hull er aktivt; Hvis det er, vil vi få det til å utføre denne koden:

 lang sprayDif = System.currentTimeMillis () - lastSprayTime; hvis ((System.currentTimeMillis () / 250)% 2 == 0 && sprayDif> 20) lastSprayTime = System.currentTimeMillis (); Vector3f sprayVel = MonkeyBlasterMain.getVectorFromAngle (sprayAngle) .mult (rand.nextFloat () * 3 +6); Vector3f randVec = MonkeyBlasterMain.getVectorFromAngle (rand.nextFloat () * FastMath.PI * 2); randVec.multLocal (4 + rand.nextFloat () * 4); Vector3f posisjon = spatial.getLocalTranslation (). Legg til (sprayVel.mult (2f)). AddLocal (randVec); particleManager.sprayParticle (stilling, sprayVel.mult (30f));  sprayAngle - = FastMath.PI * tpf / 10f;

Vi har et par nye variabler her. Du må deklarere og initialisere lang sisteSprayTime, de float sprayAngle og Tilfeldig rand. Også, du må deklarere particleManager og send det ned fra hovedklassen slik at vi faktisk kan spraye partiklene.

Metoden vil føre til at de svarte hullene spruter sprut av lilla partikler som danner en kul glødende ring som kretser rundt det svarte hullet

Den faktiske sprayParticle () metoden er ikke noe spesielt. Vi lager en partikkel, bruker en lilla farge, legger til en kontroll og så videre:

 offentlig tomrom spraypartikkel (Vector3f posisjon, Vector3f sprayVel) Romlig partikkel = standardParticle.clone (); particle.setLocalTranslation (posisjon); ColorRGBA color = new ColorRGBA (0.8f, 0.4f, 0.8f, 1f); particle.addControl (new ParticleControl (sprayVel, 3500, color, screenWidth, screenHeight)); particle.setUserData ("affectedByGravity", true); ((Node) guiNode.getChild ("partikler")). AttachChild (partikkel); 

Start spillet og se hvordan det ser ut.

Tips: Hvis du vil endre partikkelens sirkeladferd, kan du ikke leke med verdiene i applyGravity () i ParticleControl.

Dette forbedrer det generelle utseendet til de svarte hullene, men det er ikke godt nok ennå! Det er en annen effekt vi kan legge til dem ...


Black Hole Explosions

Nå vil vi ikke få de svarte hullene til å eksplodere når de dør. Vi vil i stedet utløse en partikkeleksplosjon hver gang et svart hull blir rammet.

Legg til følgende metode for ParticleManager:

 Offentlig tomt blackHoleExplosion (Vector3f posisjon) float hue = ((System.currentTimeMillis () - spawnTime) * 0.003f)% 6f; int numParticles = 150; ColorRGBA color = hsvToColor (fargetone, 0.25f, 1); float startOffset = rand.nextFloat () * FastMath.PI * 2 / numParticles; for (int i = 0; i 

Dette fungerer stort sett 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 tilfeldige farger samtidig som det tillater variasjon.


Ship Exhaust Fire

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, skaper vi tre strømmer av partikler: en sentral strøm som brenner rett ut bak skipet, og to sidestrømmer hvis vinkler svinger frem og tilbake i forhold til skipet. De to sidestrømmene svinger i motsatt retning for å lage et kryssovergangsmønster, og har en røde farge, mens senterstrømmen har en varmere gul-hvit farge.

For å få brannen til å lyse mer lys enn det ville blomstre alene, vil vi få skipet avgir ytterligere partikler som ser slik ut:


En enkelt glødpartikkel.

Disse partiklene blir tonet og blandet med de vanlige partiklene. Koden for hele effekten er vist nedenfor:

 Offentlig tomgangsmaksjonsfire (Vector3f-posisjon, svingrotasjon) ColorRGBA midColor = ny ColorRGBA (1f, 0.73f, 0.12f, 0.7f); ColorRGBA sideColor = ny ColorRGBA (0.78f, 0.15f, 0.04f, 0.7f); Vector3f direction = MonkeyBlasterMain.getVectorFromAngle (rotasjon); float t = (System.currentTimeMillis () - spawnTime) / 1000f; Vector3f baseVel = direction.mult (-45f); Vector3f perpVel = ny Vector3f (baseVel.y, -baseVel.x, 0) .multLocal (2f * FastMath.sin (t * 10f)); Vector3f pos = position.add (MonkeyBlasterMain.getVectorFromAngle (rotasjon) .multLocal (-25f)); // midtstrøm Vector3f randVec = MonkeyBlasterMain.getVectorFromAngle (nytt tilfeldig (). nextFloat () * FastMath.PI * 2); Vector3f velMid = baseVel.add (randVec.mult (7.5f)); Spatial particleMid = standardParticle.clone (); particleMid.setLocalTranslation (pos); particleMid.addControl (new ParticleControl (velMid, 800, midColor, screenWidth, screenHeight)); particleMid.setUserData ("affectedByGravity", true); ((Node) guiNode.getChild ("partikler")) attachChild (particleMid); Spatial particleMidGlow = glowParticle.clone (); particleMidGlow.setLocalTranslation (pos); particleMidGlow.addControl (new ParticleControl (velMid, 800, midColor, screenWidth, screenHeight)); particleMidGlow.setUserData ("affectedByGravity", true); ((Node) guiNode.getChild ("partikler")). AttachChild (particleMidGlow); // side streams Vector3f randVec1 = MonkeyBlasterMain.getVectorFromAngle (ny tilfeldig (). nextFloat () * FastMath.PI * 2); Vector3f randVec2 = MonkeyBlasterMain.getVectorFromAngle (nytt tilfeldig (). NextFloat () * FastMath.PI * 2); Vector3f velSide1 = baseVel.add (randVec1.mult (2.4f)). AddLocal (perpVel); Vector3f velSide2 = baseVel.add (randVec2.mult (2.4f)). SubtrahereLocal (perpVel); Spatial particleSide1 = standardParticle.clone (); particleSide1.setLocalTranslation (pos); particleSide1.addControl (new ParticleControl (velSide1, 800, sideColor, screenWidth, screenHeight)); particleSide1.setUserData ("affectedByGravity", true); ((Node) guiNode.getChild ("partikler")). AttachChild (particleSide1); Spatial particleSide2 = standardParticle.clone (); particleSide2.setLocalTranslation (pos); particleSide2.addControl (new ParticleControl (velSide2, 800, sideColor, screenWidth, screenHeight)); particleSide2.setUserData ("affectedByGravity", true); ((Node) guiNode.getChild ("partikler")). AttachChild (particleSide2); Spatial particleSide1Glow = glowParticle.clone (); particleSide1Glow.setLocalTranslation (pos); particleSide1Glow.addControl (new ParticleControl (velSide1, 800, sideColor, screenWidth, screenHeight)); particleSide1Glow.setUserData ("affectedByGravity", true); ((Node) guiNode.getChild ("partikler")). AttachChild (particleSide1Glow); Spatial particleSide2Glow = glowParticle.clone (); particleSide2Glow.setLocalTranslation (pos); particleSide2Glow.addControl (new ParticleControl (velSide2, 800, sideColor, screenWidth, screenHeight)); particleSide2Glow.setUserData ("affectedByGravity", true); ((Node) guiNode.getChild ("partikler")). AttachChild (particleSide2Glow); 

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 standardpartikkel og en glødpartikkel bak den.

Sett inn denne delen av koden i PlayerControl, ved slutten av controlUpdate (float tpf):

 hvis (opp || ned || venstre || til høyre) particleManager.makeExhaustFire (spatial.getLocalTranslation (), rotasjon); 

Selvfølgelig må du ikke glemme å passere particleManager fra MonkeyBlasterMain.


Konklusjon

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