美文网首页
在Unity中显示Bilibili的弹幕

在Unity中显示Bilibili的弹幕

作者: Walk_In_Jar | 来源:发表于2020-03-23 14:27 被阅读0次

    首先需要获取弹幕的xml文件
    我是在https://www.jijidown.com/下载到的,冷门的视频或者弹幕可能没有收集,需要自己去爬取。
    这里以《华晨宇、苏诗丁《南屏晚钟》.xml》为例

    <?xml version="1.0" encoding="UTF-8"?>
    <i>
    <chatserver>chat.bilibili.com</chatserver>
    <chatid>12532425</chatid>
    <mission>0</mission>
    <maxlimit>1500</maxlimit>
    <state>0</state>
    <real_name>0</real_name>
    <source>k-v</source>
    <!--”弹幕出现时间/单位秒,弹幕模式,字体大小,颜色,发送时间戳,弹幕池,用户Hash,数据库ID”-->
    <d p="25.77600,1,25,16777215,1578935002,0,2dbcbbd,27154357842608132">全程高能</d>
    <d p="48.12600,1,25,16777215,1578935074,0,2dbcbbd,27154395814166528">钢琴:我总感觉不大对劲</d>
    <d p="28.86600,1,25,16777215,1578935020,0,2dbcbbd,27154367484264448">侧脸太好看了</d>
    <d p="30.66600,1,25,16777215,1578935031,0,2dbcbbd,27154373332697090">爱死这个侧脸了</d>
    <d p="59.40600,1,25,16777215,1578935088,0,2dbcbbd,27154403168354304">钢琴:我总感觉不大对劲</d>
    <d p="146.10600,1,25,16777215,1578935188,0,2dbcbbd,27154455602921474">钢琴即将损命</d>
    <d p="202.47000,1,25,16777215,1578936519,0,d29e09db,27155153118822400">苏苏苏苏苏苏苏苏苏苏苏苏苏苏苏苏苏苏苏苏苏苏苏苏苏</d>
    <d p="238.65000,1,25,16777215,1578959705,0,6a0ed7a7,27167309393559552">哥哥太酷了</d>
    <d p="243.77000,1,25,16777215,1578959715,0,6a0ed7a7,27167314568806402">我爱花花</d>
    </i>
    

    比如第一行<d>
    25.77600 就是视频的多少秒出现的
    弹幕模式 看到有1/4/5;1出现最多,应该是滚动弹幕,4/5是顶端弹幕或者底端弹幕
    字体大小25
    颜色16777215 是10进制 的白色



    时间戳 1578935002 代表这是2020-01-14 01:03:22发送的弹幕 ,应该用不到这个数据
    ,后面的数据应该也用不到了。



    然后是弹幕内容

    开搞。

    首先我们知道,要显示滚动弹幕且不重复,要让前一条这行的弹幕显示完全 再显示新的弹幕,否则它就到下一行去进行这个判断,直到有能显示这行的位置



    暂时不考虑超出屏幕下方的弹幕。
    顶部和底部的弹幕也是这么判断。



    字体统一为25在1080p下设定文本框的高度为40,1080/40=27
    最多显示27行弹幕

    加上Content Size Fitter组件,将横向设置为最佳,此时Text物体的宽度将随文本的长度变化。弹幕速度也由文本的宽度决定。将其作为Prefab。
    创建VideoPlayer组件


    将其放置到UI后方,并匹配内部,过高和过宽的屏幕都将显示黑边

    创建ChatPanel空物体作为所有弹幕的父对象,右上角对齐



    三个按钮 开启关闭三个位置的弹幕



    编写脚本ChatManager.cs
    
    using DG.Tweening;
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Data.SqlTypes;
    using System.Xml;
    using UnityEngine;
    using UnityEngine.UI;
    using UnityEngine.Video;
    
    public class ChatManager : MonoBehaviour
    {
        // Start is called before the first frame update
        public VideoPlayer vp;
        private string path;
        public GameObject chatText;
        public Transform chatPanel;
    
        private bool isRollOff, isBottomOff, isTopOff;
    
        private List<GameObject> roll, bottom, top;
    
        //一些播放参数
        private float rollRatio = 1; //滚动弹幕的播放速度,越大越快
        private float botopRatio = 4; //底部/顶部弹幕的显示时间;
    
        void Start()
        {
            DOTween.SetTweensCapacity(500,50);//提高最大值,弹幕真的很多
            roll = new List<GameObject>();
            bottom = new List<GameObject>();
            top = new List<GameObject>();
            vp.url = Application.streamingAssetsPath + "/mv.mp4";
            vp.Play();
            path = Application.streamingAssetsPath + "/chat.xml";
    
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(path);
            XmlNodeList nodes = xmlDoc.SelectSingleNode("i").ChildNodes;
    
            foreach (XmlElement xe in nodes)
            {
                if (xe.Name=="d")
                {
                    float showTime = float.Parse(xe.GetAttribute("p").Split(',')[0]);
                    int type= int.Parse(xe.GetAttribute("p").Split(',')[1]);
                   // int size= int.Parse(xe.GetAttribute("p").Split(',')[2]);//默认字体大小25。
                    Color color = TenToColor(xe.GetAttribute("p").Split(',')[3]);
                    string chat = xe.InnerText;
                    StartCoroutine(InitChat(showTime, type, color, chat));
                }
            }
        }
        IEnumerator InitChat(float showTime,int type,Color color, string chat)
        {
            yield return new WaitForSeconds(showTime);
    
            GameObject go = Instantiate(chatText, chatPanel);
            Text te = go.GetComponent<Text>();
            te.color = color;
            te.text = chat;
            yield return new WaitForEndOfFrame();
            if (type == 1)//滚动
            {
                if (isRollOff)
                {
                    Destroy(go);
                }
                else
                {
                    float y = 0;
                    foreach (var item in roll)
                    {
                        RectTransform t = item.GetComponent<RectTransform>();
                        if (t.localPosition.y == y)
                        {
                            if (t.localPosition.x + t.sizeDelta.x >= 0)
                            {
                                y -= 40;
                            }
                        }
                    }
                    go.transform.localPosition = new Vector3(0, y);
                    roll.Add(go);
                    go.transform.DOLocalMoveX(-1920 - go.GetComponent<RectTransform>().sizeDelta.x, MoveTime(chat))
                        .SetEase(Ease.Linear)
                        .OnComplete(() =>
                        {
                            roll.Remove(go);
                            Destroy(go);
                        });
                }
            }
            else if (type == 4)//底部
            {
                if (isBottomOff)
                {
                    Destroy(go);
                }
                else
                {
                    float y = -1040;
                    foreach (var item in bottom)
                    {
                        RectTransform t = item.GetComponent<RectTransform>();
                        if (t.localPosition.y == y)
                        {
    
                            y += 40;
                        }
                    }
                    bottom.Add(go);
                    go.transform.localPosition = new Vector3(-960 - go.GetComponent<RectTransform>().sizeDelta.x / 2, y);
                    go.transform.DOScale(1, botopRatio).OnComplete(() =>
                        {
                            bottom.Remove(go);
                            Destroy(go);
                        });
                }
            }
            else if (type == 5)//顶部
            {
                if (isTopOff)
                {
                    Destroy(go);
                }
                else
                {
                    float y = 0;
                    foreach (var item in top)
                    {
    
                        RectTransform t = item.GetComponent<RectTransform>();
                        if (t.localPosition.y == y)
                        {
                            y -= 40;
                        }
                    }
                    top.Add(go);
                    go.transform.localPosition = new Vector3(-960 - go.GetComponent<RectTransform>().sizeDelta.x / 2, y);
                    go.transform.DOScale(1, botopRatio).OnComplete(() =>
                        {
                            top.Remove(go);
                            Destroy(go);
                        });
                }
            }  
            else//其他高级弹幕就不显示了,有余力的同学可以去尝试
    // 比如 
    //<d p="6.43300,7,20,16737792,1569854839,0,a77032eb,22393737377742850">["0.32","0.52","1-1","9","@@@@@@@@@@@@@@@@@@@@@@@",0,0,"0.32","0.52",500,0,1,"SimHei",1]</d>
    //类型为7的弹幕不清楚他的意思
            {
                Destroy(go);
            }
        }
    
    
        //颜色转换
        Color TenToColor(string s)
        {
            Color nowColor;
            string s16 = Convert.ToString(int.Parse(s), 16);
    
            ColorUtility.TryParseHtmlString("#" + s16,out nowColor);
            return nowColor;
        }
        //时间戳转日期  1970年是起始
        public  DateTime UnixTimestampToDateTime(DateTime target, long timestamp)
        {
            DateTime start = new DateTime(1970, 1, 1, 0, 0, 0, target.Kind);
            return start.AddSeconds(timestamp);
        }
        float MoveTime(string chat)//根据字数返回时长
        {
            return  15/rollRatio/ Mathf.Log(chat.Length+8, 16)-2;
        }
        public void OnRollClick()
        {
            isRollOff = !isRollOff;
            if (isRollOff)
            {
                foreach (var item in roll)
                {
                    Destroy(item);
                }
                roll.Clear();
            }
        }
        public void OnBottomClick()
        {
            isBottomOff = !isBottomOff;
            if (isBottomOff)
            {
                foreach (var item in bottom)
                {
                    Destroy(item);
                }
                bottom.Clear();
            }
    
        }
        public void OnTopClick()
        {
            isTopOff = !isTopOff;
            if (isTopOff)
            {
                foreach (var item in top)
                {
                    Destroy(item);
                }
                top.Clear();
            }
        }
    }
    
    

    没有什么难点,关键的地方就是判断当前位置是否可以放置新的弹幕。

    相关文章

      网友评论

          本文标题:在Unity中显示Bilibili的弹幕

          本文链接:https://www.haomeiwen.com/subject/ejuiyhtx.html