生成AI の力を借りつつ超シンプルな「PlayCanvas」での Hello World と関連情報の調査

## はじめに
2024/8/31 にオンラインで開催された以下の合同イベントに参加して、そこで PlayCanvas をコードベースでサクッと試してみたくてやってみた、という内容です。

●Babylon.js × PlayCanvas コミュニティ合同LT会 - connpass
　https://babylonjs.connpass.com/event/325499/

前に少しだけ PlayCanvas を試したことがあり、その時、エディタでなくライブラリを読みこんで試すやり方だった感じがするのですが、その方法でやってみます。

それと、コードの作成には ChatGPT（GPT-4o）と、Claude 3.5 Sonnet を使ってみようと思います。さらに、生成されたコードをベースにして、関連する情報を見ていったりなどしようと思います。

### 余談： エディタの共同編集機能
上記の合同イベント後の懇親会で、エディタの共同編集を試すというに加わったのですが、これも楽しかったです。

https://x.com/youtoy/status/1829767580879348059

ブラウザ上でエディタを開き、それを複数メンバーでリアルタイムに共有して扱えるというのは良い感じでした。

## 試した内容
それでは、元の話に戻ります。

### 生成されたもの
まずは、生成された内容を掲載してみます。

プロンプトは、 ChatGPT（GPT-4o）にも Claude 3.5 Sonnet にも、同じものを使ってやってみます。

用いたプロンプトは適当に作ったやつですが、以下の内容です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/99f829af-0062-9261-0fff-1d20c7892336.png)

```
以下のHTMLを生成して

・PlayCanvasをライブラリとして用い、CDNから読みこむ
・オブジェクトを1つ置いたシーンを作るだけのシンプルなもの
```

#### 描画内容： ChatGPT（GPT-4o）版

https://x.com/youtoy/status/1829776190099575028

#### 描画内容： Claude 3.5 Sonnet 版

https://x.com/youtoy/status/1829782272465387550

### コード
次にコードを見ていきます。

#### コード： ChatGPT（GPT-4o）版

```html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>PlayCanvas Simple Scene</title>
    <script src="https://code.playcanvas.com/playcanvas-latest.min.js"></script>
    <style>
      body,
      html {
        margin: 0;
        padding: 0;
        overflow: hidden;
        height: 100%;
      }
      #application-canvas {
        width: 100%;
        height: 100%;
      }
    </style>
  </head>
  <body>
    <canvas id="application-canvas"></canvas>
    <script>
      // Create a new PlayCanvas application
      const canvas = document.getElementById("application-canvas");
      const app = new pc.Application(canvas, {
        mouse: new pc.Mouse(canvas),
        touch: new pc.TouchDevice(canvas),
      });

      // Set canvas to fill the window and automatically resize
      app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);
      app.setCanvasResolution(pc.RESOLUTION_AUTO);
      window.addEventListener("resize", function () {
        app.resizeCanvas(canvas.width, canvas.height);
      });

      // Start the application
      app.start();

      // Create a new box entity
      const box = new pc.Entity();
      box.addComponent("model", {
        type: "box",
      });

      // Create a new camera entity
      const camera = new pc.Entity();
      camera.addComponent("camera", {
        clearColor: new pc.Color(0.5, 0.5, 0.8),
      });
      camera.translate(0, 0, 3);

      // Create a new directional light entity
      const light = new pc.Entity();
      light.addComponent("light", {
        type: "directional",
        color: new pc.Color(1, 1, 1),
        intensity: 1.5,
      });
      light.setLocalEulerAngles(45, 0, 0);

      // Add the entities to the application's root
      app.root.addChild(box);
      app.root.addChild(camera);
      app.root.addChild(light);

      // Set an update loop
      app.on("update", function (dt) {
        box.rotate(10 * dt, 20 * dt, 30 * dt);
      });
    </script>
  </body>
</html
```

#### コード： Claude 3.5 Sonnet 版

