29

.NET 斗鱼直播弹幕客户端(下)

 4 years ago
source link: https://www.tuicool.com/articles/3u2AVra
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前言

在上篇文章中,我们提到了如何使用 .NET 连接斗鱼TV直播弹幕的基本操作。然而想要做得好,做得容易扩展,就需要做进一步的代码整理。

本文将涉及以下内容:

  • 介绍如何使用  ReactiveExtensions (  Rx ),演示这一系列操作用起来,就像写  HelloWorld 一样简单;

  • 用我自制的“准游戏引擎”  FlysEngine ,只需少量代码,即可实现桌面弹幕的效果;

  • 最后提供一波“伸手党”福利,文中所有可运行、完整代码,将按原样奉上。

Rx.NET

Rx ,是 ReactiveExtensions 的缩写,据说 Rx 发明于 .NET2.0 时代的微软。那时候还没有 async/await 。后来,也许由于 RX 对编程语言要求不高(如不要求内置 协程 - coroutine ), RX 反倒在 .NET 之外的其它编程语言中大行其道。如 rx.jsRxJava 等等。

C#.NET2.0 就提供了 yield 关键字,然后 3.0 提供了 LINQ5.0 提供了 async/await ,因此很多时候 RX 的意义不大。但在某些情况下(如这种情况),就有意义了,原因请见下图:

- 单数据 多数据 同步 T IEnumerable<T> 异步 Task<T> Observable<T>IAsyncEnumerable<T>

C#协程 支持同步多数据,异步单数据,但不支持同步多数据( C# 8.0 现在已经支持 IAsyncEnumerable<T> ),本文将使用 Rx 来包装上一篇文章的斗鱼TV直播弹幕客户端。

来先看一波老代码:

jQRN3yM.jpg!web

注意剪头所指的位置,那是基础代码“出口”,或者业务逻辑“入口”,基础代码不能简单地 return 打断,因为它要不停地输出数据,这时就需要像 协程 等编程语言功能,或者 Rx 的支持。

Rx -Hello World

首先引入 NuGetSystem.Reactive ,一个简单的“异步多值返回”的 Rx 示例代码如下:

麻雀虽小,五脏俱全,如代码所示,几乎只需在正常代码外包一层 Rx ,即可享受 Rx 的好处。

使用 Rx

使用起来就更简单了,上篇展示的长达 252 行代码的 demo ,现在只需一行代码,即可无侵入式地调用:

调用结果如下(和昨天效果完全一样):

JB7Zfi7.gif

Rx 的其它好处

除了调用简单之外, Rx 的扩展也非常非常简单,比如完成以下操作,以前可能非常麻烦,需要改多处代码,而使用 Rx ,只需像 LINQ 一样加几个指令即可:

同时抓多个直播间的弹幕

效果如下:

MNVfYb6.gif

只需一个 Merge 指令即可合并两个直播间的弹幕( Observable<T>

扩展简单

比如只想提取特殊的弹幕,或者数据之前想做一些转换,可以使用 WhereSelect 等数据过滤和转换操作符,符合 LINQ 的习惯,非常好用。比如我正常弹幕的提取,其实是从 JObjectFromUrl 转换而来, JObjectFromUrl ,又是从 RawFromUrl 转换而来,这提高了扩展性,又无需修改老代码,正是所谓“对扩展开放,对修改封闭”的开放-封闭原则:

又比如可能我只想提取彩色弹幕,我只需 ChatMessageFromUrl().Where(x=>x.Color!=0xffffff) 即可,非常方便。

桌面弹幕

这可能是另一个主题——实时渲染,用到了我自己写的“准游戏引擎” FlysEngine ,因此需要安装 NuGet 包: FlysEngine.Desktop

桌面弹幕 不同于 网页弹幕 ,只能在网页中显示,而 桌面弹幕 可以直接显示在屏幕最上方。有些公司年会可能用到了 桌面弹幕 ,这无疑增加了主持人与观众们的互动,提高了群众参与的积极性。

注意:本文中所说 FlysEngine 的实质是 Direct2DWindowsAPI - UpdateLayeredWindowIndirect 函数。如果不想使用 FlysEngine ,完全可以使用其它方式代替。最简单的方式是使用 WPF ,然后设置 AllowsTransparency=true ,但这样性能会差一些。本文介绍的方法, CPU 使用率将保持在 0% 左右!

桌面弹幕的要点

  • 渲染文字 DirectWrite

  • 文字移动将文字从屏幕右边移动到左边;

  • 检测是否离开屏幕如果屏幕上不显示弹幕,即可将弹幕删除;

  • 初始位置确定如果一行显示不下,则将弹幕放在下一行。

渲染文字

渲染文字一般是通过 DirectWrite ,它性能很好,功能也强大。 FlysEngineDirectWrite 封装了,因此直接用便是。

注意: DirectWrite 不仅渲染文字,还提供了 .Metrics 属性,可以计算文字 渲染之后 的大小,这会让事情变得容易很多。

文字移动

文字移动首先需要一个位置,随着时间变化,将该位置的 X 坐标不段减少即可。这可以通过 FlysEngine 中的 UpdateLogic 事件实现,它会定期调用,传入一个 floatdt ,代码离上一次调用 UpdateLogic 的时间间隔。因此可以利用这个 dt 变量,计算是弹幕的新位置:

检测是否离开屏幕

由于我们已知弹幕是矩形,(很显然屏幕也是矩形)因此这个检测比较简单,直接判断文字的 右边缘 是否 大于0 即可。

也由于需要经常/频繁地删除在屏幕上的弹幕对象,因此最好储存弹幕的数据结构别使用 O(n) 的集合,如最好别使用 List<T> ,它是线性表。我这里使用的是 链表.NET 的链表实现是 LinkedList<T> (很多人以为是 List<T> )。

多说一句,链接的遍历算法如下( while 循环):

之所以不使用 foreach 来遍历,因为这样遍历可以实现高性能的“边遍历、边删除”的实现。

初始位置确定

这一点思想需要多想想,需要从第一行开始,从后往前看,看最后那一边弹幕是否大于屏幕右边。只要想清楚了,代码很容易:

有了这些,就可以愉快地感受屏幕弹幕啦!

彩色 emoji 表情

Direct2D 支持——但默认不显示弹幕 emoji 表情:

rquau2E.png!web

要多加一个枚举让其支持:

支持彩色 emoji 表情后,效果如下:

iQbeymM.png!web

最终效果昨天已经见过了,如下:

yemeqy6.gif

本文(包括上文)所用的代码如下:

id 链接 老式代码 https://github.com/sdcb/blog-data/blob/master/2019/20191013-douyu-barrage-with-dotnet-2/barrage_tranditional.linq 新式代码 https://github.com/sdcb/blog-data/blob/master/2019/20191013-douyu-barrage-with-dotnet-2/barrage.linq 合并弹幕 https://github.com/sdcb/blog-data/blob/master/2019/20191013-douyu-barrage-with-dotnet-2/barrage-combine.linq 桌面弹幕 https://github.com/sdcb/blog-data/blob/master/2019/20191013-douyu-barrage-with-dotnet-2/desktop-barrage.linq

喜欢的朋友请“刷一波666:rocket::rocket::rocket:”,并关注我的微信公众号:【DotNet骚操作】

NFnErin.jpg!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK