Gör en Neon Vector Shooter Med jME Partikel Effects

Vi har kodat spel, ljud och användargränssnitt för vårt jMonkeyEngine-powered Geometry Wars-inspirerade spel, och nu kan vi vända oss till några snygga grafiska effekter och polera. I den här delen kommer vi speciellt att fokusera på partikeleffekter (inklusive några väldigt färgglada explosioner).


Översikt

Här är vad vi arbetar mot över hela serien:


... och här är en video som visar bort de partikeleffekter vi lägger till i den här delen:


Det kommer att finnas olika typer av partiklar såväl som olika emitterare:

  • När fiender blir slagna, kommer de att dö i en färgstark explosion.
  • När spelaren dör, exploderar hans skepp i en stor gyllene explosion.
  • Spelarens motor utlämnar en enkel partikelbrand effekt.
  • Kulor som ramar gränsen på skärmen exploderar.
  • Svarta hål avger ständigt lila partiklar (så att de ser svalare ut).
  • När ett svart hål förlorar träffpunkter, avger det en färgstark partikelblastning.

Förutom den sista partikeltypen påverkas alla partiklar av gravitation och sugas i svarta hål. Så när ett svart hål suger i många partiklar åt gången börjar det glöda på grund av alla partiklarna - vilket ser ganska cool ut.

En annan effekt vi lägger till är att våra partiklar blir större och därför ljusare ju snabbare de är. Detta innebär att en explosion ser väldigt ljus ut och glänsande först, men förlorar snabbt sin ljusstyrka när partiklarna saktar ner.

För att uppnå våra mål måste vi lägga till två nya klasser:

  • ParticleManager: Denna chefsklass tar hand om attributen för varje slags explosion.
  • ParticleControl: Jag tror att du redan kan gissa att den här klassen åter kontrollerar våra partikers beteende.

Låt oss börja med den mest märkbara effekten: exploderande fiender.


Enemy Explosions

Den första klassen vi behöver genomföra är ParticleManager klass. Eftersom det är ansvarigt för gytpartiklar, behöver det vissa variabler, t.ex. guiNode, de particleNode och den standardParticle.

Vi kommer att klona detta när vi behöver det, men ta en titt på grundläggande koden:

 offentlig klass ParticleManager private Node guiNode; Privat rumslig standardParticle, glowParticle; privat nodpartikelnode; privat slumpmässig rand offentliga ParticleManager (Node guiNode, rumslig standardParticle, rumslig glödspartikel) this.guiNode = guiNode; this.standardParticle = standardParticle; this.glowParticle = glowParticle; particleNode = ny nod ("partiklar"); guiNode.attachChild (particleNode); rand = nytt slumpmässigt (); 

Integrerar chefen i MonkeyBlasterMain är ingen stor sak. Vi förklarar det bara i början och kallar konstruktören i simpleInitApp ():

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

För att faktiskt göra en fiende explodera, måste vi ha rätt metod att göra detta i ParticleManager:

 public void enemyExplosion (Vector3f position) // init färger 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); // skapa 120 partiklar för (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);  

Denna metod är kort, men det gör mycket, så vi går igenom det steg för steg.

Färger partiklarna

För att göra våra partiklar mer intressanta, tilldelar vi slumpmässiga färger till dem.

Ett sätt att producera slumpmässiga färger är att slumpmässigt välja de röda, blå och gröna komponenterna, men det kommer att producera en massa tråkiga färger och vi vill att våra partiklar ska ha ett "neonljus" utseende.

