Unityで作ったアプリをiPhoneで動かしたい

## 3Dに挑戦

今日はUnityにチャレンジします。

3Dで動く簡単なゲームを作ってみました。
迷路を作って、その中を球が動いていくゲームです。

こんな感じです。全然動いてないですが。

https://x.com/tadokuno/status/1815398579768578453

ここまで作ってはみたのですが、ここからバグが取れないし、iPhoneで動くようにWebGLを使ってみましたが、結局そちらも動いていません。

残念ながらここまででタイムアップなので、いったんレポートします。

## まずは企画
- iPhoneのジャイロでUnityの座標を動かして、迷路を球を転がしてゴールするゲームを作る。
- iPhoneからGitHub PagesでホスティングしたWebGLアプリを動かす。

ということで、Webアプリとして作って、スマートフォンのセンサーを使う形を考えた。

## とりあえず、PCで動かしてみる。

Unityの構造としては、

- Scene
構成要素が見える形で表示されている。ここには、Hierarchyで表示されているオブジェクトが表示される。
- Hierarchy
Hierarchyで作ったオブジェクトは空間上に存在しており、それを前提に他のプログラムを作成する。
- Project
Projectにもさまざまなオブジェクトを作ることができる。ただし、ここにあるオブジェクトはプログラムにより利用して初めて実体化される。
- Inspector
ここには、選択したオブジェクトの属性値や、設定したコンポーネントが表示される。コンポーネントとは、オブジェクトに設定できる様々な要素をいう。例えば色や重力の働き、イベントを処理するプログラムなどもここに設定できる。

プログラムは、Project内のAssetsとして定義される。そして、それらのコードはオブジェクトのコンポーネントとして設定し、処理されるイベントが決まってくる。

## 迷路を作る

Unity Hubを起動して、新しいプロジェクトを作成する。テンプレートは3Dを使う。

Hierarchyに、迷路の床となるPlaneと、壁を生成するためのMazeGeneratorを追加する。

Project内に、Assetsとして、壁の構成要素となるCubeと、迷路を移動するPlayerとしての球Sphereを作成する。

オブジェクトのイベント処理を行うプログラムは、
CameraController.cs
MazeGenerator.cs
PlaneController.cs

を作り、それぞれ
Main Camera
MazeGenerator
Plane

にコンポーネントとして割り当てる。

とりあえず、PCで動かすように、キーボードイベントをPlaneController.csで受けて、それに応じてPlaneを回転させる処理を入れた。

