React × Three.jsでクマを歩かせてみた!Meshy AIで簡単3Dモデリング!

React × Three.jsでクマを歩かせてみた!Meshy AIで簡単3Dモデリング!
  • URLをコピーしました!

AI技術が進化する中で、3Dモデリングがより手軽に、そして楽しくなりました。
特にプログラミング初心者でも簡単に使える「Meshy AI」の登場で、これまで難しいと感じていた3Dモデリングが一気にハードルを下げています。

📺 Meshyを確認する:こちらのリンクから公式ページで確認できます。

今回の記事では、Meshy AIで作成したクマの3DモデルをReact × Three.jsを使って動かす方法を詳しく解説します。

クマが画面の中で動き出す様子は、見ていて楽しく、Web開発の可能性を広げるきっかけにもなるはずです。
初心者の方もぜひチャレンジして、あなたのオリジナル3Dアニメーションを作り上げてみてください!

TypeScript x React Three Fiberのレッスンや作品は、今後もどんどん作成していきます!
YouTubeで告知致しますので、ぜひYouTubeのチャンネル登録をして通知をお待ちください!

📺 YouTubeを見る:こちらのリンクから視聴できます。

TypeScript x React Three Fiber

React Three Fiberで何ができるのか知りたい方は、下記を参考にしてみてください!
簡単にできる作品を用意しております!

目次

3Dモデル表示のデモ

以下の動画で、完成形のデモを見ることができます。

📺 YouTubeでデモを見る:こちらのリンクから視聴できます。

さらに、このプロジェクトのソースコードはGitHubに公開しています。
ぜひコードをダウンロードして試してみてください!

💾 GitHubリポジトリ:こちらのリンクでソースコードをチェック!

「見てみたけどどうやって作るの?」と思った方、安心してください!
この記事では、この3Dモデルを表示する方法をステップバイステップで解説していきます。

必要な技術と主要ライブラリ

このプロジェクトでは、以下の技術とライブラリを使用しています。それぞれの役割を簡単に説明します。

  • React
    • フロントエンド開発のためのライブラリ
    • コンポーネントベースの設計でUIを効率的に構築できます
  • Three.js
    • 3DグラフィックスをWeb上で描画するためのライブラリ
    • WebGLの複雑な処理を簡単に扱えるようにしてくれます
  • React Three Fiber
    • ReactでThree.jsを扱うためのラッパーライブラリ
    • Reactの開発スタイルでThree.jsの強力な機能を利用できます
  • React Thee Drei
    • React Three Fiberの拡張ライブラリ
    • React Three Fiberの拡張ライブラリ
  • Meshy
    • AIを活用して高品質な3Dモデルを簡単に生成できるサービス

実装の全体設計

このプロジェクトでは、「AIツールを使って3Dモデルを生成し、ReactとThree.jsでブラウザ上に表示する」というシンプルな流れを実現します。

設計図(簡易フロー)
モデルの読み込み
  • FBXLoaderを使ってMeshy AIで生成したクマの3Dモデルをプロジェクト内にインポート
  • スケール初期位置回転を調整して、シーン内で適切に配置します
アニメーションの管理
  • Three.jsのAnimationMixerを使用して、クマの「歩く」アニメーションを再生
  • 状態管理: isPlayingという状態変数を使用して、アニメーションの再生と停止を制御
    • isPlaying = true: アニメーションが再生される
    • isPlaying = false: アニメーションが停止する
フレームごとの更新
  • React Three Fiberの**useFrame**フックを使用して、フレームごとに以下の動作を更新
    • モデルの前進: クマが画面上で歩くように、z軸の位置を増加
    • アニメーションの更新: AnimationMixerで現在のアニメーションフレームを進める
ボタンによる操作
  • Reactのボタンを使用して、isPlayingの状態を切り替え可能にします
    • ボタンを押すとアニメーションの再生/停止が切り替わる簡単なUI
