<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      撲克子

      博客園 首頁 新隨筆 聯系 訂閱 管理

      效果

      顯示大量彈幕、允許重疊、彈幕字號允許不同

      image

      約定

      為了更好地進行討論,我們先聲明一些共識:

      1. 彈幕會從屏幕右邊緣發射,并向左滾動

      2. 彈幕出現位置應該盡量靠上

      3. 幾條彈幕之間應該盡量不要重疊,如果要重疊也要盡量重疊長度少一些

      此外本文會創造/使用一些概念:

      1. 彈幕:計算的對象實體,有以下成員:

        • 發射時間:這個實際上決定了某時刻彈幕的x坐標
        • 坐標:只有y坐標,是算法最后計算出應該出現的位置
        • 寬度:根據彈幕字數、字號計算出的長度
        • 高度:由彈幕的字號決定
      2. 屏幕右邊緣:由于彈幕是從右邊出現的,所以右邊緣和屏幕寬度都很重要

      3. 屏幕寬度:由窗口大小決定

      4. 位置(room),可以放置彈幕的空位,由于只需要關注屏幕右邊緣線上的空位,所以位置實際上是一個一維變量,并且屏幕邊緣上所有的位置合起來是一個一維數組,有以下成員:

        • 高度:位置的高度
        • 坐標:位置的坐標,實際上不是一個字段,而是由前面所有的位置高度綜合算出的
        • 上條彈幕:這個位置最近發射的彈幕
      5. 停留時間:彈幕在屏幕上停留的時間

      流程

      如圖中的彈幕情況。紅色新彈幕發射時,應該插在第幾行呢?

      --- displayMode: compact --- gantt dateFormat ss axisFormat | 彈幕1 : 00, 4s 彈幕2 : 01, 5s 彈幕3 : 02, 4s 彈幕4 : 02, 2s 新彈幕 : crit, 05, 3s 屏幕右邊緣 : milestone, 05, 0

      大家肯定可以一眼看出來是第一行發射,那如何編程實現?我們先梳理一遍流程:

      1. 將彈幕按照發射時間排序,然后依次判斷彈幕:

      2. 從上往下依次判斷位置,如果有一個空位距離為正數,則將彈幕插入。

      3. 計算該位置中上一條彈幕距離本彈幕的距離
        (如果彈幕在邊緣左側,則為正數,在右側為負數,負數意味著:此時在此處發射彈幕會和上一條彈幕重疊,正數則不會重疊)

      4. 如果有正數距離,則插入在這個位置。

      5. 如果沒有正數距離,而且允許彈幕重疊,則選擇最大的距離插入。

      sort 彈幕 by 彈幕.發射時間
      sort 位置(從上至下)
      foreach 彈幕
          var 最大距離
          foreach 位置
              var 距離 := get_dictance(彈幕, 位置.上條彈幕)
              距離.對應位置 = 位置
              if 距離 > 0
                  位置.上條彈幕 := 彈幕
                  彈幕.坐標 = 位置.坐標
                  break
              else
                  最大距離 := max(最大距離, 距離)
          if 彈幕.坐標 = null 
              if 允許重疊
                  最大距離.對應位置.上條彈幕 := 彈幕
                  彈幕.坐標 = 位置.坐標
              else
                  // 這條彈幕不會顯示
      
      flowchart TD start-->A-->B-->C-->D-->E--彈幕和位置遍歷結束-->fin E--位置遍歷結束-->H-->B E--負數-->G-->C E--正數-->F-->B start([開始]) A[將彈幕按照出現時間排序] B[依次遍歷彈幕] C[依次遍歷位置] D[計算該位置中上一條彈幕距離本彈幕的距離] E{位置的距離} F[插入彈幕到該位置] G[記錄下目前最大的距離和相應位置] H[插入到最大距離的位置] fin([結束])

      距離計算

      距離表面上就是彈幕的右端距離屏幕右邊緣的距離,但實際上計算時還是要考慮蠻多因素的:

      如果設置一條彈幕在屏幕上停留的時間為duration秒的話,彈幕的結束時間為:

      var 結束時間 := 彈幕.發射時間 + duration
      

      而且滾動彈幕實際上是要在duration秒內,走過屏幕寬度+自身寬度的距離。我們可以算出某時刻彈幕左邊緣和屏幕右邊緣的距離:

      func get_position (彈幕, 屏幕寬度, 某時刻, duration)
          var 彈幕已發射時間 := 某時刻 - 彈幕.發射時間
          var 彈幕要走的總長度 := 屏幕寬度 + 彈幕.寬度
          var 彈幕已走的長度 := 彈幕要走的總長度 * 彈幕已發射時間 / duration
          return 彈幕已走的長度
      

      但是也由于這個原因,長彈幕走的速度會比短彈幕快。也就是說如果本彈幕在這個位置發射:

      • 如果上一條彈幕比本彈幕長(即速度比本彈幕快),那么本彈幕剛發射的時間就是兩條彈幕距離最近的時候。

      • 如果上一條彈幕比本彈幕短(即速度比本彈幕慢),那么上條彈幕的結束時間就是兩條彈幕距離最近的時候。

      綜上,我們可以寫出函數計算彈幕的位置:

      func get_dictance (彈幕, 上條彈幕)
          var 某時刻
          if 彈幕.寬度 > 上條彈幕.寬度
              某時刻 := 彈幕.發射時間
          else
              某時刻 := 彈幕.發射時間 + duration
          var 屏幕寬度 := get_viewport_width()
          var duration := get_duration()
          var 上條彈幕位置 := get_position(上條彈幕, 屏幕寬度, 某時刻, duration)
          var 本彈幕位置 := get_position(彈幕, 屏幕寬度, 某時刻, duration)
          return 上條彈幕位置 - 本彈幕位置 - 上條彈幕.寬度
      

      處理不同大小的彈幕

      但是不一定所有彈幕都是一樣大小的,那“位置”的高度都不相同如何解決?如果只有大中小幾種,我們也許可以按最大公約數設置高度等方法解決。但我這里要給出一種方法同時兼容所有大小的彈幕:

      首先使用鏈表實現,使用鏈表是因為我們遍歷位置時,更常會訪問相鄰的位置(如前一個位置、后一個位置)而非隨機訪問。

      鏈表的每個節點都記錄了當前位置的高度(位置的坐標可以由之前節點高度推算出),和在該位置中上一個彈幕的信息。

      • 當有小彈幕進入大位置時,可以把位置拆為兩個相同的位置,其中靠上的位置放置新彈幕,下面的位置維持原樣;

        sankey-beta big room (old danmaku), danmaku (new danmaku), 5 big room (old danmaku), rest (old danmaku), 2
      • 當有大彈幕進入小位置時,可以把相鄰的幾個位置合并為一個,位置的上條彈幕取時間最近一條作為新位置的上條彈幕,然后再像上一條一樣拆為兩個處理。

        sankey-beta small room1 (old danmaku1), big room (old danmakuX), 3 small room2 (old danmaku2), big room (old danmakuX), 1 small room3 (old danmaku3), big room (old danmakuX), 3 big room (old danmakuX), danmaku (new danmaku), 5 big room (old danmakuX), rest (old danmakuX), 2

      我們只需要將開始時的位置,初始化為一個節點的鏈表,這個節點的高度是屏幕的高度。

      在對一條彈幕計算的最后,在彈幕中記錄下當前位置的坐標即可。

      代碼示例(C#)

      我使用C#實現過一個軟件,可供大家參考,如果還有不理解的歡迎大家聯系我:

      DamakuPlayer: https://github.com/Poker-sang/DanmakuPlayer/blob/master/DanmakuPlayer/Models/Danmaku.Position.cs

      posted on 2024-01-25 16:58  撲克子  閱讀(124)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 人人爽人人爽人人片av东京热 | 国产v综合v亚洲欧美大天堂| 韩国午夜福利片在线观看| 亚洲欧洲日产国无高清码图片| 性xxxx欧美老妇胖老太性多毛| 亚洲欧美综合精品成人网站| 亚洲永久精品日韩成人av| 又大又紧又粉嫩18p少妇| 亚洲一区二区三区在线| 久久99国产一区二区三区| 国产高清在线A免费视频观看| 国产精品午夜福利视频| 国产成人av三级在线观看| 日韩精品区一区二区三vr| 福利一区二区不卡国产| 国产一区二区三区色老头| 毛片在线播放网址| 激情国产一区二区三区四区| 久久精品免费自拍视频| 亚洲一区二区在线无码| 亚洲人成网站在线播放2019| 国产午夜91福利一区二区| 亚洲少妇人妻无码视频| 人妻教师痴汉电车波多野结衣| 国产日韩av二区三区| 亚洲国产欧美在线观看| 91亚洲国产成人精品性色| 国产精品多p对白交换绿帽| 国产欧美精品区一区二区三区| 日韩国产成人精品视频| 2021国产成人精品久久| 亚洲AV成人无码久久精品四虎| 欧美激情 亚洲 在线| 久久88香港三级台湾三级播放| 国产精品福利自产拍在线观看| 在线播放亚洲成人av| 开心婷婷五月激情综合社区| 亚洲色最新高清AV网站| 国产乱码精品一区二区三区中文 | 中文字幕亚洲精品人妻| 18av千部影片|