Unityで、ポケポケみたいにカードを傾ける🃏

リクエストがあったので、今日はUnityを触ってみようと思います！
3年ぶりのUnityなので、簡単なところから。

（Unityのバージョニング、一桁数字に戻ったんですね〜）

## 今回の目標

最近遊んでいるポケポケ（Pokémon Trading Card Game Pocket）のカードを眺められる画面を作ってみようと思います！
<iframe width="560" height="315" src="https://www.youtube.com/embed/LmIHMqT5vx4?si=wZP6LlyblB39Spjn&amp;start=65" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

執筆時間の関係上、**今回は「スワイプでカードを傾ける」までを目標にしたいと思います！**

ゆくゆくは、立体的に見えるイラストや、シェーダーを使ったキラキラの再現などもしたいと思っています！

## 制作の流れ

自分のUnityリハビリも兼ねて、今回はシンプルな実装にします。
Unityを触ったことがないという友人からの執筆リクエストのため、丁寧めに書いていこうと思います！

1. Unityプロジェクトの作成
2. カードオブジェクトをシーンに配置
3. カードを傾けるコードの作成
4. 動作確認
5. デプロイ

では始めていきましょう！

## 1. Unityプロジェクトの作成

[Unity公式ページ](https://unity.com/ja)から、Unityをダウンロードしましょう

![Unity公式ページ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80170/7d998565-fd16-d909-b0cc-78bb1edd4b56.png)

Unityのバーションやプロジェクトを管理するためのデスクトップアプリ「Unity Hub」がダウンロードできます。
ダウンロードしたら、Unityアカウントでログインするなどしてください。
（自分はもともと環境が整っていたので、細かい手順はわからず...公式を参考にしてください！）

Unity Hubが立ち上がったら、右上の`New Project`からプロジェクトを作成できます。
今回は3Dのプロジェクトにしたいので、`Universal 3D`を選びましょう。
右メニューでは、プロジェクト名や保存場所を指定できます。
一番右下にある`Connect to Unity cloud`という設定は、今回は使わないのでOFFで構わないです。
![プロジェクト作成画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80170/bcf8f75c-89f4-8838-3168-a73b0b63952e.png)

`Create Project`を押して、以下のようなウィンドウが立ち上がれば、プロジェクト作成の完了です！
![初期状態のProject](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80170/b89bba35-8f9c-3387-d654-ece407590d2e.png)

## 2. カードオブジェクトをシーンに配置

さて、現在見えているエディタ内のパネルについて説明しておきましょう。

1. 真ん中の`Game`パネル
    - 完成したゲームの見た目・動作を確認できるパネルです。ウィンドウ上部の再生ボタンを押すっと、ゲームを起動できます。（今はまだ何も動かないと思いますが...）
    - `Scene`タブに切り替えることで、ゲーム内オブジェクトの編集画面を開けます。ゲームを作っているときは、こっちを使いことが多いです
2. 左の`Hierarchy`パネル
    - 配置中のゲームオブジェクトの一覧です
    - デフォルトでは、「Main Camera」「Directional Light」「Global Volume」の3つが用意されています
        - Main Camera: その名の通りカメラです。`Game`パネルで見えている景色は、このカメラを通した視点です
        - Directional Light: ライトです。これを消すと、`Game`パネルでの視点が真っ暗になります
        - Global Volume: 少し難しいのですが、カメラのフィルタだと思ってください。いろんなエフェクトを付けたりできます
3. 右の`Inspector`パネル
    - ゲームオブジェクトの詳細画面です
    - `Hierarchy`パネルで、例えば「Main Camera」を選択すると、色々出てきます。上部の`Transform`では位置や角度などの調整、その下の`Camera`ではカメラ特有の視野の調整などができます
4. 下の`Project`パネル
    - 見た目の通り、ゲームに関わるファイルを管理できます。表示されているのは、プロジェクト作成時に指定したフォルダ内の、プロジェクトフォルダの中身です
    - いろんな種類のファイルがあるので詳細は省きますが、`Scenes`フォルダ内にある`Sample Scene`が、いま開いている"ゲームの世界"です。このSceneの中に、いまは3つのオブジェクト(Main Cameraなど)があると思ってください


では早速、カードを作りましょう！

左の`Hierarchy`パネルの空いているところを右クリックして、「3D Object > Cube」を選びましょう。
すると、中央の`Game`パネルの真ん中に、小さな立方体が登場しました:tada:

![Cubeがある世界](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80170/c16dbec8-bfa4-1382-b3c4-71737aeedced.png)

左の`Hierarchy`パネルで`Cube`を選択した後、右の`Inspector`パネルで立方体の大きさを変えてみましょう。
`Inspector`パネル内の、「Transform > Scale」が大きさの設定です。

今回は、以下のような設定にしてみましょう
- X: 5.5
- Y: 7.6
- Z: 0.1

以下のような、カードっぽい大きさになりました！
（数値はポケポケの画面を定規で測って決めました笑）

![これは誰がどう見てもカード](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80170/0292a732-4daa-c1bb-9290-bc5eaf26d4a1.png)

## 3. カードを傾けるコードの作成

次はコードを書いていきましょう。Unityで扱う言語はC#です。

下の`Project`パネルの空いている箇所を右クリックして、「Create > MonoBehaviour Script」を選びましょう。
「＃」マークの付いたファイルが作られるかと思います。
ファイル名は、「CardTilt.cs」にしましょう！
![CardTilt.cs作成](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80170/8c707e6a-e288-32dd-c920-04fefa2e92cd.png)

ファイルをダブルクリックで開いてください。

エディタが開き、以下のようなコードが見えたでしょうか？
環境設定にもよるかなと思うのですが、VS Codeを使って開くことができるはずです。
![はじめてのMonoBehaviour](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80170/6967c1de-7dc5-513e-a682-72c77b41b1b7.png)

ここにコードを書いていきましょう。


...今回は、いきなり完成コードから出してしまいます！
![完成コード](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80170/9374986e-acf1-c9c8-3f5b-da4c7e97cbac.png)


```cs
using UnityEngine;

public class CardTilt : MonoBehaviour
{
    private Vector2 dragStartPos;

    void Update()
    {

        if (Input.GetMouseButtonDown(0))
        {
            dragStartPos = Input.mousePosition;
        }

        if (Input.GetMouseButton(0))
        {
            Vector2 difference = ((Vector2)Input.mousePosition - dragStartPos) * 0.1f;
            transform.rotation = Quaternion.Euler(difference.y, -difference.x, 0);
        }

        if (Input.GetMouseButtonUp(0))
        {
            transform.rotation = Quaternion.identity;
        }
    }
}

```

上からひとつずつ見ていきましょう！


```cs
using UnityEngine;
```
Unity用のいろいろをインポートするための設定です

```cs
public class CardTilt : MonoBehaviour
{
```
クラス名を、ファイル名に合わせて`CardTilt`にしました。
`MonoBehaviour`は、これを継承することでゲームオブジェクトのコンポーネントとして動作するクラスを作れます。
"Behaviour"はイギリス英語のスペルで、アメリカ英語で言う"Behavior"です。

`{`の改行に気持ち悪さを感じる方もいると思いますが、今回は、Unityが作るデフォルトコードのスタイルに従っておきます...

```cs
private Vector2 dragStartPos;
```
`Vector2`は、2次元ベクトルです。
これから、「スワイプでカードを傾ける」という機能を作ります。
このスワイプ時の、初期地点を保存しておく変数です。

今回のコードでは、タッチパネルでの指の操作とマウスによる操作を区別せずに実装します。
とりあえずマウスのドラッグ動作の気持ちでコードを書くので、変数名は`drag`にしました。
完成したものを実行する際には、マウス操作と指操作を区別すること無く、同じコードで処理できます。

```cs
    void Update()
    {
```

`Update()`関数は、初期コードのコメント分にも書かれていましたが、常に実行が繰り返される処理です。
だいたい1秒間に60~80回とかの頻度ですが、環境やCPU負荷によって変動します。

今回はこの`Update()`関数で、カードの傾きの更新処理を行います。


```cs
        if (Input.GetMouseButtonDown(0))
        {
            dragStartPos = Input.mousePosition;
        }
```

「もしマウスが左クリックされたら、`dragStartPos`に現在のカーソル位置を保存する」というコードです。
`Input.GetMouseButtonDown(0)`は、左クリックされた**瞬間**のみ`True`になります。
（"`Down`"が、”指を押し下げる動作”のイメージです）
`0`で左クリック、`1`で右クリック、`2`でミドルクリックを検知できます。

`Input.mousePosition`はその名の通り、カーソル位置を取得できます。

※ [`Input.GetMouseButtonDown`の公式ドキュメント](https://docs.unity3d.com/ScriptReference/Input.GetMouseButtonDown.html)を見ると、僕が知らないうちに非推奨となっていたようです...ですが、今回は簡単のために使わせてください:pray:


```cs
        if (Input.GetMouseButton(0))
        {
            ...
        }
```
次は`Input.GetMouseButton(0)`です。
これは先ほどと異なり、"`Down`"がついていません。
これは、左クリックが**押されている間ずっと**`True`になります。

この中で、カードの傾きを更新します。

```cs
            Vector2 difference = ((Vector2)Input.mousePosition - dragStartPos) * 0.1f;
```

「ドラッグでどれだけカーソル移動したか」を保存しています。
これをもとに次の行でカードを傾けるのですが、そのままだとカードがぐるんぐるん回転してしまったので、`0.1f`を掛けてちょうどいい大きさにしています。(`f`は`float`の頭文字です)



```cs
            transform.rotation = Quaternion.Euler(difference.y, -difference.x, 0);
```

さて、この行が今回のプログラムの難所です。

まずは、ざっくり説明から。
`Quaternion.Euler()`は、引数を3つ取って、"オイラー角"という傾きを管理する値を作ってくれます。
`transform.rotation`を更新することで、実際にゲームオブジェクトの傾きに反映されます。

では、詳しく説明しましょう。
`Quaternion.Euler()`の引数は、`Quaternion.Euler(x軸の回転, y軸の回転, z軸の回転)`という意味があります。

各軸の回転は、カードの回転でいうと、以下のようなイメージです。
- X軸回転
  - カードが「うなずく」ような回転
  - カーソルを上に動かす → カードの上が奥に傾いてほしい
  - カーソルを下に動かす → カードの上が手前に傾いてほしい
- Y軸回転
  - カードが「首を振る」ような回転
  - マウスを右に動かす → カードが右を向いてほしい
  - マウスを左に動かす → カードが左を向いてほしい
- Z軸回転
  - カードが「首をかしげる」ような回転
  - 今回は使わない

また、コード内で引数にわたしている値は、以下のような意味です。

- X軸回転: `difference.y`
  - カーソルの上下移動量
- Y軸回転: `-difference.x`
  - 左右反転した、カーソルの左右移動量
- z軸回転: `0`
    - 今回は回転させないので`0`

ちなみに、`Quaternion.Euler()`が生成する値は、数学で言う"四元数(Quaternion)"という値です。
"複素数"が実部と虚部の2パラメーターを持っているのに対し、"四元数"は4パラメーター持っている数だと考えてください。
3次元回転の管理では"ジンバルロック"という厄介な"四元数"問題が発生しがちなのですが、それを解決するために、Unityでは"四元数"を使っています。

```cs
        if (Input.GetMouseButtonUp(0))
        {
            transform.rotation = Quaternion.identity;
        }
```

`Input.GetMouseButtonUp(0)`は、「マウスの左クリックを離したとき」です。
このとき、オブジェクトの傾きを、デフォルトの傾き（`Quaternion.identity`）に戻しています。

以上のコードで、カードを傾けられるようになりました！

## 4. 動作確認

では、実際にカードを傾けてみましょう！

CS Codeでコードを保存し、Unityに戻ると、1,2秒のモーダルが開いてコードの読み込みが行われます。

読み込みが終わったら、左の`Hierarchy`パネルで`Cube`を選択してください。

その後、下の`Project`パネルからドラッグアンドドロップで、作成した`CardTilt`ファイルを、右の`Inspector`パネル内の下の方の何も無い場所に入れてください。

下記画像のように、`Inspector`パネル内に`CardTilt`というコンポーネントが表示されればOKです！
![CardTiltコンポーネント作成](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80170/c027beec-3348-3721-8318-880c30703f35.png)

たったこれだけで、このオブジェクトで`CardTilt`プログラムが動くようになりました！

早速、ウィンドウ上部の再生ボタンでゲームを起動してみましょう。

中央の`Game`パネル内でマウスをドラッグすると、カードが傾きます！！:tada:

## 5. デプロイ

最後に、せっかくなので、このプロジェクトをデプロイしてみましょう。
ブラウザで動かせるように、GitHubで配信してみましょう。

まずはUnity画面内で`Cmd+S`や`Ctrl+S`を押して、プロジェクトを保存してください。

次に、画面左上のメニューから、「File > BuildSettings」を開き、Platformを「Web」にします。
![PlatformはWeb](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80170/b0983779-91a1-b96f-540f-ef4ed569abf0.png)

自分の場合、この画像のような注意書きが出たので、`Install with Unity Hub`を押しました。
出てきた設定のまま、"WebGL Build Support"をインストールしましょう。

`Unity Hub`がインストールしてくれます...
![Install WebGL Build Support](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80170/54bae1e8-917b-1051-b06f-a7685f44c520.png)

インストールが終わったら、Unityプロジェクトを一度閉じて、`Unity Hub`から再度開きましょう。
もう一度、画面左上のメニューから「File > BuildSettings」を開くと「Web」が使えるようになっています。

![BuildSettings Web](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80170/6230f043-eca0-58a1-8799-f8a261f3dcee.png)

「Switch Platform」を押し、処理が終わったら「Build」を押しましょう！
ファイル名を聞かれますが、自分は`CardTilt`にしました。
![Save as CardTilt](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80170/cdd04872-6e35-aa60-5df7-9aab715e8ba2.png)

ビルドには少し時間がかかります。

ビルドが終わったら、再度VSCodeを開いてみましょう。
プロジェクトファイル内に、`CardTilt`というフォルダが作成され、その中に`index.html`があります！！
![VSCodeでビルド成果物を確認](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80170/e02f8997-53fc-e312-ae82-65e3e27986b7.png)

最後にこれを、GitHubPagesでホストしましょう！

GitHubで、新しいリポジトリを作りましょう。
READMEも含めず、完全に空っぽの状態で作るのがおすすめです。
![空のリポジトリ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80170/4c40c828-3936-7432-8944-1be466e68ff4.png)

ページ真ん中の、`echo`から始まるコマンドをコピーしちゃいましょう。

VS Codeに戻り、コマンドパレットを開いてください。
`cd CardTilt`で、ビルド成果物のフォルダに移動しましょう。

![cd CardTilt](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80170/116601da-3fc2-185b-5dd6-886c721b6696.png)

ここで、先ほどコピーしたコマンドを実行しましょう！
![initial commit](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80170/9001eeb5-862c-d49b-8976-08afec3d4c38.png)


また、ビルド成果物もコミットしちゃいましょう！
```shell
git add .                                                 
git commit -m "Add build files"
git push
```

GitHubでも確認できるはずです！
![pushされたGitHub](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80170/3269b26b-9cc7-122b-906f-c450e8744646.png)

最後に、「Settings > Pages」にて、Branchで`main`を選択しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80170/a30751db-70b4-f358-89da-95d2504f062d.png)

「Save」を押した後、しばらく待ちます。

1分ほど待ってリロードすると、ページが公開されます！
![ページが公開](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80170/b364c995-5ab3-a092-a3fc-4f995f377e5b.png)

「Visit site」からページに移動してみましょう!!!

![Unable to parse Build/CardTilt.framework.js.br!](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80170/3a88da4c-215c-a3ed-c412-a6d3f586201e.png)


:cry:

---

ビルド、デプロイには失敗してしまいましたが、エディタ上では動作が確認できたのではないでしょうか？

また次回、最後のエラーを解消する記事を書けたららなと思います！