環境の構築
  • 背景: Three.jsで空の色を設定
  • ライト: 環境光と点光源を配置し、3Dモデルを見やすくする

環境準備

このセクションでは、プロジェクトの初期設定を行います。npxコマンドでReactアプリを作成し、必要なライブラリをインストールしてフォルダ構成を整えます。

Reactアプリの作成

まず、npxコマンドを使用してReactアプリを作成します。

npx create-react-app meshy-3d-model-animathion --template typescript
  • meshy-3d-model-animathion はプロジェクトの名前です
  • --template typescript を指定することで、TypeScript対応のテンプレートを使用します

必要なライブラリのインストール

React Three Fiberやその他のライブラリをインストールします。

cd meshy-3d-model-animathion
npm install three @react-three/fiber @react-three/drei
  • three: Three.js本体
  • @react-three/fiber: ReactでThree.jsを使うためのラッパー
  • @react-three/drei: カメラコントロールやテキスト描画など、便利なヘルパー

フォルダ構成の見直しと不要ファイルの削除

初期状態から以下のようにフォルダを整理・追加します。
基本的には初期値だったりしますが、わからないファイルなどは、GitHubを見てみてください。

meshy-3d-model-animation/
├── node_modules/
├── public/
│   ├── models/                     // 3Dモデルと関連ファイル
│   │   ├── cuddly_bear/            // クマの3Dモデルフォルダ
│   │   │   ├── Animation_Walking_withSkin.fbx  // 歩行アニメーション付きのFBXファイル
│   │   │   └── Character_output.fbx           // 他の用途のFBXファイル
├── src/
│   ├── components/                 // 必要なら共通コンポーネントを格納
│   ├── pages/                      // ページ単位で管理するコンポーネント
│   │   ├── ThreeDModelAnimation/   // 3Dアニメーションページ
│   │   │   ├── ThreeDModelAnimation.tsx  // メインのReactコンポーネント
│   │   │   └── ThreeDModelAnimation.css  // ページのスタイルシート
│   │   └── index.ts                // ページをエクスポートするインデックスファイル
│   ├── App.tsx                     // アプリのエントリーポイント
│   ├── index.tsx                   // Reactのレンダリング処理
│   ├── App.css                     // グローバルスタイル
│   ├── index.css                   // グローバルCSS
├── package.json                    // プロジェクト設定
├── tsconfig.json                   // TypeScriptの設定
└── README.md                       // プロジェクト概要

今回手を加えるファイルは、下記となります。

  • App.tsx: アプリのエントリーポイント
  • pages>index.ts:ページをエクスポートするインデックスファイル
  • pages/ThreeDModelAnimation: 今回作成対象のフォルダ
    • ThreeDModelAnimation.tsx: メインページのコンポーネント。
    • ThreeDModelAnimation.css:ページのスタイルシート

各ステップ毎にソースコードの詳細解説

以下のセクションでは、下記の5つのPARTを順番に詳しく解説していきます。

PART
PART
Meshyで3Dモデルの生成
  • Meshy AIを使用して、AIでクマの3Dモデルを生成
  • FBX形式でエクスポートして、プロジェクトのpublic/models/cuddly_bearディレクトリに配置
PART
定数定義
  • 3Dモデルのスケール、初期位置、回転角度などを定義
  • 地面やランダムな石の大きさ、カメラやライトの初期設定を管理する定数を設定
  • 定数化することで、後の調整や管理を容易に
PART
アニメーションモデル
  • FBXLoaderを使用して、Meshyで生成したクマのモデルを読み込み
  • AnimationMixerを利用して、アニメーションを管理
  • primitiveを用いてモデルを描画
  • Reactの状態管理を利用して、再生/停止の切り替えを実装
PART
地面の設定
  • PlaneGeometryを使用して、地面を生成
  • meshStandardMaterialを利用して、地面の色や質感を設定
  • 地面を静止させ、背景とモデルを引き立たせる役割を担う
