#UNITYでゲーム作るときに難易度調整するためのデータ作りについて
ScriptableObjectを使います。
このような数値調整をUNITY上でやりたい場合のやり方です。
上記の画像にあるデータは、いくつのモノを壊したら、次のレベルに進めるか、そのレベルの難易度はどのぐらいかといったデータを表しています。
#どんなゲームを作った際に使ったものか
まず、Garbagersというゲームを作ったのですが、その際の難易度などに関するデータ作りに関して作ったものです。
https://mosq.xyz/Garbagers/
#まず基本となるデータ
スリーマッチパズルゲームで、多くのゴミを壊すと次のレベルに進み少しづつ難しくなるゲームでした。テトリスのように。
なので、いくつ壊したら次のレベルに進めるか(int)、ゴミが出現するインターバル時間(float)を設定できるようにしました。(GVLevelInfo)
さらに、ゴミがつっかえて溜まってくるとゴミが速く出るようにしました。
横、縦への速度、回転速度、インターバルを指定できるようにしました。(GVPressureInfo)
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宣言以降、三行だけが重要であとはおまけの関数です。
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のリストも同様に作ります。
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エディタ上でのみ利用され、ビルドして出力したアプリなどには含まれません。
エディタ上で、データを作ったりする際に使います。
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
これをゲームで使っていきます。
#実際に使う
普通に、MonoBehaviourにインスペクタから設定することができます。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GarbagersSystem : MonoBehaviour {
[SerializeField]
LevelInfoUI _levelInfo;
[SerializeField]
GVPressureInfoList _pressureInfo;
...
インスペクタに先ほどのメニューから作ったデータをドラッグ&ドロップすればデータが入るので、使いましょう。