[Unity] A simple sorting method for mixed barrage (message)

1. Project introduction

          Recently, an old message item has been modified, from a pure text message to a mixed message of text, voice, picture, and video. Viewers can click on the message to view the specific content. Since Party A does not require the review function, whatever is sent will be displayed. Saved a lot of things.

2. Use Unity for development

I skipped the preliminary work directly, let me talk about the sorting of messages in detail

2.1 Create a template 

     Created four kinds of bullet screen GameObjects, so that I don't need to use scripts to create them. When using them, I can use GameObject.Instantiate to clone them and put the bullet screens where they should go.

2.2 Sorting

         Because there are many types of bullet screens, and the width and height of each type are not necessarily the same. In order to prevent them from being blocked as much as possible, we need to increase the distance between them.

         For example, in the vertical direction, my screen allows a maximum of five video barrages to be arranged vertically. In order to increase the gap, I can only set four lines of display, and then I have a simple test for the approximate Y coordinate. (UI coordinates are as follows)

float[] BarragePosYArr = { 450, 250, 50, -150 };//下部分还要排放其他的,因此只到这里

        Since I am lazy, I also set 4 horizontal coordinates directly, so combined, I have a total of 16 points

float[] BarragePosXArr = { 1200, 1600, 2000, 2400 };

        The next step is to find a way to make the barrage generated at a random point of these 16 points (move to this point), and when it is generated (moved), it cannot block other active barrages .

        The specific method is as follows

2.2.1 Establish a management queue

         Create three Lists for the purpose of managing the currently active (scrolling) bullet chatting, managing the bullet chatting that is ready to join the activity queue (already created, but not scrolling), and all successfully created bullet chatting.

    private List<GameObject> ActiveList = new List<GameObject>();//已经显示的队列
    private List<GameObject> ReadyList = new List<GameObject>();//创建的缓存队列
    private List<GameObject> BarragesList = new List<GameObject>();//所有弹幕的队列

         When creating a barrage, you need to specify the type and content of the barrage, as well as the parent component in the UI. Here is a sample code that cannot be used.

void CreateBarrage(Barrages.BarrageTypes _type, string _content,int _id)
    {
        GameObject Obj = GameObject.Instantiate(BarrageModels[(int)_type]);
        Obj.name += _id.ToString();
        //指定父组件
        Obj.transform.SetParent(Parent.transform);
        //Barrages类是我自己创建的弹幕的基础类
        Barrages barrages = Obj.GetComponent<Barrages>();
        if (barrages != null)
        {
            //初始化
            barrages.ID = _id;
            barrages.InitBarrage(_content, ShowWindow);
            //更新一下弹幕队列
            BarragesList.Add(Obj);
            //将新的弹幕加入缓存队列
            ReadyList.Add(Obj);

            return;
        }
        Destroy(Obj);
    }

2.2.2 Get the outline Rect after bullet chatting initialization

       In order to avoid occlusion, we must know the width, height and current position coordinates of the barrage. Use the Overlaps function in the Rect class to quickly get whether two Rects are occluded. The following code is also a sample code. First, get the position of the GameObject, and then add the height and width of the sub-object to calculate the maximum outline of the barrage. (Note: Change the Pivot of the barrage body to 0, and fix its center to the upper left corner )

 public Rect GetBoundingRect()
    {
        Rect rc = new Rect(0, 0, 0, 0);
        //起始位置,
        rc.x = this.gameObject.GetComponent<RectTransform>().localPosition.x ;
        rc.y = this.gameObject.GetComponent<RectTransform>().localPosition.y ;
        switch (CurrentType)
        {
            case BarrageTypes.Text:
                {
                    rc.width = this.transform.Find("Icon").gameObject.GetComponent<RectTransform>().rect.width + this.transform.Find("Word").gameObject.GetComponent<RectTransform>().rect.width + 50;
                    rc.height = this.transform.Find("Icon").gameObject.GetComponent<RectTransform>().rect.height;
                    break;
                }
            case BarrageTypes.Image:
                {
                    rc.width = this.transform.Find("Icon").gameObject.GetComponent<RectTransform>().rect.width + gShowImage.gameObject.GetComponent<RectTransform>().rect.width + 50;
                    rc.height = this.gameObject.GetComponent<RectTransform>().rect.height;
                    break;
                }
            case BarrageTypes.Vioce:
                {
                    rc.width = this.gameObject.GetComponent<RectTransform>().rect.width;
                    rc.height= this.gameObject.GetComponent<RectTransform>().rect.height;
                    break;
                }
            case BarrageTypes.Video:
                {
                    rc.width = this.transform.Find("Icon").gameObject.GetComponent<RectTransform>().rect.width + gVideoComponent.gameObject.GetComponent<RectTransform>().rect.width + 50; 
                    rc.height = this.gameObject.GetComponent<RectTransform>().rect.height;
                    break;
                }
        }
        return rc;
    }