PART
ランダムな石の配置
  • useStateを用いて、ランダムな位置と大きさの石を初回レンダリング時に生成
  • sphereGeometryとmeshStandardMaterialで石を作成
PART
メインコンポーネント
  • Canvasを用いて、地面、石、クマのアニメーションモデルを統合
  • DreiライブラリのOrbitControlsを利用し、カメラ操作を可能に
  • ボタンを追加し、isPlaying状態を切り替えてアニメーションを再生/停止

全体ソースを確認

以下は、この記事で作成するMeshy AIで作成したクマの3DモデルをReact × Three.jsを使って動かすの完成版ソースコードです。すべてのコードをまとめていますので、動作のイメージをつかみやすいと思います。

これ以外のソースを見たい方は、GitHubで確認してください。

import './App.css';
import { ThreeDModelAnimation } from './pages';

function App() {
  return (
    <div className="App">
      <ThreeDModelAnimation />
    </div>
  );
}

export default App;
import ExplodingButtonPage from "./ExplodingButtonPage/ExplodingButtonPage";
import GamingRoomLogin from "./GamingRoomLogin/GamingRoomLogin";
import Meshy3DModel from "./Meshy3DModel/Meshy3DModel";
import ThreeDModelAnimation from "./ThreeDModelAnimation/ThreeDModelAnimation";

export {ExplodingButtonPage, GamingRoomLogin, Meshy3DModel, ThreeDModelAnimation}
// 3DModelAnimation.tsx

// ================================
// PART 1: Meshyでのモデル生成
// ================================

// Meshy AIを利用して、3Dモデルを生成する

// ================================
// PART 2: 定数定義
// ================================
import React, { useRef, useEffect, useState } from 'react';
import { Canvas, useFrame } from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei';
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader';
import { AnimationMixer, Clock, DoubleSide, Vector3 } from 'three';
import './ThreeDModelAnimation.css';

// 定数定義
// モデル関連の設定
const MODEL_SCALE = 0.1; // モデルのスケール
const MODEL_INITIAL_POSITION = new Vector3(0, 0.5, 0); // モデルの初期位置
const MODEL_ROTATION_X = -0.3; // モデルのX軸回転

// 地面の設定
const GROUND_COLOR = 'green';
const GROUND_POSITION = new Vector3(0, 0, 0);
const GROUND_SIZE = 200; // 地面のサイズ
const GROUND_SEGMENTS = 50; // 地面の分割数

// 石の設定
const STONE_COUNT = 50; // 石の数
const STONE_SCALE_MIN = 0.1; // 石の最小スケール
const STONE_SCALE_MAX = 0.6; // 石の最大スケール

// カメラとライトの設定
const CAMERA_POSITION = new Vector3(0, 20, 50);
const AMBIENT_LIGHT_INTENSITY = 0.7; // 環境光の強さ
const POINT_LIGHT_POSITION = new Vector3(10, 20, 10);
const POINT_LIGHT_INTENSITY = 1; // 点光源の強さ

// 背景の設定
const BACKGROUND_COLOR = 'skyblue'; // 背景色

interface AnimatedFBXModelProps {
    path: string;
    isPlaying?: boolean;
}

