Ccmmutty logo
Commutty IT
8 min read

React + typescript + threejsでアバター表示してみた

https://cdn.magicode.io/media/notebox/ad296b9c-4c6a-4167-b1b6-40dbcff4832f.jpeg

初めに

いつもUnityで3Dプログラムを書いているけど、UI作ったりめんどくさいし、吉良区間がないなと思い、js環境で動かせるか試してみた。

環境

React, TypeScript, Three.jsの組み合わせを利用
  • Reactは人気のあるJavaScriptライブラリで、UIの再利用や保守が容易になる。
  • TypeScriptはJavaScriptのスーパーセットで、型付けやオブジェクト指向プログラミングが利用できる。
  • Three.jsはWebGLを使いやすくするライブラリで、3Dオブジェクトやアニメーションなど、3Dコンテンツを簡単に作成できる。

この記事で学べること

  • React, TypeScript, Three.jsを使った環境構築方法を解説する
  • Mixamoから3Dアバターとアニメーションをダウンロードして、Webアプリで表示・再生する方法も一緒に説明する

環境構築

必要なツールのインストール

  1. Node.jsのインストール
  2. Visual Studio Codeのインストール

Reactプロジェクトの作成

  1. create-react-appでプロジェクトを作成
    • npx create-react-app my-avatar-app --template typescript コマンドでReactプロジェクトを作成
  2. プロジェクトフォルダに移動
    • cd my-avatar-appでプロジェクトフォルダに移動

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

  1. Three.js, react-three-fiber, @react-three/dreiをインストール
    • npm install three react-three-fiber @react-three/drei three-stdlib コマンドで必要なライブラリを一気にインストールする プロジェクトの起動
  2. npm start コマンドでプロジェクトを起動し、ブラウザで表示されることを確認する

Mixamoからアバターをダウンロード

  1. Mixamoにアクセス
    • Mixamoにアクセスして、Adobeアカウントでログインします。
  2. アバターを選択
    • Charactersタブを選択し、好みのアバターを選択
  3. アニメーションを選択
    • アバターが選択できたら、Animationsタブからアニメーションを選ぶ。今回は「Idle」で検索して選択した。
  4. ダウンロードオプション
    • ダウンロードオプションで、FBXファイルを選択し、バイナリ形式でダウンロードします。
    • ダウンロードしたファイルをプロジェクトに追加
      • ダウンロードしたFBXファイルをプロジェクトのpublic/assetsフォルダに追加します。もしassetsフォルダがなければ作成する。
my-avatar-app
├─ public
│   ├─ assets
│   │   ├─ idle.fbx
│   │   └─ ...
│   └─ ...
└─ src
    └─ ...

アバターを表示しアニメーションを再生する

以下に概要を示す。 コード全体は後程記載する。

Avatarコンポーネントを作成

srcフォルダ内にAvatar.tsxという名前で新しいファイルを作成し、アバターの表示とアニメーション再生に関するコードを追加。

アバターの表示

useLoaderフックを使ってfbx をロードする。
const fbx = useLoader(FBXLoader, process.env.PUBLIC_URL + "/assets/idle.fbx");

アバターのアニメーション再生

下記により実現できる。
  1. AnimationMixierの設定をする。
    • 今回はfbxに入っているAnimationClipの先頭を再生している
  2. 毎フレームで実行する関数updateを作り、updateの中ではAnimationMixer.update()を呼び出す
  3. useFrameフックを使って、毎フレームupdate()を呼び出すようにする
function Avatar() {
    const fbx = useLoader(FBXLoader, process.env.PUBLIC_URL + "/assets/idle.fbx");
    const mixer = useRef<AnimationMixer>();
  
    // アニメーションを適用
    if (fbx.animations.length > 0 && !mixer.current) {
      mixer.current = new AnimationMixer(fbx);
      const action = mixer.current.clipAction(fbx.animations[0]);
      action.play();
    }

    // アニメーションの制御を行うupdate関数
    const update = (delta: number) => {
      if (mixer.current) {
        mixer.current.update(delta);
      }
    };
  
    // useFrameフックを使用して、update関数を毎フレーム実行
    useFrame((_, delta) => {
      update(delta);
    });

  return <primitive object={fbx} />;
}

Appコンポーネントの修正

react three fiberのおまじない

Canvasコンポーネントを使ってthree js表示できるようにし、 Canvasコンポーネントの中に先ほど作成したAvatarコンポーネントを配置する。
<Canvas style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%" }}>
      <Suspense fallback={null}>
        <Avatar />
      </Suspense>
</Canvas>

カメラとライティングを設定

Avatarを配置しただけだとレンダリングされないので、カメラとライティングを設定する。
位置の調整のためcssでスタイリングもしている。
<Canvas style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%" }}>
      <Suspense fallback={null}>
        <Avatar />
      </Suspense>
      <PerspectiveCamera makeDefault position={[0, 150, 300]} />
      <OrbitControls target={[0, 50, 0]} />
      <ambientLight intensity={0.5} />
      <directionalLight color="white" intensity={0.6} position={[0, 50, 50]} />
    </Canvas>
コード全体
Avartar.tsx
import { useRef } from "react";
import { FBXLoader } from "three-stdlib";
import { useLoader, useFrame } from "@react-three/fiber";
import { AnimationMixer } from "three";

function Avatar() {
    const fbx = useLoader(FBXLoader, process.env.PUBLIC_URL + "/assets/idle.fbx");
    const mixer = useRef<AnimationMixer>();

    // アニメーションを適用
    if (fbx.animations.length > 0 && !mixer.current) {
      mixer.current = new AnimationMixer(fbx);
      const action = mixer.current.clipAction(fbx.animations[0]);
      action.play();
    }
  
    // アニメーションの制御を行うupdate関数
    const update = (delta: number) => {
      if (mixer.current) {
        mixer.current.update(delta);
      }
    };
  
    // useFrameフックを使用して、update関数を毎フレーム実行
    useFrame((_, delta) => {
      update(delta);
    });

  return <primitive object={fbx} />;
}

export default Avatar;
App.tsx
import React, { Suspense } from "react";
import { Canvas } from "@react-three/fiber";
import { PerspectiveCamera, OrbitControls } from "@react-three/drei";
import Avatar from "./Avatar";

const App = () => {
  return (
    <Canvas style={{ position: "absolute", top: 0, left: 0, width: "100%", height: "100%" }}>
      <Suspense fallback={null}>
        <Avatar />
      </Suspense>
      <PerspectiveCamera makeDefault position={[0, 150, 300]} />
      <OrbitControls target={[0, 50, 0]} />
      <ambientLight intensity={0.5} />
      <directionalLight color="white" intensity={0.6} position={[0, 50, 50]} />
    </Canvas>
  );
};

export default App;
App.css
.App {
  text-align: center;
}

.App-logo {
  height: 40vmin;
  pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
  .App-logo {
    animation: App-logo-spin infinite 20s linear;
  }
}

.App-header {
  background-color: #282c34;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
  color: white;
}

.App-link {
  color: #61dafb;
}

@keyframes App-logo-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

body {
  margin: 0;
  overflow: hidden;
}

#root,
canvas {
  display: block;
  width: 100%;
  height: 100%;
}

終わりに

ReactとTypeScriptを使って3Dアバターを表示し、アニメーションを再生する方法をまとめた。 Unityとかを使うとGameEngineとしてリッチな機能を使える半面、Ui作りにくかったりお手軽間なかったりするので、ちょっとしたものはjsで作れるといいなと思いました。

Discussion

コメントにはログインが必要です。