在Unity中显示Bilibili的弹幕

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



chat.bilibili.com
12532425
0
1500
0
0
k-v

全程高能
钢琴:我总感觉不大对劲
侧脸太好看了
爱死这个侧脸了
钢琴:我总感觉不大对劲
钢琴即将损命
苏苏苏苏苏苏苏苏苏苏苏苏苏苏苏苏苏苏苏苏苏苏苏苏苏
哥哥太酷了
我爱花花

比如第一行
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 roll, bottom, top;

    //一些播放参数
    private float rollRatio = 1; //滚动弹幕的播放速度,越大越快
    private float botopRatio = 4; //底部/顶部弹幕的显示时间;

    void Start()
    {
        DOTween.SetTweensCapacity(500,50);//提高最大值,弹幕真的很多
        roll = new List();
        bottom = new List();
        top = new List();
        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();
        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();
                    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().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();
                    if (t.localPosition.y == y)
                    {

                        y += 40;
                    }
                }
                bottom.Add(go);
                go.transform.localPosition = new Vector3(-960 - go.GetComponent().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();
                    if (t.localPosition.y == y)
                    {
                        y -= 40;
                    }
                }
                top.Add(go);
                go.transform.localPosition = new Vector3(-960 - go.GetComponent().sizeDelta.x / 2, y);
                go.transform.DOScale(1, botopRatio).OnComplete(() =>
                    {
                        top.Remove(go);
                        Destroy(go);
                    });
            }
        }  
        else//其他高级弹幕就不显示了,有余力的同学可以去尝试
// 比如 
//["0.32","0.52","1-1","9","@@@@@@@@@@@@@@@@@@@@@@@",0,0,"0.32","0.52",500,0,1,"SimHei",1]
//类型为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的弹幕)