React Nativeで3D表示してみよう

<h1>（１）React Native(Expo)で3D表示</h1>

<p>普段はソーシャルワーカーとして障がいをお持ちの方の支援をしています。ReactやReact Nativeでアプリ作成を楽しんでおり、最近は3D表示に関心がありThree.jsなどを使っています。ただ、バージョン違いで動作しない、ライブラリが更新されていないなどReact Nativeでの3D表示の情報はまだ少ないため、簡単なコードを書いておこうと思います。</p>



<h1>（２）一人称視点で3D空間を前後左右に動く</h1>

<p>今回のコードはただ一人称視点で動くだけなのですが、Three.jsは様々な機能があり本格的な3Dアプリの作成もできそうです。GIFが少しカクついているのはタップ連打で移動しているからです💦</p>

![hocIof9TNq7pkVk4y2bc1638630612-1638630637.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635004/f6adb915-f7b2-2702-46aa-bb73da858654.gif)

<a href="https://threejs.org/">Three.js公式</a><br>
<a href="https://docs.expo.dev/versions/latest/sdk/gl-view/">Expo公式 GLView</a><br>

<h1>（３）使った技術</h1>

<p>
・react-native<br>
・expo 42.0.1<br>
・expo-gl 10.4.2<br>
・expo-three 6.0.1<br>
・gsap 3.6.0<br>
・three 0.134.0

<h1>（４）コード</h1>
<p>読み込みまーす</p>
```ts
import React from "react";
import { View, Pressable, Text } from "react-native";
import { GLView } from "expo-gl";
import { Renderer } from "expo-three";
import { TweenMax } from "gsap";
import {
  PointLight,
  GridHelper,
  Mesh,
  PerspectiveCamera,
  Scene,
  BoxGeometry,
  MeshLambertMaterial,
} from "three";
```
可能な限りコメントしました

```ts

export default function App() {
  const camera = new PerspectiveCamera(30, 1, 1, 100); // カメラが映し出す設定(視野角, アスペクト比, near, far)

  // カメラの初期座標
  let cameraInitialPositionX = 0;
  let cameraInitialPositionY = 2;
  let cameraInitialPositionZ = 7;

  // TweenMax.to(何が, 何秒で, { z軸に distance 分移動 })
  const moveUd = (distance) => {
    TweenMax.to(camera.position, 0.2, {
      z: camera.position.z + distance,
    });
  };

  //TweenMax.to(何が, 何秒で, { x軸に distance 分移動 })
  const moveLr = (distance) => {
    TweenMax.to(camera.position, 0.2, {
      x: camera.position.x + distance,
    });
  };

  return (
    <View style={{ flex: 1 }}>
      <GLView
        style={{ flex: 1 }}
        onContextCreate={async (gl) => {
          // 3D空間の準備
          const { drawingBufferWidth: width, drawingBufferHeight: height } = gl;
          const renderer = new Renderer({ gl }); // レンダラーの準備
          renderer.setSize(width, height); // 3D空間の幅と高さ
          renderer.setClearColor("white"); // 3D空間の配色
          const scene = new Scene(); // これが3D空間
          scene.add(new GridHelper(10, 10)); //グリッドを表示

          // 配置するオブジェクト
          const geometry = new BoxGeometry(2, 2, 2); // 四角い物体
          const material = new MeshLambertMaterial({ color: "blue" }); // 物体に光を反射させ色や影を表現する
          cube = new Mesh(geometry, material); // geometryとmaterialでオブジェクト完成
          cube.position.set(0, 0, 0); // 配置される座標 (x,y,z)
          scene.add(cube); // 3D空間に追加

          // 3D空間の光！
          const pointLight = new PointLight(0xffffff, 2, 1000, 1); //一点からあらゆる方向への光源(色, 光の強さ, 距離, 光の減衰率)
          pointLight.position.set(0, 200, 200); //配置される座標 (x,y,z)
          scene.add(pointLight); //3D空間に追加

          // カメラの座標　＝　一人称視点
          camera.position.set(
            cameraInitialPositionX,
            cameraInitialPositionY,
            cameraInitialPositionZ
          );
   
          const render = () => {
            requestAnimationFrame(render); // アニメーション　moveUd関数、moveLr関数でカメラ座標が移動
            renderer.render(scene, camera); // レンダリング
            gl.endFrameEXP(); // 現在のフレームを表示する準備ができていることをコンテキストに通知するpresent (Expo公式)
          };
          render();
        }}
      />
```

画面表示部分です　Pressable タグを使いこなせると幅が広がりそう

```ts
      <View>
        <Pressable onPress={() => moveUd(-0.2)}>
          <Text
            style={{
              fontSize: 36,
              color: "red",
              textAlign: "center",
            }}
          >
            前へ
          </Text>
        </Pressable>
        <Pressable onPress={() => moveUd(0.2)}>
          <Text
            style={{
              fontSize: 36,
              color: "red",
              textAlign: "center",
            }}
          >
            後へ
          </Text>
        </Pressable>
        <Pressable onPress={() => moveLr(-0.2)}>
          <Text
            style={{
              fontSize: 36,
              color: "red",
              textAlign: "center",
            }}
          >
            左へ
          </Text>
        </Pressable>
        <Pressable onPress={() => moveLr(0.2)}>
          <Text
            style={{
              fontSize: 36,
              color: "red",
              textAlign: "center",
            }}
          >
            右へ
          </Text>
        </Pressable>
      </View>
   </View>
  );
}
```

<h1>（５）終わりに</h1>

<p>Advent Calendarに初めて参加してみました！モバイルカテゴリーだとFlutterが人気ですねー:blush:。EASの無料化でより使いやすくなったExpo、来年はReact Nativeも盛り上がりそうですね:raised_hands:</p>