1.项目介绍
? ? ? ? ? 最近修改了一个老的留言项目,由纯文字留言,变成文字、语音、图片、视频混合留言,观众可以任意点击留言观看具体内容,由于甲方不要求审核功能,发送什么就显示什么。省了很多事情。
2.使用Unity进行开发
前期工作就直接跳过了,具体讲一下,关于留言排序的事情
2.1创建模板?
? ? ?新建了四种弹幕的GameObject,这样我就不用使用脚本创建了,使用的时候可以使用GameObject.Instantiate来进行克隆,把弹幕放到它该去的地方。
2.2排序
? ? ? ? ?因为弹幕由很多种类,每种的宽高不一定相同,为了尽可能的使他们不会遮挡,我们需要拉开他们的间距。
? ? ? ? ?例如在竖直方向上,我的屏幕最大允许五个视频弹幕竖直排列,为了加大间隙我只能设置四行显示,然后大致的Y坐标也我简单测试出来了。(UI坐标的如下)
float[] BarragePosYArr = { 450, 250, 50, -150 };//下部分还要排放其他的,因此只到这里
? ? ? ? 由于我比较懒,我直接也设置4个横向坐标,这样组合起来,我一共有16个点
float[] BarragePosXArr = { 1200, 1600, 2000, 2400 };
? ? ? ? 接下来就是想办法,让弹幕在这16个点的随机一个点生成(移动到这个点),生成(移动)的时候,还不能遮挡到其他正在活动的弹幕。
? ? ? ? 具体的办法如下
2.2.1 建立一套管理队列
? ? ? ? ?创建三个 List ,目的是用于管理当前正在活动(滚动)的弹幕,管理已经准备好加入活动队列的弹幕(已经创建好,但是没有开始滚动),所有已经成功创建的弹幕。
private List<GameObject> ActiveList = new List<GameObject>();//已经显示的队列
private List<GameObject> ReadyList = new List<GameObject>();//创建的缓存队列
private List<GameObject> BarragesList = new List<GameObject>();//所有弹幕的队列
? ? ? ? ?创建弹幕时候,需要指定好弹幕的类型和内容,还有在UI里的父组件,下面给一段示例代码,无法使用的。
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 获取弹幕初始化后的轮廓Rect
? ? ? ?为了避免遮挡,我们肯定要知道弹幕的宽高和当前位置坐标 。使用Rect类中的Overlaps函数可以快速得到两个Rect是否存在遮挡。以下代码也是示例代码,先去获取GameObject的位置,然后加上子物体里的高度和宽度,计算出弹幕的最大轮廓。(注意:要把弹幕的本体的Pivot改成0,将他的中心固定在左上角)
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 弹幕遮挡判断
? ? ? ? ?简单来说,在Update里去移动ReadyList [0]的位置,在上述的16个点依次移动,移动完成后,判断一下当前有没有遮挡,没有的话就将ReadyList [0]加入ActiveList ,然后ReadyList删除这个弹幕,即这个弹幕已经从准备状态变成活动状态了。下面的示范代码依旧不可使用,只是示范
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;
}
结尾
再加一下移动示范的吧,再update使用
//当前活动的弹幕
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);
}
}
}
效果如下,这部分是画面外的准备效果,闪动的是在自己更新位置
?
|