// ================================
// PART 3: アニメーションモデル
// ================================
const AnimatedFBXModel: React.FC<AnimatedFBXModelProps> = ({ path, isPlaying = false }) => {
    const mixer = useRef<AnimationMixer | null>(null);
    const clock = useRef(new Clock());
    const [model, setModel] = useState<any>(null);
    const [action, setAction] = useState<any>(null);

    // フレームごとにモデルの位置とアニメーションを更新
    useFrame(() => {
        if (model && isPlaying) {
            model.position.z += 0.05; // モデルを前進させる
        }

        if (mixer.current && isPlaying) {
            const delta = clock.current.getDelta();
            mixer.current.update(delta); // アニメーションの更新
        }
    });

    // FBXモデルの読み込み
    useEffect(() => {
        const loader = new FBXLoader();

        loader.load(
            path,
            (fbx) => {
                if (!model) {
                    fbx.scale.set(MODEL_SCALE, MODEL_SCALE, MODEL_SCALE);
                    fbx.position.copy(MODEL_INITIAL_POSITION);
                    fbx.rotateX(MODEL_ROTATION_X);
                }

                mixer.current = new AnimationMixer(fbx);

                // アニメーションがあれば設定
                if (fbx.animations.length > 0) {
                    const newAction = mixer.current.clipAction(fbx.animations[0]);
                    newAction.paused = !isPlaying;
                    setAction(newAction);
                }

                setModel(fbx);
            },
            undefined,
            (error) => {
                console.error('Error loading FBX model:', error);
            }
        );
    }, [path]);

    // アニメーションの再生/停止を切り替え
    useEffect(() => {
        if (action) {
            action.paused = !isPlaying;
            if (isPlaying) {
                action.play();
            } else {
                action.stop();
            }
        }
    }, [isPlaying, action]);

    return model ? <primitive object={model} /> : null;
};

// ================================
// PART 4: 地面の設定
// ================================
const Ground: React.FC = () => {
    return (
        <mesh rotation={[-Math.PI / 2, 0, 0]} position={GROUND_POSITION}>
            <planeGeometry args={[GROUND_SIZE, GROUND_SIZE, GROUND_SEGMENTS, GROUND_SEGMENTS]} />
            <meshStandardMaterial color={GROUND_COLOR} side={DoubleSide} />
        </mesh>
    );
};

// ================================
// PART 5: ランダムな石の配置
// ================================
const RandomStones: React.FC = () => {
    // 初回レンダリング時にランダムな位置とスケールで石を生成
    const [stones] = useState(() => {
        return Array.from({ length: STONE_COUNT }, () => {
            const x = Math.random() * GROUND_SIZE - GROUND_SIZE / 2;
            const y = GROUND_POSITION.y;
            const z = Math.random() * GROUND_SIZE - GROUND_SIZE / 2;
            const scale = Math.random() * (STONE_SCALE_MAX - STONE_SCALE_MIN) + STONE_SCALE_MIN;

            return (
                <mesh key={`${x}-${z}`} position={[x, y, z]} scale={[scale, scale, scale]}>
                    <sphereGeometry args={[1, 8, 8]} />
                    <meshStandardMaterial color="gray" />
                </mesh>
            );
        });
    });

    return <>{stones}</>;
};

// ================================
// PART 6: メインコンポーネント
// ================================
const ThreeDModelAnimation: React.FC = () => {
    const [isPlaying, setIsPlaying] = useState(false);

    return (
        <div className="canvas-container">
            <button
                style={{ position: 'absolute', top: '10px', left: '10px', zIndex: 100 }}
                onClick={() => setIsPlaying(!isPlaying)}
            >
                {isPlaying ? 'Pause Animation' : 'Play Animation'}
            </button>
            <Canvas camera={{ position: CAMERA_POSITION.toArray() }}>
                <ambientLight intensity={AMBIENT_LIGHT_INTENSITY} />
                <pointLight position={POINT_LIGHT_POSITION.toArray()} intensity={POINT_LIGHT_INTENSITY} />
                <OrbitControls />

                {/* 地面の表示 */}
                <Ground />

                {/* ランダムな石の配置 */}
                <RandomStones />

                {/* 背景色の設定 */}
                <color attach="background" args={[BACKGROUND_COLOR]} />

                {/* アニメーションモデル */}
                <AnimatedFBXModel path="/models/cuddly_bear/Animation_Walking_withSkin.fbx" isPlaying={isPlaying} />
            </Canvas>
        </div>
    );
};

