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

扫一扫,访问微社区

开发者专栏

关注:2337

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

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

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

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

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

4

主题

152

帖子

693

积分

Rank: 9Rank: 9Rank: 9

UID
21402
好友
3
蛮牛币
268
威望
0
注册时间
2014-4-14
在线时间
300 小时
最后登录
2018-10-23

认证开发者

发表于 2018-7-16 16:57:09 | 显示全部楼层 |阅读模式

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

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

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

0x00  前言
首先回答一下上篇文章中大家的问题点: 一. 性能消耗问题:
     1).这么多静态方法会不会很占用内存?  表示不会很占用内存,我们只是用来扩展类方法而已,主要逻辑代码不在扩展方法中直接写=-=。
     2).频繁调用Update会不会很消耗资源?  我们是采用委托事件的方式进行调用不会很消耗的,且我们没有频繁调用 - -
二. 复杂度问题:
     1).这样书写代码会不会增加代码的复杂度?  表示对新手来说会的,但是万事无规则不成方圆,我觉得要进度,首先要先多看,多学,然后开始创造自己的规则!对老手来说这点复杂度往往可以解决很多问题!也节省很多麻烦
     2).新手该如何学会这种风格,需要哪些前置的条件?   首先数据结构要玩好,其次OO概念要清晰(面向对象),设计模式要清楚,最好去看一下两本书 大话设计模式大话数据结构,设计模式很重要!
0x01 本篇目标
对unity中VideoPlayer组件进行简单的改造,对VideoPlayer中存在的bug进行规避性的修复,实现基于VideoPlayer的透明视频播放。
已知VideoPlayer中的bug没有详细测试!!! 1.frameReady 事件没有正确的执行 也就是说我们要在某帧执行事件是没法直接实现的
我们要制作的函数列表
1.GetEventDriver
2.SetStyle
3.OnPrepareCompleted
4.OnLoopPointReached
5.OnUpdate
当然还有很多可以改造 这里只是举一个例子
先看一下成品演示
1.gif

