8.ScriptableObject多态

8.ScriptableObject应用-数据带来的多态行为


8.1 知识点

什么是数据带来的多态行为

某些行为的变化是因为数据的不同带来的。我们可以利用面向对象的特性和原则,以及设计模式相关知识点,结合 ScriptableObject 做出更加方便的功能。比如随机音效,物品拾取,AI等等等。

随机音效 (里氏替换原则和依赖倒转原则)

播放音乐时,可能会随机播放多个音效当中的一种。

物品拾取(里氏替换原则和依赖倒转原则)

比如拾取一个物品,物品给玩家带来不同的效果。

AI

不同数据带来的不同行为模式。

为了方便我们使用,我们可以利用 ScriptableObject 的可配置性来制作这些功能

举例说明

随机音效

创建 AudioPlayBase 抽象类脚本,作为音效播放抽象基类,继承 ScriptableObject。定义一个播放音乐的抽象方法 Play,外部传入一个音效源组件。

// 声明一个抽象类AudioPlayBase,继承自ScriptableObject类
public abstract class AudioPlayBase : ScriptableObject
{
    // 声明一个抽象方法Play,接收一个AudioSource参数,无返回值
    public abstract void Play(AudioSource source);
}

创建 RandomPlayAudio 随机播放音效类。继承音效播放基类,注意就间接继承了 ScriptableObject。添加 [CreateAssetMenu()] 特性,这样可以在 Inspector 窗口右键创建随机播放音效数据文件。定义一个音乐切片列表,存储音乐切片。实现播放音乐的方法 Play,随机播放音乐切片文件的音乐。

[CreateAssetMenu()]
public class RandomPlayAudio : AudioPlayBase
{
    public List<AudioClip> clips;

    public override void Play(AudioSource source)
    {
        if (clips.Count == 0)
            return;

        int randomIndex = Random.Range(0, clips.Count);
        AudioClip randomClip = clips[randomIndex];

        source.clip = randomClip;
        source.Play();
    }
}


主脚本可以声明一个音效基类变量,在开始时调用音效基类变量的播放音效方法。把主脚本挂载到场景中一个空对象上,空对象添加音效源组件。关联随机播放音效数据文件,这里用到了里氏替换原则。这样就实现了随机播放音效。

public class Lesson08_ScriptableObject应用_数据带来的多态行为 : MonoBehaviour
{
    public AudioPlayBase audioPlay;

    void Start()
    {
        audioPlay.Play(this.GetComponent<AudioSource>());
    }
}

可以再创建一个继承AudioPlayBase音效播放基类的唯一播放类。只能播放唯一关联的音频。也添加 [CreateAssetMenu()] 特性并创建对应数据文件。这样场景中的主脚本也可以关联唯一播放类的数据文件,实现多态。

[CreateAssetMenu()]
public class PlayerAudio : AudioPlayBase
{
    public AudioClip clip;

    public override void Play(AudioSource source)
    {
        source.clip = clip;
        source.Play();
    }
}

物品拾取

定义物品效果抽象基类继承 ScriptableObject,定义添加效果抽象方法。

public abstract class ItemEffect : ScriptableObject
{
    public abstract void AddEffect(GameObject obj);
}

分别定义加攻击力和加血类继承物品效果抽象基类。实现添加效果方法。分别创建数据文件。

[CreateAssetMenu]
public class AddAtkItemEffect : ItemEffect
{
    public int atk;

    public override void AddEffect(GameObject obj)
    {
        // 具体加多少攻击力的逻辑
    }
}

[CreateAssetMenu]
public class AddHealthItemEffect : ItemEffect
{
    public int num;

    public override void AddEffect(GameObject obj)
    {
        // 通过获取到的对象 让其加血了 加 num 的值
    }
}

定义一个物品对象类。碰撞时添加调用物品效果抽象基类对象的添加效果方法。挂载到场景中的方块中当做一个物品,这样物品上的物品对象类脚本可以自己选择关联哪个具体效果类数据文件。

public class ItemObj : MonoBehaviour
{
    public ItemEffect eff;

