[身内向け] ハッカソンUnityチートシート(2D)

# はじめに
Unityでやりたいことを逆引きで列挙
基本2Dなので、3Dの場合は適宜読み替えてください。
また実装の一例ですので過信は禁物。

なるべくコピペはせず、コピペをする場合でもちゃんとコードの意味を理解してから使いましょう。

# キャラクターを動かす
## 2Dジャンプアクション
### 左右に動く
```c#
public class Player : MonoBehaviour {

    private Rigidbody2D _rigidbody;

	void Start () {
        _rigidbody = GetComponent<Rigidbody2D>();
	}
	
	void Update () {
        var speed = 5.0f;
        var vx = speed * Input.GetAxis("Horizontal");

        _rigidbody.velocity = new Vector2(vx, _rigidbody.velocity.y);
	}
}
```
### ジャンプする
```c#
public class Player : MonoBehaviour {

    private Rigidbody2D _rigidbody;

	void Start () {
        _rigidbody = GetComponent<Rigidbody2D>();
	}
	
	void Update () {
        var jumpPower = 10.0f;
        if(Input.GetKeyDown(KeyCode.Space)){
            _rigidbody.velocity = new Vector2(_rigidbody.velocity.x, jumpPower);

        }
	}
}

```

### 重力を変える
<img width="743" alt="スクリーンショット 2018-06-27 19.29.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/82356/a580a3a3-d99d-af8c-94cf-8409185a58a6.png">
<img width="374" alt="スクリーンショット 2018-06-27 19.30.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/82356/60370e98-e31a-e9a9-ed54-79c37fd6df56.png">

Edit -> ProjectSetting -> Physics2D
Gravityの値を変える

### 2段ジャンプ防止
泥臭い実装だがとりあえず確実。

<img width="263" alt="スクリーンショット 2018-06-27 19.33.14.png" src="https://qiita-image-store.s3.amazonaws.com/0/82356/3f63dedf-f4ca-399b-71da-7501f143d27e.png">
Playerの子供に足元センサーとしてEmpty Objectを追加
<img width="384" alt="スクリーンショット 2018-06-27 19.40.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/82356/c7eda126-d0c5-6a32-a50e-c61f0ab403e9.png">
作ったオブジェクトに当たり判定としてColliderをアタッチ。位置やサイズを調整しておく。IsTriggerにチェックを入れる。
またセンサー用の新しいスクリプトをアタッチ

```c#:Player.cs
public class Player : MonoBehaviour {

    private Rigidbody2D _rigidbody;
    public bool isGround = false;

	void Start () {
        _rigidbody = GetComponent<Rigidbody2D>();

	}
	
	void Update () {
        var jumpPower = 10.0f;
        if(isGround && Input.GetKeyDown(KeyCode.Space)){
            _rigidbody.velocity = new Vector2(_rigidbody.velocity.x, jumpPower);
        }
	}


}
```
ジャンプを実装したPlayerスクリプトに追記。
isGroundがtrueの時は地面にいるとし、ジャンプができるようにする。


```c#:FootSensor.cs
public class FootSensor : MonoBehaviour {

    private Player _player;

	void Start () {
        _player = GetComponentInParent<Player>();	
	}
	
    private void OnTriggerEnter2D(Collider2D col)
    {
        if(col.tag == "Ground"){
            _player.isGround = true;   
        }
    }
    private void OnTriggerExit2D(Collider2D col)
    {
        if(col.tag == "Ground"){
            _player.isGround = false;   
        }
    }
}

```
センサー用スクリプト。
地面と衝突すればPlayerのisGroundをtrueに。離れればfalseにする。
地面オブジェクトと当たっているかどうかは今回はタグを使って調べる。

<img width="371" alt="スクリーンショット 2018-06-27 19.44.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/82356/c6ca7c58-ea5c-ba2d-d9ad-9f20af0dee25.png">
最後に地面にしたいオブジェクトのTagをGroundに設定する。
新しいタグを追加する時はAdd Tag... を選ぼう。