0x02  首先老规矩 先实现驱动器类  下面为驱动器类代码
[C#] 纯文本查看 复制代码
    /// <summary>
    /// 视频事件驱动器
    /// </summary>
    public class VideoEventDriver : MonoBehaviour
    {
        /// <summary>
        /// 委托定义
        /// </summary>
        /// <param name="source">播放器</param>
        /// <param name="frameIdx">当前帧</param>
        /// <param name="time">当前时间</param>
        public delegate void FrameReadyEventHandler(VideoPlayer source, float frameIdx,float time);
        /// <summary>
        /// 委托实例
        /// </summary>
        public VideoEventDriver.FrameReadyEventHandler onUpdate = null;
        private VideoPlayer videoPlayer = null;
        /// <summary>
        /// 驱动方法
        /// </summary>
        /// <param name="videoPlayer_"></param>
        /// <param name="onUpdate_"></param>
        public void OnUpdate(VideoPlayer videoPlayer_, VideoEventDriver.FrameReadyEventHandler onUpdate_)
        {
            onUpdate = onUpdate_;
            videoPlayer = videoPlayer_;
        }
        /// <summary>
        /// MonoBehaviour Update 更新驱动函数
        /// </summary>
        private void Update()
        {
            if (onUpdate != null && videoPlayer!=null)
            {
                if (videoPlayer.isPlaying)
                {
                    onUpdate(videoPlayer, (float)videoPlayer.frame, (float)videoPlayer.time);
                }
            }
        }
    }


上面代码 我们实现了一个FrameReadyEventHandler的事件 并且 定义了onUpdate委托 ,利用unity的Update生命周期去驱动onUpdate委托的执行。
下面我们实现一下
GetEventDriver、OnLoopPointReachedOnPrepareCompletedOnUpdate
注意这里我们是扩展方法实现 所以先定义一个静态的类 名为 VideoExtend
1).GetEventDriver
[C#] 纯文本查看 复制代码
        /// <summary>
        /// 获得视频事件驱动
        /// </summary>
        /// <param name="videoPlayer"></param>
        /// <returns></returns>
        private static VideoEventDriver GetEventDriver(VideoPlayer videoPlayer)
        {
            VideoEventDriver videoEventDriver = videoPlayer.gameObject.GetComponent<VideoEventDriver>();
            if (videoEventDriver == null)
            {
                videoEventDriver = videoPlayer.gameObject.AddComponent<VideoEventDriver>();
            }
            return videoEventDriver;
        }

这个不需要解释吧!

2).OnLoopPointReached  OnPrepareCompleted
[C#] 纯文本查看 复制代码
 /// <summary>
        ///  播放结束或播放到循环的点时回调
        /// </summary>
        /// <param name="videoPlayer"></param>
        /// <param name="onLoopPointReached"></param>
        /// <returns></returns>
        public static VideoPlayer OnLoopPointReached(this VideoPlayer videoPlayer, VideoPlayer.EventHandler onLoopPointReached)
        {
            videoPlayer.loopPointReached += onLoopPointReached;
            return videoPlayer;
        }
        /// <summary>
        /// 视频准备完成时回调
        /// </summary>
        /// <param name="videoPlayer"></param>
        /// <param name="onPrepareCompleted"></param>
        /// <returns></returns>
        public static VideoPlayer OnPrepareCompleted(this VideoPlayer videoPlayer, VideoPlayer.EventHandler onPrepareCompleted)
        {
            videoPlayer.prepareCompleted += onPrepareCompleted;
            return videoPlayer;
        }

利用原组件已有的事件,对其进行改造为链式!


3).OnUpdate
[C#] 纯文本查看 复制代码
        /// <summary>
        /// 视频播放中回调
        /// </summary>
        /// <param name="videoPlayer"></param>
        /// <param name="onUpdate"></param>
        /// <returns></returns>
        public static VideoPlayer OnUpdate(this VideoPlayer videoPlayer, VideoEventDriver.FrameReadyEventHandler onUpdate)
        {
            GetEventDriver(videoPlayer).OnUpdate(videoPlayer, onUpdate);
            return videoPlayer;
        }


到此我们的基础功能就ok了
我们可以这样用
QQ截图20180716162746.png


0x03  继续改进
上面基本实现了功能,但是没有对功能进行大幅度的扩展,下面我们就扩展点东西,比如:对透明视频的支持=-=
要支持透明视频大家都在用的方法就是对视频限制要求并且编写对应的shader
因为这里不是专门讲解shader代码的,所以我只说一下原理部分然后放上我写的shader
QQ截图20180716163313.png

如上图就是一个视频的一帧,我们可以把这个图片从中间一分为二
QQ截图20180716163431.png 图1       QQ截图20180716163437.png 图2
我这里截的不准 凑活着看吧
然后用图2的像素颜色 作为 图1 的像素透明度,也就是用图2的取抠图1。
shader如下
[C#] 纯文本查看 复制代码
Shader "zFrame/Video/Transparent" {
    Properties {
        _MainTex ("Base (RGB)", 2D) = "white" {}
                _TransVal("Transparency Value", Range(0,10)) = 1.0
                _Color("_Color",Color) = (1,1,1,1)
                _Emission("_Emission",float) = 1
        }
                SubShader{
                        Tags { "RenderType"="Opaque" "Queue"="Transparent"} 
                        LOD 200
                        cull front
                        CGPROGRAM
                #pragma surface surf Lambert  alpha 
                sampler2D _MainTex;
                float4  _Color;
                float _TransVal;
                float _Emission;
                struct Input {
                        float2 uv_MainTex;
                };
                void surf(Input IN, inout SurfaceOutput o) {
                        half4 h = half4(0, 0, 0, 1);
                        if (IN.uv_MainTex.x >= 0.5)
                        {
                                h.a = 0;
                        }
                        else
                        {
                                half4 he = tex2D(_MainTex, half2(IN.uv_MainTex.x + 0.5, IN.uv_MainTex.y));
                                h.a = (he.r + he.g + he.b)*_TransVal;
                                if(h.a>1)
                                {
                                        h.a =1;        
                                }
                                half4 c = tex2D(_MainTex, IN.uv_MainTex);
                                //o.Albedo = c.rgb;
                                o.Emission = c*_Emission*_Color;
                        }
                        o.Alpha = h.a;
                }
                ENDCG
                cull back
                CGPROGRAM
                #pragma surface surf Lambert alpha 
                sampler2D _MainTex;
                float4  _Color;
                float _TransVal;
                float _Emission;
                struct Input {
                        float2 uv_MainTex;
                };
                void surf(Input IN, inout SurfaceOutput o) {
                        half4 h = half4(0, 0, 0, 1);
                        if (IN.uv_MainTex.x >= 0.5)
                        {
                                h.a = 0;
                        }
                        else
                        {
                                half4 he = tex2D(_MainTex, half2(IN.uv_MainTex.x + 0.5, IN.uv_MainTex.y));
                                h.a = (he.r + he.g + he.b)*_TransVal;
                                if(h.a>1)
                                {
                                        h.a =1;        
                                }
                                half4 c = tex2D(_MainTex, IN.uv_MainTex);
                                o.Emission = c*_Color*_Emission;
                        }
                        o.Alpha = h.a;
                }
                ENDCG
        }
}


这是一个双面的surf透明shader 如果只需要一面 删掉cull back以下代码
下面我们来继续实现代码功能
1.SetMeshRenderer       应对MeshRenderer渲染的对象(3D对象)私有的
2.SetRawImage 应对RawImage渲染的对象(UI部分)私有的
3.SetMaterial    渲染顺序的封装 私有的
4.SetStyle   用户可调用的扩展方法
这里直接贴代码了
[C#] 纯文本查看 复制代码
        /// <summary>
        /// MeshRenderer 渲染
        /// <summary>
        private static bool SetMeshRenderer(VideoPlayer videoPlayer, VideoStyle videoStyle, bool Add = false)
        {
            MeshRenderer meshRenderer = videoPlayer.gameObject.GetComponent<MeshRenderer>();

            if (meshRenderer == null && !Add)
            {
                return false;
            }
            else if (meshRenderer == null)
            {
                meshRenderer = videoPlayer.gameObject.AddComponent<MeshRenderer>();
            }
            switch (videoStyle)
            {
                case VideoStyle.Default:
                    meshRenderer.materials = new Material[] { new Material(Shader.Find("zFrame/Video/Default")) };
                    break;
                case VideoStyle.Transparent:
                    meshRenderer.materials = new Material[] { new Material(Shader.Find("zFrame/Video/Transparent")) };
                    break;
                default:
                    meshRenderer.materials = new Material[] { new Material(Shader.Find("zFrame/Video/Default")) };
                    break;
            }

            RenderTexture renderTexture = new RenderTexture((int)videoPlayer.clip.width, (int)videoPlayer.clip.height, 24);
            videoPlayer.renderMode = VideoRenderMode.RenderTexture;
            videoPlayer.targetTexture = renderTexture;
            meshRenderer.material.SetTexture("_MainTex", renderTexture);
            meshRenderer.material.SetTextureScale("_MainTex", new Vector2(0.5f, 1f));
            return true;
        }
        /// <summary>
        /// RawImage 渲染
        /// <summary>
        private static bool SetRawImage(VideoPlayer videoPlayer, VideoStyle videoStyle, bool Add = false)
        {
            RawImage rawImage = videoPlayer.gameObject.GetComponent<RawImage>();

            if (rawImage == null && !Add)
            {
                return false;
            }
            else if (rawImage == null)
            {
                rawImage = videoPlayer.gameObject.AddComponent<RawImage>();
            }
            switch (videoStyle)
            {
                case VideoStyle.Default:
                    rawImage.material = new Material(Shader.Find("zFrame/Video/Default"));
                    break;
                case VideoStyle.Transparent:
                    rawImage.material = new Material(Shader.Find("zFrame/Video/Transparent"));
                    break;
                default:
                    rawImage.material = new Material(Shader.Find("zFrame/Video/Default"));
                    break;
            }


            RenderTexture renderTexture = new RenderTexture((int)videoPlayer.clip.width, (int)videoPlayer.clip.height, 24);
            videoPlayer.renderMode = VideoRenderMode.RenderTexture;
            videoPlayer.targetTexture = renderTexture;
            rawImage.material.SetTexture("_MainTex", renderTexture);
            rawImage.material.SetTextureScale("_MainTex", new Vector2(0.5f, 1f));
            return true;
        }
        /// <summary>
        /// 渲染顺序封装
        /// </summary>
        private static void SetMaterial(VideoPlayer videoPlayer, VideoStyle videoStyle)
        {
            if (SetRawImage(videoPlayer, videoStyle))
            {
                return;
            }
            else if (SetMeshRenderer(videoPlayer, videoStyle))
            {
                return;
            }
            SetMeshRenderer(videoPlayer, videoStyle, true);
        }
        /// <summary>
        /// 设置视频样式 
        /// </summary>
        /// <param name="videoPlayer">视频组件</param>
        /// <param name="videoStyle">样式枚举</param>
        /// <returns></returns>
        public static VideoPlayer SetStyle(this VideoPlayer videoPlayer, VideoStyle videoStyle = VideoStyle.Default)
        {
            SetMaterial(videoPlayer, videoStyle);

            return videoPlayer;
        }
//别忘了我们的枚举

public enum VideoStyle{
 /// <summary>
/// 默认样式
/// </summary>
Default,
/// <summary>
/// 透明样式
/// </summary>
Transparent
}



到此我们的视频改造差不多要完工了。但是我们发现几个可以改进的地方
1.我们发现我们用的话,要先拿到VideoPlayer。能不能有一个方法扩展自GameObject然后直接获取VideoPlayer,没有则创建。
2.我们还没有实现VideoPlayer的大部分功能的改造。
以上问题欢迎大家自己试着写写。

下面完整代码回复拿走
游客,如果您要查看本帖隐藏内容请回复
其他章节
http://www.manew.com/forum-47-497-1.html





评分

参与人数 1蛮牛币 +10 收起 理由
ubbs + 10 神马都是浮云

查看全部评分


回复

使用道具 举报

5熟悉之中
549/1000
排名
4398
昨日变化

3

主题

61

帖子

549

积分

Rank: 5Rank: 5

UID
259967
好友
0
蛮牛币
189
威望
0
注册时间
2017-12-16
在线时间
159 小时
最后登录
2018-10-23
发表于 2018-7-16 17:02:42 | 显示全部楼层
大佬,我来给你助阵了

回复 支持 反对

使用道具 举报

2初来乍到
102/150
排名
14908
昨日变化

0

主题

14

帖子

102

积分

Rank: 2Rank: 2

UID
267376
好友
0
蛮牛币
14
威望
0
注册时间
2018-2-1
在线时间
40 小时
最后登录
2018-8-28
发表于 2018-7-16 17:29:32 | 显示全部楼层
真的棒棒 支持

回复

使用道具 举报

5熟悉之中
774/1000
排名
2875
昨日变化

0

主题

54

帖子

774

积分

Rank: 5Rank: 5

UID
263664
好友
0
蛮牛币
2315
威望
0
注册时间
2018-1-9
在线时间
238 小时
最后登录
2018-10-23
发表于 2018-7-16 17:46:56 | 显示全部楼层

回复

使用道具 举报

7日久生情
3305/5000
排名
1931
昨日变化

0

主题

2122

帖子

3305

积分

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

UID
219676
好友
1
蛮牛币
2422
威望
0
注册时间
2017-7-12
在线时间
515 小时
最后登录
2018-10-23

活力之星

发表于 2018-7-16 17:53:26 | 显示全部楼层
谢谢分享

回复

使用道具 举报

5熟悉之中
966/1000
排名
3336
昨日变化

1

主题

159

帖子

966

积分

Rank: 5Rank: 5

UID
139214
好友
0
蛮牛币
1430
威望
0
注册时间
2016-3-12
在线时间
388 小时
最后登录
2018-10-23
发表于 2018-7-16 17:58:54 | 显示全部楼层
6666666666666666

回复 支持 反对

使用道具 举报

7日久生情
1912/5000
排名
4302
昨日变化

0

主题

1335

帖子

1912

积分

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

UID
267381
好友
6
蛮牛币
3205
威望
0
注册时间
2018-2-1
在线时间
257 小时
最后登录
2018-10-6
发表于 2018-7-16 18:21:40 | 显示全部楼层
炒鸡棒,谢谢分享。
[发帖际遇]: 墨染青天 乐于助人,奖励 3 蛮牛币. 幸运榜 / 衰神榜

回复

使用道具 举报

4四处流浪
429/500
排名
8044
昨日变化

0

主题

223

帖子

429

积分

Rank: 4

UID
281167
好友
1
蛮牛币
858
威望
0
注册时间
2018-5-14
在线时间
64 小时
最后登录
2018-9-27
发表于 2018-7-16 19:24:39 | 显示全部楼层
厉害了,感谢分享

回复

使用道具 举报

4四处流浪
357/500
排名
5145
昨日变化

0

主题

15

帖子

357

积分

Rank: 4

UID
282063
好友
0
蛮牛币
1727
威望
0
注册时间
2018-5-21
在线时间
80 小时
最后登录
2018-10-23
发表于 2018-7-16 21:04:04 | 显示全部楼层
学习了,谢谢

回复

使用道具 举报

5熟悉之中
795/1000
排名
3269
昨日变化

2

主题

76

帖子

795

积分

Rank: 5Rank: 5

UID
9302
好友
1
蛮牛币
1606
威望
0
注册时间
2013-12-1
在线时间
291 小时
最后登录
2018-10-23
发表于 2018-7-17 08:18:06 | 显示全部楼层
支持大佬!

回复

使用道具 举报

5熟悉之中
844/1000
排名
2143
昨日变化

0

主题

41

帖子

844

积分

Rank: 5Rank: 5

UID
165111
好友
0
蛮牛币
1778
威望
0
注册时间
2016-8-31
在线时间
189 小时
最后登录
2018-10-23
发表于 2018-7-17 08:57:30 | 显示全部楼层
666666666666

回复

使用道具 举报

5熟悉之中
676/1000
排名
3228
昨日变化

0

主题

42

帖子

676

积分

Rank: 5Rank: 5

UID
233767
好友
0
蛮牛币
806
威望
0
注册时间
2017-7-23
在线时间
202 小时
最后登录
2018-10-23
发表于 2018-7-17 09:01:48 | 显示全部楼层
saaaaaaaaaaaaaaaaaaaaa

回复 支持 反对

使用道具 举报

7日久生情
2221/5000
排名
899
昨日变化

0

主题

224

帖子

2221

积分

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

UID
147766
好友
0
蛮牛币
666
威望
0
注册时间
2016-5-6
在线时间
913 小时
最后登录
2018-10-22
QQ
发表于 2018-7-17 09:08:02 | 显示全部楼层

真的棒棒 支持

回复

使用道具 举报

5熟悉之中
680/1000
排名
4526
昨日变化

2

主题

114

帖子

680

积分

Rank: 5Rank: 5

UID
252095
好友
1
蛮牛币
569
威望
0
注册时间
2017-11-2
在线时间
258 小时
最后登录
2018-10-23
发表于 2018-7-17 09:45:11 | 显示全部楼层
跪拜大神
[发帖际遇]: 一个袋子砸在了 一切为了孩子 头上,一切为了孩子 赚了 1 蛮牛币. 幸运榜 / 衰神榜

回复

使用道具 举报

6蛮牛粉丝
1399/1500
排名
1480
昨日变化

0

主题

90

帖子

1399

积分

Rank: 6Rank: 6Rank: 6

UID
49519
好友
1
蛮牛币
2996
威望
0
注册时间
2014-10-17
在线时间
495 小时
最后登录
2018-10-22
发表于 2018-7-17 09:51:24 | 显示全部楼层
看看源码怎么样

回复

使用道具 举报

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

本版积分规则

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