【Unity2D】Spriteをクリックして接触したオブジェクトを飛ばす

#はじめに
この記事は[Life is Tech！アドベントカレンダー](https://qiita.com/advent-calendar/2020/life-is-tech)の5日目の記事です！

今回の記事で紹介するのは、**Unityの2Dゲームを作る時に、ここをタップ/クリックしたらこういう動作がしたい！**っていうのを叶えてくれるやつです。主にUnity初心者の方向けです！
私が2Dゲームを初めて作った時に、今回の実装をググっても出てこなかったので、誰でも簡単にできる過程を書こうと思いました。

#完成イメージ
こんな感じです。
今回はわかりやすく、バネをタップしてPlayerを跳ねさせるということをしてみます。画像がバネではなく、トランポリンなのは気にしないでください笑
![Videotogif (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562324/5e7e5a02-e447-549b-d88e-60bdb34a54a7.gif)

####実装手順
1. Playerがバネに接触したかどうかを調べる
2. 接触してる時だけバネをクリックできるようにする
3. バネをクリックしたらPlayerが跳ねる
 
の流れで説明します。

#準備
####①以下をすべてSpriteで用意しましょう。

* Player
* バネ
* バネを置く床

適当に画像を引っ張ってきて画像を突っ込んでおくとわかりやすいです。(今回はアセットストアで探してきたものと、画像検索で引っかかったフリー画像を使いました。)

####②C#スクリプトを作っておきます。
バネにアタッチするSpringScript.csをつくってバネのゲームオブジェクトにアタッチしておきましょう。

####③コンポーネントを追加していきます
最初、高いところからPlayerを落としたいので**PlayerにRigidbody 2Dコンポーネントを追加**します。
また当たり判定も取りたいので、すべてのゲームオブジェクトにそれぞれの形にあった**Collider2D**をつけます。
**この時に2DとついていないRigidbodyやColliderをつけないように気をつけてください！**
<img width = 200px; src =https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562324/055f7c05-3d6f-f5c2-29af-3bb1b6226b73.png>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562324/ca8a0ef9-703e-3721-aac1-d152b3b2dc07.png)

プレビューしてPlayerがバネに当たる様子ができていれば準備完了です！
![Videotogif (2).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562324/59581db2-2da4-c237-349b-cd88120ca25d.gif)


#ここから必要な知識

今回の実装では以下の二つを組み合わせます。

* Raycasthit2D
* Layerの概念

###Raycasthit
Unityでは、ray（レイ）という光線のようなものを飛ばしてオブジェクトの情報を取得する機能があります。また2DにはRididbody2DやCollider2Dなどと同じようにRaycasthit2Dというものが用意されているので、コード時は間違えないように注意しましょう。
※オブジェクトにコライダーがないと取得できないので注意してください。
Rayについてわかりやすい記事は以下を参考にしてください。
[【Unity】RayCastを使いこなせ!判定や表示に使ってみよう](https://www.sejuku.net/blog/83620)

###Layer
UnityにはLayer（レイヤー）というグループ分けができる機能があります。もう少し細かくいうと、特定のオブジェクトを無視できます。例えば、PlayerとEnemyがそれぞれ複数いた場合、Playerグループ(主人公とその仲間)とEnemyグループ(スライム・ドラキーetc.)のオブジェクトをlayerを指定することで分けてあげることができます。

似たようなものにtag（タグ）がありますが、tagはオブジェクトをまとめて処理したい時に向いています。layerは個数制限があるのに対してtagはいくつでも作れるので、実装方法によってはtagを使ってみてもいいと思います。

###AddForce
Rigidbodyコンポーネントを持っているオブジェクトに対して力を加えることができるメソッドです。AddForceについて詳しく知りたい方は[こちらの記事](https://ekulabo.com/force-mode)がとても参考になるので見てみてください！

###どう組み合わせるのか
これら二つを、**layerで分けたそれぞれのオブジェクト情報をrayを飛ばすことで取得する**ことで利用します。
layerを分けずに指定すると、rayは表面のlayer情報（初期状態はDefault）しか取得してくれません。
![Layer01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562324/7fca28c7-4bed-6dcc-e254-d3c264a85ed9.jpeg)
layerを分けることでそれぞれのオブジェクト情報をrayが取得してくれるようになります。
![layer2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562324/3b51c40f-05a4-50c4-7424-f758f1d0757a.jpeg)


#作っていきます。
####①layerを作成

layerとrayの衝突は複数あるのですが、今回は任意のlayerと衝突させたいのでLayermaskを使用します。
<details><summary>layerとrayの衝突するオブジェクトの制限について</summary>
・ビット演算のシフト演算を使用して作成
・レイヤー名の配列からマスクを取得する
・Insoectorから作成して任意のオブジェクトで選択する
・定数クラスでレイヤーマスクを指定
　[RayCastその2、衝突するオブジェクトの制限【Unity】](https://kan-kikuchi.hatenablog.com/entry/RayCast2)
</details>


まずUnity上で右上のlayerを開き、springというlayerを用意し、バネのゲームオブジェクトにそのレイヤーを指定してください。
<img width = 300px; src =https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562324/4e5b885b-2597-0fa4-890a-cae83a9cc6ac.png> <img width = 200px; src =https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562324/b2d624ee-094c-b154-e611-c996f507dcb8.png>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562324/c59bce0c-c49f-d818-086f-d4645fa5a31e.png)

####②rayをオブジェクトに飛ばして情報を取得する

```C#:SpringScript

 public Rigidbody2D rb; //PlayerのRigidbody
 public LayerMask layerMask;//レイヤーマスク作成

 bool isBounce = false; //バネで跳ねられるか
```

マウスを左クリックしたらrayを飛ばします。

```C#:SpringScript

  void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            //Rayをマウスの位置から飛ばす
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            float maxDistance = 10; //Rayの長さ
            RaycastHit2D hit2D = Physics2D.Raycast((Vector2)ray.origin, (Vector2)ray.direction, maxDistance, layerMask);

            //rayが衝突したObjのcolliderを取得
             if (hit2D.collider)
            {
                Debug.Log(hit2D.collider.gameObject);
            }
        }
    }
```

スクリプトが書けたら、Unity上で、バネについているSpringScript.csのコンポーネントを参照します。
PlayerのRigidbodyをアタッチし、layerMaskからspringを選びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562324/cf972c67-46a5-bc0d-8606-c204b7619b2c.png)

####③Playerとの接触を調べる
Playerと接触しているかどうかを調べたいので、PlayerにPlayerタグをつけます。
何回も跳ねてしまっては困るのでboolで接触可能かどうかを判定します。

<img width = 300px; src =https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562324/c3c2df6e-46c3-8e0f-e7d5-f2f7ac2b1e7b.png>

```C#:SpringScript
      //Playerと接触しているか
    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.tag == "Player")
        {
            isBounce = true; //接触可能に
        }
    }
```

####④Playerを跳ねさせるメソッドを書く

```C#:SpringScript
      if(isBounce == true)
        {
            //加える力の大きさ
            Vector2 force = new Vector2(0.5f, 10f);
            rb.AddForce(force, ForceMode2D.Impulse);
            //接触していない時は跳ねないように
            isBounce = false;
        }
```

#完成
こちらがすべてのコードになります！

```C#:SpringScript
 public Rigidbody2D rb; //PlayerのRigidbody
 public LayerMask layerMask;//レイヤーマスク作成

 bool isBounce = false; //バネで跳ねられるか

public class SpringScript : MonoBehaviour{
  void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            //Rayをマウスの位置から飛ばす
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            float maxDistance = 10; //Rayの長さ
            RaycastHit2D hit2D = Physics2D.Raycast((Vector2)ray.origin, (Vector2)ray.direction, maxDistance, layerMask);

            //rayが衝突したObjのcolliderを取得
             if (hit2D.collider)
            {
                Debug.Log(hit2D.collider.gameObject);
                AddForce();
            }
        }
    }

      //Playerと接触しているか
    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.tag == "Player")
        {
            isBounce = true; //接触可能に
        }
    }

    void AddForce()
   {
      if(isBounce == true)
        {
            //加える力の大きさ
            Vector2 force = new Vector2(0.5f, 10f);
            rb.AddForce(force, ForceMode2D.Impulse);
            //接触していない時は跳ねないように
            isBounce = false;
        }
    }
}
```

#最後に
Unityを初めたてでまだ右も左も分からなくても、なんとか手を動かして少しずつ実装できることを増やせるといいなと思います！この記事が何かのお役に立てるとそれより嬉しいことはないです！
私もまだまだ勉強中ですので、ぜひUnityのいろんな機能を触ってみてくれると嬉しいです！✨
今後の[Life is Tech！のアドベントカレンダー](https://qiita.com/advent-calendar/2020/life-is-tech)もお楽しみに！

#参考記事
今回の記事を書くにあたって、参考にさせていただいた記事を添付しておくのでぜひ一緒に見てみてください！

**RaycastとLayerについて**

* [RayCastその2、衝突するオブジェクトの制限【Unity】](https://kan-kikuchi.hatenablog.com/entry/RayCast2)
* [RayCastその4、Raycastを2Dで使う【Unity】](https://kan-kikuchi.hatenablog.com/entry/RayCast4)

**ビット演算について**
(コンピューターの処理の話になってきます。)

* [ビットをどう使うかよく判らない人へ](https://qiita.com/satoshinew/items/566bf91707b5371b62b6) ※ちょっと古いかもしれないです
* [二進数の計算](https://www.infraexpert.com/study/ip1.html)

#更新内容
**2020/12/08**

* 必要な知識にAddForceメソッドを書き加えました。
* LayerとRayの関係性についてイメージ図を用意しました。