En esta serie de tutoriales, te mostraré cómo hacer un juego de disparos de neón, como Geometry Wars, en XNA. El objetivo de estos tutoriales no es dejarte con una réplica exacta de Geometry Wars, sino repasar los elementos necesarios que te permitirán crear tu propia variante de alta calidad..
En la serie hasta ahora, hemos configurado la jugabilidad y hemos agregado la floración. A continuación, añadiremos efectos de partículas..
Advertencia: Alto!Los efectos de partículas se crean al hacer un gran número de partículas pequeñas. Son muy versátiles y se pueden usar para agregar estilo a casi cualquier juego. En Shape Blaster realizaremos explosiones utilizando efectos de partículas. También usaremos efectos de partículas para crear fuego de escape para la nave del jugador y para agregar un toque visual a los agujeros negros. Además, veremos cómo hacer que las partículas interactúen con la gravedad de los agujeros negros.
Administrador de partículas
ClaseEmpezaremos creando un Administrador de partículas
Clase que almacenará, actualizará y dibujará todas las partículas. Haremos esta clase lo suficientemente general como para poder reutilizarla fácilmente en otros proyectos. Para mantener el Administrador de partículas
en general, no será responsable de cómo se ven o se mueven las partículas; manejaremos eso en otro lugar.
Las partículas tienden a ser creadas y destruidas rápidamente y en grandes cantidades. Utilizaremos un conjunto de objetos Para evitar la creación de grandes cantidades de basura. Esto significa que asignaremos una gran cantidad de partículas por adelantado y luego seguiremos reutilizando estas mismas partículas. También haremos Administrador de partículas
tener una capacidad fija. Esto lo simplificará y ayudará a garantizar que no excedamos nuestro rendimiento o limitaciones de memoria al crear demasiadas partículas. Cuando se exceda el número máximo de partículas, comenzaremos a reemplazar las más antiguas por otras nuevas..
Haremos el Administrador de partículas
una clase genérica Esto nos permitirá almacenar información de estado personalizada para las partículas sin tener que codificarla en el Administrador de partículas
sí mismo. También crearemos un anidado. Partícula
clase.
ParticleManager de clase públicaPartícula de clase pública Textura de textura pública2; Posición Vector2 pública; flotación pública Orientación; Escala pública Vector2 = Vector2.One; color público color; flotación pública Duración; flotador público PercentLife = 1f; Estado T público;
los Partícula
La clase tiene toda la información necesaria para mostrar una partícula y administrar su vida útil. El parametro genérico, Estado T
, está ahí para contener cualquier información adicional que podamos necesitar para nuestras partículas. Los datos necesarios variarán dependiendo de los efectos de partículas deseados; podría usarse para almacenar velocidad, aceleración, velocidad de rotación o cualquier otra cosa que pueda necesitar.
Para ayudar a manejar las partículas, necesitaremos una clase que funcione como matriz circular, lo que significa que los índices que normalmente estarían fuera de límites se ajustarán al principio de la matriz. Esto facilitará la sustitución de las partículas más antiguas primero si nos quedamos sin espacio para nuevas partículas en nuestra matriz. Agregamos lo siguiente como una clase anidada en Administrador de partículas
.
clase privada CircularParticleArray private int start; public int Start get return start; establecer inicio = valor% lista.Longitud; public int Count get; conjunto; public int Capacity get return list.Length; partícula privada [] lista; circular CircularParticleArray (int capacity) list = new Particle [capacity]; público Participe esto [int i] get return list [(start + i)% list.Length]; set list [(start + i)% list.Length] = valor;
Podemos configurar el comienzo
propiedad para ajustar donde el índice cero en nuestra CircularParticleArray
corresponde a en la matriz subyacente, y Contar
se utilizará para rastrear cuántas partículas activas hay en la lista. Nos aseguraremos de que la partícula en el índice cero sea siempre la partícula más antigua. Si reemplazamos la partícula más antigua por una nueva, simplemente incrementaremos comienzo
, que esencialmente gira la matriz circular.
Ahora que tenemos nuestras clases de ayuda, podemos comenzar a llenar el Administrador de partículas
clase. Necesitaremos algunas variables miembro, y un constructor..
// Este delegado será llamado para cada partícula. acción privadaupdateParticle; Private CircularParticleArray particleList; ParticleManager público (capacidad int, acción updateParticle) this.updateParticle = updateParticle; particleList = new CircularParticleArray (capacidad); // Rellenar la lista con objetos de partículas vacías, para reutilizar. para (int i = 0; i < capacity; i++) particleList[i] = new Particle();
La primera variable declarada., Artículo de actualización
, será un método personalizado que actualiza las partículas adecuadamente para el efecto deseado. Un juego puede tener múltiples Administradores de partículas
que se actualizan de forma diferente si es necesario. También creamos un CircularParticleList
y llenarlo con partículas vacías. El constructor es el único lugar donde Administrador de partículas
asigna memoria.
A continuación agregamos el CreateParticle ()
Método, que crea una nueva partícula utilizando la siguiente partícula no utilizada en el grupo, o la partícula más antigua si no hay partículas no utilizadas.
Public Void CreateParticle (textura Texture2D, posición Vector2, tinte de color, duración de flotación, escala Vector2, estado T, theta flotante = 0) Partícula partícula; if (particleList.Count == particleList.Capacity) // si la lista está llena, sobrescribe la partícula más antigua y rota la lista circular partícula = particleList [0]; particleList.Start ++; else particle = particleList [particleList.Count]; particleList.Count ++; // Crea la partícula de partículas. Textura = textura; partícula. posición = posición; partícula.Tinta = tinte; Partícula.Duración = duración; partícula.PercentLife = 1f; partícula.escala = escala; partícula.Orientación = theta; partícula.estado = estado;
Las partículas pueden ser destruidas en cualquier momento. Necesitamos eliminar estas partículas mientras aseguramos que las otras partículas permanezcan en el mismo orden. Podemos hacerlo iterando a través de la lista de partículas mientras hacemos un seguimiento de cuántas han sido destruidas. A medida que avanzamos, movemos cada partícula activa frente a todas las partículas destruidas intercambiándola con la primera partícula destruida. Una vez que todas las partículas destruidas están al final de la lista, las desactivamos configurando la lista Contar
Variable al número de partículas activas. Las partículas destruidas permanecerán en la matriz subyacente, pero no se actualizarán ni dibujarán.
ParticleManager.Update ()
Maneja la actualización de cada partícula y elimina las partículas destruidas de la lista..
Public void Update () int removalCount = 0; para (int i = 0; i < particleList.Count; i++) var particle = particleList[i]; updateParticle(particle); particle.PercentLife -= 1f / particle.Duration; // sift deleted particles to the end of the list Swap(particleList, i - removalCount, i); // if the particle has expired, delete this particle if (particle.PercentLife < 0) removalCount++; particleList.Count -= removalCount; private static void Swap(CircularParticleArray list, int index1, int index2) var temp = list[index1]; list[index1] = list[index2]; list[index2] = temp;
Lo último para implementar en Administrador de partículas
está dibujando las partículas.
Draw público vacío (SpriteBatch spriteBatch) para (int i = 0; i < particleList.Count; i++) var particle = particleList[i]; Vector2 origin = new Vector2(particle.Texture.Width / 2, particle.Texture.Height / 2); spriteBatch.Draw(particle.Texture, particle.Position, null, particle.Color, particle.Orientation, origin, particle.Scale, 0, 0);
Estado de partícula
StructLo siguiente que debe hacer es crear una clase o estructura personalizada para personalizar el aspecto de las partículas en Shape Blaster. Habrá varios tipos diferentes de partículas en Shape Blaster que se comportarán de manera ligeramente diferente, así que comenzaremos creando una enumerar
para el tipo de partícula. También necesitaremos variables para la velocidad de la partícula y la longitud inicial..
public enum ParticleType None, Enemy, Bullet, IgnoreGravity public struct ParticleState public Vector2 Velocity; Tipo de tipo de partícula público; flotador público LengthMultiplier;
Ahora estamos listos para escribir el método de actualización de la partícula. Es una buena idea hacer que este método sea rápido, ya que podría ser necesario para una gran cantidad de partículas..
Vamos a empezar simple. Agregue el siguiente método a Estado de partícula
.
PublicPidticle vacío público (ParticleManager.Particle partícula) var vel = particle.State.Velocity; Partícula.Posición + = vel; partícula.Orientación = vel.ToAngle (); // los flotadores desnormalizados causan problemas de rendimiento significativos si (Math.Abs (vel.X) + Math.Abs (vel.Y) < 0.00000000001f) vel = Vector2.Zero; vel *= 0.97f; // particles gradually slow down x.State.Velocity = vel;
Volveremos y mejoraremos este método en un momento. Primero, vamos a crear algunos efectos de partículas para que podamos probar nuestros cambios. En GameRoot
, declarar un nuevo Administrador de partículas
y llama a su Actualizar()
y Dibujar()
metodos.
// en GameRoot public static ParticleManager ParticleManager get; conjunto privado // en GameRoot.Initialize () ParticleManager = new ParticleManager (1024 * 20, ParticleState.UpdateParticle); // en GameRoot.Update () ParticleManager.Update (); // en GameRoot.Draw () spriteBatch.Begin (SpriteSortMode.Deferred, BlendState.Additive); ParticleManager.Draw (); spriteBatch.End ();
Además, declarar un nuevo Textura2d
llamado LineParticle
por la textura de la partícula en el Art º
Clase, y carga la textura como hicimos para los otros sprites..
Ahora hagamos explotar a los enemigos. Modificar el Enemigo.WasShot ()
método de la siguiente manera.
public void WasShot () IsExpired = true; para (int i = 0; i < 120; i++) float speed = 18f * (1f - 1 / rand.NextFloat(1f, 10f)); var state = new ParticleState() Velocity = rand.NextVector2(speed, speed), Type = ParticleType.Enemy, LengthMultiplier = 1f ; GameRoot.ParticleManager.CreateParticle(Art.LineParticle, Position, Color.LightGreen, 190, 1.5f, state);
Esto crea 120 partículas que dispararán hacia el exterior con diferentes velocidades en todas las direcciones. La velocidad aleatoria está ponderada de modo que las partículas tienen más probabilidades de viajar cerca de la velocidad máxima. Esto hará que haya más partículas en el borde de la explosión a medida que se expande. Las partículas duran 190 cuadros, o algo más de tres segundos..
Ahora puedes correr el juego y ver explotar a los enemigos. Sin embargo, todavía hay algunas mejoras por hacer para los efectos de partículas..
El primer problema es que las partículas desaparecen bruscamente una vez que se agota su duración. Sería mejor si pudieran desaparecer sin problemas. Pero vayamos un poco más lejos que esto y hagamos que las partículas se vuelvan más brillantes cuando se mueven rápido. Además, se ve bien si alargamos las partículas en movimiento rápido y acortamos las partículas en movimiento lento.
Modificar el ParticleState.UpdateParticle ()
Método como sigue (los cambios están resaltados).
PublicPidticle vacío público (ParticleManager.Particle partícula) var vel = particle.State.Velocity; Partícula.Posición + = vel; partícula.Orientación = vel.ToAngle (); velocidad de flotación = vel.Length (); float alpha = Math.Min (1, Math.Min (partícula.PercentLife * 2, speed * 1f)); alfa * = alfa; partícula.Color.A = (byte) (255 * alfa); partícula.Escala.X = partícula.State.LengthMultiplier * Math.Min (Math.Min (1f, 0.2f * speed + 0.1f), alfa); if (Math.Abs (vel.X) + Math.Abs (vel.Y) < 0.00000000001f) // denormalized floats cause significant performance issues vel = Vector2.Zero; vel *= 0.97f; // particles gradually slow down x.State.Velocity = vel;
Las explosiones se ven mucho mejor ahora, pero todas son del mismo color. Podemos darles más variedad eligiendo colores al azar. Un método para producir colores aleatorios es elegir los componentes rojo, azul y verde al azar, pero esto producirá muchos colores apagados y nos gustaría que nuestras partículas tuvieran una apariencia de luz de neón. Podemos tener más control sobre nuestros colores al especificarlos en el espacio de color HSV. HSV significa tono, saturación y valor. Nos gustaría elegir colores con un tono aleatorio pero con una saturación y un valor fijos. Necesitamos una función auxiliar que pueda producir un color a partir de valores HSV..
clase estática ColorUtil public static Color HSVToColor (float h, float s, float v) if (h == 0 && s == 0) devuelve un nuevo Color (v, v, v); float c = s * v; float x = c * (1 - Math.Abs (h% 2 - 1)); float m = v - c; si (h < 1) return new Color(c + m, x + m, m); else if (h < 2) return new Color(x + m, c + m, m); else if (h < 3) return new Color(m, c + m, x + m); else if (h < 4) return new Color(m, x + m, c + m); else if (h < 5) return new Color(x + m, m, c + m); else return new Color(c + m, m, x + m);
Ahora podemos modificar Enemigo.WasShot ()
para utilizar colores al azar. Para hacer que el color de la explosión sea menos monótono, elegiremos dos colores clave cercanos para cada explosión e interpolaremos linealmente entre ellos con una cantidad aleatoria para cada partícula..
public void WasShot () IsExpired = true; float hue1 = rand.NextFloat (0, 6); float hue2 = (hue1 + rand.NextFloat (0, 2))% 6f; Color color1 = ColorUtil.HSVToColor (hue1, 0.5f, 1); Color color2 = ColorUtil.HSVToColor (hue2, 0.5f, 1); para (int i = 0; i < 120; i++) float speed = 18f * (1f - 1 / rand.NextFloat(1f, 10f)); var state = new ParticleState() Velocity = rand.NextVector2(speed, speed), Type = ParticleType.Enemy, LengthMultiplier = 1 ; Color color = Color.Lerp(color1, color2, rand.NextFloat(0, 1)); GameRoot.ParticleManager.CreateParticle(Art.LineParticle, Position, color, 190, 1.5f, state);
Las explosiones deben verse como la animación de abajo..
Puedes jugar con la generación de colores para que se adapte a tus preferencias. Una técnica alternativa que funciona bien es elegir a mano una serie de patrones de color para las explosiones y elegir aleatoriamente entre sus esquemas de color preseleccionados.
También podemos hacer explotar las balas cuando llegan al borde de la pantalla. Básicamente haremos lo mismo que hicimos para las explosiones enemigas..
Agrega una estática Aleatorio
miembro de la Bala
clase.
Rand aleatorio estático privado = nuevo aleatorio ();
Luego modificar Bullet.Update ()
como sigue.
// eliminar las viñetas que salen de la pantalla si (! GameRoot.Viewport.Bounds.Contains (Position.ToPoint ())) IsExpired = true; para (int i = 0; i < 30; i++) GameRoot.ParticleManager.CreateParticle(Art.LineParticle, Position, Color.LightBlue, 50, 1, new ParticleState() Velocity = rand.NextVector2(0, 9), Type = ParticleType.Bullet, LengthMultiplier = 1 );
Puede notar que dar una dirección aleatoria a las partículas es un desperdicio, porque al menos la mitad de las partículas saldrán inmediatamente de la pantalla (más si la bala explota en una esquina). Podríamos hacer un trabajo adicional para garantizar que las partículas solo tengan velocidades opuestas a la pared que están enfrentando. Sin embargo, en cambio, seguiremos el ejemplo de Geometry Wars y haremos que todas las partículas reboten en las paredes. Cualquier partícula que salga de la pantalla será devuelta.
Agrega las siguientes líneas a ParticleState.UpdateParticle ()
En cualquier lugar entre las primeras y últimas líneas..
var pos = x.Posición; int width = (int) GameRoot.ScreenSize.X; int height = (int) GameRoot.ScreenSize.Y; // colisionar con los bordes de la pantalla si (pos.X < 0) vel.X = Math.Abs(vel.X); else if (pos.X > ancho) vel.X = -Math.Abs (vel.X); si (pos.Y < 0) vel.Y = Math.Abs(vel.Y); else if (pos.Y > altura) vel.Y = -Math.Abs (vel.Y);
Haremos una explosión realmente grande cuando el jugador muere. Modificar PlayerShip.Kill ()
al igual que:
public void Kill () framesUntilRespawn = 60; Color amarillo = nuevo Color (0.8f, 0.8f, 0.4f); para (int i = 0; i < 1200; i++) float speed = 18f * (1f - 1 / rand.NextFloat(1f, 10f)); Color color = Color.Lerp(Color.White, yellow, rand.NextFloat(0, 1)); var state = new ParticleState() Velocity = rand.NextVector2(speed, speed), Type = ParticleType.None, LengthMultiplier = 1 ; GameRoot.ParticleManager.CreateParticle(Art.LineParticle, Position, color, 190, 1.5f, state);
Esto es similar a las explosiones enemigas, pero usamos más partículas y siempre usamos el mismo esquema de color. El tipo de partícula también se establece en ParticleType.None
.
En la demostración, las partículas de las explosiones enemigas se ralentizan más rápido que las partículas de la nave del jugador que explotan. Esto hace que la explosión del jugador dure un poco más y se vea un poco más épica.
Ahora que tenemos efectos de partículas, revisemos los agujeros negros y hagamos que interactúen con las partículas..
Los agujeros negros deberían afectar las partículas además de otras entidades, por lo que necesitamos modificar ParticleState.UpdateParticle ()
. Añade las siguientes líneas.
if (x.State.Type! = ParticleType.IgnoreGravity) foreach (var blackHole en EntityManager.BlackHoles) var dPos = blackHole.Position - pos; distancia de flotación = dPos.Length (); var n = dPos / distancia; vel + = 10000 * n / (distancia * distancia + 10000); // agregar aceleración tangencial para partículas cercanas si (distancia < 400) vel += 45 * new Vector2(n.Y, -n.X) / (distance + 100);
aquí, norte
Es el vector unitario que apunta hacia el agujero negro. La fuerza atractiva es una versión modificada de la función del cuadrado inverso. La primera modificación es que el denominador es \ (distancia ^ 2 + 10,000 \). Esto hace que la fuerza de atracción se aproxime a un valor máximo en lugar de tender hacia el infinito a medida que la distancia se vuelve muy pequeña. Cuando la distancia es mucho mayor que 100 píxeles, \ (distancia ^ 2 \) se vuelve mucho mayor que 10,000. Por lo tanto, agregar 10,000 a \ (distancia ^ 2 \) tiene un efecto muy pequeño, y la función se aproxima a una función cuadrada inversa normal. Sin embargo, cuando la distancia es mucho menor que 100 píxeles, la distancia tiene un pequeño efecto sobre el valor del denominador, y la ecuación se vuelve aproximadamente igual a:
vel + = n;
La segunda modificación es agregar un componente lateral a la velocidad cuando las partículas se acercan lo suficiente al agujero negro. Esto tiene dos propósitos. Primero, hace que las partículas formen una espiral en sentido horario hacia el agujero negro. Segundo, cuando las partículas se acercan lo suficiente, alcanzarán el equilibrio y formarán un círculo brillante alrededor del agujero negro..
Propina: Para rotar un vector, V, 90 ° hacia la derecha, tome(V.Y, -V.X)
. Del mismo modo, para girar 90 ° en sentido antihorario, tome (-V.Y, V.X)
. Los agujeros negros producirán dos tipos de partículas. Primero, rociarán periódicamente partículas que orbitarán alrededor de ellas. Segundo, cuando se dispara un agujero negro, rociará partículas especiales que no se ven afectadas por su gravedad.
Agregue el siguiente código a la BlackHole.WasShot ()
método.
float hue = (float) ((3 * GameRoot.GameTime.TotalGameTime.TotalSeconds)% 6); Color color = ColorUtil.HSVToColor (tono, 0.25f, 1); const int numParticles = 150; float startOffset = rand.NextFloat (0, MathHelper.TwoPi / numParticles); para (int i = 0; i < numParticles; i++) Vector2 sprayVel = MathUtil.FromPolar(MathHelper.TwoPi * i / numParticles + startOffset, rand.NextFloat(8, 16)); Vector2 pos = Position + 2f * sprayVel; var state = new ParticleState() Velocity = sprayVel, LengthMultiplier = 1, Type = ParticleType.IgnoreGravity ; GameRoot.ParticleManager.CreateParticle(Art.LineParticle, pos, color, 90, 1.5f, state);
Esto funciona principalmente de la misma manera que las otras explosiones de partículas. Una diferencia es que elegimos el tono del color en función del tiempo total transcurrido del juego. Si disparas al agujero negro varias veces en rápida sucesión, verás que el tono de las explosiones gira gradualmente. Esto parece menos desordenado que el uso de colores aleatorios mientras se permite la variación.
Para el rociado de partículas en órbita, necesitamos agregar una variable a la Agujero negro
clase para rastrear la dirección en la que actualmente estamos rociando partículas.
flotador privado sprayAngle = 0;
Ahora agregue lo siguiente a la BlackHole.Update ()
método.
// Los agujeros negros rocían algunas partículas en órbita. El spray se activa y desactiva cada cuarto de segundo. if ((GameRoot.GameTime.TotalGameTime.Milliseconds / 250)% 2 == 0) Vector2 sprayVel = MathUtil.FromPolar (sprayAngle, rand.NextFloat (12, 15)); Color color = ColorUtil.HSVToColor (5, 0.5f, 0.8f); // Vector2 púrpura claro pos = Posición + 2f * Vector2 nuevo (sprayVel.Y, -sprayVel.X) + rand.NextVector2 (4, 8); var state = new ParticleState () Velocity = sprayVel, LengthMultiplier = 1, Type = ParticleType.Enemy; GameRoot.ParticleManager.CreateParticle (Art.LineParticle, pos, color, 190, 1.5f, estado); // gire la dirección de rociado sprayAngle - = MathHelper.TwoPi / 50f;
Esto hará que los agujeros negros rocien chorros de partículas púrpuras que formarán un anillo brillante que orbita alrededor del agujero negro, de esta manera:
Según lo dictado por las leyes de la física geométrica-neón, la nave del jugador se propulsa lanzando un chorro de partículas ardientes por su tubo de escape. Con nuestro motor de partículas en su lugar, este efecto es fácil de hacer y agrega un toque visual al movimiento de la nave.
A medida que la nave se mueve, creamos tres corrientes de partículas: una corriente central que se dispara directamente desde la parte posterior de la nave, y dos corrientes laterales cuyos ángulos giran de un lado a otro en relación con la nave. Las dos corrientes laterales giran en direcciones opuestas para hacer un patrón entrecruzado. Las corrientes laterales tienen un color más rojo, mientras que la transmisión central tiene un color amarillo-blanco más cálido. La siguiente animación muestra el efecto..
Para hacer que el fuego brille más intensamente de lo que lo haría solo con la floración, haremos que la nave emita partículas adicionales con este aspecto:
Estas partículas se teñirán y se mezclarán con las partículas regulares. El código para el efecto completo se muestra a continuación..
private void MakeExhaustFire () if (Velocity.LengthSquared ()> 0.1f) // configura algunas variables Orientación = Velocity.ToAngle (); Quaternion rot = Quaternion.CreateFromYawPitchRoll (0f, 0f, Orientación); doble t = GameRoot.GameTime.TotalGameTime.TotalSeconds; // La velocidad primaria de las partículas es de 3 píxeles / cuadro en la dirección opuesta a la que se desplaza la nave. Vector2 baseVel = Velocity.ScaleTo (-3); // Calcula la velocidad lateral para las dos corrientes laterales. La dirección es perpendicular a la velocidad de la nave y la // magnitud varía de forma sinusoidal. Vector2 perpVel = nuevo Vector2 (baseVel.Y, -baseVel.X) * (0.6f * (float) Math.Sin (t * 10)); Color de lado Color = nuevo Color (200, 38, 9); // Color rojo intenso color medio = nuevo Color (255, 187, 30); // Vector2 naranja-amarillo pos = Posición + Vector2.Transformación (nuevo Vector2 (-25, 0), rot); // Posición del tubo de escape de la nave. const float alpha = 0.7f; // flujo de partículas medias Vector2 velMid = baseVel + rand.NextVector2 (0, 1); GameRoot.ParticleManager.CreateParticle (Art.LineParticle, pos, Color.White * alpha, 60f, nuevo Vector2 (0.5f, 1), nuevo ParticleState (velMid, ParticleType.Enemy)); GameRoot.ParticleManager.CreateParticle (Art.Glow, pos, midColor * alpha, 60f, nuevo Vector2 (0.5f, 1), nuevo ParticleState (velMid, ParticleType.Enemy)); // flujos de partículas laterales Vector2 vel1 = baseVel + perpVel + rand.NextVector2 (0, 0.3f); Vector2 vel2 = baseVel - perpVel + rand.NextVector2 (0, 0.3f); GameRoot.ParticleManager.CreateParticle (Art.LineParticle, pos, Color.White * alpha, 60f, nuevo Vector2 (0.5f, 1), nuevo ParticleState (vel1, ParticleType.Enemy)); GameRoot.ParticleManager.CreateParticle (Art.LineParticle, pos, Color.White * alpha, 60f, nuevo Vector2 (0.5f, 1), nuevo ParticleState (vel2, ParticleType.Enemy)); GameRoot.ParticleManager.CreateParticle (Art.Glow, pos, sideColor * alpha, 60f, nuevo Vector2 (0.5f, 1), nuevo ParticleState (vel1, ParticleType.Enemy)); GameRoot.ParticleManager.CreateParticle (Art.Glow, pos, sideColor * alpha, 60f, nuevo Vector2 (0.5f, 1), nuevo ParticleState (vel2, ParticleType.Enemy));
No hay nada furtivo en este código. Utilizamos una función sinusoidal para producir el efecto de giro en las corrientes laterales variando su velocidad de lado a lo largo del tiempo. Para cada flujo, creamos dos partículas superpuestas por fotograma: una semitransparente-blanca LineParticle
y una partícula de color brillante detrás de ella. Llamada MakeExhaustFire ()
al final de PlayerShip.Update ()
, Inmediatamente antes de ajustar la velocidad del barco a cero.
Con todos estos efectos de partículas, Shape Blaster está empezando a verse muy bien. En la parte final de esta serie, agregaremos un efecto impresionante más: la cuadrícula de fondo combada.