```html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>PlayCanvas Simple Scene</title>
    <style>
      body {
        margin: 0;
        padding: 0;
      }
      canvas {
        width: 100%;
        height: 100%;
      }
    </style>
    <script src="https://code.playcanvas.com/playcanvas-stable.min.js"></script>
  </head>
  <body>
    <canvas id="application"></canvas>
    <script>
      // アプリケーションを作成
      const canvas = document.getElementById("application");
      const app = new pc.Application(canvas);

      // キャンバスのサイズをウィンドウに合わせる
      app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);
      app.setCanvasResolution(pc.RESOLUTION_AUTO);

      // カメラを作成
      const camera = new pc.Entity("camera");
      camera.addComponent("camera", {
        clearColor: new pc.Color(0.1, 0.2, 0.3),
      });
      camera.translate(0, 0, 10);
      app.root.addChild(camera);

      // 光源を作成
      const light = new pc.Entity("light");
      light.addComponent("light");
      light.translate(0, 10, 10);
      app.root.addChild(light);

      // 箱を作成
      const box = new pc.Entity("box");
      box.addComponent("model", {
        type: "box",
      });
      app.root.addChild(box);

      // 箱を回転させるアップデート関数
      app.on("update", (dt) => {
        box.rotate(10 * dt, 20 * dt, 30 * dt);
      });

      // アプリケーションを開始
      app.start();
    </script>
  </body>
</html>
```

### コードの内容を見ていく
上記のコードの内容を、一部ピックアップして見てみようと思います。

#### ライブラリの読み込み
それぞれ、スクリプトタグで読みこんでいるライブラリの URL は以下となるようです。

```html
    <script src="https://code.playcanvas.com/playcanvas-latest.min.js"></script>
```

```html
    <script src="https://code.playcanvas.com/playcanvas-stable.min.js"></script>
```

どちらも、 `code.playcanvas.com` から読みこんでいて、バージョンは異なっているようです（最新版 or 安定版）。

ちなみに、有名どころの CDN から読みこんでも良さそうです。記事執筆時点で、以下のどちらからも 2.0 などのバージョンを読みこめるようです。

●playcanvas - Libraries - cdnjs - The #1 free and open source CDN built to make life easier for developers
　https://cdnjs.com/libraries/playcanvas

●playcanvas CDN by jsDelivr - A CDN for npm and GitHub
　https://www.jsdelivr.com/package/npm/playcanvas

#### Canvas要素への紐づけやループ処理など
Canvas要素への紐づけやループ処理などの処理は、それぞれ以下のような内容でした。

以下、ChatGPT（GPT-4o） ⇒ Claude 3.5 Sonnet の順で掲載しています。

```javascript
      // Create a new PlayCanvas application
      const canvas = document.getElementById("application-canvas");
      const app = new pc.Application(canvas, {
        mouse: new pc.Mouse(canvas),
        touch: new pc.TouchDevice(canvas),
      });

      // Set canvas to fill the window and automatically resize
      app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);
      app.setCanvasResolution(pc.RESOLUTION_AUTO);
      window.addEventListener("resize", function () {
        app.resizeCanvas(canvas.width, canvas.height);
      });

      // Start the application
      app.start();

      。。。

      // Set an update loop
      app.on("update", function (dt) {
        box.rotate(10 * dt, 20 * dt, 30 * dt);
      });
```

```javascript
      // アプリケーションを作成
      const canvas = document.getElementById("application");
      const app = new pc.Application(canvas);

      // キャンバスのサイズをウィンドウに合わせる
      app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);
      app.setCanvasResolution(pc.RESOLUTION_AUTO);

      。。。

      app.on("update", (dt) => {
        box.rotate(10 * dt, 20 * dt, 30 * dt);
      });

      // アプリケーションを開始
      app.start();
```

ChatGPT（GPT-4o）が作成したもののほうは、マウス・タッチ操作関連の処理を入れていたり、またウィンドウのリサイズ時の処理を入れていたりしますが、おおまかな処理の内容は共通しています。


#### オブジェクト・カメラの作成
カメラや光源を準備したり、箱のオブジェクトを準備している部分を見てみます。

以下、ChatGPT（GPT-4o） ⇒ Claude 3.5 Sonnet の順で掲載しています。

```javascript
      // Create a new box entity
      const box = new pc.Entity();
      box.addComponent("model", {
        type: "box",
      });

      // Create a new camera entity
      const camera = new pc.Entity();
      camera.addComponent("camera", {
        clearColor: new pc.Color(0.5, 0.5, 0.8),
      });
      camera.translate(0, 0, 3);

      // Create a new directional light entity
      const light = new pc.Entity();
      light.addComponent("light", {
        type: "directional",
        color: new pc.Color(1, 1, 1),
        intensity: 1.5,
      });
      light.setLocalEulerAngles(45, 0, 0);

      // Add the entities to the application's root
      app.root.addChild(box);
      app.root.addChild(camera);
      app.root.addChild(light);
```

