栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 游戏开发 > Unity3D

Unity中Scriptable Object的使用总结

Unity3D 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Unity中Scriptable Object的使用总结

Scriptable Object
  • 一.什么是Scriptable Object?
  • 二.Scriptable Object可以用来做什么?
    • 1.替代enum
    • 2.作为数据容器
    • 3.扩展编辑器
    • 4.把ScriptObject当成事件来使用
    • 5.RuntimeSet

一.什么是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();
    }
    //因为_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
5.RuntimeSet

例:当有n个敌人,其中一个死了后,其他敌人需要做出某行为(例如赶到死亡地点)

原始做法:创建一个单例类,包含一个List字段,将所有敌人登记进去,当某个敌人死亡后,删除它并遍历List中所有的敌人调用行为函数
ScriptObject做法:创建一个SO,内包含一个List字段,添加功能,删除功能,和遍历所有敌人调用行为函数功能,这样我们就不需要新建一个单例并额外挂载脚本到场景中了。

[CreateAssetMenu(fileName = "RuntimeSetSo",menuName = "ScriptableObjects/RuntimeSetSo",order = 1)]
public class RuntimeSetSo:ScriptableObject
{
	public List _ListEnemys  = new List();
	
	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);
		}		
	}
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/898320.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号