export default ThreeDModelAnimation;
/* 3DModelAnimation.css */
.canvas-container {
    width: 100vw;
    height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    background-color: #000;
  }
  

PART 1: Meshyでのモデル生成

今回、3Dモデルは、MeshyというAI自動生成サービスを利用します。
リンクは、下記になりますので、参考にしてみてください。

毎月200クレジット分は、無料で利用可能です。

📺 Meshyを確認する:こちらのリンクから公式ページで確認できます。

今回の内容は、PRO版(月3000円程度)が必須になります。(アニメーションはPRO版のみ)

コミュニティを選択して、すでに作成されているモデルを選びます。
こちらは、テキストや画像から自分で生成しても構いません。

今回は、下記のクマの3Dモデルを選びました。
カーソルを合わせると「モデルを再生成」というボタンがあると思いますので、それを選択します。

ワークスペースに移動すると思いますので、そのまま「生成する」ボタンを選択しましょう。

クマの3Dモデルが4つ出てきますので、気に入ったモデルを選びます

テクスチャ&リメッシュは、そのままで確認するを選びます。

これで、選択したモデルと同じようなモデルが生成されます。

次に、アニメーションをつけるため、アニメ―ト>リギングを選択します。

今回は、少し難しいのですが、ヒューマノイドが近いので、ヒューマノイドを選択します。

次の画面は、特に変更は不要かなと思いますので、そのまま次へ進みましょう。

マーカーの配置になります。今回ひざが難しいのですが、私は足首と重ねてしまいました。

これでアニメーションが付いている3Dモデルが生成されます。
歩き方がぎこちないですが、キャラクター的にこれで私は十分です。(笑)

動きが問題なければ、fbxなどでエクスポートしましょう。
今回は、fbxで解説しますが、glbの方が軽量なのでWEBの場合は良いかもしれません。

PART 2: 定数定義

まずは、各ライブラリのimportと、定数定義です。
定数を定義することで、アプリケーション内で使用する値を一元管理し、再利用性と可読性を向上させます。3Dモデルやシーンの設定値をコード内の随所で使うため、定数化しておくとメンテナンスが容易になります。

// ================================
// PART 2: 定数定義
// ================================
import React, { useRef, useEffect, useState } from 'react';
import { Canvas, useFrame } from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei';
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader';
import { AnimationMixer, Clock, DoubleSide, Vector3 } from 'three';
import './ThreeDModelAnimation.css';

// 定数定義
// モデル関連の設定
const MODEL_SCALE = 0.1; // モデルのスケール
const MODEL_INITIAL_POSITION = new Vector3(0, 0.5, 0); // モデルの初期位置
const MODEL_ROTATION_X = -0.3; // モデルのX軸回転

// 地面の設定
const GROUND_COLOR = 'green';
const GROUND_POSITION = new Vector3(0, 0, 0);
const GROUND_SIZE = 200; // 地面のサイズ
const GROUND_SEGMENTS = 50; // 地面の分割数

// 石の設定
const STONE_COUNT = 50; // 石の数
const STONE_SCALE_MIN = 0.1; // 石の最小スケール
const STONE_SCALE_MAX = 0.6; // 石の最大スケール

// カメラとライトの設定
const CAMERA_POSITION = new Vector3(0, 20, 50);
const AMBIENT_LIGHT_INTENSITY = 0.7; // 環境光の強さ
const POINT_LIGHT_POSITION = new Vector3(10, 20, 10);
const POINT_LIGHT_INTENSITY = 1; // 点光源の強さ

// 背景の設定
const BACKGROUND_COLOR = 'skyblue'; // 背景色

interface AnimatedFBXModelProps {
    path: string;
    isPlaying?: boolean;
}

