公司在做一款模拟经营类的卖车游戏,需要一个简单的历史记录功能,放在左上角,记录最近20条的收入/支出记录。超过2秒不动则收起。收起时展示最近的一个消息记录。
用到的组件是ScrollView,使用方法可以参考我写过的一篇博客。ScrollView踩坑记录
要求:
1、像正常的聊天框那样,消息从上向下展示,界面最多展示五条,如果用户当前看的是最底部的消息,那么有新的收入/支出消息的时候,需要将最新一条添加到底部,并将其他消息向上移动一格。如果用户正在查询历史记录,那么正常添加即可。不需要添加到底部。
2、当用户两秒内对历史记录无任何操作时,收起历史记录,并展示最新一条消息,此后如果有最新记录,展示最新的记录。
先实现最简单的功能。
1、正常的添加历史记录。
先看下最简单的功能,可以正常添加,但是最新一条聊天记录并不会将其他消息顶上去。
如果想顶上去,需要加上几行代码
Canvas.ForceUpdateCanvases();
scrollRect.verticalNormalizedPosition = 0f;
Canvas.ForceUpdateCanvases();
这个代码是强制刷新画布(不刷新会有延迟,出现错误的效果,此处为了简略就不掩饰了),并且将滑动条的垂直变量变成0(即到达底部)。
如上图所示,现在虽然沉底了,但是当用户查询历史记录的时候,此时如果新加入一条记录,就会出现用户正翻着记录,却被沉底的情况,这种情况不符合要求,需要改。
此时可以加个判断,判断verticalNormalizedPosition 的值,注意不能和0对比,滑动后的verticalNormalizedPosition 会有一定的误差,可能是0.0000000几。
我这里觉得这样判断比较麻烦,没有深入研究。使用的是另一种方法。
第二种方法:
更改自身的中心点(pivot)。
当自身pivot点在中心时,自身变大,是向四周扩大,如果在上面,则向下面扩大,如果是在下面,那么则向上面扩大。
那么解决方法就出来了,当历史记录少于五条时,我们让他正常的向下增长。
当历史记录少于五条时,中心点在上方,当中心点大于等于五条时,中心点在上方,此时后续新增的历史记录需要调用SetAsLastSibling()方法,将最新的消息置于底部。
效果如上图所示。此处有一个注意点,当通过代码更改物体的中心点时,物体会发生抽搐,算是unity的一个BUG把,需要手动的给物的位置赋值。
完整代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class HistoryView : MonoBehaviour
{
/// <summary>
/// 历史记录
/// </summary>
public List<string> historyStringList = new List<string>();
public Button getButton1;
public Button costButton1;
public Button getButton2;
public Button costButton2;
/// <summary>
/// 子类的父物体
/// </summary>
public Transform parentTransform;
/// <summary>
/// 预制体
/// </summary>
public GameObject historyItemPrefab;
/// <summary>
/// 滑动框
/// </summary>
public ScrollRect scrollRect;
private void Awake()
{
getButton1.onClick.AddListener(() =>
{
AddHistory("+100");
});
getButton2.onClick.AddListener(() =>
{
AddHistory("+200");
});
costButton1.onClick.AddListener(() =>
{
AddHistory("-100");
});
costButton2.onClick.AddListener(() =>
{
AddHistory("-200");
});
}
void AddHistory(string historyString)
{
historyStringList.Add(historyString);
if (historyStringList.Count < 5)
{
scrollRect.content.pivot = new Vector2(0, 1);
}
else if (historyStringList.Count == 5)
{
scrollRect.content.pivot = new Vector2(0, 0);
Canvas.ForceUpdateCanvases();
scrollRect.verticalNormalizedPosition = 0f;
Canvas.ForceUpdateCanvases();
}
else
{
scrollRect.content.pivot = new Vector2(0, 0);
}
GameObject go = Instantiate(historyItemPrefab, parentTransform);
go.transform.SetAsLastSibling();
var historyItem = go.GetComponent<HistoryItem>();
historyItem.contentText.text = historyString;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class HistoryItem : MonoBehaviour
{
public Text contentText;
}
隐藏功能:将Content的锚点改为下方,这样我们只需要改动ViewReport的下面大小,Content的坐标就会跟着改动。
隐藏的时候将scrollRect.verticalNormalizedPosition = 0f;然后根据实际情况调整ViewPort的大小就行,这里就偷个懒不做演示了。
至于显示,可以用一个透明的button贴在最上面,检测玩家是否按下,按下则改变大小,也可以通过Unity ScrollRect提供的onValueChanged去监听,这就看个人习惯了。
ps:onValueChanged在有新物体创建的时候有时会导致value改变,有时不会,是个小bug。
Demo