Vi kan få mer kontroll över våra färger genom att specificera dem i HSV (färgton, mättnad och värde) färgutrymme. Vi skulle vilja välja färger med en slumpmässig färgton, men en fast mättnad och värde, så att alla ser ljusa och glänsande ut, så vi behöver en hjälparfunktion som kan producera en färg från HSV-värden.

 public colorRGBA hsvToColor (float h, float s, float v) if (h == 0 && s == 0) returnera nya ColorRGBA (v, v, v, 1);  float c = s * v; float x = c * (1 - Math.abs (h% 2 - 1)); float m = v-c; om (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: Oroa dig inte för mycket om på vilket sätt den här funktionen fungerar förstår bara att det kan generera en RGBA-färg från HSV-värdet. Metoden ligger utanför omfattningen och fokuseringen av denna handledning.

Varför behöver vi två färger?

Nu tillbaka till vår explosionsmetod. Ta en titt på de markerade linjerna:

 public void enemyExplosion (Vector3f position) // init färger 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); // skapa 120 partiklar för (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);  

För att göra explosionen mer färgstark, beräknar vi två slumpmässiga färger och interpolerar slutpartikelfärgen slumpmässigt för varje partikel.

Gör partiklarna rörliga

Nästa sak vi gör är att beräkna hastigheten för varje partikel. Vi hanterar detta i en extra metod eftersom vi vill att riktningen ska vara slumpmässig, men inte hastigheten:

 privat Vector3f getRandomVelocity (float max) // generera Vector3f med slumpmässig riktning Vector3f hastighet = ny Vector3f (rand.nextFloat () - 0.5f, rand.nextFloat () - 0.5f, 0) .normalizeLocal (); // Applicera semi-slumpmässigt partikelhastighetsflöde slumpmässigt = rand.nextFloat () * 5 + 1; float particleSpeed ​​= max * (1f - 0,6f / slumpmässigt); velocity.multLocal (particleSpeed); returhastighet; 

Först genererar vi en slumpmässig hastighetsvektor och normaliserar den. Därefter beräknar vi en slumpmässig hastighet i intervallet mellan 40% och 90% av max.

Nu tillbaka till enemyExplosion () metod. Här är den del vi ännu inte har diskuterat:

 Spatialpartikel = standardParticle.clone (); particle.setLocalTranslation (positionen); ColorRGBA color = new ColorRGBA (); color.interpolate (color1, color2, rand.nextFloat () * 0.5f); partikel.addControl (New ParticleControl (hastighet, 3100, färg)); particleNode.attachChild (partikel);

Vi klonar standardParticle och sätta sin översättning till explosionens ursprung. Därefter interpolerarar vi partikelfärgen mellan de två slumpmässiga (som nämnts ovan). Som ni kan se lägger vi till en ParticleControl som kommer att styra partikelns beteende. Slutligen måste vi lägga till partikeln till noden för att den ska visas.

Styrning av partiklarna

Nu när vår ParticleManager är klar måste vi genomföra ParticleControl. Delar av koden kommer att bli bekanta för dig:

 offentlig klass ParticleControl utökar AbstractControl privat Vector3f hastighet; privat flytande livslängd privat lång spawnTime; privat ColorRGBA-färg; Offentlig partikelkontroll (Vector3f-hastighet, flytningslängd, ColorRGBA-färg) this.velocity = speed; this.lifespan = livslängd; this.color = color; spawnTime = System.currentTimeMillis ();  @Override protected void controlUpdate (float tpf) // movement spatial.move (hastighet.mult (tpf * 3f)); velocity.multLocal (1-3f * TPF); om (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 > livstid) spatial.removeFromParent ();  @Override protected void controlRender (RenderManager rm, ViewPort vp)  privat float lesserValue (float a, float b) returnera en < 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);  

I toppen av klassen förklarar och initierar vi några variabler. deras namn borde vara självförklarande nu. Om du tittar på controlUpdate () Du hittar välkänd kod: Vi flyttar partikeln med sin hastighet, sakta ner den lite och rotera den i hastighetsriktningen.
Om partikeln är väldigt långsam sätter vi dess hastighet på Vector3f.ZERO. Det är mycket snabbare att göra beräkningar med noll än ett mycket litet antal, och skillnaden är inte synlig ändå.

För att göra en explosion, gå verkligen bom, Vi kommer att göra partikeln större när den rör sig snabbt, vilket vanligtvis är direkt efter explosionen. På samma sätt gör vi partikeln mindre och jämn transparent när den rör sig mycket långsamt eller når slutet av dess livslängd. För att göra det mer transparent kallar vi en hjälparätt, setAlpha (float alfa).

Tips: Om du inte vet hur man får barns rumsliga ämnen och ställer in materialet, kan du antingen kopiera eller klistra in metoden eller titta på SeekerControl eller WandererControl från det andra kapitlet det förklaras där.

Nu när vi har avslutat ParticleControl, du kan starta spelet och se ... ingenting.
Vet du vad vi glömde?

Sätta ihop det

När en fiende dör, måste vi ringa enemyExplosion () i ParticleManager, annars kommer ingenting att hända! Ta en titt på MonkeyBlasterMain och leta efter metoden handleCollisions (), det är här fiender dör. Lägg bara in samtalet i rätt linje:

 // ... annars om (enemyNode.getChild (i) .getName (). Equals ("Wanderer")) hud.addPoints (1);  particleManager.enemyExplosion (enemyNode.getChild (i) .getLocalTranslation ()); enemyNode.detachChildAt (i); bulletNode.detachChildAt (j); sound.explosion (); ha sönder; // ... 

Och du får inte glömma att det finns ett andra sätt att fiender kan dö: när de sugs i svarta hål. Lägg bara in (nästan) samma linje några rader längre ner när vi kontrollerar kollisioner med det svarta hålet:

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

Nu kan du äntligen starta spelet och spela lite. De partiklarna lägger verkligen till atmosfären, tycker du inte? Men låt oss inte sluta med en effekt; det finns många fler att komma ...


Bullet Explosions

När en kula träffar gränsen på skärmen kommer vi att få det att explodera också.

Ta en titt på BulletControl. Det finns redan kod som kontrollerar om kulan ligger utanför skärmens gränser, så låt oss utlösa explosionen där. För att kunna göra det måste vi förklara ParticleManager i BulletControl och skicka den i konstruktören:

 offentlig BulletControl (Vector3f riktning, int skärmvidd, int screenHeight, ParticleManager particleManager) this.particleManager = particleManager;

Glöm inte att du måste passera particleManager i MonkeyBlasterMain.

Vi lägger in samtalet här:

 om (loc.x screenWidth || loc.y> screenHeight) particleManager.bulletExplosion (loc); spatial.removeFromParent (); 

De bulletExplosion (Vector3f position) Metoden är mycket lik den fiendexplosion (vektor3f-position) metod. De enda skillnaderna är att vi inte kommer att göra partiklarna lika snabbt och att vi använder en fast färg (en ljusblå). Dessutom minskar vi partiklarnas livslängd.

 public void bulletExplosion (Vector3f position) 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);  

