We hebben de gameplay, audio en UI gecodeerd voor onze door jMonkeyEngine aangedreven Geometry Wars-geïnspireerde game, en nu kunnen we enkele leuke grafische effecten en Pools gebruiken. In dit deel zullen we ons specifiek richten op deeltjeseffecten (inclusief enkele zeer kleurrijke explosies).
Dit is waar we naartoe werken in de hele serie:
... en hier is een video waarin de partikeleffecten worden weergegeven die we in dit gedeelte toevoegen:
Er zijn verschillende soorten deeltjes en verschillende emitters:
Naast het laatste deeltjestype, worden alle deeltjes beïnvloed door de zwaartekracht en worden ze in zwarte gaten gezogen. Dus, wanneer een zwart gat in veel deeltjes tegelijk opzuigt, begint het te gloeien vanwege alle deeltjes - wat er best cool uitziet.
Een ander effect dat we zullen toevoegen is om onze deeltjes groter en dus helderder te maken, hoe sneller ze zijn. Dit betekent dat een explosie in het begin erg helder en glanzend lijkt, maar snel zijn helderheid verliest zodra de deeltjes langzamer gaan.
Om onze doelen te bereiken, moeten we twee nieuwe klassen toevoegen:
ParticleManager
: Deze managersklasse zorgt voor de attributen voor elke soort explosie.ParticleControl
: Ik denk dat je al kunt raden dat deze klasse, nogmaals, het gedrag van onze deeltjes controleert.Laten we beginnen met het meest opvallende effect: exploderende vijanden.
De eerste klasse die we moeten implementeren is de ParticleManager
klasse. Omdat het verantwoordelijk is voor het afzetten van deeltjes, heeft het enkele variabelen nodig, zoals de guiNode
, de particleNode
en de standardParticle
.
We zullen dit klonen wanneer we het nodig hebben, maar kijk eens naar de basiscode:
public class ParticleManager private Node guiNode; privé Ruimtelijke standaardGedeelte, gloeispaander; private Node particleNode; privé Willekeurige rand; public ParticleManager (Node guiNode, Spatial standardParticle, Spatial glowParticle) this.guiNode = guiNode; this.standardParticle = standardParticle; this.glowParticle = glowParticle; particleNode = new Node ("particles"); guiNode.attachChild (particleNode); rand = nieuw Willekeurig ();
Integratie van de manager in MonkeyBlasterMain
is geen probleem. We verklaren het gewoon in het begin en roepen de constructeur binnen simpleInitApp ()
:
particleManager = nieuwe ParticleManager (guiNode, getSpatial ("Laser"), getSpatial ("Glow"));
Om een vijand echt te laten ontploffen, moeten we de juiste methode hebben om dit in de. Te doen ParticleManager
:
public void enemyExplosion (Vector3f positie) // init colors 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); // creëer 120 deeltjes voor (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);
Deze methode is kort, maar het doet veel, dus we zullen het stap voor stap doornemen.
Om onze deeltjes interessanter te maken, wijzen we ze willekeurige kleuren toe.
Eén methode om willekeurige kleuren te produceren, is door willekeurig de rode, blauwe en groene componenten te kiezen, maar dit levert veel saaie kleuren op en we willen dat onze deeltjes een "neonlicht" uiterlijk hebben.
We kunnen meer controle krijgen over onze kleuren door ze op te geven in de HSV (tint, verzadiging en waarde) kleurruimte. We willen kleuren kiezen met een willekeurige tint, maar een vaste verzadiging en waarde, zodat ze er allemaal helder en glanzend uitzien, dus we hebben een hulpfunctie nodig die een kleur kan produceren op basis van HSV-waarden.
openbare ColorRGBA hsvToColor (float h, float s, float v) if (h == 0 && s == 0) retourneer nieuwe ColorRGBA (v, v, v, 1); zweven c = s * v; float x = c * (1 - Math.abs (h% 2 - 1)); zweven m = v - c; als (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);
Tip: Maak je niet al te veel zorgen hoe deze functie werkt; begrijp gewoon dat het een RGBA-kleur van HSV-waarde kan genereren. De methode valt buiten het bereik en de focus van deze zelfstudie.
Nu terug naar onze explosiemethode. Bekijk de gemarkeerde lijnen:
public void enemyExplosion (Vector3f positie) // init colors 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); // creëer 120 deeltjes voor (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);
Om de explosie kleurrijker te maken, berekenen we twee willekeurige kleuren en interpoleren de uiteindelijke deeltjeskleur willekeurig voor elk deeltje.
Het volgende dat we doen is de snelheid berekenen voor elk deeltje. We verwerken dit in een extra methode omdat we willen dat de richting willekeurig is, maar niet de snelheid:
private Vector3f getRandomVelocity (float max) // genereren Vector3f met willekeurige richting Vector3f velocity = new Vector3f (rand.nextFloat () - 0.5f, rand.nextFloat () - 0.5f, 0) .normalizeLocal (); // apply semi-random particle speed float random = rand.nextFloat () * 5 + 1; float particleSpeed = max * (1f - 0,6f / willekeurig); velocity.multLocal (particleSpeed); terugkeer snelheid;
Eerst genereren we een willekeurige snelheidsvector en normaliseren deze. Vervolgens berekenen we een willekeurige snelheid in het bereik tussen 40% en 90% van max
.
Nu terug naar de enemyExplosion ()
methode. Dit is het deel dat we nog niet hebben besproken:
Ruimtelijk deeltje = standardParticle.clone (); particle.setLocalTranslation (positie); ColorRGBA-kleur = nieuwe ColorRGBA (); color.interpolate (color1, color2, rand.nextFloat () * 0.5f); particle.addControl (nieuwe ParticleControl (velocity, 3100, kleur)); particleNode.attachChild (deeltjes);
We klonen het standardParticle
en stel de vertaling in op de oorsprong van de explosie. Daarna interpoleren we de deeltjeskleur tussen de twee willekeurige (zoals hierboven vermeld). Zoals je ziet, voegen we ook een toe ParticleControl
die het gedrag van het deeltje zal beheersen. Ten slotte moeten we het deeltje aan het knooppunt toevoegen om te worden weergegeven.
Nu dat onze ParticleManager
is voltooid, we moeten het ParticleControl
. Delen van de code zullen u bekend voorkomen:
openbare klasse ParticleControl verlengt AbstractControl private Vector3f-snelheid; privé-levensduur van de vlotter; privé lange spawnTime; private ColorRGBA-kleur; openbare ParticleControl (Vector3f-snelheid, levensduur van de vlotter, ColorRGBA-kleur) this.velocity = velocity; this.lifespan = levensduur; this.color = kleur; spawnTime = System.currentTimeMillis (); @Override protected void controlUpdate (float tpf) // verplaatsing spatial.move (velocity.mult (tpf * 3f)); velocity.multLocal (1-3f * TPF); if (Math.abs (velocity.x) + Math.abs (velocity.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 > levensduur) spatial.removeFromParent (); @Override beschermd void controlRender (RenderManager rm, ViewPort vp) private float lesserValue (float a, float b) retourneer 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);
In de top van de klas declareren en initialiseren we een paar variabelen; hun namen zouden nu vanzelfsprekend moeten zijn. Als je ernaar kijkt controlUpdate ()
je zult vertrouwde code vinden: we verplaatsen het deeltje met zijn snelheid, vertragen het een beetje en draaien het in de richting van de snelheid.
Als het deeltje erg langzaam is, stellen we de snelheid in op Vector3f.ZERO
. Het is veel sneller om berekeningen met nul uit te voeren dan een heel klein aantal, en het verschil is hoe dan ook niet zichtbaar.
Om een explosie echt te laten gaan boom, we zullen het deeltje groter maken als het snel beweegt, wat meestal direct na de explosie is. Op dezelfde manier maken we het deeltje kleiner en zelfs transparant wanneer het heel langzaam beweegt of het einde van zijn levensduur bereikt. Om het transparanter te maken, noemen we een hulpmethode, setAlpha (float alpha)
.
Tip: Als u niet weet hoe u kinderen-spatialen kunt krijgen en hun materiaal kunt instellen, kunt u de methode kopiëren of plakken of een kijkje nemen op SeekerControl
of WandererControl
uit het tweede hoofdstuk; het wordt daar uitgelegd.
Nu we klaar zijn met het ParticleControl
, je kunt het spel starten en ... niets zien.
Weet je wat we vergeten zijn??
Wanneer een vijand sterft, moeten we bellen enemyExplosion ()
in de ParticleManager
, anders gebeurt er niets! Kijk eens naar MonkeyBlasterMain
en zoek naar de methode handleCollisions ()
, dit is waar vijanden sterven. Steek nu gewoon het gesprek in de juiste rij:
// ... else if (enemyNode.getChild (i) .getName (). Equals ("Wanderer")) hud.addPoints (1); particleManager.enemyExplosion (enemyNode.getChild (i) .getLocalTranslation ()); enemyNode.detachChildAt (i); bulletNode.detachChildAt (j); sound.explosion (); breken; // ...
En je moet niet vergeten dat er een tweede manier is waarop vijanden kunnen sterven: wanneer ze in zwarte gaten worden gezogen. Plaats gewoon de (bijna) dezelfde regel een paar regels verder naar beneden als we controleren op botsingen met het zwarte gat:
if (checkCollision (enemyNode.getChild (j), blackHole)) particleManager.enemyExplosion (enemyNode.getChild (j) .getLocalTranslation ()); enemyNode.detachChildAt (j);
Nu kunt u eindelijk de game starten en een beetje spelen. Die deeltjes dragen echt bij aan de atmosfeer, vind je niet? Maar laten we niet bij één effect stoppen; er komen nog veel meer ...
Wanneer een kogel de rand van het scherm raakt, laten we hem ook exploderen.
Kijk eens naar BulletControl
. Er is al code die controleert of de kogel zich buiten de grenzen van het scherm bevindt, dus laten we de explosie daar activeren. Om dat te doen, moeten we de ParticleManager
in BulletControl
en geef het door aan de constructeur:
public BulletControl (Vector3f direction, int screenWidth, int screenHeight, ParticleManager particleManager) this.particleManager = particleManager;
Vergeet niet dat je moet slagen voor de particleManager
in MonkeyBlasterMain
.
We zullen de oproep hier invoegen:
if (loc.x screenWidth || loc.y> screenHeight) particleManager.bulletExplosion (loc); spatial.removeFromParent ();
De bulletExplosion (Vector3f-positie)
methode lijkt erg op de enemyExplosion (Vector3f-positie)
methode. Het enige verschil is dat we de deeltjes niet zo snel maken en dat we een vaste kleur gebruiken (een felblauw). Ook verminderen we de levensduur van de deeltjes.
public void bulletExplosion (Vector3f-positie) 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);
Omdat we alle benodigde code hebben, is het gemakkelijk om nieuwe explosies toe te voegen, zoals je kunt zien. Voordat we een nieuwe explosie toevoegen voor de dood van de speler, voegen we een nieuwe functionaliteit toe aan de ParticleControl
.
Wanneer een kogel de rand van het scherm raakt, is ongeveer de helft van de deeltjes nutteloos. Die deeltjes verschijnen nooit echt op het scherm omdat ze er vanaf vliegen. Laten we dat veranderen.
We zullen nu de snelheid van elk deeltje dat het scherm verlaat, veranderen, zodat ze door de grenzen worden 'afgestoten'.
Vector3f loc = spatial.getLocalTranslation (); if (loc.x < 0) velocity.x = Math.abs(velocity.x); else if (loc.x > screenWidth) velocity.x = -Math.abs (velocity.x); if (loc.z < 0) velocity.y = Math.abs(velocity.y); else if (loc.y > screenHeight) velocity.y = -Math.abs (velocity.y);
We keren niet de hele vector om, alleen de X
of Y
variabele (afhankelijk van de rand die is geraakt). Dit resulteert in een goed afstotend effect, zoals een spiegel die licht weerkaatst.
Tip: Je moet niet vergeten om te slagen screenWidth
en screenHeight
van MonkeyBlasterMain
naar ParticleManager
en van daar naar elke ParticleControl
. Als je niet zoveel om schone code geeft kon maak twee statische variabelen in MonkeyBlasterMain
en werk met hen samen.
Start het spel en je zult zien dat kogeluitbarstingen er nu veel helderder uitzien. Deeltjes van vijandige explosies worden ook afgestoten.
Wanneer de speler sterft, willen we een werkelijk grote explosie die het hele scherm beslaat. We noemen de methode opnieuw in killPlayer ()
in MonkeyBlasterMain
.
particleManager.playerExplosion (player.getLocalTranslation ());
De code voor playerExplosion
is vrijwel hetzelfde als voorheen. Deze keer gebruiken we echter twee kleuren, wit en geel, en interpoleren ertussen. We stellen de snelheid in op 1000
en de levensduur van 2800
milliseconden.
public void playerExplosion (Vector3f-positie) ColorRGBA color1 = ColorRGBA.White; ColorRGBA color2 = ColorRGBA.Yellow; voor (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);
Nu we heel wat deeltjeseffecten hebben, laten we er zwaartekracht aan toevoegen. Wanneer ze dicht genoeg bij een zwart gat komen, moeten ze worden opgezogen, maar dit geldt niet voor elk deeltje. Later willen we een soort deeltje hebben dat wel wordt opgezogen en een type dat dat niet doet. Daarom moeten we een attribuut toevoegen aan onze deeltjes:
particle.setUserData ("affectedbyGravity", true);
Alle deeltjestypes die we tot nu toe hebben gemaakt, moeten worden opgezogen door zwarte gaten, dus je kunt deze lijn toevoegen aan elke methode waarin we deeltjes spawnen.
Nu naar de afhandeling van de zwaartekracht. Ga naar handleGravity ()
in MonkeyBlasterMain
-hier hebben we de zwaartekracht geïmplementeerd in het derde deel van de serie.
Deze keer zullen we niet controleren of een deeltje zich binnen het bereik van het zwarte gat bevindt, we zullen gewoon de zwaartekracht op alle delen toepassen. Als een specifiek deeltje ver weg is, zal het zwaartekrachteffect toch niet erg sterk zijn.
We controleren of het deeltje wordt beïnvloed door de zwaartekracht en, als dat zo is, passen we het toe:
// controleer Deeltjes voor (int j = 0; jNu moeten we uitbreiden
applyGravity ()
ook:// ... else if (target.getName (). Equals ("Laser") || target.getName (). Equals ("Glow")) target.getControl (ParticleControl.class) .applyGravity (gravity.mult () 15000), afstand);We moeten de naam van het doel voor beide controleren Laser en Gloed, omdat dit twee verschillende soorten deeltjes zijn die hetzelfde gedrag vertonen.
Wat ook opvalt, is dat we niet alleen de gemodificeerde zwaartekrachtvector doorgeven, maar ook de afstand tot het zwarte gat. Dit is belangrijk bij de berekening van de kracht in
applyGravity ()
inParticleControl
:Vector3f additionalVelocity = gravity.mult (1000f / (afstand * afstand + 10000f)); velocity.addLocal (additionalVelocity); als (afstand < 400) additionalVelocity = new Vector3f(gravity.y, -gravity.x, 0).mult(3f / (distance + 100)); velocity.addLocal(additionalVelocity);Hier,
zwaartekracht
is de eenheidsvector die naar het zwarte gat wijst. De aantrekkingskracht is een aangepaste versie van de inverse vierkante functie.De eerste wijziging is die van de noemer
(afstand * afstand) + 10000
-dat wil zeggen, het bevat een afstand-kwadraat term. Dit zorgt ervoor dat de aantrekkende kracht een maximale waarde nadert in plaats van naar oneindig te neigen naarmate de afstand erg klein wordt.Wanneer de afstand groter wordt dan 100 pixels,
(afstand * afstand)
wordt snel veel groter dan 10.000. Daarom 10.000 toe te voegen aan(afstand * afstand)
heeft een heel klein effect en de functie benadert een normale inverse vierkante functie.Wanneer de afstand echter veel kleiner is dan 100 pixels, heeft de afstand een klein effect op de waarde van de noemer en wordt de vergelijking ongeveer gelijk aan:
vel + = n;De tweede wijziging die we hebben aangebracht is het toevoegen van een zijwaartse component aan de snelheid wanneer de deeltjes dicht genoeg bij het zwarte gat komen. Dit dient twee doelen: ten eerste, het maakt de deeltjes spiraalvormig met de klok mee in de richting van het zwarte gat; ten tweede, wanneer de deeltjes dichtbij genoeg komen, zullen ze een evenwicht bereiken en een gloeiende cirkel rondom het zwarte gat vormen.
Tip: Een vector roteren,v
, 90 ° met de klok mee, nemen(v.y, -v.x)
. Evenzo, nemen om 90 ° tegen de klok in te roteren(-v.y, v.x)
.Dit deeltje-effect lijkt mooi als je het spel start en ernaar kijkt, en dit geldt vooral als er veel explosies en deeltjes in de buurt zijn. Maar als er geen explosies zijn, zien zwarte gaten er nogal saai uit. We zullen dat snel veranderen.
Sproeien van deeltjes uit zwarte gaten
Om zwarte gaten continu deeltjes te laten produceren, moeten we een kijkje nemen naar de
controlUpdate (float tpf)
methode inBlackHoleControl
. Er is eenals
verklaring die controleert of het zwarte gat actief is; als dat zo is, laten we het deze code uitvoeren:long sprayDif = System.currentTimeMillis () - lastSprayTime; if ((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 positie = spatial.getLocalTranslation (). Add (sprayVel.mult (2f)). AddLocal (randVec); particleManager.sprayParticle (positie sprayVel.mult (30f)); sprayAngle - = FastMath.PI * tpf / 10f;We hebben een paar nieuwe variabelen hier. U moet het. Declareren en initialiseren
long lastSprayTime
, defloat sprayAngle
en deWillekeurige rand
. U moet ook hetparticleManager
en geef het door aan de hoofdklasse zodat we de deeltjes daadwerkelijk kunnen spuiten.De methode zorgt ervoor dat de zwarte gaten spatten van paarse deeltjes sproeien die een koele gloeiende ring vormen die rond het zwarte gat draait.
De daadwerkelijke
sprayParticle ()
methode is niets bijzonders. We maken een deeltje, passen een paarse kleur toe, voegen een besturingselement toe enzovoort:openbare lege spuitDeeltjes (Vector3f positie, Vector3f sprayVel) Ruimtelijke deeltjes = standaardDeeltjes.kloon (); particle.setLocalTranslation (positie); ColorRGBA-kleur = nieuwe ColorRGBA (0.8f, 0.4f, 0.8f, 1f); particle.addControl (nieuwe ParticleControl (sprayVel, 3500, color, screenWidth, screenHeight)); particle.setUserData ("affectedbyGravity", true); ((Node) guiNode.getChild ("particles")). AttachChild (particle);Start het spel en kijk hoe het eruit ziet.
Tip: Als je het cirkelende gedrag van de deeltjes wilt veranderen, voel je dan vrij om te spelen met de waarden in
applyGravity ()
inParticleControl
.Dit verbetert het algemene uiterlijk van de zwarte gaten, maar het is nog niet goed genoeg! Er is nog een ander effect dat we eraan kunnen toevoegen ...
Black Hole Explosions
Nu laten we de zwarte gaten niet exploderen als ze dood gaan. In plaats daarvan activeren we een deeltje-explosie telkens wanneer een zwart gat wordt geraakt.
Voeg de volgende methode toe aan
ParticleManager
:public void blackHoleExplosion (Vector3f-positie) float hue = ((System.currentTimeMillis () - spawnTime) * 0.003f)% 6f; int numParticles = 150; ColorRGBA-kleur = hsvToColor (tint, 0,25f, 1); float startOffset = rand.nextFloat () * FastMath.PI * 2 / numParticles; voor (int i = 0; iDit werkt meestal op dezelfde manier als de andere deeltjesexplosies. Een verschil is dat we de tint van de kleur kiezen op basis van de totale verstreken speeltijd. Als je het zwarte gat meerdere keren snel na elkaar schiet, zie je de tint van de explosies geleidelijk roteren. Dit ziet er minder rommelig uit dan het gebruik van willekeurige kleuren, terwijl er nog steeds variatie mogelijk is.
Uitlaatbrand van het schip
Zoals voorgeschreven door de wetten van de geometrisch-neon fysica, stuwt het schip van de speler zichzelf door een stroom vurige deeltjes uit zijn uitlaatpijp te spuiten. Met onze deeltjesmotor op zijn plaats, is dit effect eenvoudig te maken en voegt het visuele flair toe aan de bewegingen van het schip.
Terwijl het schip beweegt, creëren we drie stromen deeltjes: een centrale stroom die recht uit de achterkant van het schip vuurt, en twee zijstromen waarvan de hoeken heen en weer zwenken ten opzichte van het schip. De twee zijstromen draaien in tegengestelde richting om een kriskras patroon te maken en hebben een rodere kleur, terwijl de middenstroom een heter, geel-witte kleur heeft.
Om het vuur helderder te laten gloeien dan alleen uit de bloei, laten we het schip extra deeltjes uitstoten die er als volgt uitzien:
Een enkel gloeideeltje.Deze deeltjes worden getint en gemengd met de reguliere deeltjes. De code voor het volledige effect wordt hieronder getoond:
openbare ongeldige makeExhaustFire (Vector3f-positie, vlotterrotatie) ColorRGBA midColor = new ColorRGBA (1f, 0.73f, 0.12f, 0.7f); ColorRGBA sideColor = nieuwe ColorRGBA (0.78f, 0.15f, 0.04f, 0.7f); Vector3f-richting = MonkeyBlasterMain.getVectorFromAngle (rotatie); float t = (System.currentTimeMillis () - spawnTime) / 1000f; Vector3f baseVel = direction.mult (-45f); Vector3f perpVel = nieuwe Vector3f (baseVel.y, -baseVel.x, 0) .multLocal (2f * FastMath.sin (t * 10f)); Vector3f pos = position.add (MonkeyBlasterMain.getVectorFromAngle (rotatie) .multLocal (-25f)); // middle stream Vector3f randVec = MonkeyBlasterMain.getVectorFromAngle (nieuw Willekeurig (). nextFloat () * FastMath.PI * 2); Vector3f velMid = baseVel.add (randVec.mult (7.5f)); Ruimtelijk deeltjeMid = standardParticle.clone (); particleMid.setLocalTranslation (pos); particleMid.addControl (nieuwe ParticleControl (velMid, 800, midColor, screenWidth, screenHeight)); particleMid.setUserData ("affectedbyGravity", true); ((Knoop) guiNode.getChild ("deeltjes")). AttachChild (particleMid); Ruimtelijk deeltjeMidGlow = glowParticle.clone (); particleMidGlow.setLocalTranslation (pos); particleMidGlow.addControl (nieuwe ParticleControl (velMid, 800, midColor, screenWidth, screenHeight)); particleMidGlow.setUserData ("affectedbyGravity", true); ((Node) guiNode.getChild ("particles")). AttachChild (particleMidGlow); // side streams Vector3f randVec1 = MonkeyBlasterMain.getVectorFromAngle (nieuw Willekeurig (). nextFloat () * FastMath.PI * 2); Vector3f randVec2 = MonkeyBlasterMain.getVectorFromAngle (nieuw Willekeurig (). NextFloat () * FastMath.PI * 2); Vector3f velSide1 = baseVel.add (randVec1.mult (2.4f)) addLocal (perpVel); Vector3f velSide2 = baseVel.add (randVec2.mult (2.4f)). SubtractLocal (perpVel); Ruimtelijk deeltje Side1 = standardParticle.clone (); particleSide1.setLocalTranslation (pos); particleSide1.addControl (nieuwe ParticleControl (velSide1, 800, sideColor, screenWidth, screenHeight)); particleSide1.setUserData ("affectedbyGravity", true); ((Node) guiNode.getChild ("particles")). AttachChild (particleSide1); Ruimtelijk deeltje Side2 = standardParticle.clone (); particleSide2.setLocalTranslation (pos); particleSide2.addControl (nieuwe ParticleControl (velSide2, 800, sideColor, screenWidth, screenHeight)); particleSide2.setUserData ("affectedbyGravity", true); ((Node) guiNode.getChild ("particles")). AttachChild (particleSide2); Ruimtelijk deeltje Side1Glow = glowParticle.clone (); particleSide1Glow.setLocalTranslation (pos); particleSide1Glow.addControl (nieuwe ParticleControl (velSide1, 800, sideColor, screenWidth, screenHeight)); particleSide1Glow.setUserData ("affectedbyGravity", true); ((Node) guiNode.getChild ("particles")). AttachChild (particleSide1Glow); Ruimtelijk deeltjeSide2Glow = glowParticle.clone (); particleSide2Glow.setLocalTranslation (pos); particleSide2Glow.addControl (nieuwe ParticleControl (velSide2, 800, sideColor, screenWidth, screenHeight)); particleSide2Glow.setUserData ("affectedbyGravity", true); ((Node) guiNode.getChild ("particles")). AttachChild (particleSide2Glow);Er is niets sneaky aan de hand in deze code. We gebruiken een sinusfunctie om het zwenkeffect in de zijstromen te produceren door hun zijwaartse snelheid in de loop van de tijd te variëren. Voor elke stream maken we twee overlappende deeltjes per frame: een standaarddeeltje en een gloeideeltje erachter.
Voeg dit stukje code in
PlayerControl
, aan het einde vancontrolUpdate (float tpf)
:if (up || down || left || right) particleManager.makeExhaustFire (spatial.getLocalTranslation (), rotatie);Natuurlijk moet je niet vergeten om de
particleManager
vanMonkeyBlasterMain
.
Conclusie
Met al deze deeltjeseffecten begint Shape Blaster er behoorlijk gaaf uit te zien. In het laatste deel van deze serie voegen we nog een geweldig effect toe: het kromme achtergrondraster