# ミニゲーム①：Egg Race!

スマホの傾きセンサーとタッチで３Dパズルゲームを作ってみましょう。

# プロジェクト仕組みの理解

## ゲームの目的

スマホを傾いて、卵を転げて、巣まで届かせましょう！ただ、強くぶつかると、卵が割れてしまうので、優しくね！生まれたひよこちゃんが邪魔するので、ドアをうまく使って、移動制限させましょう！

## ゲームシーン

Scenesフォルダーの中にある「EggRace」を開いてください

![image.png](https://class.illogic.games/uploads/images/gallery/2026-07/scaled-1680-/6ANimage.png)

ヒエラルキーを見ると：

- Main Camera：ゲームのカメラで、上から真下見るようにしちます：

![image.png](https://class.illogic.games/uploads/images/gallery/2026-07/scaled-1680-/OCuimage.png)

- Directional Light：光源
- Black：見せたくないステージの一部を黒いバーで隠すためのオブジェクト
- Stage：ステージ。いくつかのパーツで作られている： 
    - Static：動かないもの - このオブジェクトがナビゲーションメッシュが付いている 
        - Floor：床面
        - Walls：外側の４つの壁
        - Maze：内側の迷路の壁
        - Roof：屋根（コライダーのみ）

![image.png](https://class.illogic.games/uploads/images/gallery/2026-07/scaled-1680-/jnAimage.png)

- - Goal：迷路のゴール - トリガー付き
    - Doors：スライドできるドア 
        - Horizontal：水平に移動できるドア
        - Vertical：垂直に移動できるドア

## プロジェクト整理

すでにいくつかのフォルダが準備された：

- Animations：ひょこのアニメーションコントローラー
- Materials：フェンス、ドア、ひょこなどのマテリアル
- Models：3Dモデル
- Prefab：コライダーなどが付いているプレハブ。スクリプトなし
- Script：スクリプト。カメラの高さを調整するスクリプトが準備された
- Textures：3Dモデルのテクスチャ
- UI：UI作成に必要なスプライト

## 初期設定

Androidスマホで実行できるため、プロジェクト設定でプラットホームを変えないといけない。「File ⇒ Build Profiles」を開き、「Windows」から「Android」へ変換し、「Switch Platform」を押しててください。

![image.png](https://class.illogic.games/uploads/images/gallery/2026-07/scaled-1680-/ArDimage.png)

# 卵の操作

これからスクリプトを作成し、卵の動き処理を実装しましょう。

## シーン設定

迷路の左上に Prefabs の中にある Egg を入れてください：

![image.png](https://class.illogic.games/uploads/images/gallery/2026-07/scaled-1680-/PaEimage.png)

## キーボードによる卵の動き

スマホで遊ぶ前に、まずUnityで動作を確認したいので、キーボードを使い、重力の向きを変えて、卵が転がるようにしましょう。

![image.png](https://class.illogic.games/uploads/images/gallery/2026-07/scaled-1680-/hNMimage.png)

重力を変えるには２つの方法がある：

#### プロジェクト設定で重力を変える

1回だけ重力を変え、後がずっと変わらないなら、プロジェクト設定で重力ベクトルを変えることができる。「Edit ⇒ Project Settings」を開き「Physics⇒Settings」（物理設定）を選ぶと

![image.png](https://class.illogic.games/uploads/images/gallery/2026-07/scaled-1680-/w6yimage.png)

「Gravity」（重力）という設定がある。デフォルト値としては、地球の重力になっている（真下のベクトルで、加速度は9.81）。今回は実行中に重力を変えるので、次の方法で変えましょう：

#### スクリプトで重力を変える

スクリプトでは、「`Physics.gravity`」の値を代入すれば、重力をいつでも帰ることができる。例えば：

```c#
// 地球の重力
Physics.gravity = new Vector3(0, -9.81f, 0);

// 月のの重力
Physics.gravity = new Vector3(0, -1.625f, 0);

// 木星の重力
Physics.gravity = new Vector3(0, -24.5f, 0);
```

### スクリプト

キーボードの WASD で重力ベクトルの向きを変えましょう W と S で Z軸の前後にし、A と D でＸ軸の左右にしましょう。

```c#
// 実行中に重力を変える
public class GravityController : MonoBehaviour
{
	// 重力の加速度（地球）
	const float Gravity = 9.81f;

	// 重力に与える倍率（強さの調整）
	[SerializeField] 
	private float gravityScale = 1.0f;

	private void Update()
	{
		// 重力のベクトル（Yはいつも下：-1）
		Vector3 vector = new Vector3(0, -1, 0);
		
		// WASDの入力（ -1 ～ 1 ）
		vector.x = Input.GetAxis("Horizontal"); // 左右（x軸）
		vector.z = Input.GetAxis("Vertical"); // 前後 (z軸）
	
		// 単位ベクトルにする
		vector.Normalize();
		
		// 最後に、重力の向きを変える
		Physics.gravity = vector * (Gravity * gravityScale);
	}
}
```

スクリプトを空のゲームオブジェクトに追加し、操作を確認してください：

![image.png](https://class.illogic.games/uploads/images/gallery/2026-07/scaled-1680-/GKiimage.png)

## 加速度センサーによる卵の動き

加速度センサー（Accelerometer）はスマホの加速と減速を測定し、3Dベクトル（向きと強さ）として返す装置です。重力は地球の方向へ引っ張る力であり、真下の加速ベクトルととして現れる。

つまり、加速度センサーを使うことにより、重力ベクトルを測定できます。なお、スマホの加速度センサーの座標系は以下の通りです：

![image.png](https://class.illogic.games/uploads/images/gallery/2026-07/scaled-1680-/RL1image.png)

ただし、今回のゲームでは、カメラで見た座標系は：

![image.png](https://class.illogic.games/uploads/images/gallery/2026-07/scaled-1680-/dB7image.png)

なので、加速度センサーの ＺとＹ軸を交換しないといけない。

#### 加速度センサーを有効化

加速度センサーを使うために、まず有効化しないといけない。Unityは次の命令がある：

```c#
// 加速度センサーを使えるか？
if (Accelerometer.current != null)
{
    // 有効化する
    InputSystem.EnableDevice(Accelerometer.current);
}
```

#### 加速度センサーの値を読み込む

そして、実行中に、以下のように加速度センサーのベクトルを読む：

```c#
// 加速度センサーを使えるか？
if (Accelerometer.current != null)
{
	// 加速度センサーの向きを取得
	Vector3 acc = Accelerometer.current.acceleration.ReadValue();
}
```

### スクリプト

GravityController を更新し、スマホの加速度センサーを使える場合は、実際の重力データを利用しましょう：

```c#
// 実行中に重力を変える
public class GravityController : MonoBehaviour
{
	// 重力の加速度（地球）
	const float Gravity = 9.81f;

	// 重力に与える倍率（強さの調整）
	[SerializeField] 
	private float gravityScale = 1.0f;

	private void Start()
	{
		// 加速度センサーを使えるか？ → 有効化
		if (Accelerometer.current != null)
			InputSystem.EnableDevice(Accelerometer.current);
	}
	
	private void Update()
	{
		// 重力のベクトル（Yはいつも下：-1）
		Vector3 vector = new Vector3(0, -1, 0);
		
		// 加速度センサーを使えるか？
		if (Accelerometer.current != null)
		{
			// 加速度センサーの向きを取得
			Vector3 acc = Accelerometer.current.acceleration.ReadValue();

			// センサーの軸をゲームの軸へ変換
			vector.x = acc.x;  // X はそのまま
			vector.z = acc.y;  // ゲームの Z は、加速度センサーの Y である
 		}
		else
		{
			// WASDの入力
			vector.x = Input.GetAxis("Horizontal"); // 左右（x軸）
			vector.z = Input.GetAxis("Vertical"); // 前後 (z軸）
		}
	
		// 単位ベクトルにする
		vector.Normalize();
		
		// 最後に、重力の向きを変える
		Physics.gravity = vector * (Gravity * gravityScale);
	}
}
```

## 実機で確認

スマホとPCをUSBケーブルで接続し、Build Profiles を開いてください。「Run Device」で端末を選び、「Build And Run」でビルドし、実行を確認してください。

![image.png](https://class.illogic.games/uploads/images/gallery/2026-07/scaled-1680-/BQnimage.png)

# 扉の操作

画面のタッチ機能を使い、指のスライドで、扉を動かしてみましょう。

## タッチ機能

加速度センサーと同様に、タッチ機能を使う前には、機能の有効化をしないといけない。Unityでは：

```c#
// タッチ機能を有効化
EnhancedTouchSupport.Enable();
```

その次に、タッチのイベントに登録しなければならない：

```c#
// 画面を触れ始めたとき
Touch.onFingerDown += 処理メソッド;

// 指は移動中のとき
Touch.onFingerMove += 処理メソッド;

// 指が画面から放したとき
Touch.onFingerUp += 処理メソッド;
```

また、処理するメソッドは、「Finger」という引数を受け取り、指の画面の位置などが渡される。例：

```c#
private void Start()
{
	// タッチ機能を有効化
	EnhancedTouchSupport.Enable();

	// イベントに登録
	Touch.onFingerDown += OnFingerDown;
}

// 画面をタッチし始めた
private void OnFingerDown(Finger finger)
{
	// 指の画面の位置を表示
	Debug.Log(finger.screenPosition);
}
```

## 水平扉をスライド

扉を指でスライドするのを次の考えで実現できる：

![image.png](https://class.illogic.games/uploads/images/gallery/2026-07/scaled-1680-/LPDimage.png)

初めて画面を触れるとき、指データを保管しておく。Unityがマルチタッチ（多数の指）に対応できるので、同じ指かどうかの確認が必要。そして、指が動くときに、移動量を計算し、その分を使って扉をスライドする。最後に、指を離したら、保管したデータを解放し、処理終了にする。

### スクリプト

DoorSlider のスクリプトを作成し、まず水平（Horizontal）の扉を移動しましょう：

```c#
using UnityEngine.InputSystem.EnhancedTouch;
using Touch = UnityEngine.InputSystem.EnhancedTouch.Touch; // << ここ注意！

// 扉を移動（スライド）する
public class DoorSlider : MonoBehaviour
{
    // 水平の扉のグループ（親オブジェクト）
    [SerializeField] private Transform horizontalDoors;

    // 使った指
    private Finger useFinger = null;

    // 指の初期位置
    private Vector2 fingerStart;
    
    // 水平扉の初期位置（X軸のみ）
    private float doorStartX;
    
    private void Start()
    {
        // タッチ機能有効化
        EnhancedTouchSupport.Enable();

        // 各種のタッチイベント登録
        Touch.onFingerDown += OnFingerDown;
        Touch.onFingerMove += OnFingerMove;
        Touch.onFingerUp += OnFingerUp;
    }

    private void OnDestroy()
    {
        // 各種のタッチイベント登録解除
        Touch.onFingerDown -= OnFingerDown;
        Touch.onFingerMove -= OnFingerMove;
        Touch.onFingerUp -= OnFingerUp;
        
        // タッチ機能無効化
        EnhancedTouchSupport.Disable();
    }

    private void OnFingerDown(Finger finger)
    {
        // マルチタッチできるため、すでに指一本を使っていたら、2本目から無視する
        if (useFinger != null) return;

        // 指を保管
        useFinger = finger;
        
        // 初期位置も取っておく
        fingerStart = finger.screenPosition;
        doorStartX = horizontalDoors.position.x;
    }

    private void OnFingerMove(Finger finger)
    {
        // マルチタッチできるので、同じ指かどうかを確認
        if (useFinger != finger) return;
        
        // 指の移動量を計算（画面のピクセル単位）
        Vector2 delta = useFinger.screenPosition - fingerStart;
        
        // 様々な画面サイズがあるので、移動量正規化しましょう（最大値：１）
        delta.x /= Screen.width;
        delta.y /= Screen.height;
        
        // 現在の位置からを取得し、X軸だけを更新
        Vector3 hPos = horizontalDoors.position;
        hPos.x = doorStartX + delta.x;
        horizontalDoors.position = hPos;
    }
    
    private void OnFingerUp(Finger finger)
    {
        // 指を解放する
        if (useFinger == finger)
            useFinger = null;
    }
}
```

#### 正規化に関して

いろんなスマホがあり、それぞれの画面サイズが異なるので、同じ指の動きでも大幅に異なる移動量が発生する可能性がある。例えば：

![image.png](https://class.illogic.games/uploads/images/gallery/2026-07/scaled-1680-/mwvimage.png)

スワイプの長さを画面サイズ（`Screen.width` と `Screen.height`）で割れば、必ず 0 と 1の間になり、どの画面サイズであっても、同じ数値になる。この計算が「正規化」という。

### Unityで確認

スクリプトを「Stage &gt; Doors」にアタッチし、「Horizontal Doors」に「Stage &gt; Doors &gt; Horizontal」を入れてください

![image.png](https://class.illogic.games/uploads/images/gallery/2026-07/scaled-1680-/zo1image.png)

## タッチをシミュレーションする

タッチで扉を動くようにしたので、どれだけマウスを使っても、扉が全く動かない。そのため、Unityは<span style="color:rgb(186,55,42);">**マウスを使ってタッチを疑似する**</span>機能がある。もちろん、マウス１つしかないので、<span style="color:rgb(186,55,42);">**指１本のみで、マルチタッチが再現できない**</span>が、今回のゲームなら十分。

疑似タッチを有効化するのは Window &gt; Analysis &gt; Input Debugger を選択し、次のウィンドウを開く。

![image.png](https://class.illogic.games/uploads/images/gallery/2026-07/scaled-1680-/lLfimage.png)

このウィンドウの「Options」を開く、「Simulate Touch Input From Mouse or Pen」を選択すれば、マウスでタッチを疑似する。

![image.png](https://class.illogic.games/uploads/images/gallery/2026-07/scaled-1680-/mjeimage.png)

## 速度調整、移動制限を追加

これで、扉が移動できるようになったが、２つの問題が現れた。

1. 扉の動きが多少遅く感じる
2. 画面から消えるぐら、無限にスライドできる

この２つの問題を解決しましょう：

### 速度調整

スクリプトに調整できる「speed」変数を追加し、`OnFingerMove` で使いましょう。先頭に：

```c#
// スライドの速度
[SerializeField] private float speed;
```

`OnFingerMove` では：

```c#
// 現在の位置からを取得し、X軸だけを更新
Vector3 hPos = horizontalDoors.position;
hPos.x = doorStartX + delta.x * speed; // 速度を適用
horizontalDoors.position = hPos;
```

Unity で適切に速度を設定し、確認してください

![image.png](https://class.illogic.games/uploads/images/gallery/2026-07/scaled-1680-/lWSimage.png)

### 移動制限

Unityで最大Ｘと最小Ｘの位置を設定できるようにし、`OnFingerMove` で制限をかける。まず、スクリプトの先頭に：

```c#
// 最低 X 位置
[SerializeField] 
private float xMin;

// 最大 X 位置
[SerializeField] 
private float xMax;
```

そして、`OnFingerMove` では：

```c#
// 現在の位置からを取得し、X軸だけを更新
Vector3 hPos = horizontalDoors.position;
hPos.x = doorStartX + delta.x * speed;     // 速度を適用
hPos.x = Mathf.Clamp(hPos.x, xMin, xMax);　// 移動制限を適用
horizontalDoors.position = hPos;
```

Unity で適切に数値を設定し、確認してください

![image.png](https://class.illogic.games/uploads/images/gallery/2026-07/scaled-1680-/AGwimage.png)

## 垂直をスライド

同じ処理を使って、DoorSlider スクリプトに垂直の扉が Z 軸で移動できるようにしましょう。スクリプトの先頭に：

```c#
// 垂直の扉のグループ（親オブジェクト）
[SerializeField] 
private Transform verticalDoors;

// 最低 Z 位置
[SerializeField] 
private float zMin;

// 最大 Z 位置
[SerializeField] 
private float zMax;

//（他の[SerializeField]）

// 垂直扉の初期位置（Z軸のみ）
private float doorStartZ;
```

つぎ、`OnFingerDown` では、垂直扉の初期位置を取る

```c#
// 初期位置も取っておく
fingerStart = finger.screenPosition;
doorStartX = horizontalDoors.position.x;
doorStartZ = verticalDoors.position.z;
```

そして、`OnFingerMove` では：

```c#
// 現在の位置からを取得し、Z軸だけを更新
Vector3 vPos = verticalDoors.position;
vPos.z = doorStartZ + delta.y * speed;     // 速度を適用
vPos.z = Mathf.Clamp(vPos.z, zMin, zMax);　// 移動制限を適用
verticalDoors.position = vPos;
```

最後に Unity でパラメータを設定して、試してみてください：

![image.png](https://class.illogic.games/uploads/images/gallery/2026-07/scaled-1680-/q8Zimage.png)

## 実機で確認

つぎ、端末を接続し、「Build And Run」でタッチと傾きを使用し、卵を巣まで動かしてみてください。