生成した3Dモデルによっては、微調整が必要だと思いますので、下記を参考に数字をいじってみてください。

  • モデル関連の設定
    • MODEL_SCALE
      • モデルの大きさを指定します。0.1で小さめに設定。
      • 例: クマのモデルが画面に対して適切なサイズで表示されるよう調整します。
    • MODEL_INITIAL_POSITION
      • モデルの初期位置を指定します。
      • 例: new Vector3(0, 0.5, 0)で地面の少し上に配置。
    • MODEL_ROTATION_X
      • モデルの初期回転角度を指定します。
      • 例: -0.3で少し前傾姿勢になるように調整。
  • 地面の設定
    • GROUND_COLOR
      • 地面の色を指定します。ここではgreenを指定。
    • GROUND_POSITION
      • 地面の位置を指定します。new Vector3(0, 0, 0)で原点に配置。
    • GROUND_SIZE
      • 地面の大きさ(幅と高さ)を指定します。200の正方形サイズ。
    • GROUND_SEGMENTS
      • 地面を分割する数を指定します。50で適度な細かさに設定。
  • 石の設定
    • STONE_COUNT
      • 配置する石の数を指定します。50個を配置。
    • STONE_SCALE_MIN
      • 石の最小スケール(大きさ)を指定します。0.1で小さめの石を表現。
    • STONE_SCALE_MAX
      • 石の最大スケール(大きさ)を指定します。0.6で少し大きめの石を表現。
  • カメラとライトの設定
    • CAMERA_POSITION
      • カメラの初期位置を指定します。new Vector3(0, 20, 50)で少し上からクマを見下ろす視点に。
    • AMBIENT_LIGHT_INTENSITY
      • 環境光の強さを指定します。0.7で自然な明るさ。
    • POINT_LIGHT_POSITION
      • 点光源の位置を指定します。new Vector3(10, 20, 10)でモデルを斜め上から照らす。
    • POINT_LIGHT_INTENSITY
      • 点光源の強さを指定します。1で適度な明るさを設定。
  • 背景の設定
    • BACKGROUND_COLOR
      • シーンの背景色を指定します。ここではskyblueを指定

PART 3: アニメーションモデル

Meshyで生成した3Dモデルを読み込み、Three.jsのアニメーション機能を活用して動かします。アニメーションの管理と、Reactの状態管理を組み合わせることで、モデルを動かしたり停止させたりする機能を実現します。

// ================================
// PART 3: アニメーションモデル
// ================================
const AnimatedFBXModel: React.FC<AnimatedFBXModelProps> = ({ path, isPlaying = false }) => {
    const mixer = useRef<AnimationMixer | null>(null);
    const clock = useRef(new Clock());
    const [model, setModel] = useState<any>(null);
    const [action, setAction] = useState<any>(null);

    // フレームごとにモデルの位置とアニメーションを更新
    useFrame(() => {
        if (model && isPlaying) {
            model.position.z += 0.05; // モデルを前進させる
        }

        if (mixer.current && isPlaying) {
            const delta = clock.current.getDelta();
            mixer.current.update(delta); // アニメーションの更新
        }
    });

    // FBXモデルの読み込み
    useEffect(() => {
        const loader = new FBXLoader();

        loader.load(
            path,
            (fbx) => {
                if (!model) {
                    fbx.scale.set(MODEL_SCALE, MODEL_SCALE, MODEL_SCALE);
                    fbx.position.copy(MODEL_INITIAL_POSITION);
                    fbx.rotateX(MODEL_ROTATION_X);
                }

                mixer.current = new AnimationMixer(fbx);

                // アニメーションがあれば設定
                if (fbx.animations.length > 0) {
                    const newAction = mixer.current.clipAction(fbx.animations[0]);
                    newAction.paused = !isPlaying;
                    setAction(newAction);
                }

                setModel(fbx);
            },
            undefined,
            (error) => {
                console.error('Error loading FBX model:', error);
            }
        );
    }, [path]);

    // アニメーションの再生/停止を切り替え
    useEffect(() => {
        if (action) {
            action.paused = !isPlaying;
            if (isPlaying) {
                action.play();
            } else {
                action.stop();
            }
        }
    }, [isPlaying, action]);

    return model ? <primitive object={model} /> : null;
};
  • モデルの読み込み
    • FBXLoaderを使用して、public/models/cuddly_bear/Animation_Walking_withSkin.fbxを読み込みます。
    • Three.jsのprimitiveを利用して、読み込んだ3Dモデルを描画します。
  • アニメーションの管理
    • AnimationMixerを利用して、3Dモデルのアニメーションを制御します。
    • アニメーションの再生・停止の切り替えには、ReactのuseStateフックで管理するisPlayingを使用します。
  • モデルの前進
    • Three.jsのuseFrameフックを使用して、フレームごとにz軸の値を増加させ、モデルが前進する動きを実現します。

