游戏蛮牛学习群(纯技术交流,不闲聊):159852603
游戏蛮牛 手机端
开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

开发者专栏

关注:2353

当前位置:游戏蛮牛 技术专区 开发者专栏

__________________________________________________________________________________
开发者干货区版块规则:

  1、文章必须是图文形式。(至少2幅图)
      2、文章字数必须保持在1500字节以上。(编辑器右下角有字数检查)
      3、本版块只支持在游戏蛮牛原创首发,不支持转载。
      4、本版块回复不得无意义,如:顶、呵呵、不错......【真的会扣分的哦】
      5、......
__________________________________________________________________________________
查看: 2686|回复: 133

[志远] 大话链式编程之Audio改造术(二)

  [复制链接]  [移动端链接]
排名
9520
昨日变化
121

4

主题

159

帖子

729

积分

Rank: 9Rank: 9Rank: 9

UID
21402
好友
4
蛮牛币
175
威望
0
注册时间
2014-4-14
在线时间
327 小时
最后登录
2018-11-16

认证开发者

发表于 2018-7-6 11:58:11 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x
本帖最后由 zhiyuan 于 2018-7-16 17:00 编辑

没事没事我这不是来了嘛!上篇文章已经给大家对比了一下iTween和DoTween的编码思想区别,当然我是力挺DoTween的咯!
大家平时做项目百分之60乃至70的时间都在写基础且无逻辑的代码!  
那为什么我们不把这些常用的代码或者说模块改造成链式编程的模式囊?    怎么改?  让我慢慢道来~~

1).工欲善其事,必先利其器
       其实.net已经为我们提供了一个改造代码的好帮手了 -- 扩展方法
        来一下下面的代码
[AppleScript] 纯文本查看 复制代码
//获取音频组件并播放
gameObject.GetComponent<AudioSource>().Play();

