【Unity】「floatの値でAudioSourceの再生、停止のコントロール」を考える。(JoystickPack)

# この記事について
Unityでスマホ向けにゲームを開発中なんですが、アセットストアにあるJoyStickPackを使って、「スティックを上下に動かした時に音を鳴らし続ける。」という処理を実装する上での私なりのやり方をご紹介します。
### Joystick Pack

https://assetstore.unity.com/packages/tools/input-management/joystick-pack-107631

今回使用しているJoyStickはこれです。


# やりたい事
今作っているフォークリフトのゲームがあるんですが、JoystickPackをいじってこんな感じのコントローラーを作りました。

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280116/5e7dd367-4b6a-08cb-a3a2-fa00e78386ac.png)

このコントローラのこの部分、

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280116/e1f0c60c-ec13-5a1f-35d1-c98f17f826c6.png)

これを上下させるとリフトの爪も上下するんですが、
それに合わせてｳｨｨｨｨﾝみたいに音を鳴らそうと思います。

このJoystickはぐりぐり動かした時にfloat型で-1.0f～1.0fを返してきます。

何もしてない時は0を返してくるので、値が変化した時に音を鳴らせば…
と思いきや、Update()内で適当にオーディオを再生すると、毎フレーム音を鳴らし続けてえらいことになってしまいます。

更に上げから下げに転じた時に一度止めてまた再生、爪の高さが限界に達したら音を止める。などなど。

というわけでやりたい事をまとめると、

### ・floatが0fでない時は再生し続ける。
### ・floatが0fになったら音を止める。
### ・操作中にfloatが＋からー（その逆も）に転じたらリプレイする。
### ・floatが一定の値になったら操作中でも音を止める。

です。

# 実装コード

まずは完成したコードがこちらです。

```c#
using UnityEngine;

public class RiftFork : MonoBehaviour
{
    public float power;//速度
    public float max;//上げの最大値
    public float min;//下げの最小値
    public GameObject mast;//フォークリフトの爪オブジェクト
    private AudioSource audioSource;
    private float vertical;//ジョイスティックの値
    private enum MODE//爪の動作状態
    {
        STOP,//停止
        UP,//上げ
        DOWN//下げ
    };
    MODE mode = MODE.STOP;

    //リフトの爪用のジョイスティック
    [SerializeField] private VariableJoystick mastJoyStick;

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

    private void FixedUpdate()
    {
        //ジョイスティックの値
        vertical = mastJoyStick.Vertical;

        //停止
        if (vertical == 0)
        {
            if (mode != MODE.STOP)
            {
                audioSource.Stop();
                mode = MODE.STOP;
            }
            else return;
        }
        
        //上昇
        else if (vertical > 0) RiftUp();


        //下降
        else if (vertical < 0) RiftDown();

        //範囲を制限して動かす
        transform.localPosition += Vector3.up * mastJoyStick.Vertical * power * Time.deltaTime;
        transform.localPosition = (new Vector3(transform.localPosition.x, Mathf.Clamp(transform.localPosition.y, min, max), transform.localPosition.z));
    }

    //上昇時
    void RiftUp()
    {
         
        if(mode!=MODE.UP)
        {
            ForkSoundReplay();
            mode = MODE.UP;
        }

        //一番上まで上がったら音を停止
        if(transform.localPosition.y==max)
        {
            ForkSoundStop();
        }

        else return;
    }

    //下降時
    void RiftDown()
    {
        if (mode != MODE.DOWN)
        {
            ForkSoundReplay();
            mode = MODE.DOWN;
        }

        //一番下まで下がったら音を停止
        if(transform.localPosition.y==min)
        {
            ForkSoundStop();
        }

        else return;
    }

    //リプレイ
    void ForkSoundReplay()
    {
        audioSource.Stop();
        audioSource.Play();
    }

    //再生中なら停止
    void ForkSoundStop()
    {
        if(audioSource.isPlaying)
        {
            audioSource.Stop();
        }
    }
}

```
もっと汎用性のあるコードにするべきかもですが思いっきり私のゲーム向けの実装になってます。ｽｲﾏｾﾝ。