PART 4: 地面の設定

クマが歩く舞台となる地面を設定します。地面は単純な平面ジオメトリを使用して表現し、カメラやモデルと調和するように配置します。地面があることで、クマの動きにリアリティを持たせることができます。

今回は、シンプルに緑一色ですが、テクスチャなどを活用するとよりリアルになると思います。

// ================================
// PART 4: 地面の設定
// ================================
const Ground: React.FC = () => {
    return (
        <mesh rotation={[-Math.PI / 2, 0, 0]} position={GROUND_POSITION}>
            <planeGeometry args={[GROUND_SIZE, GROUND_SIZE, GROUND_SEGMENTS, GROUND_SEGMENTS]} />
            <meshStandardMaterial color={GROUND_COLOR} side={DoubleSide} />
        </mesh>
    );
};
  • 地面の生成
    • React Three FiberのPlaneGeometryを使用して、平面を作成します。
    • 平面の幅、高さ、分割数を定義済みの定数を利用して設定します。
  • 地面の見た目
    • meshStandardMaterialを使用して、地面の色や質感を設定します。
    • 地面の色はGROUND_COLORとしてgreenを指定します。
  • 配置
    • 平面を水平にするために、X軸に対して90度回転させます。
    • 地面の位置をGROUND_POSITIONとして、new Vector3(0, 0, 0)に設定します。

PART 5: ランダムな石の配置

地面にランダムな大きさと位置の石を配置し、シーンに自然な雰囲気を加えます。緑一色だと、シンプルすぎたので、石をランダムに配置することで、よりリアリティのあるシーンを演出します。

// ================================
// PART 5: ランダムな石の配置
// ================================
const RandomStones: React.FC = () => {
    // 初回レンダリング時にランダムな位置とスケールで石を生成
    const [stones] = useState(() => {
        return Array.from({ length: STONE_COUNT }, () => {
            const x = Math.random() * GROUND_SIZE - GROUND_SIZE / 2;
            const y = GROUND_POSITION.y;
            const z = Math.random() * GROUND_SIZE - GROUND_SIZE / 2;
            const scale = Math.random() * (STONE_SCALE_MAX - STONE_SCALE_MIN) + STONE_SCALE_MIN;

            return (
                <mesh key={`${x}-${z}`} position={[x, y, z]} scale={[scale, scale, scale]}>
                    <sphereGeometry args={[1, 8, 8]} />
                    <meshStandardMaterial color="gray" />
                </mesh>
            );
        });
    });

    return <>{stones}</>;
};
  • 石の生成
    • ReactのuseStateを使用して、石の配置データを生成します。
    • 配置データには、ランダムな位置とスケールを持つ複数の石を含めます。
  • ランダムな配置
    • Math.randomを利用して、石のx座標とz座標をランダムに生成。
    • STONE_SCALE_MINSTONE_SCALE_MAXの範囲内でスケールをランダムに設定。
  • 石の見た目
    • sphereGeometryを使用して石を球体として表現します。
    • meshStandardMaterialで灰色に設定して、石の質感を再現します。
  • 再描画を防ぐ
    • useStateで初回レンダリング時に石を生成し、固定します。
    • 再描画時にも同じ石の配置を維持することで、パフォーマンスを向上。

