Fireworks

Starter pack

ようこそ、Sky Adventureへ。銀河系で最高の花火を提供する未来の会社です! 🎇

Three.js、React Three Fiber、およびVFXエンジンを使用して、私たちの花火を紹介する3Dウェブサイトを作成します。

これが一緒に作成するものです!

スタータープロジェクト

私たちのスタータープロジェクトにはすでに以下が含まれています:

  • 美しい浮島から花火を打ち上げるための基本的なReact Three Fiberセットアップ。
  • モデル(後に花火も)の光をブームさせるポストプロセシングエフェクト。
  • 後で花火を打ち上げるための3つのボタンを含む Tailwind CSS を使ったシンプルなUI。

浮島を持つSky Adventureのプレビュー

スタータープロジェクトを実行するときに得られるものがこちらです。

花火

花火を作成するために、前のレッスンで構築したVFXエンジンを使用します。このエンジンを使用すると、さまざまな動作を持つ複数のパーティクルシステムを作成して管理できます。

useFireworks

花火を効率的に管理するために、useFireworksというカスタムフックを作成します。このフックは花火の作成と管理を行います。

まず、プロジェクトにzustandライブラリを追加します:

yarn add zustand

次に、hooksというフォルダにuseFireworks.jsという新しいファイルを作成します:

import { create } from "zustand";

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

export { useFireworks };

空の花火配列を持つシンプルなストアです。

花火を作成するメソッドを追加しましょう:

// ...
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は、新しい花火を以下の属性でストアに追加します:

  • id: Reactコンポーネント内でキーとして使用するユニークな識別子。
  • position: 花火が始まる位置。
  • velocity: 爆発前の花火の方向と速度。
  • delay: 花火が爆発するまでの時間。
  • color: 花火爆発のパーティクルに使用する色の配列。

これで、UIにこのフックを接続して花火を打ち上げることができます。

UI.jsxを開き、addFireworkメソッドをボタンに接続します:

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>
  );
};

動作を確認するために、Fireworks.jsxコンポーネントを作成します。今はコンソールに花火をログ表示するだけにします:

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

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

  console.log(fireworks);
};

そして、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>

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

Fireworksコンポーネントをインポートし、<Float />コンポーネント内のSkyIslandの隣に追加します。

Fireworks in the console

ボタンを押すと、コンソールに花火が正しくストアに追加されていることが確認できます。

シーンに表現する前に、花火のライフサイクルを管理する必要があります。現在、花火は追加されるだけで削除されません。

useFireworksフック内のaddFireworkメソッドで、一定時間経過後に花火を削除するためにsetTimeoutを追加することができます。

まず、花火が出現した時間を知る必要があります。花火オブジェクトにtimeプロパティを追加します:

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

次にsetTimeoutを呼び出し、爆発して消えた花火を削除します:

addFirework: () => {
  set((state) => {
    // ...
  });
  setTimeout(() => {
    set((state) => ({
      fireworks: state.fireworks.filter(
        (firework) => Date.now() - firework.time < 4000 // 最大遅延2秒 + パーティクルの最大寿命2秒
      ),
    }));
  }, 4000);
},

4秒以上経過した花火をフィルタリングします。この方法で、まだアクティブな花火を維持します。

花火の最終設定に応じて時間を調整してください。

Fireworks in the console

ボタンを押すと、一定時間後にコンソールで花火が正しく削除されていることが確認できます。

これで、あなたが待ち望んでいた部分、シーン内で花火を作成することができます!

VFXエンジン (Wawa VFX)

前のレッスンからコンポーネントをコピー/ペーストしないように、VFXエンジンをnpmパッケージとして公開しました: Wawa VFX。以下のコマンドを実行してインストールできます:

yarn add wawa-vfx@^1.0.0

@^1.0.0を使用することで、常にパッケージのメジャーバージョン1を使用しつつ、最新のマイナーおよびパッチバージョンを含めることができます。これにより、互換性を損なうことなく、最新の機能やバグ修正を享受できます。

これでプロジェクトで<VFXParticles /><VFXEmitter />を使用することができます!

エクスペリエンスにおいて、シーンにVFXParticlesコンポーネントを追加してみましょう:

// ...
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>
    </>
  );
};

VFXParticlesコンポーネントを次の設定で追加します:

  • 100000個のパーティクル。制限に達した場合、最も古いパーティクルが削除されますが、同時に多くの花火を表示するには十分です。
  • パーティクルに重力をシミュレートするためのgravity-9.8は地球の重力です。 (でも待って、ここは宇宙だよ!👀)
  • 常にカメラに向くようにrenderModebillboardに。
  • 夜空でパーティクルを輝かせるためにintensity3に。

<VFXParticles />コンポーネントの配置場所はそれほど重要ではありません。ただし、シーンのトップレベルに配置してください。

そして、VFXParticlesの横にVFXEmitterコンポーネントを追加し、debugモードで爆発の形を整え、ビジュアルコントロールにアクセスできるようにしましょう:

// ...
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 />

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

emitterプロップをVFXParticlesコンポーネントと同じnameに設定し、debugtrueにしてコントロールを表示できるようにしてください。

*花火の爆発の最初のバージョンを作成します。*💥

設定に満足したら、VFXEmitterコンポーネントからdebugプロップを削除し、エクスポートボタンを押して、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],
  }}
/>

VFXエンジン花火を接続する準備が整いました!

Fireworks explosion

Fireworks.jsxファイル内で、1つの花火を表すFireworkコンポーネントを作成しましょう。

// ...
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>
    </>
  );
};

単にExperienceコンポーネントからVFXEmitterを切り取って、Fireworkコンポーネントに貼り付けます。

シーン内でpositionvelocityに基づいて花火を動かせるように、それを<group />でラップします。

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.