Eftersom vi har all nödvändig kod på plats, är det lätt att lägga till nya explosioner, som du kan se. Innan vi lägger till en annan explosion för spelarens död lägger vi till en ny funktionalitet till ParticleControl.


Repelling partiklar från skärmgränserna

När en kula träffar skärmens kant är ungefär hälften av partiklarna värdelösa. De partiklarna visas faktiskt inte på skärmen eftersom de flyger bort från den. Låt oss ändra det.

Vi vänder nu hastigheten på varje partikel som lämnar skärmen runt, så att de blir "avstoppade" av gränserna.

 Vector3f loc = spatial.getLocalTranslation (); om (loc.x < 0)  velocity.x = Math.abs(velocity.x);  else if (loc.x > skärmvidd) hastighet.x = -Math.abs (hastighet.x);  om (loc.z < 0)  velocity.y = Math.abs(velocity.y);  else if (loc.y > screenHeight) hastocity.y = -Math.abs (hastighet.y); 

Vi inverterar inte hela vektorn, bara den x eller y variabel (beroende på gränsen som träffades). Detta resulterar i en korrekt avstötande effekt, som en spegel som reflekterar ljus.

Tips: Du får inte glömma att passera screenWidth och screenHeight från MonkeyBlasterMain till ParticleManager och därifrån till alla ParticleControl. Om du inte bryr dig om ren kod så mycket du skulle kunna gör två statiska variabler i MonkeyBlasterMain och arbeta med dem.

Börja spelet, och du kommer att märka att kulaxplosioner ser mycket ljusare ut nu. Partiklar från fiendens explosioner blir också avstörda.


Player Explosion

När spelaren dör, vill vi ha en verkligen stor explosion som täcker hela skärmen. Vi kallar igen metoden igen killPlayer () i MonkeyBlasterMain.

 particleManager.playerExplosion (player.getLocalTranslation ());

Koden för playerExplosion är ganska mycket detsamma som tidigare. Men den här gången använder vi två färger, vitt och gult, och interpolerar mellan dem. Vi sätter hastigheten på 1000 och livslängden till 2800 millisekunder.

 public void playerExplosion (Vector3f position) ColorRGBA color1 = ColorRGBA.White; ColorRGBA color2 = ColorRGBA.Yellow; för (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);  

Sugande partiklar i svarta hål

Nu när vi har en hel del partikel effekter, låt oss lägga gravitation till dem. När de kommer nära nog till ett svart hål, borde de sugas in - men det är inte sant för varje partikel. Senare vill vi ha en typ av partikel som sugs in och en typ som inte gör det. Därför måste vi lägga till ett attribut för våra partiklar:

 particle.setUserData ("affectedByGravity", true);

Alla partikeltyper som vi har skapat hittills bör sugas in av svarta hål, så du kan lägga till den här raden till varje metod där vi spolar partiklar.

Nu till hanteringen av gravitationen. Gå till handleGravity () i MonkeyBlasterMain -Det är här vi implementerade gravitationen i den tredje delen av serien.