貌似没什么问题,但是如果我现在提出需求要换一个clip再播放该怎么办?你会说简单啊!
[C#] 纯文本查看 复制代码
AudioSource audioSource = gameObject.GetComponent<AudioSource>();
//更换clip
audioSource.clip = 我是clip;
//获取音频组件并播放
audioSource.Play();
貌似有点麻烦了!我们想要的效果是audioSource.SetClip(xxx).play()那应该怎么做囊?
我们可以利用扩展方法的特性:使你能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。 扩展方法是一种特殊的静态方法,但可以像扩展类型上的实例方法一样进行调用举个例子
[C#] 纯文本查看 复制代码
/// <summary>
/// 创建一个扩展测试类 注意!扩展方法必须是静态的所以类也必须是静态
/// </summary>
public static class TestExtend
{
    /// <summary>
    /// 一个静态的扩展方法
    /// </summary>
    /// <param name="audioSource">主要要用this关键字标注第一个参数,也就是你要扩展的类是哪个</param>
    public static void DebugClip(this AudioSource audioSource)
    {
        Debug.Log(audioSource.clip);
    }
}
拿我们如何使用它?看下面的使用代码
1.png


我们发现我们方法的第一个参数可以不用传,并且我们AudioSource的类多了一个我们自定义的方法。下面是运行图

1.png 1.png
到此我们已经给AudioSource 添加了一个方法,那如何实现audioSource.DebugClip().DebugClip() 这样,像一个链条 一个一个接下去囊?
看代码:
[C#] 纯文本查看 复制代码
/// <summary>
/// 创建一个扩展测试类 注意!扩展方法必须是静态的所以类也必须是静态
/// </summary>
public static class TestExtend
{
    /// <summary>
    /// 一个静态的扩展方法
    /// </summary>
    /// <param name="audioSource">主要要用this关键字标注第一个参数,也就是你要扩展的类是哪个</param>
    /// <returns>我们只需要把要扩展的类进行返回就可以了</returns>
    public static AudioSource DebugClip(this AudioSource audioSource)
    {
        Debug.Log(audioSource.clip);
        //返回扩展的类
        return audioSource;
    }
}

使用方法  audioSource.DebugClip().DebugClip(); 当然如果你想 1.png
我们看运行结果:
1.png 运行了5遍 没问题,到此我们知道了我们的工具,也就是实现的方式! 下面我们就去做一个实用的例子。
2).音频播放器的改造
我们往往会遇到这种场景 1.我们要想要知道音频什么时候播放完成。 2.我们想要在指定的音频播放中的某个时间或帧中运行自己的一个方法。
我们先来分析一下这两个场景会有的解决方案 1.利用协程计时器(因为我们可以拿到音频总长度) 2.Update(可以利用unity生命周期)
我们最后选择第2中方式 Update(可以利用unity生命周期) 个人认为协程不是最好的方式原因在于 协程的消耗太大(我们要暂停播放的话还要关闭协程)
下面就请各位跟着我的思路 和我一起书写一个强大的音频扩展组件
根据需求定义大体方法:
OnUpdate
OnComplete
我们主要实现以上两个方法      
当先实现扩展类之前我们要先实现一个驱动器AudioEventDriver,用来驱动Update
先贴出这个类的代码 然后慢慢解释
[C#] 纯文本查看 复制代码
        /// <summary>
        /// 委托定义
        /// </summary>
        /// <param name="Percentage">百分比</param>
        public delegate void OnUpdateEventHandler(float Percentage);
        public delegate void OnCompleteEventHandler();
        /// <summary>
        ///  委托实例
        /// </summary>
        public AudioEventDriver.OnUpdateEventHandler onUpdate = null;
        public AudioEventDriver.OnCompleteEventHandler onComplete = null;
        private AudioSource audioSource = null;
        /// <summary>
        /// OnUpdate 驱动方法
        /// </summary>
        /// <param name="audioSource_"></param>
        /// <param name="onUpdateEventHandler"></param>
        public void OnUpdate(AudioSource audioSource_, AudioEventDriver.OnUpdateEventHandler onUpdateEventHandler)
        {
            onUpdate = onUpdateEventHandler;
            audioSource = audioSource_;
        }
        public void OnComplete(AudioSource audioSource_, AudioEventDriver.OnCompleteEventHandler onCompleteEventHandler)
        {
            audioSource = audioSource_;
            onComplete = onCompleteEventHandler;
        }
        private void Update()
        {
            //OnUpdate 驱动
            if (onUpdate != null && audioSource != null)
            {
                if (audioSource.isPlaying)
                {
                    onUpdate(audioSource.time / audioSource.clip.length);
                }
            }
            //OnComplete 驱动
            if (audioSource.time == audioSource.clip.length)
            {
                onComplete();
            }
        }
    }
我们可以看出我们写了两个事件委托分别取承接OnUpdate中用户的方法和OnComplete中用户的方法。
然后在Update中去分别调用者两个驱动事件。ok我们该怎么用这个驱动器类囊? 看下面的代码
我们首先先写一个静态的AudioExtend
然后在其中书写方法GetEventDriver方法,这个方法是一个静态的
[C#] 纯文本查看 复制代码
        /// <summary>
        /// 获取音频事件驱动
        /// </summary>
        /// <param name="audioSource"></param>
        /// <returns></returns>
        private static AudioEventDriver GetEventDriver(this AudioSource audioSource)
        {
            AudioEventDriver AudioEventDriver = audioSource.gameObject.GetComponent<AudioEventDriver>();
            if (AudioEventDriver == null)
            {
                AudioEventDriver = audioSource.gameObject.AddComponent<AudioEventDriver>();
            }
            return AudioEventDriver;
        }


上面的实现逻辑很简单 就是有这个驱动就获取 没有就添加并获取。这里相信各位都能理解!
然后我们去实现关键的两个方法OnUpdate扩展方法和OnComplete扩展方法
[C#] 纯文本查看 复制代码
        /// <summary>
        /// 音频播放中回调
        /// </summary>
        /// <param name="audioSource"></param>
        /// <param name="onUpdateEventHandler"></param>
        /// <returns></returns>
        public static AudioSource OnUpdate(this AudioSource audioSource, AudioEventDriver.OnUpdateEventHandler onUpdateEventHandler)
        {
            GetEventDriver(audioSource).OnUpdate(audioSource, onUpdateEventHandler);
            return audioSource;
        }
        /// <summary>
        /// 音频播放完成回调
        /// </summary>
        /// <param name="audioSource"></param>
        /// <param name="onUpdateEventHandler"></param>
        /// <returns></returns>
        public static AudioSource OnComplete(this AudioSource audioSource, AudioEventDriver.OnCompleteEventHandler onCompleteEventHandler)
        {
            GetEventDriver(audioSource).OnComplete(audioSource, onCompleteEventHandler);
            return audioSource;
        }


这两个扩展方法只是去调用了一下我们私有的GetEventDriver这个方法会返回驱动类,然后调用驱动类的OnUpdate和OnComplete(注意这里不要搞错了哦
然后传入一个事件 其实就是传给了GetEventDriver类中,然后驱动类就开始运作了,直到音频播放中或完成就会调用用户传入的事件
ok怎么用囊?
[C#] 纯文本查看 复制代码
audioSource
            .OnUpdate((v) =>
            {
                Debug.Log("播放进度:" + v);
            })
            .OnComplete(() => {
                Debug.Log("播放完成咯!");
            })
            .Play();



下面放上完整的扩展类代码 供大家参考
[C#] 纯文本查看 复制代码
public static class AudioExtend
    {
        /// <summary>
        /// 获得一个音频播放器
        /// </summary>
        /// <param name="gameObject"></param>
        /// <returns></returns>
        public static AudioSource GetAudio(this GameObject gameObject, bool add = false)
        {
            AudioSource audio = null;
            if (!add)
            {
                audio = gameObject.GetComponent<AudioSource>();
                if (audio == null)
                {
                    audio = gameObject.AddComponent<AudioSource>();
                }
            }
            if (add)
            {
                audio = gameObject.AddComponent<AudioSource>();
            }
            return audio;
        }
        /// <summary>
        /// 设置Clip
        /// </summary>
        /// <param name="audioSource"></param>
        /// <returns></returns>
        public static AudioSource SetClip(this AudioSource audioSource, AudioClip clip)
        {
            if (clip)
            {
                audioSource.clip = clip;
            }
            return audioSource;
        }
        /// <summary>
        /// 获取音频事件驱动
        /// </summary>
        /// <param name="audioSource"></param>
        /// <returns></returns>
        private static AudioEventDriver GetEventDriver(this AudioSource audioSource)
        {
            AudioEventDriver AudioEventDriver = audioSource.gameObject.GetComponent<AudioEventDriver>();
            if (AudioEventDriver == null)
            {
                AudioEventDriver = audioSource.gameObject.AddComponent<AudioEventDriver>();
            }
            return AudioEventDriver;
        }
        /// <summary>
        /// 音频播放中回调
        /// </summary>
        /// <param name="audioSource"></param>
        /// <param name="onUpdateEventHandler"></param>
        /// <returns></returns>
        public static AudioSource OnUpdate(this AudioSource audioSource, AudioEventDriver.OnUpdateEventHandler onUpdateEventHandler)
        {
            GetEventDriver(audioSource).OnUpdate(audioSource, onUpdateEventHandler);
            return audioSource;
        }
        /// <summary>
        /// 音频播放完成回调
        /// </summary>
        /// <param name="audioSource"></param>
        /// <param name="onUpdateEventHandler"></param>
        /// <returns></returns>
        public static AudioSource OnComplete(this AudioSource audioSource, AudioEventDriver.OnCompleteEventHandler onCompleteEventHandler)
        {
            GetEventDriver(audioSource).OnComplete(audioSource, onCompleteEventHandler);
            return audioSource;
        }
    }
    /// <summary>
    /// 音频事件驱动器
    /// </summary>
    public class AudioEventDriver : MonoBehaviour
    {
        /// <summary>
        /// 委托定义
        /// </summary>
        /// <param name="Percentage">百分比</param>
        public delegate void OnUpdateEventHandler(float Percentage);
        public delegate void OnCompleteEventHandler();
        /// <summary>
        ///  委托实例
        /// </summary>
        public AudioEventDriver.OnUpdateEventHandler onUpdate = null;
        public AudioEventDriver.OnCompleteEventHandler onComplete = null;
        private AudioSource audioSource = null;
        /// <summary>
        /// OnUpdate 驱动方法
        /// </summary>
        /// <param name="audioSource_"></param>
        /// <param name="onUpdateEventHandler"></param>
        public void OnUpdate(AudioSource audioSource_, AudioEventDriver.OnUpdateEventHandler onUpdateEventHandler)
        {
            onUpdate = onUpdateEventHandler;
            audioSource = audioSource_;
        }
        public void OnComplete(AudioSource audioSource_, AudioEventDriver.OnCompleteEventHandler onCompleteEventHandler)
        {
            audioSource = audioSource_;
            onComplete = onCompleteEventHandler;
        }
        private void Update()
        {
            //OnUpdate 驱动
            if (onUpdate != null && audioSource != null)
            {
                if (audioSource.isPlaying)
                {
                    onUpdate(audioSource.time / audioSource.clip.length);
                }
            }
            //OnComplete 驱动
            if (audioSource.time == audioSource.clip.length)
            {
                onComplete();
            }
        }
    }
这种方式 我形容为驱动器方式, 好了期待下一节吧!全部帖子
http://www.manew.com/forum-47-497-1.html



评分

参与人数 13鲜花 +45 收起 理由
Sunry + 2 很给力!
林霞之恋 + 3 很给力!
kckbkckb2 + 5 很给力!
yanis + 5 很给力!
Mrwang86 + 5 很给力!
QQAQ + 5 很给力!
LeoOS + 5
asai8diao2410 + 5 很给力!
雨落随风 + 2 赞一个!
luwenwan666 + 2 很给力!
bce + 2 赞一个!
lilybeast + 2 很给力!
uselessShadow + 2 神马都是浮云

查看全部评分


回复

使用道具 举报

排名
9520
昨日变化
121

4

主题

159

帖子

729

积分

Rank: 9Rank: 9Rank: 9

UID
21402
好友
4
蛮牛币
175
威望
0
注册时间
2014-4-14
在线时间
327 小时
最后登录
2018-11-16

认证开发者

 楼主| 发表于 2018-7-9 20:49:10 | 显示全部楼层
fd578 发表于 2018-7-9 19:41
GetEventDriver里的GetComponent在OnUpdate?
还有为什么用的扩展方法this却用了普通的函数调用方法? ...

OnUpdate 是扩展方法 用户调用一次就只执行一次儿哟  调用普通方法是因为我们需要unity的Update生命周期,这里OnUpdate只是用来让用户传递委托的,不是真正的Update,真正的Update在我们的驱动器里,是不是很神奇,下一篇文章马上写好了,主要是再做一个实例 以及做一点有趣的实验

回复 支持 反对

使用道具 举报

4四处流浪
498/500
排名
4718
昨日变化
23

2

主题

41

帖子

498

积分

Rank: 4

UID
253227
好友
0
蛮牛币
1472
威望
0
注册时间
2017-11-8
在线时间
159 小时
最后登录
2018-11-17
发表于 2018-7-6 13:56:13 | 显示全部楼层
卢本伟,牛逼!

点评

ICP
技术贴 你这个回复  发表于 2018-8-11 10:35

回复

使用道具 举报

7日久生情
2965/5000
排名
360
昨日变化

1

主题

374

帖子

2965

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
2219
好友
1
蛮牛币
7951
威望
0
注册时间
2013-8-19
在线时间
906 小时
最后登录
2018-11-16
发表于 2018-7-6 14:23:55 | 显示全部楼层
牛逼!没事没事我这不是来了嘛!

回复 支持 反对

使用道具 举报

6蛮牛粉丝
1119/1500
排名
2643
昨日变化
14

0

主题

125

帖子

1119

积分

Rank: 6Rank: 6Rank: 6

UID
160175
好友
0
蛮牛币
1026
威望
0
注册时间
2016-8-2
在线时间
472 小时
最后登录
2018-11-16
发表于 2018-7-6 14:24:34 | 显示全部楼层
6666666666666666666666666

回复 支持 反对

使用道具 举报

4四处流浪
400/500
排名
6010
昨日变化
41

0

主题

26

帖子

400

积分

Rank: 4

UID
251989
好友
0
蛮牛币
1574
威望
0
注册时间
2017-11-1
在线时间
156 小时
最后登录
2018-11-16
发表于 2018-7-6 14:54:18 | 显示全部楼层
看看大佬的代码
[发帖际遇]: 一个袋子砸在了 hell425 头上,hell425 赚了 1 蛮牛币. 幸运榜 / 衰神榜

回复

使用道具 举报

3偶尔光临
289/300
排名
9238
昨日变化
5

1

主题

26

帖子

289

积分

Rank: 3Rank: 3Rank: 3

UID
182254
好友
0
蛮牛币
868
威望
0
注册时间
2016-11-9
在线时间
134 小时
最后登录
2018-11-6
QQ
发表于 2018-7-6 15:03:00 | 显示全部楼层
或许这就是我和大佬的区别吧

回复 支持 反对

使用道具 举报

6蛮牛粉丝
1487/1500
排名
1348
昨日变化
1

3

主题

110

帖子

1487

积分

Rank: 6Rank: 6Rank: 6

UID
80787
好友
1
蛮牛币
2054
威望
0
注册时间
2015-3-17
在线时间
498 小时
最后登录
2018-11-16
QQ
发表于 2018-7-6 16:48:57 | 显示全部楼层
厉害,学到了,如果能完成整个引擎的改造,需要有巨大量的静态函数,是不是应该考虑放个项目到github上面?
[发帖际遇]: goodboy3 在网吧通宵,花了 3 蛮牛币. 幸运榜 / 衰神榜

回复 支持 反对

使用道具 举报

6蛮牛粉丝
1122/1500
排名
1957
昨日变化
3

1

主题

24

帖子

1122

积分

Rank: 6Rank: 6Rank: 6

UID
84554
好友
1
蛮牛币
1486
威望
0
注册时间
2015-3-25
在线时间
434 小时
最后登录
2018-11-15
发表于 2018-7-6 17:24:39 | 显示全部楼层
谢谢楼主分享!!!!

回复

使用道具 举报

7日久生情
1866/5000
排名
889
昨日变化

0

主题

112

帖子

1866

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
56231
好友
1
蛮牛币
1106
威望
0
注册时间
2014-11-18
在线时间
650 小时
最后登录
2018-11-13
QQ
发表于 2018-7-6 17:46:39 | 显示全部楼层
谢谢楼主分享!!!!

回复

使用道具 举报

排名
26125
昨日变化
15

1

主题

5

帖子

55

积分

Rank: 2Rank: 2

UID
276097
好友
0
蛮牛币
36
威望
0
注册时间
2018-4-8
在线时间
35 小时
最后登录
2018-11-16
QQ
发表于 2018-7-6 17:53:52 | 显示全部楼层
学到了

回复

使用道具 举报

排名
9520
昨日变化
121

4

主题

159

帖子

729

积分

Rank: 9Rank: 9Rank: 9

UID
21402
好友
4
蛮牛币
175
威望
0
注册时间
2014-4-14
在线时间
327 小时
最后登录
2018-11-16

认证开发者

 楼主| 发表于 2018-7-6 18:51:09 | 显示全部楼层
goodboy3 发表于 2018-7-6 16:48
厉害,学到了,如果能完成整个引擎的改造,需要有巨大量的静态函数,是不是应该考虑放个项目到github上面? ...

后面会讲非静态链式

回复 支持 反对

使用道具 举报

5熟悉之中
864/1000
排名
3164
昨日变化

1

主题

145

帖子

864

积分

Rank: 5Rank: 5

UID
194381
好友
0
蛮牛币
1873
威望
0
注册时间
2016-12-31
在线时间
280 小时
最后登录
2018-8-22
发表于 2018-7-6 21:31:54 | 显示全部楼层
受教了!
[发帖际遇]: Smilexs 在网吧通宵,花了 1 蛮牛币. 幸运榜 / 衰神榜

回复

使用道具 举报

7日久生情
2227/5000
排名
918
昨日变化

0

主题

225

帖子

2227

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
147766
好友
0
蛮牛币
664
威望
0
注册时间
2016-5-6
在线时间
916 小时
最后登录
2018-11-16
QQ
发表于 2018-7-7 09:06:27 | 显示全部楼层
学习学习

回复

使用道具 举报

5熟悉之中
692/1000
排名
4489
昨日变化
3

2

主题

114

帖子

692

积分

Rank: 5Rank: 5

UID
252095
好友
1
蛮牛币
576
威望
0
注册时间
2017-11-2
在线时间
264 小时
最后登录
2018-11-13
发表于 2018-7-7 09:43:01 | 显示全部楼层
感谢大神的我还需要努力

回复 支持 反对

使用道具 举报

3偶尔光临
299/300
排名
7648
昨日变化
2

2

主题

38

帖子

299

积分

Rank: 3Rank: 3Rank: 3

UID
70743
好友
0
蛮牛币
223
威望
0
注册时间
2015-1-23
在线时间
103 小时
最后登录
2018-10-11
发表于 2018-7-7 09:46:10 | 显示全部楼层
厉害,打开新的思路。学习学习
[发帖际遇]: lilybeast 发帖时在路边捡到 1 蛮牛币,偷偷放进了口袋. 幸运榜 / 衰神榜

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则

关闭

站长推荐 上一条 /1 下一条

快速回复 返回顶部 返回列表