PART 6: メインコンポーネント

地面、ランダムな石、クマのアニメーションモデルを統合し、全体のシーンを管理するメインコンポーネントを作成します。加えて、ユーザーがアニメーションの再生・停止を切り替えるための操作UI(ボタン)を実装します。

// ================================
// PART 6: メインコンポーネント
// ================================
const ThreeDModelAnimation: React.FC = () => {
    const [isPlaying, setIsPlaying] = useState(false);

    return (
        <div className="canvas-container">
            <button
                style={{ position: 'absolute', top: '10px', left: '10px', zIndex: 100 }}
                onClick={() => setIsPlaying(!isPlaying)}
            >
                {isPlaying ? 'Pause Animation' : 'Play Animation'}
            </button>
            <Canvas camera={{ position: CAMERA_POSITION.toArray() }}>
                <ambientLight intensity={AMBIENT_LIGHT_INTENSITY} />
                <pointLight position={POINT_LIGHT_POSITION.toArray()} intensity={POINT_LIGHT_INTENSITY} />
                <OrbitControls />

                {/* 地面の表示 */}
                <Ground />

                {/* ランダムな石の配置 */}
                <RandomStones />

                {/* 背景色の設定 */}
                <color attach="background" args={[BACKGROUND_COLOR]} />

                {/* アニメーションモデル */}
                <AnimatedFBXModel path="/models/cuddly_bear/Animation_Walking_withSkin.fbx" isPlaying={isPlaying} />
            </Canvas>
        </div>
    );
};

export default ThreeDModelAnimation;
  • シーンの構築
    • React Three FiberのCanvasコンポーネントを使用して3Dシーン全体を描画します。
    • Canvas内に地面(Ground)、ランダムな石(RandomStones)、アニメーションモデル(AnimatedFBXModel)を配置します。
  • ライトとカメラの設定
    • 環境光(ambientLight)と点光源(pointLight)を追加して、シーン全体を明るくします。
    • DreiライブラリのOrbitControlsを使用してカメラ操作を可能にします。
  • アニメーションの操作UI
    • 再生・停止を切り替えるボタンを作成し、ReactのuseStateフックでisPlaying状態を管理します。
    • ボタンのラベルをisPlayingの状態に応じて動的に変更します。

最後に

「React × Three.jsでクマを歩かせる」プロジェクトの全ステップが完了しました。これで、AIツール(Meshy)で生成した3DモデルをReactとThree.jsで動かす楽しさを体感できるシーンが完成です!

Meshyを利用して、様々な3Dモデルを表示してみてください!

📺 YouTubeでデモを見る:こちらのリンクから視聴できます。

このチュートリアルが役に立ったと感じたら、ぜひYouTubeのチャンネル登録&高評価お願いいたします!

さらに、このプロジェクトのソースコードはGitHubに公開しています。
ぜひコードをダウンロードして試してみてください!

💾 GitHubリポジトリ:こちらのリンクでソースコードをチェック!

参考になった方は、是非チャンネル登録をお願いします!

TypeScript x React Three Fiberのレッスンや作品は、今後もどんどん作成していきます!
YouTubeで告知致しますので、ぜひYouTubeのチャンネル登録をして通知をお待ちください!

📺 YouTubeを見る:こちらのリンクから視聴できます。

TypeScript x React Three Fiber

React Three Fiberで何ができるのか知りたい方は、下記を参考にしてみてください!
簡単にできる作品を用意しております!

よかったらシェアしてね!
  • URLをコピーしました!

この記事を書いた人

情報セキュリティを勉強するために始めたブログです。
新人のため、広い心を持って見ていただけると嬉しく思います。
楽しくプログラミングを勉強するために、「Teech Lab.」もありますので、ソフトウェア開発にも興味があればぜひ覗いて見てください!

目次