当前位置:   article > 正文

Unity中Scriptable Object的使用总结_unity scriptobject

unity scriptobject

一.什么是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
{
   //可以包含更多的数据,信息
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

当我们需要一个新的标志时,创建并命名一个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");
        }

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

在这里插入图片描述

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();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

在这里插入图片描述
我们需要创建一个脚本,为这个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);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

在这里插入图片描述
现在我们点击面板上新创建的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();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

事件的发送者和接收者分别有一个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;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
//事件的发送者
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();
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

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); 
	 	}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
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);
		}		
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/112495
推荐阅读
相关标签
  

闽ICP备14008679号