Fuochi d'Artificio

Starter pack

Benvenuti a Sky Adventure, un'azienda futuristica che offre i migliori fuochi d'artificio della galassia! 🎇

Creeremo un sito web 3D per mostrare i nostri fuochi d'artificio utilizzando Three.js, React Three Fiber e il nostro motore VFX.

Questo è quello che costruiremo insieme!

Progetto Iniziale

Il nostro progetto iniziale include già i seguenti elementi:

  • Una configurazione base di React Three Fiber con una deliziosa isola fluttuante da cui lanceremo i nostri fuochi d'artificio.
  • Effetti di post-processing per far brillare le luci del modello (e successivamente i fuochi d'artificio).
  • Una semplice interfaccia utente realizzata con Tailwind CSS con tre pulsanti per lanciare successivamente i fuochi d'artificio.

Anteprima di Sky Adventure con isola fluttuante

Ecco cosa otteniamo quando eseguiamo il progetto iniziale.

Fuochi d'Artificio

Per creare i fuochi d'artificio, utilizzeremo il motore VFX che abbiamo costruito nella lezione precedente. Questo motore ci permette di creare e gestire più sistemi di particelle con comportamenti differenti.

useFireworks

Per gestire efficacemente i fuochi d'artificio, creeremo un hook custom chiamato useFireworks. Questo hook si occuperà della creazione e gestione dei fuochi d'artificio.

Aggiungiamo la libreria zustand al nostro progetto:

yarn add zustand

Ora in una cartella chiamata hooks, creiamo un nuovo file chiamato useFireworks.js:

import { create } from "zustand";

const useFireworks = create((set, get) => {
  return {
    fireworks: [],
  };
});

export { useFireworks };

Un semplice store con un array vuoto di fuochi d'artificio.

Aggiungiamo un metodo per creare un fuoco d'artificio:

// ...
import { randFloat, randInt } from "three/src/math/MathUtils.js";

const useFireworks = create((set) => {
  return {
    fireworks: [],
    addFirework: () => {
      set((state) => {
        return {
          fireworks: [
            ...state.fireworks,
            {
              id: `${Date.now()}-${randInt(0, 100)}-${state.fireworks.length}`,
              position: [0, 0, 0],
              velocity: [randFloat(-8, 8), randFloat(5, 10), randFloat(-8, 8)],
              delay: randFloat(0.8, 2),
              color: ["skyblue", "pink"],
            },
          ],
        };
      });
    },
  };
});

// ...

addFirework aggiungerà un nuovo fuoco d'artificio allo store con:

  • id: un identificatore unico da usare come chiave nel componente React.
  • position: dove inizierà il fuoco d'artificio.
  • velocity: la direzione e la velocità del fuoco d'artificio prima di esplodere.
  • delay: il tempo prima che il fuoco d'artificio esploda.
  • color: un array di colori da usare per le particelle dell'esplosione del fuoco d'artificio.

Ora possiamo connettere questo hook alla nostra interfaccia utente per lanciare fuochi d'artificio.

Apri UI.jsx e connetti il metodo addFirework ai bottoni:

import { useFireworks } from "../hooks/useFireworks";

export const UI = () => {
  const addFirework = useFireworks((state) => state.addFirework);

  return (
    <section className="fixed inset-0 z-10 flex items-center justify-center">
      {/* ... */}
      <div
      // ...
      >
        {/* ... */}
        <div className="flex gap-4">
          <button
            // ..
            onClick={addFirework}
          >
            🎆 Classic
          </button>
          <button
            // ..
            onClick={addFirework}
          >
            💖 Love
          </button>
          <button
            // ..
            onClick={addFirework}
          >
            🌊 Sea
          </button>
        </div>
        {/* ... */}
      </div>
    </section>
  );
};

Per verificare se funziona, creiamo un componente Fireworks.jsx. Al momento registreremo semplicemente i fuochi d'artificio nella console:

import { useFireworks } from "../hooks/useFireworks";

export const Fireworks = () => {
  const fireworks = useFireworks((state) => state.fireworks);

  console.log(fireworks);
};

E aggiungiamolo al componente Experience:

// ...
import { Fireworks } from "./Fireworks";

export const Experience = () => {
  // ...

  return (
    <>
      {/* ... */}

      <Float
        speed={0.6}
        rotationIntensity={2}
        position-x={4}
        floatIntensity={2}
      >
        <Fireworks />
        <Gltf src="/models/SkyIsland.glb" />
      </Float>

      {/* ... */}
    </>
  );
};

Importiamo il componente Fireworks e aggiungiamolo accanto a SkyIsland nel nostro componente <Float />.

Fuochi d'artificio nella console

Premendo i bottoni, possiamo vedere nella console che i fuochi d'artificio vengono correttamente aggiunti allo store.

Prima di rappresentarli nella scena, dobbiamo gestire il ciclo di vita dei fuochi d'artificio. Attualmente vengono aggiunti ma mai rimossi.

Nel addFirework nel nostro hook useFireworks, possiamo aggiungere un setTimeout per rimuovere il fuoco d'artificio dopo un certo tempo.

Prima dobbiamo sapere quando viene generato il fuoco d'artificio. Possiamo aggiungere una proprietà time all'oggetto fuoco d'artificio:

{
  id: `${Date.now()}-${randInt(0, 100)}-${state.fireworks.length}`,
  // ...
  time: Date.now(),
},

Poi chiama setTimeout per rimuovere i fuochi d'artificio che sono esplosi e svaniti:

addFirework: () => {
  set((state) => {
    // ...
  });
  setTimeout(() => {
    set((state) => ({
      fireworks: state.fireworks.filter(
        (firework) => Date.now() - firework.time < 4000 // Max delay di 2 secondi + Durata massima delle particelle di 2 secondi
      ),
    }));
  }, 4000);
},

Filtriamo i fuochi d'artificio che sono stati aggiunti più di 4 secondi fa. In questo modo, manteniamo i fuochi d'artificio che sono ancora attivi.

Regola il tempo in base alle impostazioni finali che utilizzerai per i fuochi d'artificio.

Fuochi d'artificio nella console

Premendo i bottoni, possiamo vedere nella console che i fuochi d'artificio vengono correttamente rimossi dopo un certo tempo.

Ora possiamo immergerci nella parte che aspettavi: creare i fuochi d'artificio nella scena!

Motore VFX (Wawa VFX)

Per evitare di copiare/incollare il componente dalla lezione precedente, ho pubblicato il motore VFX come pacchetto su npm: Wawa VFX. Puoi installarlo eseguendo:

yarn add wawa-vfx@^1.0.0

Usando @^1.0.0, ci assicuriamo di utilizzare sempre la versione principale 1 del pacchetto, ma includendo le ultime versioni minori e patch. In questo modo, possiamo beneficiare delle ultime funzionalità e correzioni di bug senza cambiamenti distruttivi.

Ora possiamo utilizzare i componenti <VFXParticles /> e <VFXEmitter /> nel nostro progetto!

Nell'esperienza, aggiungiamo il componente VFXParticles alla scena:

// ...
import { VFXParticles } from "wawa-vfx";

export const Experience = () => {
  const controls = useRef();

  return (
    <>
      {/* ... */}

      <VFXParticles
        name="firework-particles"
        settings={{
          nbParticles: 100000,
          gravity: [0, -9.8, 0],
          renderMode: "billboard",
          intensity: 3,
        }}
      />

      <EffectComposer>
        <Bloom intensity={1.2} luminanceThreshold={1} mipmapBlur />
      </EffectComposer>
    </>
  );
};

Abbiamo aggiunto il componente VFXParticles con le seguenti impostazioni:

  • 100000 particelle. Visto che quando raggiungiamo il limite, le particelle più vecchie verranno rimosse, dovrebbe essere più che sufficiente per molti fuochi d'artificio contemporaneamente.
  • gravity per simulare la gravità sulle particelle. -9.8 è la gravità sulla Terra. (Ma aspetta, siamo nello spazio! 👀)
  • renderMode su billboard per fronteggiare sempre la telecamera.
  • intensity a 3 per far brillare le particelle nel cielo notturno.

La posizione del componente <VFXParticles /> non è molto importante. Assicurati solo che sia al livello superiore della scena.

E aggiungiamo un componente VFXEmitter accanto ai VFXParticles per modellare l'esplosione in modalità debug e avere accesso ai controlli visivi:

// ...
import { VFXEmitter, VFXParticles } from "wawa-vfx";

export const Experience = () => {
  const controls = useRef();

  return (
    <>
      {/* ... */}

      <VFXParticles
        name="firework-particles"
        settings={{
          nbParticles: 100000,
          gravity: [0, -9.8, 0],
          renderMode: "billboard",
          intensity: 3,
        }}
      />
      <VFXEmitter emitter="firework-particles" debug />

      {/* ... */}
    </>
  );
};

Assicurati di impostare la prop emitter con lo stesso name del componente VFXParticles e di impostare debug su true per vedere i controlli.

Abbozza una prima versione dell'esplosione dei fuochi d'artificio. 💥

Una volta che sei soddisfatto delle impostazioni, puoi rimuovere la prop debug dal componente VFXEmitter, premere il pulsante di esportazione e incollare le impostazioni nel componente VFXEmitter.

<VFXEmitter
  emitter="firework-particles"
  settings={{
    nbParticles: 5000,
    delay: 0,
    spawnMode: "burst",
    colorStart: ["skyblue", "pink"],
    particlesLifetime: [0.1, 2],
    size: [0.01, 0.4],
    startPositionMin: [-0.1, -0.1, -0.1],
    startPositionMax: [0.1, 0.1, 0.1],
    directionMin: [-1, -1, -1],
    directionMax: [1, 1, 1],
    startRotationMin: [degToRad(-90), 0, 0],
    startRotationMax: [degToRad(90), 0, 0],
    rotationSpeedMin: [0, 0, 0],
    rotationSpeedMax: [3, 3, 3],
    speed: [1, 12],
  }}
/>

Abbiamo tutto pronto per collegare i fuochi d'artificio al motore VFX!

Esplosione di fuochi d'artificio

All'interno del file Fireworks.jsx, creiamo un componente Firework che rappresenterà un singolo fuoco d'artificio:

// ...
import { useRef } from "react";
import { VFXEmitter } from "wawa-vfx";

export const Fireworks = () => {
  // ...
};

const Firework = ({ velocity, delay, position, color }) => {
  const ref = useRef();
  return (
    <>
      <group ref={ref} position={position}>
        <VFXEmitter
          emitter="firework-particles"
          settings={{
            nbParticles: 5000,
            delay: 0,
            spawnMode: "burst",
            colorStart: ["skyblue", "pink"],
            particlesLifetime: [0.1, 2],
            size: [0.01, 0.4],
            startPositionMin: [-0.1, -0.1, -0.1],
            startPositionMax: [0.1, 0.1, 0.1],
            directionMin: [-1, -1, -1],
            directionMax: [1, 1, 1],
            startRotationMin: [degToRad(-90), 0, 0],
            startRotationMax: [degToRad(90), 0, 0],
            rotationSpeedMin: [0, 0, 0],
            rotationSpeedMax: [3, 3, 3],
            speed: [1, 12],
          }}
        />
      </group>
    </>
  );
};

Semplicemente taglia/incolla il VFXEmitter dal componente Experience al componente Firework.

Lo racchiudiamo in un <group /> per poter spostare il fuoco d'artificio nella scena basato sulla position e velocity.

Three.js logoReact logo

React Three Fiber: The Ultimate Guide to 3D Web Development

✨ You have reached the end of the preview ✨

Go to the next level with Three.js and React Three Fiber!

Get full access to this lesson and the complete course when you enroll:

  • 🔓 Full lesson videos with no limits
  • 💻 Access to the final source code
  • 🎓 Course progress tracking & completion
  • 💬 Invite to our private Discord community
Unlock the Full Course – Just $85

One-time payment. Lifetime updates included.