最近又在改前人留下来的锅,聊天界面。在上线测试中,玩家普遍反映聊天界面卡,有反应那就需要解决。看了下,项目中用的是网上拉的一份代码,LinkImageText类。时间给的短,自己又懒得再去研究重写,费时间费力,所以自己测试了这个脚本,性能还是可以的,那就是调用的逻辑代码有问题了。
仔细看了下代码,果然是逻辑有问题。首先先解释下聊天界面处理方式,界面没有隐藏是为了避免SetActive的消耗,只是把Scale全部缩放为0,这个可以理解。按理说,在没有打开聊天界面时,应该不进行任何计算,但是这边做法是有聊天信息直接用GameObject.Instante克隆并且修改Text文本,最可怕的是克隆会有350个,这不是在开玩笑吗。现在7个频道,每个历史记录50条,最多就会存在350条,而且还是挂载350个LinkImageText脚本,如果策划再增加频道和次数,不是直接炸了。界面上还有7个ScrollView,一时都不明白为啥要这样做。简单改了下,改成一个Text,只挂载一个LinkImageText脚本,去掉其余的ScrollView,剩下一个,不打开界面不进行任何计算。效果明显提升,从之前的降30帧,变为几乎不降帧(手机上),逻辑有问题后果还是蛮严重的。
降帧问题一解决,策划马上提出新需求,需要语音的图片根据时间来变化长度。吐个槽,之前前面的人在的时候怎么不让前面的人做,如果一开始定好,就不用东改改西改改,挺烦的。
效果是是实现了,如图:
LinkImageText脚本原作者的思路是对Text文本的顶点进行修改,那我就想了下,我在改本图片大小的时候,可以根据图片的大小,来对后面的文本的顶点位置进行偏移来实现效果。
UIVertex vert = new UIVertex();
for (var i = 0; i < m_ImagesVertexIndex.Count; i++)
{
var endIndex = m_ImagesVertexIndex[i];
var rt = m_ImagesPool[i].rectTransform;
var size = rt.sizeDelta;
if (endIndex < toFill.currentVertCount)
{
toFill.PopulateUIVertex(ref vert, endIndex);
rt.anchoredPosition = new Vector2(vert.position.x + size.x / 2, vert.position.y + size.y / 2-4);
// 抹掉左下角的小黑点
toFill.PopulateUIVertex(ref vert, endIndex - 3);
var pos = vert.position;
for (int j = endIndex, m = endIndex - 3; j > m; j--)
{
toFill.PopulateUIVertex(ref vert, endIndex);
vert.position = pos;
toFill.SetUIVertex(vert, j);
}
//根据图片大小偏移顶点位置
var nextIndex = 0;
if (m_ImagesVertexIndex.Count - i > 1)
nextIndex = m_ImagesVertexIndex[i + 1];
else
nextIndex = toFill.currentVertCount;
for (int textAfterImageIndex = endIndex + 1; textAfterImageIndex < nextIndex; ++textAfterImageIndex)
{
toFill.PopulateUIVertex(ref vert, textAfterImageIndex);
//Debug.LogError("textAfterImageIndex: " + textAfterImageIndex + "--vert.position.x: " + vert.position.x + "--size.x: " + size.x);
vert.position.x += (size.x - 24) / 2;
toFill.SetUIVertex(vert, textAfterImageIndex);
}
发现效果不是很好,图片总是会遮挡住1,2个文本,达不到策划的需求。然后网上搜到原作者的博客,了解到了<quad name=xb_b size=25 width=1 />这个在UGUI中会变成占位符,而且width和height值会对占位符的长宽有影响。经测试,是按作者说的一样。我就想是不是这个width参数影响到了文本之前的距离,虽然我对文本的顶点进行了偏移,那我是否可以控制width来实现图片大小的变化。
然后一堆改动,首先正则表达式修改:
/ <summary>
/ 正则取出所需要的属性
/ </summary>
//private static readonly Regex s_ImageRegex =
// new Regex(@"<quad name=(.+?) size=(\d+?) width=(\d+?) />", RegexOptions.Singleline);
private static readonly Regex s_ImageRegex =
new Regex(@"<quad name=(.+?) size=(\d+?) width=([0-9]*\.?[0-9]*) />", RegexOptions.Singleline);
语音图片大小修改
m_VoiceImage.Clear();
foreach (Match match in s_ImageRegex.Matches(m_OutputText))
{
var picIndex = match.Index;
var endIndex = picIndex * 4 + 3;
m_ImagesVertexIndex.Add(endIndex);
m_ImagesPool.RemoveAll(image => image == null);
if (m_ImagesPool.Count == 0)
{
GetComponentsInChildren<Image>(m_ImagesPool);
}
if (m_ImagesVertexIndex.Count > m_ImagesPool.Count)
{
var resources = new DefaultControls.Resources();
var go = DefaultControls.CreateImage(resources);
go.layer = gameObject.layer;
var rt = go.transform as RectTransform;
if (rt)
{
rt.SetParent(rectTransform);
rt.localPosition = Vector3.zero;
rt.localRotation = Quaternion.identity;
rt.localScale = Vector3.one;
}
m_ImagesPool.Add(go.GetComponent<Image>());
}
var spriteName = match.Groups[1].Value;
var size = float.Parse(match.Groups[2].Value);
var width = float.Parse(match.Groups[3].Value);
var img = m_ImagesPool[m_ImagesVertexIndex.Count - 1];
if (img.sprite == null || img.sprite.name != spriteName)
{
//加载图片
img.type = Image.Type.Sliced;
}
//修改图片大小
img.rectTransform.sizeDelta = new Vector2(size * width, size);
if (spriteName == VoiceSDKInterface.VoiceSpriteName)//语音喇叭图片
{
img.raycastTarget = true;
m_VoiceImage.Add(img);
int index = m_VoiceImage.Count;
EventTriggerAssist.SetTriggerEvent(img.gameObject, UnityEngine.EventSystems.EventTriggerType.PointerClick, (PointerEventData) => {
//Debug.LogError("c# index: " + index);
if (m_VoiceImage[index - 1] != img) return;
if (m_ImageLuaFunc != null)
m_ImageLuaFunc.Call(index);
});
}
else
img.raycastTarget = false;
img.enabled = true;
}
最后把之前修改顶点位置的代码注释掉,嗯,效果实现达成策划的需求。有兴趣的还是去看下原作者的博客,写的很详细,挺不错的技术文章。