2.2.3 Barrage occlusion judgment

         To put it simply, move the position of ReadyList [0] in Update, and move the above 16 points one by one. After the movement is completed, judge whether there is any occlusion. If not, add ReadyList [0] to ActiveList, and then delete ReadyList This danmaku, that is, this danmaku has changed from the ready state to the active state. The following sample code is still not available, it is just a demonstration

bool ResetBarrage(GameObject _obj)
    {
        SpawnTimes++;
        Barrages barrages = _obj.GetComponent<Barrages>();
        if (barrages != null)
        {
            //先移动过去
            RectTransform rc = _obj.GetComponent<RectTransform>();
            rc.localPosition = new Vector3(BarragePosXArr[(SpawnTimes / 4) % 4]+ GetRandom(0,1000,100), BarragePosYArr[SpawnTimes % 4], 0);
            rc.localScale = new Vector3(1, 1, 1);
            barrages.IsLocked = true;
            Rect _rect = barrages.GetBoundingRect();
            if(ActiveList.Count!=0)
            {
                bool IsOverlaped = false;
                for (int i = 0; i < ActiveList.Count; i++)
                {
                    Barrages _barr = ActiveList[i].GetComponent<Barrages>();
                    Rect _rc = _barr.GetBoundingRect();
                    if (IsRectOverlap(_rc, _rect))
                    {
                        IsOverlaped = true;
                    }
                }
                //和所有的弹幕都不相交
                if(!IsOverlaped)
                {
                    barrages.IsLocked = false;
                    return true;
                }
            }else
            {
                barrages.IsLocked = false;
                return true;
            }
        }
        return false;
    }
   public bool IsRectOverlap(Rect _rc1,Rect _rc2)
    {
        return _rc1.Overlaps(_rc2);
    }
 public float GetRandom(float min,float max, float _base)
    {
        return Random.Range(min, max) + _base;
    }

end

Let's add a mobile demonstration, and then update and use it

 //当前活动的弹幕
            for (int i = 0; i < ActiveList.Count; i++)
            {
                if(ActiveList[i]==null)
                {
                    ActiveList.RemoveAt(i);
                    continue;
                }
                Barrages barrages = ActiveList[i].GetComponent<Barrages>();
                if(barrages.IsEmptyContent())
                {
                    //剔除那些没用的
                    Destroy(ActiveList[i]);
                    ActiveList.RemoveAt(i);
                    continue;
                }
                if (barrages.IsLocked) continue;
                RectTransform rc = ActiveList[i].GetComponent<RectTransform>();
                if (rc)
                {
                    float x = rc.localPosition.x - (BarrageSpeed * Time.deltaTime);
                    rc.localPosition = new Vector3(x, rc.localPosition.y, 0);

                    if (rc.localPosition.x <= -1250)
                    {
                        //更新一下位置
                        int idx = ActiveList[i].GetComponent<Barrages>().ID;
                        //准备更新一下位置
                        ReadyList.Add(ActiveList[i]);
                        //从队伍里移除
                        ActiveList.RemoveAt(i);
                    }
                }
            }

The effect is as follows, this part is the preparation effect outside the screen, and the flashing one is updating the position by itself

 

Guess you like

Origin blog.csdn.net/qq_36251561/article/details/125678690