レベルに応じてゲームの難易度を変える実装

#UNITYでゲーム作るときに難易度調整するためのデータ作りについて
ScriptableObjectを使います。
このような数値調整をUNITY上でやりたい場合のやり方です。
![スクリーンショット 2019-09-29 0.19.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59173/5869f475-394a-0539-44dd-a741505ce15d.png)

上記の画像にあるデータは、いくつのモノを壊したら、次のレベルに進めるか、そのレベルの難易度はどのぐらいかといったデータを表しています。

#どんなゲームを作った際に使ったものか
まず、Garbagersというゲームを作ったのですが、その際の難易度などに関するデータ作りに関して作ったものです。
https://mosq.xyz/Garbagers/



#まず基本となるデータ
スリーマッチパズルゲームで、多くのゴミを壊すと次のレベルに進み少しづつ難しくなるゲームでした。テトリスのように。
なので、いくつ壊したら次のレベルに進めるか(int)、ゴミが出現するインターバル時間(float)を設定できるようにしました。(GVLevelInfo)
さらに、ゴミがつっかえて溜まってくるとゴミが速く出るようにしました。
横、縦への速度、回転速度、インターバルを指定できるようにしました。(GVPressureInfo)

~~~LevelClasses.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[Serializable]
public class GVLevelInfo
{
	public int NextBlockCount;
	public float SpawnInterval = 0f;
}

[Serializable]
public class GVPressureInfo
{
	public float HorizontalSpeed;
	public float VerticalSpeed;
	public float AngularSpeed;
	public float Interval;
}
~~~
[Serializable]アトリビュートをつけておく必要があります。
これらをレベルごとに用意するためリストにします。
それとScriptableObjectにして、UNITY上に置けるデータにします。
（全部publicで書いちゃってますが、UNITYでは[SerializeField]アトリビュートをつけても、シリアライズされます）


#GVLevelInfoのリスト化したScriptableObjectを作る
class宣言以降、三行だけが重要であとはおまけの関数です。

~~~GVLevelInfoList.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class GVLevelInfoList : ScriptableObject
{
	public List<GVLevelInfo> List = new List<GVLevelInfo>();

	public int GetLevel(int breakCount)
	{
		int level = 0;
		foreach(var v in List)
		{
			if (v.NextBlockCount <= breakCount)
			{
				++level;
				continue;
			}
			break;
		}
		return level;
	}

	public float ToNextRatio(int level,int breakCount)
	{
		int prevCount = 0;		
		if (0 <= level-1)
		{
			prevCount = List[level-1].NextBlockCount;
		}
		if (level < List.Count)
		{
			var v = List[level];
			return (breakCount - prevCount) / (float)(v.NextBlockCount-prevCount);
		}
		return 1f;
	}
}
~~~

GVPressureInfoのリストも同様に作ります。

~~~GVPressureInfoList.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GVPressureInfoList : ScriptableObject
{
	public List<GVPressureInfo> List = new List<GVPressureInfo>();

	public GVPressureInfo GetPressure(int pressure)
	{
		return List[Mathf.Clamp(pressure,0,List.Count-1)];
	}
}
~~~
ということで、データ構造としては用意できました。
あとは、このデータをUNITYプロジェクトの中に追加する方法です。

#Editorフォルダを作ります
UNITYにおいて、Editorフォルダは特別な名前です。
ここに置かれたスクリプトファイルは、UNITYエディタ上でのみ利用され、ビルドして出力したアプリなどには含まれません。
エディタ上で、データを作ったりする際に使います。

~~~LevelInfoMenu.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

public static class LevelInfoMenu
{
	[MenuItem ("Create/CreateGVLevelInfoList")]
	static void CreateGVLevelInfoList ()
	{
		var asset = ScriptableObject.CreateInstance<GVLevelInfoList> ();

		AssetDatabase.CreateAsset (asset, "Assets/LevelInfoList.asset");
		AssetDatabase.Refresh ();
	}

	[MenuItem ("Create/CreateGVPressureInfoList")]
	static void CreatePressureInfoList ()
	{
		var asset = ScriptableObject.CreateInstance<GVPressureInfoList> ();

		AssetDatabase.CreateAsset (asset, "Assets/PressureInfoList.asset");
		AssetDatabase.Refresh ();
	}
}
~~~

このスクリプトを書くと、UNITYのトップメニューにCreateが追加されています。
実行すると、指定されたパスの場所にデータが追加されます。
インスペクター上で、データを編集することが可能です。

保存場所を選べるようにしたい場合は、下記のような関数を使います。
https://docs.unity3d.com/ja/current/ScriptReference/EditorUtility.SaveFilePanel.html
→プロジェクト内に保存する場合は、こっちでした。
https://docs.unity3d.com/ScriptReference/EditorUtility.SaveFilePanelInProject.html

![スクリーンショット 2019-09-29 0.19.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59173/5869f475-394a-0539-44dd-a741505ce15d.png)

![スクリーンショット 2019-09-29 0.31.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59173/b1d7ab40-09b9-ed14-efc8-1b8b1bb79881.png)


これをゲームで使っていきます。

#実際に使う
普通に、MonoBehaviourにインスペクタから設定することができます。


~~~GarbagersSystem.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GarbagersSystem : MonoBehaviour {
	[SerializeField]
	LevelInfoUI _levelInfo;
	[SerializeField]
	GVPressureInfoList _pressureInfo;
...
~~~

インスペクタに先ほどのメニューから作ったデータをドラッグ&ドロップすればデータが入るので、使いましょう。