これで終わり
<img width="611" alt="スクリーンショット 2018-06-27 19.53.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/82356/ca3f24d7-5925-0130-49e1-dc5d9f58d3f3.png">

最終シーンイメージ。白丸がPlayerで、それにくっついているのがFootSensor。
白い長方形が地面で、TagをGroundに設定している。

### ボタンを押した長さでジャンプの強さを変える(マリオジャンプ)
スーパーマリオブラザーズのジャンプは、ボタンを押した長さでジャンプの高さが変わります。
いわゆる小ジャンプと大ジャンプができるというわけです。

この仕組みのタネは、上昇時と下降時で重力が違うという点にあります。
ジャンプボタンを押していてかつ上昇中であるなら、重力を通常より低く設定します。

```c#:Player.cs
public class Player : MonoBehaviour {

    private Rigidbody2D _rigidbody;
    public bool isGround = false;
    public float maxFallSpeed = -15.0f;

	void Start () {
        _rigidbody = GetComponent<Rigidbody2D>();

	}
	
	void Update () {
        var speed = 10.0f;
        var xv = speed * Input.GetAxis("Horizontal");

        _rigidbody.velocity = new Vector2(xv, Mathf.Max(maxFallSpeed, _rigidbody.velocity.y));

        var jumpPower = 20.0f;
        if(isGround && Input.GetKeyDown(KeyCode.Space)){
            _rigidbody.velocity = new Vector2(_rigidbody.velocity.x, jumpPower);
        }
        if(!isGround && _rigidbody.velocity.y > 0.0f && Input.GetKey(KeyCode.Space)){
            _rigidbody.gravityScale = 0.5f;
        }else{
            _rigidbody.gravityScale = 1.0f;
        }
	}
}

```

先ほどのジャンプするプレイヤーのスクリプトに、2箇所変更を加えています。
一つは

```c#
if(!isGround && _rigidbody.velocity.y > 0.0f && Input.GetKey(KeyCode.Space)){
    _rigidbody.gravityScale = 0.5f;
}else{
    _rigidbody.gravityScale = 1.0f;
}
```

この部分で、「ジャンプしていて」かつ「上昇中」かつ「ボタンが押されている」ならば、自身の重力のレートを半分の0.5fにします。
それ以外の時はデフォルトの1.0fにします。

これだけでとりあえずジャンプの高さを変えられます。
Project全体のGravityの値を調節しないとものすごいジャンプ力になったりするのでうまく調整しましょう。参考までに上記のスクリプトだとy方向のGravityは-60.0fです。

また落下時に早くなりすぎないよう、最大落下速度を設定しています。

```c#
_rigidbody.velocity = new Vector2(xv, Mathf.Max(maxFallSpeed, _rigidbody.velocity.y));
```

この部分。Mathf.Maxで引数に与えられた二つの値のうち、大きいほうを取得します。これでy速度の最大値(正確には下方向はマイナスなので最小値)を設定できます。

