文章目录
- 一、配置数据
- 二、复用数据
- 三、多态特性的利用
- 四、单例模式获取数据
一、配置数据
ScriptableObject 数据文件非常适合用来做配置文件:
- 配置文件的数据在游戏发布之前定规则
- 配置文件的数据在游戏运行时只会读出来使用,不会改变内容
- 在 Unity 的 Inspector 窗口进行配置更加的方便
对于如下角色配置信息:
可以采用如下方法进行配置:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "RoleInfo", menuName = "ScriptableObject/角色信息")]
public class RoleInfo : SingleScriptableObject<RoleInfo>
{
[System.Serializable]
public class RoleData
{
public int id;
public string res;
public int atk;
public string tips;
public int lockMoney;
public int type;
public string hitEff;
}
public List<RoleData> roleList;
}
创建数据文件后进行相应的编辑,使用时声明成员进行关联即可。
只用不改,并且经常会进行配置的数据,非常适合使用 ScriptableObject
我们可以利用 ScriptableObject 数据文件来制作编辑器相关功能,比如:Unity内置的技能编辑器、关卡编辑器等等
我们不需要把编辑器生成的数据生成别的数据文件,而是直接通过 ScriptableObject 进行存储
因为内置编辑器只会在编辑模式下运行,编辑模式下 ScriptableObject 具备数据持久化的特性
二、复用数据
以子弹对象为例,其数据结构如下:
public class Bullet : MonoBehaviour
{
public float speed;
public int atk;
// Update is called once per frame
void Update() {
this.transform.Translate(Vector3.forward * speed * Time.deltaTime);
}
}
由于相同子弹的 speed 和 atk 一样,因此创建多个子弹时,相同数据的 speed 和 atk 占据了多余的内存。作出如下修改:
Bullet.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet : MonoBehaviour
{
public BulletInfo info;
// Update is called once per frame
void Update() {
this.transform.Translate(Vector3.forward * info.speed * Time.deltaTime);
}
}
BulletInfo.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu()]
public class BulletInfo : ScriptableObject
{
public float speed;
public int atk;
}
创建 BulletInfo 数据文件后关联到 Bullet 的成员 info。这样之后创建了多个子弹,它们的数据都是一样,更改 info 数据文件,所有子弹的数据都将被修改。
三、多态特性的利用
某些行为的变化是因为数据的不同带来的,我们可以利用面向对象的特性和原则,以及设计模式相关知识点,结合 ScriptableObject 做出更加方便的功能
比如随机音效,物品拾取,AI等等等(里氏替换原则和依赖倒转原则)
- 随机音效:播放音乐时,可能会随机播放多个音效当中的一种
- 物品拾取:拾取一个物品,物品给玩家带来不同的效果
- AI:不同数据带来的不同行为模式
(一)随机音效播放
基类:播放音效设置
public abstract class AudioPlayBase : ScriptableObject
{
public abstract void Play(AudioSource source);
}
父类 1:随机播放音效
[CreateAssetMenu]
public class RandomPlayAudio : AudioPlayBase
{
// 希望随机播放的音效文件(数据文件)
public List<AudioClip> clips;
// 重写播放函数
public override void Play(AudioSource source) {
if (clips.Count == 0)
return;
// 设置音效切片文件
source.clip = clips[Random.Range(0, clips.Count)];
source.Play();
}
}
父类 2:播放指定音效
[CreateAssetMenu()]
public class PlayerAudio : AudioPlayBase
{
public AudioClip clip;
public override void Play(AudioSource source) {
source.clip = clip;
source.Play();
}
}
-
步骤一:创建 RandomPlayAudio 数据文件
-
步骤二:通过基类调用两种父类方法
public class Lesson7 : MonoBehaviour { public AudioPlayBase audioPlay; void Start() { audioPlay.Play(this.GetComponent<AudioSource>()); } }
在 Inspector 窗口中配置即可
(二)物品拾取
通过判断物品类型来实现不同的效果
基类:物品效果
public abstract class ItemEffect : ScriptableObject
{
public abstract void AddEffect(GameObject obj);
}
父类 1:加血效果
[CreateAssetMenu]
public class AddHealthItemEffect : ItemEffect
{
public int num;
public override void AddEffect(GameObject obj) {
// 通过获取到的对象 让其加血 加num的值
}
}
父类 2:加攻击力效果
[CreateAssetMenu]
public class AddAtkItemEffect : ItemEffect
{
public int atk;
public override void AddEffect(GameObject obj) {
// 具体加多少攻击力的逻辑
}
}
物品对象:
public class ItemObj : MonoBehaviour
{
public ItemEffect eff;
private void OnTriggerEnter(Collider other) {
// 为和物品产生碰撞的对象加效果
eff.AddEffect(other.gameObject);
}
}
-
步骤一:生成数据文件,配置数据
-
步骤二:创建物品预制体,关联数据文件
-
步骤三:物品实现完成
四、单例模式获取数据
对于只用不变并且要复用的数据,比如配置文件中的数据,往往需要在很多地方获取它们
将此类数据通过单例模式化的去获取,可以提升效率,减少代码量
单例模式基类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SingleScriptableObject<T> : ScriptableObject where T : ScriptableObject
{
// 所有的数据文件都放在 Resources文件夹下的 ScriptableObject 中
private static string LOAD_PATH = "ScriptableObject/";
private static T instance;
public static T Instance {
get {
// 如果为空 首先应该去资源路劲下加载 对应的 数据资源文件
if (instance == null) {
// 定两个规则
// 1.所有的 数据资源文件都放在 Resources文件夹下的 ScriptableObject 中
// 2.需要复用的 唯一的数据资源文件名 我们定一个规则:和类名是一样的
instance = Resources.Load<T>(LOAD_PATH + typeof(T).Name);
}
// 如果没有这个文件 为了安全起见 我们可以在这直接创建一个数据
if (instance == null) {
instance = CreateInstance<T>();
}
// 甚至可以在这里 从json当中读取数据,但是不建议用ScriptableObject来做数据持久化
return instance;
}
}
// 对于相同类型不同名称的数据资源,通过该方法指定名称加载
public static T Load(string name) {
return Resources.Load<T>(LOAD_PATH + name);
}
}
使用:
[CreateAssetMenu]
public class TestData : SingleScriptableObject<TestData>
{
public int i;
public bool b;
}
public class Lesson8 : MonoBehaviour
{
// Start is called before the first frame update
void Start() {
print(TestData.Instance.i);
print(TestData.Load("TestData").b);
}
}