用来存储数据的一个资源文件,像是JSON、XML、文本文件这样的存储文件,可以用来存储数据。但是这里他在最开始的时候不需要再次读文件,就像是JSON在游戏开始时,我们需要加载JSON文件中的数据,我们就需要读取这个文件,然后在赋值给对象。他就可以直接使用其中的数据。因为他是资源文件,所以他有着资源文件的特性。
Enum的缺点:必须改代码,删改不方便,不能存储更多的数据
用法如下
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName ="TabSO",menuName ="ScriptableObject/NewTab",order = 1 )]
public class TabSO : ScriptableObject
{
//可以包含更多的数据,信息
}
public class PlayerSo : MonoBehaviour
{
[SerializeField] TabSO myType;
[SerializeField] TabSO t1;
[SerializeField] TabSO t2;
// Start is called before the first frame update
void Start()
{
if (t1 == myType)
{
Debug.Log("Fire");
}
if (t2 == myType)
{
Debug.Log("Go");
}
}
}
原因:对设计师友好,对Git友好,可以被别处监听,方便打包运输
例:我们在创建了一个SO,上面携带了AudioClip,我们打算在编辑器上点击播放
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName = "Scriptable Object/NewAudioSO",order =1)]
public class AudioSO : ScriptableObject
{
public AudioClip[] clips;
[Range(0f, 1f)] public float minVolume;
[Range(0f, 1f)] public float maxVolume;
[Range(-2f, 2f)] public float minPitch;
[Range(-2f, 2f)] public float maxPitch;
public void Play(AudioSource source)
{
if (clips.Length <= 0)
return;
source.clip = clips[Random.Range(0,clips.Length)];
source.volume = Random.Range(minVolume, maxVolume);
source.pitch = Random.Range(minPitch, maxPitch);
source.Play();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEditor;
[CustomEditor(typeof(AudioSO))] //声明自己为哪个类服务
public class AudioSOEditor : Editor
{
//服务的目标
private AudioSO _target;
private AudioSource _previewAudioSource;
private void OnEnable()
{
_target = target as AudioSO;
//创建一个隐藏的AudioSource
_previewAudioSource = EditorUtility.CreateGameObjectWithHideFlags(
"AudioPreview",HideFlags.HideAndDontSave,typeof(AudioSource)).GetComponent<AudioSource>();
}
//因为_previewAudioSource是HideAndDontSave,所以在OnDisable里必须销毁
private void OnDisable()
{
DestroyImmediate(_previewAudioSource);
}
//OnInspectorGUI上创建一个按钮播放它
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
//在原来基础上加一个按钮
if(GUILayout.Button("PreviewAudio"))
{
_target.Play(_previewAudioSource);
}
}
}
首先需要一个充当桥梁的ScriptObject
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
[CreateAssetMenu(menuName ="EventsSO/Event Channel")]
public class EventSO : ScriptableObject
{
public UnityAction action;
public void Raise()
{
action?.Invoke();
}
}
事件的发送者和接收者分别有一个EventSO字段,存放新建的SO
事件的接收者为SO内的action添加事件
public class Test : MonoBehaviour
{
[SerializeField] EventSO eso;
void print()
{
Debug.Log("Button被点击");
}
private void Start()
{
eso.action += print;
}
private void OnDisable()
{
eso.action -= print;
}
}
//事件的发送者
public class NewButtonScript : MonoBehaviour
{
Button b;
[SerializeField] EventSO eso;
// Start is called before the first frame update
void Start()
{
b = this.GetComponent<Button>();
if (b == null)
Debug.LogError("未找到Button");
b.onClick.AddListener(bOnclick);
}
private void OnDisable()
{
b.onClick.RemoveAllListeners();
}
void bOnclick()
{
eso.Raise();
}
}
例:当有n个敌人,其中一个死了后,其他敌人需要做出某行为(例如赶到死亡地点)
原始做法:创建一个单例类,包含一个List字段,将所有敌人登记进去,当某个敌人死亡后,删除它并遍历List中所有的敌人调用行为函数
ScriptObject做法:创建一个SO,内包含一个List字段,添加功能,删除功能,和遍历所有敌人调用行为函数功能,这样我们就不需要新建一个单例并额外挂载脚本到场景中了。
[CreateAssetMenu(fileName = "RuntimeSetSo",menuName = "ScriptableObjects/RuntimeSetSo",order = 1)]
public class RuntimeSetSo:ScriptableObject
{
public List<Enemy> _ListEnemys = new List<Enemy>();
public Add(Enemy enemy)
{
if(!_ListEnemys.Contains(enemy))
_ListEnemys.Add(enemy);
}
public Remove(Enemy enemy)
{
if(_ListEnemys.Contains(enemy))
_ListEnemys.Remove(enemy);
}
//----遍历除死亡敌人外的所有敌人,并执行某一行为
public void OnEnemyDie(Enemy enemy,Vector3 location)
{
Remove(enemy);
foreach(var _enemy in _ListEnemys)
{
_enemy.GotoLocation(location);
}
}
}
public class Enemy:MonoBehaviour
{
[SerializeField] private RuntimeSetSo runtimeSetSo;
private void Start()
{
if(runtimeSetSo!=null)
{
runtimeSetSo.Add(this);
}
}
private void Update()
{
if(this.Die())
{
runtimeSetSo.OnEnemyDie(this,this.transform.position);
}
}
}