```javascript
      // カメラを作成
      const camera = new pc.Entity("camera");
      camera.addComponent("camera", {
        clearColor: new pc.Color(0.1, 0.2, 0.3),
      });
      camera.translate(0, 0, 10);
      app.root.addChild(camera);

      // 光源を作成
      const light = new pc.Entity("light");
      light.addComponent("light");
      light.translate(0, 10, 10);
      app.root.addChild(light);

      // 箱を作成
      const box = new pc.Entity("box");
      box.addComponent("model", {
        type: "box",
      });
      app.root.addChild(box);
```

箱のオブジェクトを用意している部分は同じような処理です。

カメラの部分は、用意している時のパラメータは異なるものの、大まかな処理は共通しています。

以下に 2つのそれぞれのコードの、カメラ関連の処理を掲載してみます。

```javascript
      // ※ ChatGPT（GPT-4o）版
      // Create a new camera entity
      const camera = new pc.Entity();
      camera.addComponent("camera", {
        clearColor: new pc.Color(0.5, 0.5, 0.8),
      });
      camera.translate(0, 0, 3);

      // ※ Claude 3.5 Sonnet 版
      // カメラを作成
      const camera = new pc.Entity("camera");
      camera.addComponent("camera", {
        clearColor: new pc.Color(0.1, 0.2, 0.3),
      });
      camera.translate(0, 0, 10);
      app.root.addChild(camera);
```

`clearColor: new pc.Color()` は背景色に関わる部分になるようで、以下のような色を設定しているようです。

- ChatGPT（GPT-4o）： 薄い紫
- Claude 3.5 Sonnet：  暗い青緑

ちなみに、以下のような意味となるようです。

●カメラの基本操作 | PlayCanvas Developer Site
　https://developer.playcanvas.com/ja/tutorials/basic-cameras/
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/da11a1e7-753d-cd78-eb69-758246cba4ad.png)

あと、カメラを移動させているパラメータもちょっと違っています。

調べるまでもないかと思われる内容（3つ目は XYZ の Z に関するパラメータと思われる）ではありましたが、公式の APIリファレンスを見てみる練習的な意味で、関連する項目を探して見てみました。

カメラ関連の部分ではなく、エンティティに関する部分で以下の記載がありました。

●Entity | PlayCanvas API Reference
　https://api.playcanvas.com/classes/Engine.Entity.html#translate

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/a1eabbae-33b4-daae-6fb1-665e4b699f90.png)

#### 光源の作成
光源に関連した処理は以下のとおりです。

```javascript
      // ※ ChatGPT（GPT-4o）版
      // Create a new directional light entity
      const light = new pc.Entity();
      light.addComponent("light", {
        type: "directional",
        color: new pc.Color(1, 1, 1),
        intensity: 1.5,
      });
      light.setLocalEulerAngles(45, 0, 0);

      // ※ Claude 3.5 Sonnet 版
      // 光源を作成
      const light = new pc.Entity("light");
      light.addComponent("light");
      light.translate(0, 10, 10);
      app.root.addChild(light);
```

Claude 3.5 Sonnet 版では light の type が未指定ですが、以下の APIリファレンスの記載を見ると、デフォルトで `type: "directional"` （ChatGPT（GPT-4o）版と同じ設定）が用いられるようです。

●LightComponent | PlayCanvas API Reference
　https://api.playcanvas.com/classes/Engine.LightComponent.html#type

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/a12b0fba-84bf-2419-f7c2-32e6c7feffc0.png)

その他、設定関連は以下となっています。

- ChatGPT（GPT-4o）版
   - color: new pc.Color(1, 1, 1)
   - intensity: 1.5
   - light.setLocalEulerAngles(45, 0, 0);
   - light.translate(): 未指定
-  Claude 3.5 Sonnet 版
   - color: 未指定
   - intensity: 未指定
   - light.setLocalEulerAngles(): 未指定
   - light.translate(0, 10, 10);

上記の差分を確認してみます。

APIリファレンスを確認したところ、color はデフォルトが(1, 1, 1)になるようなので、上記の 2つで違いはなさそうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/dd8613cf-de88-4567-d140-13ee67329416.png)

一方、intensity のデフォルトのは 1 のようなので、上記の 2つは intensity には違いがあるようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/1abccd75-da00-d94d-4f9e-33fea8946420.png)

light.setLocalEulerAngles() と light.translate() は、おそらくはそれぞれ特定の値を設定した分の違いが生じる、という感じになると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/2cf70f9a-cfd3-3f7a-7dcb-d60e058f077e.png)

今回は、生成AI にシンプルなコードを生成してもらいつつ、そこで出てきた内容を公式情報で確認してみるということをやってみました。

今後は、エディタを使ってみたり、公式／非公式のサンプルを見てみたりなど、さらに PlayCanvas に触れていければと思います。