Files
test/Assets/PerfectWorld/Scripts/Chat/UI/ChatPanelUI.cs
T
2026-03-18 15:55:24 +07:00

205 lines
5.5 KiB
C#

using System;
using System.Collections.Generic;
using CSNetwork;
using CSNetwork.GPDataType;
using UnityEngine;
using UnityEngine.Pool;
using UnityEngine.UI;
namespace BrewMonster.Scripts.ChatUI
{
[Serializable]
public struct ChatMessageData
{
public string message;
public byte channel;
}
public class ChatPanelUI : MonoBehaviour
{
[Header("UI")] public ScrollRect scrollRect;
public GameObject chatPanelUIGO;
public RectTransform content;
public ChatMessageView messagePrefab;
//public GameObject newMessageIndicator;
[Header("Config")] public int maxVisibleMessages = 30;
public int maxStoredMessages = 2000;
[Header("Chat System Data")]
public ChatSystemSO chatSystemSO;
private Dictionary<byte, Sprite> _iconCache;
private List<ChatMessageData> _messages = new();
private List<ChatMessageView> _visibleViews = new();
private ObjectPool<ChatMessageView> _pool;
private bool _userAtBottom = true;
void Awake()
{
_iconCache = new Dictionary<byte, Sprite>();
if (chatSystemSO != null && chatSystemSO.channelIcons != null)
{
foreach (var mapping in chatSystemSO.channelIcons)
{
_iconCache[(byte)mapping.channel] = mapping.icon;
}
}
EventBus.Subscribe<GameSession.ChatMessageEvent>(OnChatMessageReceived);
_pool = new ObjectPool<ChatMessageView>(
CreateItem,
OnGetItem,
OnReleaseItem,
OnDestroyItem,
false,
10,
100
);
scrollRect.onValueChanged.AddListener(OnScrollChanged);
}
private void OnDestroy()
{
EventBus.Unsubscribe<GameSession.ChatMessageEvent>(OnChatMessageReceived);
}
private void OnChatMessageReceived(GameSession.ChatMessageEvent x)
{
ChatThreadDispatcher.Instance.Post(() =>
{
AddMessage(x.context, x.channel);
});
}
ChatMessageView CreateItem()
{
var item = Instantiate(messagePrefab);
item.transform.SetParent(content, false);
return item;
}
void OnGetItem(ChatMessageView item)
{
item.gameObject.SetActive(true);
}
void OnReleaseItem(ChatMessageView item)
{
item.gameObject.SetActive(false);
}
void OnDestroyItem(ChatMessageView item)
{
Destroy(item.gameObject);
}
void OnScrollChanged(Vector2 pos)
{
_userAtBottom = scrollRect.verticalNormalizedPosition <= 0.001f;
if (_userAtBottom)
{
//newMessageIndicator.SetActive(false);
}
}
bool IsAtBottom()
{
return scrollRect.verticalNormalizedPosition <= 0.001f;
}
public void AddMessage(string msg, byte channel)
{
var data = new ChatMessageData { message = msg, channel = channel };
_messages.Add(data);
if (_messages.Count > maxStoredMessages)
_messages.RemoveAt(0);
if (!chatPanelUIGO.activeSelf)
return;
AddMessageView(data);
if (_userAtBottom)
ScrollToBottom();
}
void AddMessageView(ChatMessageData data)
{
var view = _pool.Get();
view.transform.SetParent(content, false);
view.transform.SetAsLastSibling();
Sprite icon = _iconCache.ContainsKey(data.channel) ? _iconCache[data.channel] : null;
view.Bind(icon, data.message);
_visibleViews.Add(view);
if (_visibleViews.Count > maxVisibleMessages)
{
var old = _visibleViews[0];
_visibleViews.RemoveAt(0);
_pool.Release(old);
}
Canvas.ForceUpdateCanvases();
}
void RefreshVisible()
{
foreach (var view in _visibleViews)
_pool.Release(view);
_visibleViews.Clear();
int start = Mathf.Max(0, _messages.Count - maxVisibleMessages);
for (int i = start; i < _messages.Count; i++)
{
var view = _pool.Get();
view.transform.SetParent(content, false);
view.transform.SetAsLastSibling();
var data = _messages[i];
Sprite icon = _iconCache.ContainsKey(data.channel) ? _iconCache[data.channel] : null;
view.Bind(icon, data.message);
_visibleViews.Add(view);
}
Canvas.ForceUpdateCanvases();
ScrollToBottom();
}
public void ScrollToBottom()
{
Canvas.ForceUpdateCanvases();
scrollRect.verticalNormalizedPosition = 0f;
//newMessageIndicator.SetActive(false);
}
public void ClearChat()
{
foreach (var view in _visibleViews)
_pool.Release(view);
_visibleViews.Clear();
_messages.Clear();
}
public void OnHandlerChatButton()
{
bool open = !chatPanelUIGO.activeSelf;
chatPanelUIGO.SetActive(open);
if (open)
RefreshVisible();
}
}
}