En mi tutorial anterior, Shoot Out Stars con Stardust Particle Engine, expliqué el flujo de trabajo básico de Stardust. Esta vez, iremos más allá y examinaremos un par de técnicas para crear verdaderos efectos de partículas en 3D.!
Comenzaremos con una demostración de cómo usar el motor 3D nativo de Stardust. Luego, le mostraré cómo hacer que Stardust trabaje con Papervision3D; Crearemos efectos de partículas en 3D con la clase de partículas de Papervision3D y la clase de DisplayObject3D..
Vamos a retomar lo que dejamos en el primer tutorial. La última vez, creamos partículas de estrellas y círculos que salían de un punto, crecían a un tamaño máximo y luego se reducían a nada, mientras nos movíamos gradualmente más lento con el tiempo (llamado efecto de amortiguación). Esta vez, haremos lo mismo, pero en 3D. En lugar de que las partículas se muevan en un círculo, se moverán en una esfera.
Al igual que antes, primero cree un nuevo documento Flash con dimensiones de 640x400, una velocidad de fotogramas de 60 fps y un fondo oscuro (utilicé un degradado azul oscuro).
Dibuja una estrella y un círculo blanco, luego conviértelos en símbolos, por separado. Estos son los dos símbolos que usaremos más adelante como partículas. Nombre el símbolo de estrella "Estrella" y el símbolo de círculo "Círculo", exportado para ActionScript con los mismos nombres de clase.
(Si no eres un gran artista, puedes descargar la fuente en la parte superior de la página y usar mis símbolos de la biblioteca de mi FLA).
Hacer clic Ventana> Componentes para abrir el Panel de Componentes, luego arrastre un Botón desde la carpeta de Interfaz de Usuario al escenario. Establezca la etiqueta en "Pausa" y llámela "pause_btn". Vamos a utilizar este botón para pausar los efectos de partículas en 3D, lo que permitirá a los usuarios girar la cámara para conocer mejor el entorno 3D..
Crea una nueva clase de documento y llámala StarParticles3D.
paquete import flash.display.Sprite; clase pública StarParticles3D extiende Sprite función pública StarParticles ()
¿No está seguro de cómo usar una clase de documento? Lea este consejo rápido.
Los tres paquetes principales en Stardust son:
En el tutorial anterior, utilizamos inicializadores y acciones de los paquetes comunes y twoD. En este tutorial, seguiremos utilizando elementos del paquete común, pero no del paquete twoD. En su lugar, utilizaremos elementos del paquete threeD..
La estructura de clase del paquete threeD es bastante parecida a la del paquete twoD, excepto que los elementos tienen una dimensión adicional. Un elemento 3D posee el mismo nombre que su homólogo 2D, pero su nombre termina con "3D". Por ejemplo, el Move3D La acción en el paquete 3D actualiza las posiciones de las partículas en el espacio 3D según las velocidades, al igual que su contraparte 2D en el paquete 2D, el Movimiento acción.
Crear un nuevo archivo AS llamado StarEmitter.as; Dentro de ella, crea una nueva clase. StarEmitter, que amplía la clase Emitter3D:
paquete import idv.cjcat.stardust.threeD.emitters.Emitter3D; // ¡No olvides importar esto! clase pública StarEmitter extiende Emitter3D función pública StarEmitter (reloj: Reloj) // pasa el objeto de reloj al constructor de la superclase super (reloj);
¿Recuerdas el parámetro Reloj? Se utiliza para controlar la tasa de creación de partículas. Necesitamos incluirlo en la función de constructor, para poder pasarle un Reloj más adelante..
Ya que estamos permitiendo a los usuarios pausar los efectos de partículas, vamos a empaquetar todas las acciones en un solo objeto CompositeAction, que es esencialmente un grupo de acciones. Al desactivar esta acción compuesta única, podemos "desactivar" todas las acciones subyacentes. Declare una variable para una acción compuesta en la clase de emisor. Accederemos a esta variable en la clase de documento, por lo que debe ser pública:
public var pausibleActions: CompositeAction;
Declare las constantes que se utilizarán como parámetros de partículas en la clase de emisor. Ya hemos cubierto el propósito de estas constantes en el tutorial anterior. La mayoría de los nombres se explican por sí mismos. Estos van dentro de la clase pero fuera de la función constructora. Siéntase libre de volver aquí más tarde y alterar los números para ver los efectos.
constata estática privada LIFE_AVG: Número = 30; constata estática privada LIFE_VAR: Número = 10; constata estática privada SCALE_AVG: Número = 1; constata estática privada SCALE_VAR: Número = 0.4; constata estática privada GROWING_TIME: Número = 5; constata estática privada SHRINKING_TIME: Número = 10; constata estática privada SPEED_AVG: Número = 30; constata estática privada SPEED_VAR: Number = 10; constata estática privada OMEGA_AVG: Número = 0; constata estática privada OMEGA_VAR: Número = 5; DAMPING estático privado: Número = 0.1;
En el tutorial anterior, demostré cómo usar el SwitchInitializer para crear partículas con diferentes objetos de visualización. Estaba usando el inicializador DisplayObjectClass, que inicializa la apariencia de las partículas con objetos de visualización. Eso fue para efectos de partículas 2D; Aquí vamos a usar su contraparte 3D, el inicializador DisplayObject3D..
Agregue el siguiente código a la función constructora del emisor:
// cambiar inicializadores para partículas de estrella y círculo var doc1: DisplayObjectClass3D = new DisplayObjectClass3D (Star); var doc2: DisplayObjectClass3D = new DisplayObjectClass3D (Circle); var si: SwitchInitializer = new SwitchInitializer ([doc1, doc2], [1, 1]); addInitializer (si);
Igual que el tutorial anterior; agregue los otros inicializadores que se muestran a continuación. Tenga en cuenta que algunos de ellos tienen nombres similares a los del tutorial anterior, pero terminan en "3D".
Este código va en la función de constructor de StarEmitter:
addInitializer (new Life (nuevo UniformRandom (LIFE_AVG, LIFE_VAR))); addInitializer (nueva escala (nuevo UniformRandom (SCALE_AVG, SCALE_VAR))); addInitializer (nuevo Position3D (nuevo SinglePoint3D ())); addInitializer (nuevo Velocity3D (nuevo SphereShell (0, 0, 0, SPEED_AVG, SPEED_VAR))); addInitializer (nuevo Rotation3D (nulo, nulo, nuevo UniformRandom (0, 180))); addInitializer (nuevo Omega3D (nulo, nulo, nuevo UniformRandom (OMEGA_AVG, OMEGA_VAR)));
Cree una acción compuesta y agréguele algunas acciones. Luego agregue esta acción compuesta al emisor; Esto hará que las partículas realicen las acciones. Has visto estas acciones en el tutorial anterior (algunas de ellas en versión 2D), así que no las explicaré de nuevo. De nuevo, este código entra en la función de constructor de StarEmitter:
pausibleActions = new CompositeAction (); pausibleActions.addAction (new Age ()); pausibleActions.addAction (new DeathLife ()); pausibleActions.addAction (new Move3D ()); pausibleActions.addAction (new Spin3D ()); pausibleActions.addAction (nuevo Damping3D (DAMPING)); pausibleActions.addAction (nuevo ScaleCurve (GROWING_TIME, SHRINKING_TIME)); addAction (pausibleActions);
Está bien, hemos terminado con el emisor. Ahora es el momento de construir nuestra clase de documentos.
Primero, declare las constantes para el radio de la cámara en órbita, la distancia de la cámara desde el origen y la velocidad del emisor:
constata estática privada CAMERA_RADIUS: Número = 250; constata estática privada PARTICLE_RATE: Number = 0.5;
(Como antes, consts va dentro de la clase pero fuera de la función del constructor).
Luego, declare las variables para un emisor, un reloj estable y un DisplayObjectRenderer3D (en el mismo lugar que los consts):
emisor de var privado: StarEmitter; reloj var privado: SteadyClock; procesador de var privado: DisplayObjectRenderer3D;
En el constructor, inicialice el reloj, el emisor y el renderizador. Además, establece la posición y la dirección de la cámara inicial, haciendo que mire el origen:
// crear el reloj y el reloj del emisor = nuevo SteadyClock (PARTICLE_RATE); emisor = nuevo StarEmitter (reloj); // podemos hacer esto porque le dimos al constructor de StarEmitter un parámetro de reloj // crear el renderizador y su contenedor sprite var container: Sprite = new Sprite (); container.x = 320, container.y = 200; renderer = new DisplayObjectRenderer3D (contenedor); renderer.addEmitter (emisor); // agregar el contenedor a la etapa addChild (contenedor); // agrega el botón de pausa nuevamente para que esté encima del contenedor addChild (pause_btn); // establece la posición inicial de la cámara y la dirección renderer.camera.position.set (0, 0, -CAMERA_RADIUS); renderer.camera.direction.set (0, 0, CAMERA_RADIUS);
Cree una función de controlador en la clase de documento para controlar el evento de clic del botón de pausa:
función privada togglePause (e: MouseEvent): void if (e.target.label == "Pause") e.target.label = "Resume"; clock.ticksPerCall = 0; // detiene el reloj emitter.pausibleActions.active = false; // desactivar las acciones del emisor else e.target.label = "Pausa"; clock.ticksPerCall = PARTICLE_RATE; // reinicia el reloj emitter.pausibleActions.active = true; // reactivar las acciones del emisor
... luego registre el oyente para el botón de pausa, en la función de constructor:
pause_btn.addEventListener (MouseEvent.CLICK, togglePause);
Crea un controlador para el evento ENTER_FRAME. Este es nuestro bucle principal. Actualiza la posición de la cámara llamando al método updateCamera () (que codificaremos en un minuto) y llama al método step () del emisor, que mantiene los efectos de partículas en ejecución:
Función privada mainLoop (e: Event): void updateCamera (); emitter.step ();
De nuevo, registre un oyente en el constructor:
addEventListener (Event.ENTER_FRAME, mainLoop);
Ahora defina el método updateCamera () llamado en el paso anterior. Esto se utiliza para mover la cámara en el espacio 3D dependiendo de la posición del ratón. (Si desea más información sobre cómo funciona, consulte este artículo de Wikipedia).
Los números mágicos utilizados para generar theta y phi son solo el resultado de prueba y error; siéntete libre de probar tus propias ecuaciones.
función privada updateCamera (): void var theta: Number = 0.02 * (mouseX - 320); var phi: Número = 0.02 * (mouseY - 200); phi = StardustMath.clamp (phi, -StardustMath.HALF_PI, StardustMath.HALF_PI); var x: Número = CAMERA_RADIUS * Math.cos (theta) * Math.cos (phi); var y: Número = CAMERA_RADIUS * Math.sin (phi); var z: Number = CAMERA_RADIUS * Math.sin (theta) * Math.cos (phi); renderer.camera.position.set (x, y, z); renderer.camera.direction.set (-x, -y, -z);
Tenga en cuenta que utilicé el método StardustMath.clamp (); Esto asegura que el valor phi se mantenga entre la mitad positiva y la negativa PI.
Está bien, hemos terminado! Eso es todo lo que necesitamos hacer para que un emisor 3D funcione con el motor 3D nativo de Stardust. Veamos el resultado. Puede hacer clic en el botón de pausa para pausar el efecto de partículas, y mover el mouse para orbitar la cámara:
Manifestación Verlo en líneaSi desea ver el código fuente completo, busque en la carpeta llamada "01 - Stardust Native 3D Engine" en la Fuente.
Cambiar del motor nativo 3D de Stardust a Papervision3D es fácil. Solo tendremos que usar un renderizador diferente y mostrar el inicializador de objetos..
(¿Nunca usaste Papervision3D antes? Mira este tutorial para principiantes.)
Primero usaremos la clase de partículas de Papervision3D. Puede que no estés familiarizado con esto; Te mostraré cómo usar la clase DisplayObject3D más común más adelante.
Cambie el siguiente código en la clase de emisor:
var doc1: DisplayObjectClass3D = new DisplayObjectClass3D (Star); var doc2: DisplayObjectClass3D = new DisplayObjectClass3D (Circle);
a esto:
var mat1: MovieParticleMaterial = new MovieParticleMaterial (new Star ()); var mat2: MovieParticleMaterial = new MovieParticleMaterial (new Circle ()); var doc1: PV3DParticle = new PV3DParticle ([mat1]); var doc2: PV3DParticle = new PV3DParticle ([mat2]);
Como ya sabrá, la clase MovieParticleMaterial nos permite utilizar objetos de visualización como la apariencia de partículas en Papervision3D. Creamos una instancia de Estrella y Círculo para usarla como material de partículas. El inicializador de artículo PV3DP toma el lugar del inicializador DisplayObjectClass3D; su constructor acepta una matriz de parámetros, que se agregarán a un objeto Particles.
Esto es todo lo que tenemos que hacer con respecto al emisor. A continuación modificaremos la clase de documento..
El contenedor de destino para nuestro renderizador ya no es un objeto Sprite. En su lugar, vamos a crear partículas en un objeto de partículas. Tendremos que cambiar el tipo de renderizador de DisplayObjectRenderer3D a PV3DParticleRenderer.
Declare las siguientes variables para los objetos relacionados con Papervision3D:
var escena privada: SceneObject3D; particulas de var privado: particulas; cámara privada var: Camera3D; origen var privado: DisplayObject3D; private var renderEngine: BasicRenderEngine; viewport var privado: Viewport3D;
El código en el constructor de la clase de documento es ahora:
initPV3D (); //¡Esto es nuevo! reloj = nuevo SteadyClock (PARTICLE_RATE); emisor = nuevo StarEmitter (reloj); renderer = new PV3DParticleRenderer (partículas); //¡Esto es nuevo! renderer.addEmitter (emisor); pause_btn.addEventListener (MouseEvent.CLICK, togglePause); addEventListener (Event.ENTER_FRAME, mainLoop);
El método initPV3D () configura el entorno Papervision3D. Aquí está el código:
función privada initPV3D (): void // create the scene scene = new SceneObject3D (); // crea el objeto Partículas partículas = nuevas Partículas (); // crea la cámara e inicializa su posición camera = new Camera3D (); camera.position.x = 0; camera.position.y = 0; camera.position.z = -CAMERA_RADIUS; // crear un DO3D que represente el origen = origen DisplayObject3D (); origen.x = origen.y = origen.z = 0; // apunta la cámara al origen camera.target = origin; scene.addChild (origen); scene.addChild (partículas); // crear el motor de visualización y la vista renderEngine = new BasicRenderEngine (); viewport = new Viewport3D (640, 400); // agregar la ventana gráfica al escenario addChild (ventana gráfica); // agrega el botón de pausa de nuevo para que esté en la parte superior de la ventana gráfica addChild (pause_btn);
Ahora Stardust solo actualiza las propiedades de los objetos 3D; El motor de render de Papervision3D está asumiendo la responsabilidad de renderizar. Así es como se ve nuestro nuevo bucle principal:
Función privada mainLoop (e: Event): void updateCamera (); emitter.step (); renderEngine.renderScene (escena, cámara, ventana gráfica); //¡Esto es nuevo!
Ahora que estamos usando la cámara de Papervision3D, también tendremos que modificar el método updateCamera ():
función privada updateCamera (): void var theta: Number = 0.02 * (mouseX - 320); var phi: Número = 0.02 * (mouseY - 200); phi = StardustMath.clamp (phi, -StardustMath.HALF_PI, StardustMath.HALF_PI); var x: Número = CAMERA_RADIUS * Math.cos (theta) * Math.cos (phi); var y: Número = -CAMERA_RADIUS * Math.sin (phi); // note que ahora es negativo var z: Number = CAMERA_RADIUS * Math.sin (theta) * Math.cos (phi); camera.x = x; // actualizamos cada una de las propiedades x, y, z de la cámara de PV3D camera.y = y por separado; camera.z = z;
Bien, hemos cambiado con éxito del motor 3D nativo de Stardust a Papervision3D. Ahora vamos a ver el resultado. Tenga en cuenta el efecto pixelado en las partículas. Esto se debe a que Papervision3D primero dibuja objetos vectoriales en mapas de bits antes de usarlos como materiales de partículas..
Manifestación Verlo en líneaPuede encontrar todo el código fuente para esto en la carpeta "02 - Papervision3D Particles".
Hasta ahora, hemos estado trabajando con "vallas publicitarias 2D" - objetos planos, como papel. Es posible crear objetos de partículas 3D "reales", como los objetos DisplayObject3D de Papervision3D. Vamos a tener que usar otro inicializador. Ahora vamos a la parte final de este tutorial. Crearemos partículas de cubo rojo y azul..
Vamos a cambiar el inicializador con respecto a la apariencia de las partículas por última vez..
Antes de eso, declare una variable LightObject3D en la clase de emisor. Vamos a utilizar FlatShadeMaterial para los objetos DisplayObject3D, que requieren una fuente de luz. Además, declare las siguientes constantes: las utilizaremos como parámetros para el Material de FlastShade y para determinar los tamaños de los cubos:
public var light: LightObject3D; const estática privada LIGHT_COLOR_1: uint = 0xCC3300; const estática privada LIGHT_COLOR_2: uint = 0x006699; const estática privada AMBIENT_COLOR_1: uint = 0x881100; const estática privada AMBIENT_COLOR_2: uint = 0x002244; constata estática privada CUBE_SIZE: Número = 15;
Ahora cambia el siguiente código en la clase de emisor:
var mat1: MovieParticleMaterial = new MovieParticleMaterial (new Star ()); var mat2: MovieParticleMaterial = new MovieParticleMaterial (new Circle ()); var doc1: PV3DParticle = new PV3DParticle ([mat1]); var doc2: PV3DParticle = new PV3DParticle ([mat2]);
a esto:
light = new LightObject3D (); var mat1: FlatShadeMaterial = new FlatShadeMaterial (light, LIGHT_COLOR_1, AMBIENT_COLOR_1); var mat2: FlatShadeMaterial = new FlatShadeMaterial (light, LIGHT_COLOR_2, AMBIENT_COLOR_2); var matList1: MaterialsList = new MaterialsList (all: mat1); var matList2: MaterialsList = new MaterialsList (all: mat2); var params1: Array = [matList1, CUBE_SIZE, CUBE_SIZE, CUBE_SIZE]; var params2: Array = [matList2, CUBE_SIZE, CUBE_SIZE, CUBE_SIZE]; var doc1: PV3DDisplayObject3DClass = new PV3DDisplayObject3DClass (Cube, params1); var doc2: PV3DDisplayObject3DClass = new PV3DDisplayObject3DClass (Cube, params2);
La nueva apariencia de partículas se inicializará como cubos 3D rojos y azules. El primer parámetro del constructor para el inicializador PV3DDisplayObject3DClass es la clase que deseamos instanciar para las partículas (por lo que aquí, es la clase Cube) y el segundo parámetro es una matriz de parámetros del constructor para esta clase Cube..
Anteriormente, debido a que estábamos trabajando con "carteles en 2D", solo importaba la rotación sobre el eje Z. Ahora que estamos trabajando con verdaderos objetos 3D, necesitamos pasar tres referencias de objetos aleatorios a los constructores de Rotation3D y Omega3D, una para cada eje.
Cambie el siguiente código en la clase de emisor:
addInitializer (nuevo Rotation3D (nulo, nulo, nuevo UniformRandom (0, 180))); addInitializer (nuevo Omega3D (nulo, nulo, nuevo UniformRandom (OMEGA_AVG, OMEGA_VAR)));
a esto:
var rotationRandom: UniformRandom = nuevo UniformRandom (0, 180); var omegaRandom: UniformRandom = nuevo UniformRandom (OMEGA_AVG, OMEGA_VAR); addInitializer (nuevo Rotation3D (rotationRandom, rotationRandom, rotationRandom)); addInitializer (nuevo Omega3D (omegaRandom, omegaRandom, omegaRandom));
Esta vez, en lugar de usar un objeto Partículas como nuestro contenedor de partículas, estamos usando un DisplayObject3D como el contenedor. Declare una variable para este contenedor en la clase de documento:
contenedor var privado: DisplayObject3D;
Además, necesitaremos otro tipo de renderizador para crear partículas en el nuevo contenedor. Cambie el tipo de renderizador de PV3DParticleRenderer a PV3DDisplayObject3DRenderer. El código en el constructor de la clase de documento ahora debería verse así:
initPV3D (); reloj = nuevo SteadyClock (PARTICLE_RATE); emisor = nuevo StarEmitter (reloj); renderer = new PV3DDisplayObject3DRenderer (container); // ¡Esto ha cambiado! renderer.addEmitter (emisor); pause_btn.addEventListener (MouseEvent.CLICK, togglePause); addEventListener (Event.ENTER_FRAME, mainLoop);
En la función initPV3D (), ahora necesitamos inicializar la variable de contenedor y agregarla a la escena. Agrega estas dos líneas al final de esa función:
container = new DisplayObject3D (); scene.addChild (contenedor);
En el método updateCamera (), deseamos que la luz siga a la cámara, por lo que tendremos la ilusión de que la luz siempre "sale" de nuestros ojos. Cambia el siguiente código:
camera.x = x; camera.y = y; camera.z = z;
a esto:
emitter.light.x = camera.x = x; emitter.light.y = camera.y = y; emitter.light.z = camera.z = z;
Ahora la fuente de luz está siempre en el mismo punto que la cámara..
Sí, finalmente hemos terminado con este tutorial. No más codificación. Echemos un vistazo a nuestro resultado final, con elegantes cubos 3D rojos y azules!
Manifestación Verlo en líneaEl código fuente para esto se puede encontrar en la carpeta "Papervision3D DisplayObject3D".
El flujo de trabajo para crear efectos de partículas en 3D con Stardust es muy similar al de los efectos en 2D. Simplemente elige un conjunto diferente de inicializadores, acciones y renderizadores. Stardust también admite otros motores 3D, incluidos ZedBox y ND3D. El uso es casi el mismo. Solo tendrás que usar un conjunto diferente de inicializadores y renderizadores. Incluso puede extender las clases Inicializador, Acción y Renderizador para trabajar con los motores 3D que desee.!
Ahora tienes lo básico, ¿por qué no volver a las consts creadas en el Paso 6 y jugar con ellas para ver los efectos??
Espero que este tutorial le ayude a entender más a Stardust y lo haga más familiar y cómodo con el flujo de trabajo de Stardust. Gracias por leer!