実際に動かしてみたものがこちらになります。↓
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ジョイスティックの値の変化でオーディオを再生、停止できる機能を実装しました！後ほどQiitaで記事にしたいと思います。<a href="https://twitter.com/hashtag/Unity?src=hash&amp;ref_src=twsrc%5Etfw">#Unity</a> <a href="https://twitter.com/hashtag/%E3%82%B2%E3%83%BC%E3%83%A0%E9%96%8B%E7%99%BA?src=hash&amp;ref_src=twsrc%5Etfw">#ゲーム開発</a> <a href="https://twitter.com/hashtag/gamedev?src=hash&amp;ref_src=twsrc%5Etfw">#gamedev</a> <a href="https://t.co/SZUliaNFZb">pic.twitter.com/SZUliaNFZb</a></p>&mdash; スタジオヒライワン（ゲーム開発） (@hiraiwanbass) <a href="https://twitter.com/hiraiwanbass/status/1828355166929854763?ref_src=twsrc%5Etfw">August 27, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

音はさておき、
要はfloatの値を監視して、値が0なのか0より大きいのか小さいのかで判定すればいいわけです。
が、難しい点として __Joystickをタップしたままの状態で値が＋からーへと変化した場合、__ リフトの爪も上昇から下降に変わるので、その時に音をリプレイ（正確にはいったん止めて再生）するという点です。


詳しく見てみます。

# 解説

```c#
 public float power;//速度
    public float max;//上げの最大値
    public float min;//下げの最小値
    public GameObject mast;//フォークリフトの爪オブジェクト
    private AudioSource audioSource;
    private float vertical;//ジョイスティックの値
    private enum MODE//爪の動作状態
    {
        STOP,//停止
        UP,//上げ
        DOWN//下げ
    };
    MODE mode = MODE.STOP;

```
メンバーはこのようになってます。
max、minで最大値、最小値を設定します。
mastは動かす対象のオブジェクト。
verticalは動的にジョイスティックの値を格納する変数です。
そして、

```c#
private enum MODE//爪の動作状態
    {
        STOP,//停止
        UP,//上げ
        DOWN//下げ
    };
    MODE mode = MODE.STOP;
```
この定数はオブジェクトが今どういう状態なのかを判定するために用意しています。


続いてFixedUpdate内のコードを見てみます。
```c#
        //ジョイスティックの値
        vertical = mastJoyStick.Vertical;   
```

verical変数で常にJoystickの値を格納しています。
```c#
 //停止
        if (vertical == 0)
        {
            if (mode != MODE.STOP)
            {
                audioSource.Stop();
                mode = MODE.STOP;
            }
            else return;
        }
```

verticalの値が0の時、modeがSTOPでなければオーディオの停止を実行。modeをSTOPに切り替えます。

```c#
        //上昇
        else if (vertical > 0) RiftUp();


        //下降
        else if (vertical < 0) RiftDown();

        //範囲を制限して動かす
        transform.localPosition += Vector3.up * mastJoyStick.Vertical * power * Time.deltaTime;
        transform.localPosition = (new Vector3(transform.localPosition.x, Mathf.Clamp(transform.localPosition.y, min, max), transform.localPosition.z));
```
verticalの値に応じてRiftUp()、RiftDown()メソッドを実行します。要は上昇と下降ですね。
localPositionの移動制限を最後に行っています。


```c#
//上昇時
    void RiftUp()
    {
         
        if(mode!=MODE.UP)
        {
            ForkSoundReplay();
            mode = MODE.UP;
        }

        //一番上まで上がったら音を停止
        if(transform.localPosition.y==max)
        {
            ForkSoundStop();
        }

        else return;
    }

    //下降時
    void RiftDown()
    {
        if (mode != MODE.DOWN)
        {
            ForkSoundReplay();
            mode = MODE.DOWN;
        }

        //一番下まで下がったら音を停止
        if(transform.localPosition.y==min)
        {
            ForkSoundStop();
        }

        else return;
    }

    //リプレイ
    void ForkSoundReplay()
    {
        audioSource.Stop();
        audioSource.Play();
    }

    //再生中なら停止
    void ForkSoundStop()
    {
        if(audioSource.isPlaying)
        {
            audioSource.Stop();
        }
    }
}
```
RiftUp()、RiftDown()メソッドでmodeの変更とlocalPositionが制限値に達したらオーディオの停止、
ForkSoundReplay()でオーディオのリプレイ、
ForkSoundStop()でオーディオの停止を行っています。

こんな感じです。
冗長なコードになってしまったかもですが、何故素人なのでご勘弁を…
もっとシンプルなコードが書けるよう今後もがんばります！