unity再入門　ブロック崩しを作ってみた

#対象者
unityを触ってみたいがとりあえず雰囲気を掴みたい人
長らくunityから離れていて肩慣らしとして触りたい人。

#unityの考え方
まずは、Unityの開発の考え方をざっとおさらいしてみる。

unityはあるオブジェクトに対して、振る舞いや属性(component)を付与して、それぞれのオブジェクト同士の振る舞いをデザインしてゲームを作っていきます。
例えば、３Dの球体のオブジェクトに物理挙動という振る舞いを与えることによって、落下運動をしたり、力を加えて転がすといった物理運動が可能になります。

今回は簡単にブロック崩しを作っていきたいと思います。

3Dのプロジェクトを作る云々は省略。

#ブロック崩しのオブジェクトと振る舞いを書き出してみる
まずは、ブロック崩しに必要なオブジェクトとどういった振る舞いをするかを整理しましょう

### 壁
* ボールとの当たり判定を持っている

###ボール　
* 減速せずに同じ速度を保ったまま等速運動を行う
* 何かのオブジェクトと衝突しても、摩擦、空気抵抗、跳ね返りのエネルギー損失がない。
* ゲーム開始時勝手に移動する
* 当たり判定を持つ

###バー　
* キー入力で左右に動かせる
* ボールと当たり判定を持つ

###ブロック　
* ボールと当たり判定を持つ
* ボールと衝突すると消える

今回のようにあまり複雑ではないゲームであれば、雑にオブジェクトとふるまいを書き出しておくと、
作業の全体像が把握することができます。
さらに、意図した動作をUntity側がサポートしてくれているのか?
自分で組まないと行けないのか? などの不確定要素も把握しやすくなります。

# オブジェクトを作る

オブジェクトと振る舞いは書き出せたので、今からオブジェクトを作っていきましょう。
先ほども言ったようにUnityはオブジェクトに振る舞いを付与する形でゲームを作るので、先にオブジェクトを作らないと細かい設定などができないのです。なので最初にオブジェクトを作ります。

## 壁
まず 3DObject -> cubeを４つ作って、適当に配置します。
そのあとにそれぞれ、名前をつけましょう。今回の壁は四方にあるので、適当に、

* upper-flame
* lower-flame
* right-flime
* left-flame

として、位置(position)と回転(Rotation)と縮尺(Scale)をいじって壁を作っていきます。

hierachy view のオブジェクトをクリックすると、inspector viewにそのオブジェクトが持っているcomponentが見られます。
オブジェクトの座標、縮尺はtransformというcomponentが管理しているので、これを操作して、以下の様に設定してください。

* upper-flame
  * position: x=0 y=0 z=35 
  * rotation: x=0 y=0 z=0
  * scale: x=80 y=5 z=1 
* lower-flame
  * position: x=0 y=0 z=-55 
  * rotation: x=0 y=0 z=0
  * scale: x=80 y=5 z=1 
* right-flime
  * position: x=40 y=0 z=-10
  * rotation: x=0 y=90 z=0 
  * scale: x=90 y=5 z=1 
* left-flame
  * position: x=-40 y=0 z=-10 
  * rotation: x=0 y=90 z=0
  * scale: x=90 y=5 z=1 

大体こんな感じになる予定。
![flame.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179800/02b5f20e-cb76-67a1-37fa-8715cb5224f5.png)


## ボール
3DObjectから sphere を選択して先程の壁と同じように調整していきます。
今回は好きな位置に設置してください。

## バー
3DObject -> cube でこちらも Scaleをいじって好きな座標においてください。

## ブロック　
こちらも バーと同様にcubeを置いてscaleをいじって好きなところに置いてください。

私はこんな感じに配置しました。

![complete.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179800/2d212ded-e218-f151-c322-f95f92389b28.png)


# 振る舞いを与える

さて、オブジェクトは完成したので次は振る舞いを与えていきましょう。
まず、全部のオブジェクトに共通する当たり判定ですが、これは実は3Dオブジェクトが生成された時点で自動的にふよされています。
適当に　Inspector viewで3Dオブジェクトを確認すると xxxColiderというものがあると思います。

これが当たり判定のcomponentです。
なので、当たり判定の振る舞いは今回特に弄る必要がないので、他の振る舞いを与えていきます。

## ボール
### 物理挙動
まず以下のめんどくさそうな振る舞いを考えましょう。
* 減速せずに同じ速度を保ったまま等速運動を行う
* 何かのオブジェクトと衝突しても、摩擦、空気抵抗、跳ね返りのエネルギー損失がない。

多分いちばんわけがわからないと思うでしょうし、どうすすればと疑問に思うことだとおもいますが、
これは、Unityの設定でコードを書く事なく実装できます。

![2019-11-16_23h33_49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179800/283ba762-5935-252e-de2d-e45ef17ca901.png)


ボールに RigidBody というコンポーネントを追加します。
ここで今回必要ない、回転挙動と座標軸異動を固定してしまいしょう。



上はのチェックボックスを入れることで、チェックボックスに対応する座標軸の計算を行わないように出来ます。
そして,Drag という項目の値を0にします。これが摩擦の値です。

そして、今回は重力は使わないので切ってしまいましょう。大体rigidBodyの設定はこんな感じになります。

![rigid-settings.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179800/b5d7a45b-e844-af03-f0c9-fe6c8f4fbe5b.png)



ただ、これだけだと不十分です。rigidbodyは割り当てたオブジェクト単体の振る舞いのみを定義するので、他のオブジェクトと接触した時の振る舞いまでは設定していないのです。なので、このままだとオブジェクトとぶつかった時に減速してしまいます。なのでそこらへんも設定しましょう。

左上のメニューから asset-> create -> physic materialを選択します。
すると、asset view にmaterialが作られるので、名前を付けましょう。
このマテリアルをクリックすると詳細設定が行えるので
以下の様に設定しましょう。

![add-physics-material.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179800/4b0571eb-ef11-11bb-ae81-465ce4f6d19d.png)

これで、エネルギー損失なしで跳ね返る性質のマテリアルができたので、これをボールに適用します。


適用するには、ボールのsphere colliderの materialに指定してあげます。

![accept-physics-material.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179800/26727a07-0fa6-1808-8f0e-f83c014ccf33.png)




これで終了です。

### ゲーム開始時ボールを動かす
いよいよソースコードの出番です。
unityはソースコードもcomponentの一つとして扱うので、振る舞いを当てたいオブジェクトのinspectorから　New　script コンポーネントを追加して、ソースコードの名前を決めましょう。

![2019-11-16_23h36_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179800/2c4e3bed-9709-b257-f722-f2f6aeb9656b.png)

今回書いたコードは

```C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ballScript : MonoBehaviour
{
    // Start is called before the first frame update
    public float speed = 5.0f;
    private Rigidbody rigid;
    void Start()
    {
        rigid = this.GetComponent<Rigidbody>();
        rigid.AddForce((transform.forward + transform.right) * speed, ForceMode.VelocityChange);
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

```

Start関数はオブジェクト生成時に実行されるので、ここで適当にベクトルを設定その方向に力を加えるという処理をしています。

物理系統で力を加える場合は,先ほど追加したrigidbodyから操作するので、rigidbodyのコンポーネントを取得。そこから前方ベクトルと右ベクトルを足し合わせてその方向にspeed分の力を加えて動かしています。

## バー
### キー入力で左右に動かせる
これは簡単にできます。

以下のコードを作り、ボールと同じようにコンポーネントに追加しましょう。

```C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerBar : MonoBehaviour
{
    // Start is called before the first frame update
    public float speed = 30f;
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {

        if (Input.GetKey(KeyCode.LeftArrow))
        {
            this.transform.position += Vector3.left * speed * Time.deltaTime;
        }
        if (Input.GetKey(KeyCode.RightArrow))
        {
            this.transform.position += Vector3.right * speed * Time.deltaTime;
        }


    }
}

```

実装としてはシンプルで、座標指定するときに使ったtransformの情報をスクリプトで読み込み現在のpositionの値に別の値を加算しているだけです。
ただ、Update文の中身を呼ばれる回数は使われるPCの性能に依存するので、操作感にムラがでてきてしまいます。なので、各フレームとの差分の時間を取ってその値を掛けることでフレームの速度に依存しないようにしています。

また、transformは物理処理系統の処理基本的に干渉しないので、操作したらわかるのですが、右キーを押し続けると壁を貫通できたりします。

今回は省略していますが、動かせる範囲を制限したりする必要もあるでしょう。

## ブロック
### ボールと衝突すると消える

これもソースコードで実装します。
Unityは当たり判定のオブジェクト同士が衝突したときに、それを検知する機能があるので、
それを使って、ぶつかった瞬間にオブジェクトを破棄しましょう。
というわけでコードです。

```C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class block : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {

    }
    void OnCollisionEnter(Collision col)
    {
        Destroy(this.gameObject);
    }
}

```

これをこれまでやったようにオブジェクトにコンポーネントとして追加しましょう。
OnCollisionEnter とはUnityが提供している関数でオブジェクトが衝突した瞬間にこの関数内の処理が実行されます。
今回はブロックにこのスクリプトを張り付けているので、ブロックがブロック自身を破棄する(消す)という処理にしないといけません。

なので、 Destory関数に自分自身のオブジェクト要素を指す this.GameObjectを引数として渡しています。


# 試してみる
これで振る舞いとオブジェクトの実装はおわりましたので、実行してみましょう。
カメラの場所を調整して、上部のスタートボタンを押して、確認してみましょう。

![play.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179800/7fa7951a-71fd-0e6f-9a26-bd70896deb77.png)


# 終わりに
簡単にですが、久々に3Dゲームを作りましたが、色々楽しかったです。
せっかくなので、 particleだとかtrail renderer とかでエフェクトつけていきたいと思います。
また、このままだと結構味気なくて、ボールの挙動のコントロールの幅小さかったり、そもそもゲームオーバーが無かったりなど拡張の幅はいろいろあります。ぜひ自分で弄りましょう。
それでは　よきUnity ライフを