Unity中Scriptable Object的使用总结

一.什么是Scriptable Object?

用来存储数据的一个资源文件,像是JSON、XML、文本文件这样的存储文件,可以用来存储数据。但是这里他在最开始的时候不需要再次读文件,就像是JSON在游戏开始时,我们需要加载JSON文件中的数据,我们就需要读取这个文件,然后在赋值给对象。他就可以直接使用其中的数据。因为他是资源文件,所以他有着资源文件的特性。

二.Scriptable Object可以用来做什么?

1.替代enum

Enum的缺点:必须改代码,删改不方便,不能存储更多的数据
用法如下

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName ="TabSO",menuName ="ScriptableObject/NewTab",order = 1 )]
public class TabSO : ScriptableObject
{
   //可以包含更多的数据,信息
}

当我们需要一个新的标志时,创建并命名一个SO即可
在这里插入图片描述

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");
        }

    }
}

在这里插入图片描述

2.作为数据容器

原因:对设计师友好,对Git友好,可以被别处监听,方便打包运输

数据监听:例.玩家的HP与UI血条通过Scriptable Object解耦
在这里插入图片描述
在这里插入图片描述

配置文件:例.玩家敌人的基础属性
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.扩展编辑器

如果默认的内容不足以满足我们的要求,可以自定义扩展
在这里插入图片描述

例:我们在创建了一个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();
    }
}

在这里插入图片描述
我们需要创建一个脚本,为这个ScriptObject服务

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);
        }
    }
}

在这里插入图片描述
现在我们点击面板上新创建的PreviewAudio按钮即可播放开枪声

4.把ScriptObject当成事件来使用

首先需要一个充当桥梁的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();
    }

}

5.RuntimeSet

例:当有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);
		}		
	}
}
  • 14
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值