動作イメージ
![jump.mov.gif](https://qiita-image-store.s3.amazonaws.com/0/82356/af434bd1-8ebb-9b89-0130-7981d3aaf8c3.gif)


## シューティング
<img width="371" alt="スクリーンショット 2018-06-27 19.58.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/82356/17518da6-027a-fb4a-cb1d-07a78fbfd2a8.png">
重力の影響を受けたくないのでRigidbodyのGravityScaleを0に。

```c#:ShootingPlayer.cs
public class ShootingPlayer : MonoBehaviour {

    private Rigidbody2D _rigidbody;

    void Start()
    {
        _rigidbody = GetComponent<Rigidbody2D>();

    }

    void Update()
    {
        var speed = 5.0f;
        var vx = speed * Input.GetAxisRaw("Horizontal");
        var vy = speed * Input.GetAxisRaw("Vertical");

        _rigidbody.velocity = new Vector2(vx, vy);

    }

}

```

これで上下左右に動きます。
Input.GetAxisRawを使うと、ぬるぬる感が無くなります。シャープに動きたい時はこちらを使おう。

また、このままだと斜めに動くときに速度が上下左右に動く場合のルート2倍になるので、それだと困る場合は以下のようにします。

```c#:ShootingPlayer.cs
  void Update()
    {
        var speed = 5.0f;

        var axis = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
        axis.Normalize();
        var v = speed * axis;

        _rigidbody.velocity = new Vector2(v.x, v.y);

    }
```

水平方向と鉛直方向の入力情報を一度ベクトルにします。
そのベクトルをNormalize()を使って正規化します。正規化とはベクトルの向きを変えずに長さを1にすることです。これによって斜めの時も上下左右に動くときの速度と同じになります。


# オブジェクトを新しく生成する
## 基本
### Prefab
Prefabという機能を使います。
<img width="1271" alt="スクリーンショット 2018-06-27 20.26.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/82356/0d600173-35dc-06cf-1ac4-222a6c7ddc98.png">
まずは適当に生成したいオブジェクトを作成しましょう。
<img width="621" alt="スクリーンショット 2018-06-27 20.26.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/82356/9171309c-8415-09f1-2707-3196ade1bed4.png">
作ったオブジェクトをHieralchyウィンドウからProjectウィンドウにドラッグ&ドロップ。
すると青い立方体のアイコンの何かが作られます。これがPrefabです。

Prefabはテンプレートのようなのです。
Prefabと紐づいているオブジェクトは、Prefabが変更されると自身も変更されます。

試しにその様子を見てみよう。
<img width="615" alt="スクリーンショット 2018-06-27 20.31.46.png" src="https://qiita-image-store.s3.amazonaws.com/0/82356/21ef7842-a261-d254-cd64-64cdc4dd83fa.png">
シーンにPrefabをドラッグ&ドロップしていっぱいオブジェクトを作ろう。
<img width="1003" alt="スクリーンショット 2018-06-27 20.32.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/82356/4c631435-81ce-00bc-bf17-da6df36fdf78.png">
ProjectウィンドウからPrefabを選択し、その色を変えてみよう。
するとシーンに設置したオブジェクト全てが変更される。

このようにPrefabは、いろんなところでなんども使いたいオブジェクトのテンプレートとして扱うことができる。

### Prefabを使ってスクリプトからオブジェクトを生成する
どこのスクリプトでもいいですが、今回はプレイヤースクリプトに下記コードを加えましょう。

```c#:Player.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ShootingPlayer : MonoBehaviour {

    public GameObject SpornObject;

    void Start()
    {
    }

    void Update()
    {
        if(Input.GetKeyDown(KeyCode.Space)){
            Instantiate(SpornObject, transform.position, transform.rotation);
        }
    }

}

```

これでスペースキーを押すと、SpornObjectに設定したPrefabをシーン上に生成できます。
Instantiateはオブジェクトを新しく生成する関数です。
一つ目の引数は生成したいオブジェクト、二つ目は位置、三つ目は角度です。
今回はプレイヤーの位置にオブジェクトを生成するようにしています。

<img width="372" alt="スクリーンショット 2018-06-27 20.38.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/82356/24f829cc-08a7-e50e-3674-eb27aca4ed66.png">
InspectorからSpornObjectを先ほど作成したPrefabに設定しましょう。

これで実行するとスペースキーを押すたびにオブジェクトが生成されるのが確認できると思います。

## 応用
### 自機から弾を打つ

まず弾を作ります。

```c#:Bullet.cs
public class Bullet : MonoBehaviour {

    private Rigidbody2D _rigidbody;
    public float speed = 10.0f;

	void Start () {
        _rigidbody = GetComponent<Rigidbody2D>();
	}
	
	void Update () {
        _rigidbody.velocity = speed * Vector2.up;
	}
}

```
今回は上方向に飛んでいく弾を実装しました。
Vector2.upは(0,1)のベクトルを生成します。
なおRigidbodyを使わず直接transform.positionを弄っても構いません。数値の単位が変わるのでそこだけ気をつけましょう。

これをアタッチしたオブジェクトを、Prefab化します。
あとは前節で説明したPrefabからオブジェクトを生成する方法で出現させてやればokです。

<img width="377" alt="スクリーンショット 2018-06-28 12.54.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/82356/91077288-cd3a-841b-2f4f-9f083575fc19.png">

publicで宣言した変数は、Inspector上から数値を設定することができます。

ただ、このままだと弾が画面外に行ってもシーン上に残ってしまいます。

<img width="171" alt="スクリーンショット 2018-06-28 12.58.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/82356/49e9ff65-6854-e191-fe31-f0039459d9a6.png">

メモリリークの原因になりますので不必要になったら消してあげましょう。

```c#:Bullet.cs
public class Bullet : MonoBehaviour {

    private Rigidbody2D _rigidbody;
    public float speed = 10.0f;

	void Start () {
        _rigidbody = GetComponent<Rigidbody2D>();
	}
	
	void Update () {
        _rigidbody.velocity = speed * Vector2.up;

        if(transform.position.y > 15.0f){
            Destroy(gameObject);
        }
	}
}
```

Destroyを実行すると引数に設定したgameObjectを消すことができます。
今回は適当な座標まで弾が飛んだら消しています。


## 敵を一定周期で出現させる

まずは敵を用意します。

```c#:Enemy.cs
public class Enemy : MonoBehaviour {
    
    private Rigidbody2D _rigidBody;
    public float speed = 10.0f;

	void Start () {
        _rigidBody = GetComponent<Rigidbody2D>();
	}
	
	void Update () {
        _rigidBody.velocity  = speed * Vector2.down;
        if(transform.position.y > -15.0f){
            Destroy(gameObject);
        }
	}
}

```
下に動くだけ。一定座標より下に行ったら消える。

あとはこの敵オブジェクトをPrefab化し、別のスクリプトから一定間隔で出現させてやる。
Playerのスクリプトでもできるっちゃできるが、敵スポーン用のスクリプトを作ってやるのが良い。

<img width="381" alt="スクリーンショット 2018-06-28 13.21.54.png" src="https://qiita-image-store.s3.amazonaws.com/0/82356/bb52e3b4-28cd-2102-90d6-20ad7c6c5872.png">


Create->EmptyObjectから空のオブジェクトを生成し、適当な位置に置いておく。
これに新しいスクリプトをアタッチ。

```c#:EnemySporner.cs
public class EnemySporner : MonoBehaviour {

    public GameObject EnemyPrefab;
    public float SpornInterval = 1.0f;
    private float _spornTimer;

	void Start () {
        _spornTimer = 0.0f;
	}
	
	void Update () {
        if(_spornTimer >= SpornInterval){
            Instantiate(EnemyPrefab, transform.position, transform.rotation);
            _spornTimer = 0.0f;
        }

        _spornTimer += Time.deltaTime;
	}
}
```

_spornTimerは前回出現させてからの時間。
Time.deltaTimeは前フレームからの経過時間を取ってくる。
_spornTimerが一定時間(今回はSpornIntervalで設定)経つと、EnemyPrefabをInstantiateしている。

<img width="381" alt="スクリーンショット 2018-06-28 13.28.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/82356/7cd0757f-2e49-9c4e-57db-c4aaec1dae56.png">
あとはInsperctorからいろいろ設定してあげるとok。


このままだと同じ位置から出現し続けるので、ランダムな位置から出現するよう変えてみましょう。

```c#:EnemySporner.cs
public class EnemySporner : MonoBehaviour {

    public GameObject EnemyPrefab;
    public float SpornInterval = 1.0f;
    private float _spornTimer;

	void Start () {
        _spornTimer = 0.0f;
	}
	
	void Update () {
        
        if(_spornTimer >= SpornInterval){
            Vector3 spornPos = new Vector3(Random.Range(-15.0f, 15.0f), transform.position.y, transform.position.z);
            Instantiate(EnemyPrefab, spornPos, transform.rotation);
            _spornTimer = 0.0f;
        }

        _spornTimer += Time.deltaTime;
	}
}

```

Random.Range(min, max)でminからmaxのランダムな値を取得します。
今回はx座標だけランダムにしています。


# 当たったら何かする
## 基本
当たったら、という処理は基本的にColliderを使う。
当たる方当たられる方(って言い方もおかしいけど)両方にColliderをつける必要がある。

Colliderが付いているオブジェクトのスクリプトでは、以下の関数が実行できる。

```c#
private void OnCollisionEnter2D(Collider2D col)
{

}
private void OnCollisionStay2D(Collider2D col)
{

}
private void OnCollisionExit2D(Collider2D col)
{

}
```

上から順に、
衝突した瞬間実行する関数
衝突している間実行する関数
衝突していたものから離れた瞬間実行する関数
となっている。

引数のcolには衝突した相手の衝突情報が入っている。
col.gameObjectで衝突相手のgameObjectにアクセスできる。

またColliderのInsperctorからIsTriggerにチェックを入れた場合、OnTrigger系を使う。
IsTriggerにチェックが付いているオブジェクトのスクリプトでは、以下の関数が実行できる。

```c#
private void OnTriggerEnter2D(Collider2D col)
{

}
private void OnTriggerStay2D(Collider2D col)
{

}
private void OnTriggerExit2D(Collider2D col)
{

}
```

使い方はOnCollision系と一緒。

## 応用
### 取ったら消えるアイテム

```c#:Item.cs
private void OnCollisionEnter2D(Collision2D col)
    {
        if(col.tag == "Player"){
            Destroy(gameObject);
            // あとはスコアアップの処理とかやりたいことを書く
        }
    }
```

当たった相手の識別はtagを使うのが手っ取り早い。

### シューティングの敵

```c#:Enemy.cs
private void OnCollisionEnter2D(Collision2D col)
    {
        if(col.tag == "Player"){
            //プレイヤーの体力を減らす処理
            Destroy(gameObject);
        }
        if(col.tag == "Bullet"){
            Destroy(gameObject);
        }
    }
```

プレイヤーに当たったとき、弾に当たったときと場合分けする。

# 物体の動き色々
## 特定のオブジェクトに向かって直進

```c#:ForwardToObject.cs

public class StraightToObject : MonoBehaviour {
    public GameObject TargetObject = null;
    public float speed = 5.0f;
    private Rigidbody2D _rigidbody;

	void Start () {
        _rigidbody = GetComponent<Rigidbody2D>();
        if(TargetObject){
            _rigidbody.velocity = speed * (TargetObject.transform.position - transform.position).normalized;
        }
	}
	
	void Update () {
        
	}
}
```

初期化時にTargetObjectへの向きベクトルを取得し、自身の速度に代入します。
(ターゲットの座標 - 自身の座標)で対象へのベクトルが取得できます。数学。
そのベクトルを正規化(大きさを1に)してからスピードをかけます。

初期化時に一度だけ速度を設定するのがポイントです。
Updateではなにもしません。

<img width="379" alt="スクリーンショット 2018-07-07 19.01.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/82356/e3238071-76e5-0707-d946-ddf6a789f0ce.png">

InspecterからTargetObjectを設定仕上げればおkです。

また、このオブジェクトをInstantiateした際にTargetObjectを設定してあげても意図した動作をします。
自機を狙ってくる敵の弾などに使えます。

## 特定のオブジェクトを追尾する

```c#:TrackingObject.cs
public class TrackingObject : MonoBehaviour
{

    public GameObject TargetObject = null;
    public float speed = 5.0f;
    private Rigidbody2D _rigidbody;

    void Start()
    {
        _rigidbody = GetComponent<Rigidbody2D>();

    }

    void Update()
    {
        if (TargetObject)
        {
            _rigidbody.velocity = speed * (TargetObject.transform.position - transform.position).normalized;
        }
    }
}
```

先ほどと違ってUpdate内で常にTargetObjectの方に進むよう設定します。
ホーミングショットなどで使えます。

## 特定の動作を繰り返す

動くリフトなど、周期的な動きをするオブジェクトの実装方法です。

### 原始的な実装
```c#:TickObject.cs
public class TickObject : MonoBehaviour
{

    private float _actionTime = 1.0f;
    private float _tickTimer = 0.0f;
    private int actionNumber = 0;
    private Rigidbody2D _rigidbody;

    private Vector2[] actionList = { 
        new Vector2(5.0f, 0.0f), 
        new Vector2(0.0f, 5.0f),
        new Vector2(-5.0f, 0.0f),
        new Vector2(0.0f, -5.0f)
    };

    void Start()
    {
        _rigidbody = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        _rigidbody.velocity = actionList[actionNumber];
        if(_tickTimer >= _actionTime){
            actionNumber++;
            if (actionNumber >= actionList.Length) actionNumber = 0;
            _tickTimer = 0.0f;
        }

        _tickTimer += Time.deltaTime;
    }
}
```

actionListに動作を入れておきます(今回は速度)。actionNumberが対応actionListの番号です。
仕組みとしては、一定時間たったらactionNumberを増やしてあげる。actionListの最後までいったら、0に戻す。
これでとりあえずぐるぐるオブジェクトが動きます。

### アクションそれぞれに時間を設定する。
もう少し拡張して、
{ 1秒かけて右に移動, 2秒かけて上に移動, 1秒かけて左に移動, 2秒かけて下に移動 }
みたいなことをさせてみます。

```c#:TickObject2.cs
public class TickObject : MonoBehaviour
{

    private float _tickTimer = 0.0f;
    private int actionNumber = 0;
    private Rigidbody2D _rigidbody;

    struct Action
    {
        public Vector2 Velocity;
        public float Time;
        public Action(Vector2 v, float time){
            Velocity = v;
            Time = time;
        }
    };

    private Action[] actionList = { 
        new Action(new Vector2(5.0f, 0.0f), 1.0f), 
        new Action(new Vector2(0.0f, 5.0f), 2.0f), 
        new Action(new Vector2(-5.0f, 0.0f), 1.0f), 
        new Action(new Vector2(0.0f, -5.0f), 2.0f), 
    };

    void Start()
    {
        _rigidbody = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        _rigidbody.velocity = actionList[actionNumber].Velocity;
        if(_tickTimer >= actionList[actionNumber].Time){
            actionNumber++;
            if (actionNumber >= actionList.Length) actionNumber = 0;
            _tickTimer = 0.0f;
        }

        _tickTimer += Time.deltaTime;
    }
}
```
Actionという構造体を作ります。Actionは速度と実行時間を持っています。
先ほどのactionListをActionの配列に変更します。

Update内で速度はActionのVelocityを、時間はActionのTimeを参照することで動きごとに違う時間で実行しています。

なおC#7以降ではタプルという機能を使ってもっと簡潔にできます。

これ以上拡張したい場合は後述する外部ライブラリを用いると良いでしょう。

### DOTweenを使う
DOTweenという外部ライブラリを使うと、連続した動きを作りやすくなります。
詳しくは割愛。
[くわしくはこちらなど](https://qiita.com/kagigi/items/bdf4d42835add07b0077)

# アニメーション
## 画像を分割する
ゲームを作っているとアニメーション等の素材が一枚にまとめられて送られてきます。
その場合、画像のInspectorのSpriteModeを[Multiple]にして、SpriteEditorを開きます。


![sprite_multiple.PNG](https://qiita-image-store.s3.amazonaws.com/0/270406/623a9a2e-7927-586e-0d97-d2635d8a6251.png)

SpriteEditorの中で[Slice]のボタンを押し、
Typeを[Grid By Cell Size]、切りたい縦横の長さを入れて[Slice]ボタンを押すと画像が分割されます。

操作が終わったら右上の方の[Apply]を押すのをお忘れなく。

![sprite_editor.png](https://qiita-image-store.s3.amazonaws.com/0/270406/d85d2bbb-bdac-da97-75b0-5ec78ed6b5b8.png)




# サウンド
## BGMをループ再生する
ゲームを作っていると、BGMを「イントロ→ループ部分→ループ部分→ループ部分→…」とループさせたくなるときがあります。
その場合、以下のスクリプトをシーン上のGameObjectにアタッチして、Inspectorでイントロ部分とループ部分に分けた音源を設定すればよいです。
![ss.png](https://qiita-image-store.s3.amazonaws.com/0/270409/1872817b-86d1-5d8c-72d4-e4b9b4b7fc32.png)

```c#:LoopBgmPlayer.cs
using UnityEngine;

public class LoopBgmPlayer: MonoBehaviour {

    public AudioClip intro; // イントロ部分の音源
    public AudioClip loop; // ループ部分の音源
    private float delay = 0.1f; // 再生を安定させるための遅延時間（0.1くらいあれば大丈夫）

    void Start () {
        AudioSource audioSourceIntro = gameObject.AddComponent<AudioSource>();
        AudioSource audioSourceLoop = gameObject.AddComponent<AudioSource>();
    
        if(intro != null){
            audioSourceIntro.playOnAwake = false;
            audioSourceIntro.clip = intro;
            audioSourceIntro.PlayScheduled(AudioSettings.dspTime + delay);
            if(loop != null){
                audioSourceLoop.playOnAwake = false;
                audioSourceLoop.clip = loop;
                audioSourceLoop.loop = true;
                audioSourceLoop.PlayScheduled(AudioSettings.dspTime + delay + intro.length);
            }
        }
    }
}
```

## 効果音を再生する
### 一般的なやり方
`AudioSource.PlayOneShot`を使いましょう。予めGameObjectにAudioSourceをアタッチしておいてください。
例えば敵に命中した時にヒット音を再生する弾はこんな感じ。

```c#:Bullet.cs
public class Bullet : MonoBehaviour {
    private AudioSource audioSource;
    public AudioClip clip;

    void Start () {
        audioSource = GetComponent<AudioSource> ();
    }

    void OnCollisionEnter2D (Collision2D col) {
        if(col.gameObject.tag=="Enemy") {
            audioSource.PlayOneShot (clip);
        }
    }
}
```
### GameObjectがなくなっても音が消えないようにする
上の方法だとAudioSourceをアタッチしたGameObjectがなくなると、一緒に効果音も消えてしまいます。
つまり、

```c#
if(col.gameObject.tag=="Enemy") {
    audioSource.PlayOneShot (clip);
    Destoroy(gameObject);
}
```
としていた場合は上手く効果音が鳴りません。
対処法は2つ。

1つ目はシーン上に効果音再生専用のGameObjectを作っておき、それにAudioSourceをアタッチしておいて、効果音を鳴らす時はその効果音再生用のAudioSourceから鳴らしてやるという方法。
![sep.png](https://qiita-image-store.s3.amazonaws.com/0/270409/c6a6d4d2-4dc6-5a44-a4ab-ed35be5fe00c.png)

```c#:Bullet.cs
public class Bullet : MonoBehaviour {
    private AudioSource audioSource;
    public AudioClip clip;

    void Start () {
        // audioSourceには、シーン上のGameObject「SePlayer」にアタッチされたAudioSourceを登録しておく
        audioSource = GameObject.Find("SePlayer").GetComponent<AudioSource>();
    }

    void OnCollisionEnter2D (Collision2D col) {
        if(col.gameObject.tag=="Enemy") {
            audioSource.PlayOneShot (clip);
            Destoroy(gameObject);
        }
    }
}
```

2つ目は`AudioSource.PlayClipAtPoint`を使用する方法。

```c#:Bullet.cs
public class Bullet : MonoBehaviour {
    public AudioClip clip;

    void Start () {
    }

    void OnCollisionEnter2D (Collision2D col) {
        if(col.gameObject.tag=="Enemy") {
            // 現在弾がある場所で効果音を再生する(２つ目の引数は再生する座標)
            AudioSource.PlayClipAtPoint(clip, transform.position);
            Destoroy(gameObject);
        }
    }
}
```

# ハイスコア機能

```c#:HighScoreSystem
using System.Linq;

public class HighScoreSystem : MonoBehaviour {
    
    List<int> scores = new List<int>{10000, 8000, 6000, 4000, 2000};

	void Start () {
        if(PlayerPrefs.HasKey("scores")){
            var strScores = PlayerPrefs.GetString("scores").Split(',');
            scores = strScores.Select(str => int.Parse(str)).ToList<int>();
        }else{
            SaveScores();
        }
	}

    public void UpdateScore(int score){
        scores.Add(score);
        scores.Sort((a, b) => b - a);
        scores.RemoveAt(scores.Count - 1);

        SaveScores();
    }

    private void SaveScores(){
        var saveString = string.Join(",", scores.Select(scores => scores.ToString()).ToArray());
        PlayerPrefs.SetString("scores", saveString);
        PlayerPrefs.Save();
    } 
}

```

Linqっていう機能を使って実装した例。using System.Linq;で使えるようになります。Linqの詳しいことは割愛。
UpdateScoreを呼び出してやればスコアが更新される。この場合は上位5件のみ。
スコアの表示はまた別に実装してください。

> 簡単にだけやってること解説。
> PlayerPrefsを使うと簡単なセーブ/ロードの実装が可能
> scoresっていう配列がハイスコアの記録を持っている。
> UpdateScoresが呼び出されるとsocresに新しいスコアを追加。降順にソートして末尾のスコアを削除。これで上位5件だけ残る。
> そのスコアをカンマ区切りに直して文字データとして保存。
> ロードはStartで勝手に行う。まだセーブデータがなければ新たに作る。

# タイルマップ
ツクール的にマップを作る
ここが詳しい。
https://blogs.unity3d.com/jp/2018/01/25/2d-tilemap-asset-workflow-from-image-to-level/
↑↑↑↑↑↑↑ 編集リクエストの内容

# UI
編集リクエスト募集中
# カメラ
## マリオカート風画面分割
MainCameraのViewPortRectを操作すると画面分割が作れます。

ViewPortRectは画面左下が(0,0)画面右上が(1,1)となっています。

![Camera1.png](https://qiita-image-store.s3.amazonaws.com/0/270406/1c97c68f-0072-d69b-cf0c-cd7366ac3934.png)

試しにWidthとHeightを0.5にして左下の画面(X=0,y=0)を作ってみました。

![CameraPreview.PNG](https://qiita-image-store.s3.amazonaws.com/0/270406/ca31f3f2-7367-4ae4-2b2a-0ce2259c7d9e.png)

右上の画面を作る場合はX=0.5,Y=0.5とすると同じように作れます。


# パーティクル
編集リクエスト募集中
# マウス関連
## 物体上のクリックやドラッグを検出する
OnMouseから始まる関数の中に処理を記述するのが楽。実行のタイミングはそれぞれ以下の通り。

・OnMouseEnter：マウスポインタがCollider上に入った瞬間
・OnMouseExit：マウスポインタがCollider上から外に出た瞬間
・OnMouseDown：Collider上でマウスをクリックした瞬間
・OnMouseDrag：Collider上でマウスを長押ししている間（長押しを続けていればCollider上から出ても実行される）
・OnMouseOver：Collider上にマウスが乗っている間
・OnMouseUp：Collider上でマウスのクリック状態を解除した瞬間
・OnMouseUpAsButton：Collider上でマウスがクリックされ、そのままクリックが解除された瞬間

ただし、物体にColliderかCollider2Dがアタッチされていないと動作しないので注意。
Colliderが付いてるけどマウスには反応させたくないって場合は、InspectorからLayerを「Ignore Raycast」にすればOK

```cs:MouseEventDebugger.cs
using UnityEngine;
using System.Collections;

public class MouseEventDebugger : MonoBehaviour {
	void OnMouseEnter() {
		Debug.Log("物体の上にマウスポインタが乗りました");
	}
	void OnMouseExit() {
		Debug.Log("物体の上からマウスポインタが出ていきました");
	}
	void OnMouseDown() {
		Debug.Log("物体の上でマウスがクリックされました");
	}
	void OnMouseDrag() {
		Debug.Log("物体の上でマウスが長押しされています");
	}
	void OnMouseOver() {
		Debug.Log("物体の上にマウスポインタが乗っています");
	}
	void OnMouseUp() {
		Debug.Log("物体の上でマウスのクリックが解除されました");
	}
	void OnMouseUpAsButton () {
		Debug.Log ("物体の上でマウスクリック→離すという動作が連続して行われました");
	}
}
```