.NET斗鱼直播弹幕客户端(下)
前言
在上篇文章中,我們提到了如何使用 .NET連接斗魚TV直播彈幕的基本操作。然而想要做得好,做得容易擴展,就需要做進一步的代碼整理。
本文將涉及以下內容:
介紹如何使用?ReactiveExtensions(?Rx),演示這一系列操作用起來,就像寫?HelloWorld一樣簡單;
用我自制的“準游戲引擎”?FlysEngine,只需少量代碼,即可實現桌面彈幕的效果;
最后提供一波“伸手黨”福利,文中所有可運行、完整代碼,將按原樣奉上。
Rx.NET
Rx,是 ReactiveExtensions的縮寫,據說 Rx發明于 .NET2.0時代的微軟。那時候還沒有 async/await。后來,也許由于 RX對編程語言要求不高(如不要求內置 協程- coroutine), RX反倒在 .NET之外的其它編程語言中大行其道。如 rx.js、 RxJava等等。
C#從 .NET2.0就提供了 yield關鍵字,然后 3.0提供了 LINQ, 5.0提供了 async/await,因此很多時候 RX的意義不大。但在某些情況下(如這種情況),就有意義了,原因請見下圖:
| 同步 | T | IEnumerable<T> |
| 異步 | Task<T> | Observable<T>/?IAsyncEnumerable<T> |
C#的 協程支持同步多數據,異步單數據,但不支持同步多數據( C# 8.0現在已經支持 IAsyncEnumerable<T>),本文將使用 Rx來包裝上一篇文章的斗魚TV直播彈幕客戶端。
來先看一波老代碼:
注意剪頭所指的位置,那是基礎代碼“出口”,或者業務邏輯“入口”,基礎代碼不能簡單地 return打斷,因為它要不停地輸出數據,這時就需要像 協程等編程語言功能,或者 Rx的支持。
Rx-Hello World
首先引入 NuGet包 System.Reactive,一個簡單的“異步多值返回”的 Rx示例代碼如下:
Observable.Create<int>(async (o, cancellationToken) => { for (var i = 0; i < 5; ++i) { await Task.Delay(1000); o.OnNext(i); } o.OnCompleted(); })麻雀雖小,五臟俱全,如代碼所示,幾乎只需在正常代碼外包一層 Rx,即可享受 Rx的好處。
使用?Rx
使用起來就更簡單了,上篇展示的長達 252行代碼的 demo,現在只需一行代碼,即可無侵入式地調用:
DouyuBarrage.ChatMessageFromUrl("https://www.douyu.com/scboy")調用結果如下(和昨天效果完全一樣):
Rx的其它好處
除了調用簡單之外, Rx的擴展也非常非常簡單,比如完成以下操作,以前可能非常麻煩,需要改多處代碼,而使用 Rx,只需像 LINQ一樣加幾個指令即可:
同時抓多個直播間的彈幕
#load ".\barrage.linq" DouyuBarrage.ChatMessageFromUrl("https://www.douyu.com/scboy") .Select(x => new { Room = "scboy", Message = x.Message }) .Merge(DouyuBarrage.ChatMessageFromUrl("https://www.douyu.com/topic/lscs?rid=633019") .Select(x => new { Room = "lalala", Message = x.Message}))效果如下:
只需一個 Merge指令即可合并兩個直播間的彈幕( Observable<T>)
擴展簡單
比如只想提取特殊的彈幕,或者數據之前想做一些轉換,可以使用 Where, Select等數據過濾和轉換操作符,符合 LINQ的習慣,非常好用。比如我正常彈幕的提取,其實是從 JObjectFromUrl轉換而來, JObjectFromUrl,又是從 RawFromUrl轉換而來,這提高了擴展性,又無需修改老代碼,正是所謂“對擴展開放,對修改封閉”的開放-封閉原則:
public IObservable<JToken> JObjectFromUrl(string url) => RawFromUrl(url).Select(MsgTool.DecodeStringToJObject); public IObservable<Barrage> ChatMessageFromUrl(string url) => JObjectFromUrl(url) .Where(x => x["type"].Value<string>() == "chatmsg") .Select(Barrage.FromJToken);又比如可能我只想提取彩色彈幕,我只需 ChatMessageFromUrl().Where(x=>x.Color!=0xffffff)即可,非常方便。
桌面彈幕
這可能是另一個主題——實時渲染,用到了我自己寫的“準游戲引擎” FlysEngine,因此需要安裝 NuGet包:FlysEngine.Desktop。
桌面彈幕不同于 網頁彈幕,只能在網頁中顯示,而 桌面彈幕可以直接顯示在屏幕最上方。有些公司年會可能用到了 桌面彈幕,這無疑增加了主持人與觀眾們的互動,提高了群眾參與的積極性。
注意:本文中所說 FlysEngine的實質是 Direct2D和 WindowsAPI- UpdateLayeredWindowIndirect函數。如果不想使用 FlysEngine,完全可以使用其它方式代替。最簡單的方式是使用 WPF,然后設置 AllowsTransparency=true,但這樣性能會差一些。本文介紹的方法, CPU使用率將保持在 0%左右!
桌面彈幕的要點
渲染文字?DirectWrite;
文字移動?將文字從屏幕右邊移動到左邊;
檢測是否離開屏幕?如果屏幕上不顯示彈幕,即可將彈幕刪除;
初始位置確定?如果一行顯示不下,則將彈幕放在下一行。
渲染文字
渲染文字一般是通過 DirectWrite,它性能很好,功能也強大。FlysEngine將 DirectWrite封裝了,因此直接用便是。
注意:DirectWrite不僅渲染文字,還提供了 .Metrics屬性,可以計算文字渲染之后的大小,這會讓事情變得容易很多。
文字移動
文字移動首先需要一個位置,隨著時間變化,將該位置的 X坐標不段減少即可。這可以通過 FlysEngine中的 UpdateLogic事件實現,它會定期調用,傳入一個 floatdt,代碼離上一次調用 UpdateLogic的時間間隔。因此可以利用這個 dt變量,計算是彈幕的新位置:
public void MoveLeft(float dt, float speed) { Position.X -= dt * speed; }檢測是否離開屏幕
由于我們已知彈幕是矩形,(很顯然屏幕也是矩形)因此這個檢測比較簡單,直接判斷文字的 右邊緣是否 大于0即可。
也由于需要經常/頻繁地刪除在屏幕上的彈幕對象,因此最好儲存彈幕的數據結構別使用 O(n)的集合,如最好別使用 List<T>,它是線性表。我這里使用的是 鏈表, .NET的鏈表實現是 LinkedList<T>(很多人以為是 List<T>)。
多說一句,鏈接的遍歷算法如下( while循環):
var node = barrages.First; while (node != null) { var next = node.Next; // do work here node = next; }之所以不使用 foreach來遍歷,因為這樣遍歷可以實現高性能的“邊遍歷、邊刪除”的實現。
初始位置確定
這一點思想需要多想想,需要從第一行開始,從后往前看,看最后那一邊彈幕是否大于屏幕右邊。只要想清楚了,代碼很容易:
float GetNewY() { float y = 0; while (barrages.Reverse().Where(x => x.Position.Y == y).Select(x => x.Rect.Right).FirstOrDefault() > form.Width) { y += FontSize; } return y; }有了這些,就可以愉快地感受屏幕彈幕啦!
彩色?emoji表情
Direct2D支持——但默認不顯示彈幕 emoji表情:
要多加一個枚舉讓其支持:
target.DrawText("????????????", res.TextFormats[36], rectangle, res.GetColor(Color.Blue), Direct2D.DrawTextOptions.EnableColorFont); // 重點支持彩色 emoji表情后,效果如下:
結
最終效果昨天已經見過了,如下:
本文(包括上文)所用的代碼如下:
| 老式代碼 | 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???”,并關注我的微信公眾號:【DotNet騷操作】
總結
以上是生活随笔為你收集整理的.NET斗鱼直播弹幕客户端(下)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .NET Core 3.0之深入源码理解
- 下一篇: 【 .NET Core 3.0 】框架之