Den här gången kommer vi inte att kontrollera om en partikel är inom räckhåll för det svarta hålet, vi ska helt enkelt tillämpa allvaret på alla. Om en viss partikel är långt borta kommer gravitationseffekten inte att vara väldigt stark ändå.

Vi kontrollerar om partikeln påverkas av gravitationen och, om det är, tillämpar vi det:

 // kolla Partiklar för (int j = 0; j 

Nu måste vi förlänga applyGravity () också:

 // ... annars om (target.getName (). Equals ("Laser") || target.getName (). Equals ("Glow")) target.getControl (ParticleControl.class) .applyGravity (gravity.mult 15000), avstånd); 

Vi måste kontrollera målets namn för båda Laser och Glöd, eftersom det är två olika typer av partiklar som kommer att ha samma beteende.

En annan sak att märka är att vi inte bara vidarebefordrar den modifierade gravitationvektorn utan också avståndet till det svarta hålet. Detta är viktigt vid beräkningen av kraften i applyGravity () i ParticleControl:

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

Här, allvar är enhetsvektorn som pekar mot det svarta hålet. Den attraktiva kraften är en modifierad version av den inverse kvadratfunktionen.

Den första ändringen är att nämnaren är (avstånd * avstånd) + 10000-det vill säga den innehåller en avståndskvartad term. Detta medför att den attraktiva kraften närmar sig ett maximivärde istället för att tändas mot oändlighet då avståndet blir mycket litet.

När avståndet blir större än 100 pixlar, (avstånd * avstånd) blir snabbt mycket större än 10 000. Därför lägger 10 000 till (avstånd * avstånd) har en mycket liten effekt, och funktionen approximerar en normal invers kvadratfunktion.

Men när avståndet är mycket mindre än 100 pixlar har avståndet en liten effekt på nämnarens värde och ekvationen blir ungefär lika med:

 vel + = n;

Den andra modifieringen vi har gjort är att lägga en sidokomponent till hastigheten när partiklarna blir tillräckligt nära det svarta hålet. Detta tjänar två syften: För det första gör partiklarna spiral medurs mot det svarta hålet; För det andra, när partiklarna kommer nära nog, kommer de att nå jämvikt och bilda en glödande cirkel runt det svarta hålet.

Tips: Att rotera en vektor, v, 90 ° medurs, ta (v.y, -v.x). På samma sätt, för att rotera 90 ° moturs, ta (-v.y, v.x).

Denna partikel effekt verkar ganska när du börjar spelet och tittar på det, och det är särskilt sant när det finns många explosioner och partiklar runt. Men när det inte finns några explosioner, ser svarta hål lite tråkig ut. Vi ändrar det snart.


Spruta partiklar ut ur svarta hål