    private void OnTriggerEnter(Collider other)
    {
        eff.AddEffect(other.gameObject);
    }
}

总结

这些例子中的功能就算不用 ScriptableObject 也是能够用面向对象的思想结合配置文件来完成的。但是 ScriptableObject 具备自己的几个优点:

  1. 更方便的配置
  2. 共享数据节约内存

在实现某些功能的时候,使用 ScriptableObject 会更加方便我们的使用。


8.2 知识点代码

Lesson08_ScriptableObject应用_数据带来的多态行为

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

public class Lesson08_ScriptableObject应用_数据带来的多态行为 : MonoBehaviour
{
    #region 知识点二 举例说明

    public AudioPlayBase audioPlay;

    #endregion


    void Start()
    {
        #region 知识点一 什么是数据带来的多态行为
        //某些行为的变化是因为数据的不同带来的
        //我们可以利用面向对象的特性和原则,以及设计模式相关知识点
        //结合ScriptableObject做出更加方便的功能

        //比如随机音效,物品拾取,AI等等等

        //随机音效(里氏替换原则和依赖倒转原则)
        //播放音乐时,可能会随机播放多个音效当中的一种

        //物品拾取(里氏替换原则和依赖倒转原则)
        //比如拾取一个物品,物品给玩家带来不同的效果

        //AI
        //不同数据带来的不同行为模式

        //为了方便我们使用,我们可以利用ScriptableObject的可配置性来制作这些功能
        #endregion

        #region 知识点二 举例说明

        audioPlay.Play(this.GetComponent<AudioSource>());

        #endregion

        #region 总结
        //其实这些例子中的功能就算不用ScriptableObject也是能够用
        //面向对象的思想 结合配置文件来完成的
        //但是ScriptableObject具备自己的几个优点
        //1.更方便的配置
        //2.共享数据节约内存
        //在实现某些功能的时候,使用ScriptableObject会更加方便我们的使用
        #endregion
    }

}

音效

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

// 声明一个抽象类AudioPlayBase,继承自ScriptableObject类
public abstract class AudioPlayBase : ScriptableObject
{
    // 声明一个抽象方法Play,接收一个AudioSource参数,无返回值
    public abstract void Play(AudioSource source);
}
using UnityEngine;
using System.Collections.Generic;

// 使用CreateAssetMenu特性,使得在Unity编辑器中可以创建和管理RandomPlayAudio脚本生成的ScriptableObject对象
[CreateAssetMenu()]
public class RandomPlayAudio : AudioPlayBase
{
    // 声明一个List<AudioClip>类型的字段用于存储希望随机播放的音频文件
    public List<AudioClip> clips;

    // 重写基类的抽象方法Play,实现随机播放音频的逻辑
    public override void Play(AudioSource source)
    {
        // 如果音频文件列表为空,则直接返回
        if (clips.Count == 0)
            return;

        // 从音频文件列表中随机选择一个音频文件
        int randomIndex = Random.Range(0, clips.Count);
        AudioClip randomClip = clips[randomIndex];

        // 设置要播放的音频文件到传入的AudioSource组件
        source.clip = randomClip;

        // 播放音频文件
        source.Play();
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


// 使用CreateAssetMenu特性,使得在Unity编辑器中可以创建和管理PlayerAudio脚本生成的ScriptableObject对象
[CreateAssetMenu()]
public class PlayerAudio : AudioPlayBase
{
    // 声明一个AudioClip类型的字段用于存储要播放的音频文件
    public AudioClip clip;

    // 重写基类的抽象方法Play,实现播放指定音频的逻辑
    public override void Play(AudioSource source)
    {
        // 设置要播放的音频文件到传入的AudioSource组件
        source.clip = clip;

        // 播放音频文件
        source.Play();
    }
}

物品

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

public abstract class ItemEffect : ScriptableObject
{
    public abstract void AddEffect(GameObject obj);
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu]
public class AddAtkItemEffect : ItemEffect
{
    public int atk;
    public override void AddEffect(GameObject obj)
    {
       //具体加多少攻击力的逻辑
    }
}
``

---

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