Cocos Creator 3 カンタン2Dゲーム制作(3)【衝突判定（コライダの使い方）】

# ページ一覧

__Cocos Creator 3 導入編__
- [Cocos Creator 3 ダウンロード](https://qiita.com/Hiro256/items/941b196f2e3f0db9e7da)
- [Cocos Creator 3 インストール](https://qiita.com/Hiro256/items/056e922af7821e482e88)

__Cocos Creator 3 カンタン2Dゲーム制作__
- [プロジェクト作成と画面の説明](https://qiita.com/Hiro256/items/ee21f1aa5dc452f8e992)
- [(1)【トゥイーンの使い方】](https://qiita.com/Hiro256/items/872f82fe7c875106faaf)
- [(2)【プレハブの使い方】](https://qiita.com/Hiro256/items/26c4da8b46565e319471)
- (3)【衝突判定（コライダの使い方）】← __本記事__
- [(4)【アニメーションクリップの使い方】](https://qiita.com/Hiro256/items/f077e64bdc047bc8ef16)
- [(5)【パーティクルの使い方】](https://qiita.com/Hiro256/items/4b91f426d88cfd9e2e82)
- [(6)【ラベルアトラスの使い方】](https://qiita.com/Hiro256/items/a0716a722a709414afe6)

# 衝突判定をおこなう

アイテムが出るようになったので、次は衝突判定を行います。
それには`Collider`（コライダ）という機能を使います。

## 準備

### 2D物理演算システムの設定
メニューバーから`Project`→`Project Setting`を選択します。`Project Setting`ダイアログが開きます。
左メニューの`Feature Cropping`を選択します。
`2D`という見出しのあるエリアで、`2D Physics System`の右にあるプルダウンを`Builtin 2D Physics System`に変更します。

:::note
2Dの物理演算システムには「ビルトイン」と「Box2Dベース」の二種類があります。ビルトインは複雑な物理演算はできませんが、処理が軽く、ノードが重なったかどうかの判定処理に適しています。
:::

<img width="70%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271948/f559a039-ff5a-bd25-4dfc-ef7af668e0c9.png">

### コリジョンマトリックスの設定
左メニューの`Physics`を選択します。`Collision Matrix`と書かれたエリアを表示します。
「＋」ボタンを押すとテキストボックスが現れるので、`Fish`と入力し緑のチェックマークを押します。同様に`Coin`、`Obstacle`の２つも入力します。
図に示すように`Fish`の行と、`Coin`、`Obstacle`の列の交差点にチェックマークを付けます。

この後、ここで定義したGroupとノードを紐づけます。
チェックをONにしたGroup同士が「衝突判定」の対象となります。

<img width="70%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271948/d402e1a9-bfa0-0569-6323-374a47730114.png">

`Project Setting`のダイアログを閉じます。

# ノードにコライダを追加する

ヒエラルキーパネルの`Fish`を選択し、インスペクターパネルの`Add Component`を押します。
別の小窓が出るので、`Search Name`と書かれたエリアに`collider`と入力します。
いくつか候補が出てきますがここでは`PolygonCollider2D`を選びます。
インスペクターパネルに`cc.PolygonCollider2D`が追加されるので、`Group`で`Fish`を選択します。

<img width="300px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271948/1a9aa129-f0c4-ec0c-e6d5-1932ee92bb7d.png">

### 衝突範囲の設定
`Editing`チェックボックスをONにすると、他のノードとの重なりを判定する範囲の設定ができます。
ここでは`PolygonCollider2D`を選んだので、非透過部分でくり抜かれた線が自動で設定されています。
マウス操作で頂点を移動できるので任意の範囲に設定します。
編集を終了するには`Editing`チェックボックスをOFFにします。

<img width="500px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271948/c0cb1c19-d75e-a4b4-223e-6b06afa089d4.png">


同様の手順で、アセットパネルにある`Coin`と`Syuriken`のプレハブにもコライダを設定します。
このとき、設定するコライダは`CircleCollider2D`を選びます。
また`Group`はそれぞれ`Coin`、`Obstacle`を選択します。

先ほどと同様、`Editing`チェックボックスをONにすると衝突判定する範囲の設定ができます。
今回は`CircleCollider2D`ですので多角形ではなく円形で、大きさのみ変えられます。
<img width="500px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271948/2eae0b5a-fca1-1b8b-b299-40f4086fea60.png">

# スコアラベルを追加する

メニューバーの`Panel`→`Node Library`でノードライブラリパネルを開きます。
別ウィンドウで開いた場合は、ヘッダ部分をつまんで右側のペインにドッキングします。
その中から`Label`を選んでヒエラルキーパネルの`Canvas`の配下にコピーします。
（`Canvas`を右クリック、`Create`→`2D Object`→`Label`でも同様です。）
名前を`Score`とします。

インスペクターパネルから、`position`、`Anchor Point`、`String`、`Font Size`をそれぞれ入力します。
<img width="70%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271948/ee4c1607-8b64-eba5-486e-f17e93f233a8.png">


# 衝突判定スクリプト

アセットパネルに新規スクリプトを追加し、名前を`FishCollision`とします。
以下のコードを記述します。

```ts:FishCollision.ts
import { _decorator, Component, Node, Collider2D, Contact2DType, IPhysics2DContact, log, Label } from 'cc';
const { ccclass, property } = _decorator;

// コリジョングループ定数
enum CG {
    default = 0,
    fish = 2,
    coin = 4,
    obstacle = 8
}

@ccclass('FishCollision')
export class FishCollision extends Component {

    @property(Label)
    score: Label = null;

    _score: number = 0;

    start() {
        // 衝突判定
        this.enableCollider(true);

        // スコアの初期化
        this.score.string = "0";
    }

    onDestroy() {
        this.enableCollider(false);
    }

    // 衝突判定の有効/無効切り替え
    enableCollider(enable: boolean) {
        let collider = this.getComponent(Collider2D);
        if (enable) {
            collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
        } else {
            collider.off(Contact2DType.BEGIN_CONTACT);
        }
    }

    onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) {
        switch (otherCollider.group) {
            case CG.coin:
                // コインにヒットした場合
                this._score += 100;
                break;
            case CG.obstacle:
                // 障害物にヒットした場合
                this._score -= 100;
                break;
        }
        // スコア更新
        this.score.string = this._score.toString();
        // コインまたは障害物の消滅
        otherCollider.node.destroy();
    }
}
```

## 魚のノードとスクリプトの紐づけ

ヒエラルキーパネルの`Fish`を選び、インスペクターパネルの`Add Component`で、今追加したスクリプト`FishCollision`を紐づけます。
`FishCollision`の枠が追加されたら、`Score`の枠がブランクになっているので、右側のアイコンをクリックし、`Score`を選びます。これによりスクリプト内でシーン上のスコアラベルが更新できます。

## ここまでの動きの確認

▶ボタン（または`ctrl+P`）で動きを確認してみましょう。正しく作れていれば、アイテムとヒットしたときにスコアをカウントします。

# スクリプトの解説

### 衝突判定の開始

自身のノード（`Fish`）から`Collider2D`コンポーネントを取得ます。
`collider.on()`を使って`Contact2DType.BEGIN_CONTACT`イベント発生時に、`this.onBeginContact`を呼び出すよう設定しています。

```ts:抜粋
let collider = this.getComponent(Collider2D);
collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
```

### 衝突イベント発生

`onBeginContact()`メソッドでは、イベント発生時の情報が引数として渡されます。
`otherCollider.group`を使って衝突した対象のノードの種類を特定し、スコアの加算・減算を行います。

:::note
コリジョングループはビット単位で管理されており、コリジョンマトリックスの画面で設定したGroup名に対応する`index`の1,2,3は、それぞれ2,4,8となります。
:::

ゲームの演出上、衝突したらアイテムは消したいので、衝突対象に対し`destroy()`でノードを消滅させます。

```ts:
otherCollider.node.destroy();
```