För att göra svarta hål kontinuerligt producera partiklar måste vi ta en titt på controlUpdate (float tpf) metod i BlackHoleControl. Där är en om uttalande som kontrollerar om det svarta hålet är aktivt Om det är så kommer vi att göra det köra den här koden:

 long sprayDif = System.currentTimeMillis () - lastSprayTime; om ((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 position = spatial.getLocalTranslation (). Lägg till (sprayVel.mult (2f)). AddLocal (randVec); particleManager.sprayParticle (position, sprayVel.mult (30f));  sprayAngle - = FastMath.PI * tpf / 10f;

Vi har några nya variabler här. Du måste deklarera och initialisera long lastSprayTime, de float sprayAngle och den Slumpmässig rand. Du måste också förklara particleManager och skicka den ner från huvudklassen så att vi faktiskt kan spruta partiklarna.

Metoden kommer att orsaka att de svarta hålen sprutar spetsar av lila partiklar som kommer att bilda en kall glödande ring som kretsar runt det svarta hålet

Den riktiga sprayParticle () Metoden är inget speciellt. Vi skapar en partikel, applicera en lila färg, lägg till en kontroll och så vidare:

 offentlig tomrumsprutaPartikel (Vector3f-position, Vector3f sprayVel) Spatialpartikel = standardParticle.clone (); particle.setLocalTranslation (positionen); 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 ("partiklar")) attachChild (partikel); 

Starta spelet och se hur det ser ut.

Tips: Om du vill ändra partikelns cirkulationsbeteende, känner du dig fri att leka med värdena i applyGravity () i ParticleControl.

Det förbättrar de svarta hålens allmänna utseende, men det är inte tillräckligt bra än! Det finns en annan effekt vi kan lägga till dem ...


Svarta hålsprängningar

Nu ska vi inte få de svarta hålen att explodera när de dör. Vi kommer istället att utlösa en partikel explosion varje gång ett svart hål träffas.

Lägg till följande metod till ParticleManager:

 public void blackHoleExplosion (Vector3f-position) float hue = ((System.currentTimeMillis () - spawnTime) * 0,003f)% 6f; int numParticles = 150; ColorRGBA color = hsvToColor (färgton, 0.25f, 1); float startOffset = rand.nextFloat () * FastMath.PI * 2 / numParticles; för (int i = 0; i 

Detta fungerar mestadels på samma sätt som de andra partikel explosionerna. En skillnad är att vi väljer färgtonen baserat på den totala förflutna speltiden. Om du skjuter det svarta hålet flera gånger i snabb följd så kommer du att se explosionsens nyans rotera gradvis. Det här ser mindre rotigt ut än att använda slumpmässiga färger samtidigt som den tillåter variation.


Ship Exhaust Fire

Som dikteras av lagarna i geometrisk neonfysik, driver spelarens skepp sig genom att jetting en ström av eldiga partiklar ur sitt avgasrör. Med vår partikelmotor på plats är denna effekt lätt att göra och lägger till visuell känsla för skeppets rörelse.

När fartyget rör sig skapar vi tre ström av partiklar: en central ström som brinner rakt ut på fartygets baksida och två sidströmmar vars vinklar svänger fram och tillbaka i förhållande till skeppet. De två sidströmmarna svänger i motsatta riktningar för att göra ett korsningsmönster och har en rödare färg, medan mittflödet har en varmare gulvita färg.

För att få elden att glöda mer ljus än det skulle vara av blomning ensam, kommer vi att få fartyget att avge ytterligare partiklar som ser ut så här:


En enda glödpartikel.

Dessa partiklar kommer att färgas och blandas med de vanliga partiklarna. Koden för hela effekten visas nedan:

 public void makeExhaustFire (Vector3f position, floatrotation) ColorRGBA midColor = ny ColorRGBA (1f, 0.73f, 0.12f, 0.7f); ColorRGBA sideColor = ny ColorRGBA (0,78f, 0,15f, 0,04f, 0,7f); Vector3f riktning = MonkeyBlasterMain.getVectorFromAngle (rotation); 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 (rotation) .multLocal (-25f)); // middle stream Vector3f randVec = MonkeyBlasterMain.getVectorFromAngle (ny slumpmässig (). 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 ("partiklar")) 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 ("partiklar")) attachChild (particleMidGlow); // sidströmmar Vector3f randVec1 = MonkeyBlasterMain.getVectorFromAngle (nytt slumpmässigt (). nextFloat () * FastMath.PI * 2); Vector3f randVec2 = MonkeyBlasterMain.getVectorFromAngle (nytt slumpmässigt (). NextFloat () * FastMath.PI * 2); Vector3f velSide1 = baseVel.add (randVec1.mult (2.4f)). AddLocal (perpVel); Vector3f velSide2 = baseVel.add (randVec2.mult (2.4f)). SubtraheraLocal (perpVel); Spatial particleSide1 = standardParticle.clone (); particleSide1.setLocalTranslation (pos); particleSide1.addControl (new ParticleControl (velSide1, 800, sideColor, screenWidth, screenHeight)); particleSide1.setUserData ("affectedByGravity", true); ((Node) guiNode.getChild ("partiklar")) 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 ("partiklar")) attachChild (particleSide2); SpatialpartikelSide1Glow = glowParticle.clone (); particleSide1Glow.setLocalTranslation (pos); particleSide1Glow.addControl (new ParticleControl (velSide1, 800, sideColor, screenWidth, screenHeight)); particleSide1Glow.setUserData ("affectedByGravity", true); ((Node) guiNode.getChild ("partiklar")) 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 ("partiklar")) attachChild (particleSide2Glow); 

Det finns inget lurigt pågår i den här koden. Vi använder en sinusfunktion för att producera svängningseffekten i sidströmmarna genom att ändra sin sidledshastighet över tiden. För varje ström skapar vi två överlappande partiklar per ram: en standardpartikel och en glödpartikel bakom den.

Sätt in den här koden i PlayerControl, i slutet av controlUpdate (float tpf):

 om (upp | ned ned || vänster || rätt) particleManager.makeExhaustFire (spatial.getLocalTranslation (), rotation); 

Självklart får du inte glömma att passera particleManager från MonkeyBlasterMain.


Slutsats

Med alla dessa partikeleffekter börjar Shape Blaster se ganska cool ut. I den sista delen av den här serien kommer vi att lägga till ytterligare en fantastisk effekt: det klingande bakgrundsnätet