CameraController.cs
```cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraController : MonoBehaviour
{
    public float zoomSpeed = 10.0f;
    public float minZoom = 5.0f;
    public float maxZoom = 30.0f;

    private Transform cameraTransform;

    void Start()
    {
        cameraTransform = Camera.main.transform;
    }

    void Update()
    {
        if (Input.GetKey(KeyCode.PageUp))
        {
            cameraTransform.position += cameraTransform.forward * zoomSpeed * Time.deltaTime;
            cameraTransform.position = new Vector3(
                cameraTransform.position.x, 
                Mathf.Clamp(cameraTransform.position.y, minZoom, maxZoom), 
                cameraTransform.position.z);
        }

        if (Input.GetKey(KeyCode.PageDown))
        {
            cameraTransform.position -= cameraTransform.forward * zoomSpeed * Time.deltaTime;
            cameraTransform.position = new Vector3(
                cameraTransform.position.x, 
                Mathf.Clamp(cameraTransform.position.y, minZoom, maxZoom), 
                cameraTransform.position.z);
        }
    }
}

```
MazeGenerator.cs
```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MazeGenerator : MonoBehaviour
{
    public GameObject wallPrefab;
    public GameObject playerPrefab;
    public Vector3 startPosition = new Vector3(1, 0.5f, 1);
    public Vector3 goalPosition = new Vector3(18, 0.5f, 18);
    public int width = 20;
    public int height = 20;
    public float wallHeight = 2.0f;

    private GameObject playerInstance;

    void Start()
    {
        Debug.Log("MazeGenerator Start");
        GenerateMaze();
        CreatePlayer();
    }

    void GenerateMaze()
    {
        Debug.Log("Generating Maze");
        // 周囲の壁を作成
        for (int x = 0; x < width; x++)
        {
            for (int z = 0; z < height; z++)
            {
                if (x == 0 || z == 0 || x == width - 1 || z == height - 1)
                {
                    CreateWall(x, z);
                }
            }
        }

        // ランダムな壁を生成
        for (int i = 0; i < (width * height) / 4; i++)
        {
            int x = Random.Range(1, width - 1);
            int z = Random.Range(1, height - 1);
            if ((x != startPosition.x || z != startPosition.z) && (x != goalPosition.x || z != goalPosition.z))
            {
                CreateWall(x, z);
            }
        }
    }

    void CreateWall(int x, int z)
    {
        GameObject plane = GameObject.Find("Plane");
        if (plane != null)
        {
            Vector3 position = new Vector3(x - width / 2, wallHeight / 2, z - height / 2);
            GameObject wall = Instantiate(wallPrefab, position, Quaternion.identity);
            wall.transform.parent = plane.transform; // Planeの子オブジェクトにする
        }
    }

    void CreatePlayer()
    {
        if (playerInstance == null)
        {
            playerInstance = Instantiate(playerPrefab, new Vector3(startPosition.x - width / 2, startPosition.y, startPosition.z - height / 2), Quaternion.identity);
            playerInstance.tag = "Player";
            Debug.Log("Player Created: " + playerInstance.name);
        }
    }
}
```
PlaneController.cs
```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlaneController : MonoBehaviour
{
    public float rotationSpeed = 100.0f;
    
    void rotateByKey() {
        float rotationX = Input.GetAxis("Vertical") * rotationSpeed * Time.deltaTime;
        float rotationZ = -Input.GetAxis("Horizontal") * rotationSpeed * Time.deltaTime;

        transform.Rotate(rotationX, 0, rotationZ, Space.Self);
    }

    void rotate() {
	rotationSpeed = 1.0f;
	float rotationX = Input.GetAxis("RotationX");
        float rotationZ = Input.GetAxis("RotationZ");

        // Planeの回転を更新
        transform.Rotate(rotationX * rotationSpeed, 0, rotationZ * rotationSpeed, Space.Self);
    }

    void Update()
    {
	rotateByKey();
	//rotate();
    }
    void FixedUpdate()
    {
        // Planeの回転を更新
//        transform.Rotate(rotationX * rotationSpeed * Time.fixedDeltaTime, 0, rotationZ * rotationSpeed * Time.fixedDeltaTime, Space.Self);
	rotateByKey();
    }
    
    // JavaScriptから呼び出すためのメソッドを用意する
    public void UpdateRotation(float rotationX, float rotationZ)
    {
        transform.Rotate(rotationX * rotationSpeed, 0, rotationZ * rotationSpeed, Space.Self);
    }

}
```

## iPhoneで動かす。
PlaneController.cs　の中に、rotate()という関数を用意して、Update()の際にこれを呼び出すようにすると、iPhoneのセンサーからの入力に応じて動かせるようにした。
さらに、index.htmlの中で、センサー入力を受けてイベントにその情報を渡すような処理をJavaScriptで入れることで、iPhoneでの操作は実現できる。

## WebGLでの実行環境を公開する。

Buildする際に、WebGLを使うことで、Webサイトにアップできる形で出力できる。
それを、GitHubでリポジトリに入れて、GitHub Pagesで公開すれば、iPhoneからSafariなどを使ってアクセスできるようになる。

## 動作せず断念

ここまで進めてきたが、iPhoneで動かせる所に至っていない。また、球が床をすり抜けて落ちてしまうというバグもあり、今回の製作はここで一旦終了とする。

以上