using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class ChatWin : UIBase { [SerializeField, Header("锁定当前区域比例"), Range(0, 1)] float m_Percent = 0.3f; [SerializeField] ButtonEx btnSetting; [SerializeField] ButtonEx btnClose; [SerializeField] ScrollerController scrChatTab; [SerializeField] ChatPlayerMineCell m_ChatMineCell; [SerializeField] ChatPlayerOtherCell m_ChatOtherCell; [SerializeField] ChatSysCell m_ChatSysCell; [SerializeField] Transform transInput; [SerializeField] InputField inputChat; [SerializeField] ButtonEx btnSendChat; [SerializeField] ImageEx imgSendChat; [SerializeField] TextEx txtSendChat; [SerializeField] ScrollerController scrWorld; [SerializeField] ScrollerController scrGuild; private int unreadMsgCount = 0; [SerializeField] ButtonEx btnNewMsgTip; [SerializeField] TextEx txtNewMsgTip; [SerializeField, Header("聊天弹幕设置")] Transform transSettings; [SerializeField] ClickScreenOtherSpace clickScreenOtherSpace; bool isSettingOpen = false; [SerializeField] ChatSettingButton btnWorldSetting; [SerializeField] ChatSettingButton btnGuildSetting; ChatManager manager { get { return ChatManager.Instance; } } protected override void InitComponent() { base.InitComponent(); btnClose.SetListener(CloseWindow); btnSendChat.SetListener(() => { // 如果在聊天输入界面无输入文字点击发送,则关闭聊天输入界面 if (string.IsNullOrEmpty(inputChat.text)) { CloseWindow(); return; } if (!manager.CheckChatLimit(inputChat.text, out int errorCode)) { manager.ShowChatErrorTip(errorCode); return; } manager.SendChatInfo(manager.nowChatChannel, inputChat.text); manager.AddChatChannelSendTime(manager.nowChatChannel, TimeUtility.AllSeconds); inputChat.text = string.Empty; UpdateSendButton(); }); btnNewMsgTip.SetListener(() => { if (manager.nowChatChannel == ChatChannel.World) { RefreshChat(manager.nowChatChannel, scrWorld); ScrollerJump(scrWorld, ChatChannel.World); } else if (manager.nowChatChannel == ChatChannel.Guild) { RefreshChat(manager.nowChatChannel, scrGuild); ScrollerJump(scrGuild, ChatChannel.Guild); } ClearUnreadMsg(); }); btnSetting.SetListener(() => { isSettingOpen = !isSettingOpen; transSettings.SetActive(isSettingOpen); }); } // 清理未读消息状态 private void ClearUnreadMsg() { unreadMsgCount = 0; btnNewMsgTip.SetActive(false); } // 更新未读消息UI private void UpdateUnreadMsgUI() { btnNewMsgTip.SetActive(unreadMsgCount > 0); txtNewMsgTip.text = Language.Get("Chat10", unreadMsgCount); } protected override void OnPreOpen() { base.OnPreOpen(); manager.OnChatTabChangeEvent += OnChatTabChange; manager.OnUpdateTalkEvent += OnUpdateTalkEvent; manager.OnUpdateTalkCacheListEvent += OnUpdateTalkCacheList; manager.OnDeleteTalkEvent += OnDeleteTalkEvent; scrChatTab.OnRefreshCell += OnRefreshChatTabCell; scrWorld.OnGetDynamicSize += OnGetWorldChatDynamicSize; scrWorld.OnRefreshCell += OnRefreshWorldCell; scrWorld.mScrollRect.onValueChanged.AddListener(OnWorldScrollValChange); scrGuild.OnGetDynamicSize += OnGetWorldChatDynamicSize; scrGuild.OnRefreshCell += OnRefreshGuildCell; scrGuild.mScrollRect.onValueChanged.AddListener(OnGuildScrollValChange); clickScreenOtherSpace.AddListener(OnClickScreenOtherSpace); GlobalTimeEvent.Instance.secondEvent += OnSecondEvent; isSettingOpen = false; transSettings.SetActive(isSettingOpen); btnWorldSetting.SetChannelType(ChatChannel.World); btnGuildSetting.SetChannelType(ChatChannel.Guild); inputChat.characterLimit = ChatManager.Instance.characterLimit; CreaterAll(manager.nowChatTab); } protected override void OnPreClose() { base.OnPreClose(); manager.OnChatTabChangeEvent -= OnChatTabChange; manager.OnUpdateTalkEvent -= OnUpdateTalkEvent; manager.OnUpdateTalkCacheListEvent -= OnUpdateTalkCacheList; manager.OnDeleteTalkEvent -= OnDeleteTalkEvent; scrChatTab.OnRefreshCell -= OnRefreshChatTabCell; scrWorld.OnGetDynamicSize -= OnGetWorldChatDynamicSize; scrWorld.OnRefreshCell -= OnRefreshWorldCell; scrWorld.mScrollRect.onValueChanged.RemoveListener(OnWorldScrollValChange); scrGuild.OnGetDynamicSize -= OnGetGuildChatDynamicSize; scrGuild.OnRefreshCell -= OnRefreshGuildCell; scrGuild.mScrollRect.onValueChanged.RemoveListener(OnGuildScrollValChange); GlobalTimeEvent.Instance.secondEvent -= OnSecondEvent; clickScreenOtherSpace.RemoveAllListeners(); } private void OnDeleteTalkEvent(ChatChannel channel) { if (channel == ChatChannel.World) { CreateScroller(scrWorld, channel); } else if (channel == ChatChannel.Guild) { CreateScroller(scrGuild, channel); } } private void OnRefreshGuildCell(ScrollerDataType type, CellView cell) { OnRefreshCell(type, cell); } private void OnRefreshWorldCell(ScrollerDataType type, CellView cell) { OnRefreshCell(type, cell); } private void OnRefreshCell(ScrollerDataType type, CellView cell) { if (type == ScrollerDataType.Header) { var _cell = cell.GetComponent(); _cell?.Refresh(cell); } else if (type == ScrollerDataType.Normal) { var _cell = cell.GetComponent(); _cell?.Refresh(cell); } else if (type == ScrollerDataType.Tail) { var _cell = cell.GetComponent(); _cell?.Refresh(cell); } else if (type == ScrollerDataType.Extra1) { var _cell = cell.GetComponent(); _cell?.Refresh(cell); } } private void OnSecondEvent() { UpdateSendButton(); } private void OnClickScreenOtherSpace() { isSettingOpen = !isSettingOpen; transSettings.SetActive(isSettingOpen); } private bool m_isJumpArea; public bool isJumpArea { get { return m_isJumpArea; } set { if (m_isJumpArea == value) return; m_isJumpArea = value; // isJumpArea = true (用户在底部):开启锁定底部 if (value) { if (scrWorld.lockType != EnhanceLockType.LockVerticalBottom) scrWorld.lockType = EnhanceLockType.LockVerticalBottom; // 如果回到底部,直接清零消息 ClearUnreadMsg(); } // isJumpArea = false (用户看历史):保持视觉位置 else { if (scrWorld.lockType != EnhanceLockType.KeepVertical) scrWorld.lockType = EnhanceLockType.KeepVertical; } } } private void OnWorldScrollValChange(Vector2 _pos) { OnScrollValChange(scrWorld, _pos); } private void OnGuildScrollValChange(Vector2 _pos) { OnScrollValChange(scrGuild, _pos); } private void OnScrollValChange(ScrollerController scorller, Vector2 _pos) { if (scorller.m_Scorller._ScrollSize <= 0) return; float _value = m_Percent / scorller.m_Scorller._ScrollSize * scorller.m_Scorller.ScrollRectSize; isJumpArea = _pos.y <= _value; // 检查滚动位置是否覆盖了新消息 if (unreadMsgCount <= 0) return; int totalCount = scorller.GetNumberOfCells(scorller.m_Scorller); int thresholdIndex = totalCount - unreadMsgCount; // 获取当前视窗最底部显示的 Item 索引 int currentBottomIndex = scorller.m_Scorller.EndDataIndex; // 如果当前看到的底部索引 超过了 新消息的起始线(减1是为了容错) if (currentBottomIndex >= thresholdIndex - 1) { ClearUnreadMsg(); } } private void OnChatTabChange(ChatTab entrance) { CreaterAll(entrance); } private void OnUpdateTalkEvent(ChatChannel type, TalkData data) { RefreshAll(type, playerId: data.PlayerID); } private void OnUpdateTalkCacheList() { RefreshAll(manager.nowChatChannel); } private void CreaterAll(ChatTab chatTab) { if (chatTab == ChatTab.World) { manager.nowChatChannel = ChatChannel.World; } else if (chatTab == ChatTab.Guild) { manager.nowChatChannel = ChatChannel.Guild; } UpdateSendButton(); // 打开界面时默认到底部,无未读 isJumpArea = true; scrWorld.lockType = EnhanceLockType.LockVerticalBottom; // 初始锁定底部 scrWorld.SetActive(chatTab == ChatTab.World); scrGuild.lockType = EnhanceLockType.LockVerticalBottom; // 初始锁定底部 scrGuild.SetActive(chatTab == ChatTab.Guild); transInput.SetActive(chatTab == ChatTab.World || chatTab == ChatTab.Guild); CreateChatTabScroller(); switch (chatTab) { case ChatTab.World: manager.nowChatChannel = ChatChannel.World; CreateScroller(scrWorld, ChatChannel.World); ScrollerJump(scrWorld, ChatChannel.World); ClearUnreadMsg(); break; case ChatTab.Guild: manager.nowChatChannel = ChatChannel.Guild; CreateScroller(scrGuild, ChatChannel.Guild); ScrollerJump(scrGuild, ChatChannel.Guild); ClearUnreadMsg(); break; } } float KeepArea(ScrollerController scroller) { if (scroller.GetNumberOfCells(scroller.m_Scorller) >= manager.maxTalkCount) { float totalRemovedSize = 0f; for (int i = 0; i < manager.deleteTalkCount; i++) { float cellSize = scroller.GetCellSize(i); totalRemovedSize += cellSize + scroller.m_Scorller.spacing; } return totalRemovedSize; } return 0; } private void RefreshAll(ChatChannel type, uint playerId = 0) { scrChatTab.m_Scorller.RefreshActiveCellViews(); if (type == ChatChannel.World) { RefreshChat(type, scrWorld, playerId); } else if (type == ChatChannel.Guild) { RefreshChat(type, scrGuild, playerId); } } private void RefreshChat(ChatChannel type, ScrollerController scroller, uint playerId = 0) { if (!manager.TryGetTalkData(type, out List datas) || datas == null) return; int numberOfCells = scroller.GetNumberOfCells(scroller.m_Scorller); if (numberOfCells < manager.maxTalkCount) { for (int i = numberOfCells; i < datas.Count; i++) { TalkData data = datas[i]; ScrollerDataType scrollerDataType = GetCellType(data); scroller.AddCell(scrollerDataType, i); int talkDataType = manager.GetTalkDataType(data); float height = GetHeight(talkDataType, data.Content, data.InfoList); scroller.m_Scorller.AddHeight(true, height); //Debug.Log($"ChatWin AddCell i {i} AddHeight {height}"); } } else { scroller.m_Scorller.RefreshActiveCellViews(); } if (type == ChatChannel.World && manager.nowChatTab != ChatTab.World) return; if (type == ChatChannel.Guild && manager.nowChatTab != ChatTab.Guild) return; // 1. 自己发送的消息 -> 强制跳转到底部 + 清零 if (playerId == PlayerDatas.Instance.PlayerId) { isJumpArea = true; scroller.lockType = EnhanceLockType.LockVerticalBottom; ScrollerJump(scroller, type); ClearUnreadMsg(); // 自己发的消息不用提示 } // 2. 别人发消息 & 当前在底部 -> 自动跟随 + 清零 else if (isJumpArea) { scroller.lockType = EnhanceLockType.LockVerticalBottom; ScrollerJump(scroller, type); ClearUnreadMsg(); // 在底部看着不用提示 } // 3. 别人发消息 & 当前在看历史 -> 保持位置 + 增加未读计数 else { scroller.lockType = EnhanceLockType.KeepVertical; float offset = KeepArea(scroller); if (Math.Abs(offset) > 0.001f) { ScrollerJump(scroller, offset); } if (type == manager.nowChatChannel) { unreadMsgCount++; UpdateUnreadMsgUI(); } // 特殊情况:如果未读数量巨大(超过了总显示数量),说明整个列表都被刷新了,直接消零 if (unreadMsgCount >= scrWorld.GetNumberOfCells(scrWorld.m_Scorller)) { ClearUnreadMsg(); } } } private bool OnGetWorldChatDynamicSize(ScrollerDataType _type, int _index, out float height) { return OnGetChatDynamicSize(_type, _index, out height); } private bool OnGetGuildChatDynamicSize(ScrollerDataType _type, int _index, out float height) { return OnGetChatDynamicSize(_type, _index, out height); } private bool OnGetChatDynamicSize(ScrollerDataType _type, int _index, out float height) { height = 0; if (!manager.TryGetChatData(manager.nowChatChannel, _index, out TalkData data) || data == null) return false; switch (_type) { case ScrollerDataType.Header: height = m_ChatMineCell.GetHeight(data.Content, data.InfoList); return true; case ScrollerDataType.Normal: height = m_ChatOtherCell.GetHeight(data.Content, data.InfoList); return true; case ScrollerDataType.Tail: height = m_ChatSysCell.GetHeight(data.Content, data.InfoList); return true; case ScrollerDataType.Extra1: height = 30; return true; } return true; } private void OnRefreshChatTabCell(ScrollerDataType type, CellView cell) { var _cell = cell.GetComponent(); _cell?.Display(cell.index, cell); } private void CreateChatTabScroller() { scrChatTab.Refresh(); for (int i = 0; i < manager.tabShowList.Count; i++) { CellInfo cellInfo = new CellInfo(); cellInfo.infoInt1 = (int)manager.tabShowList[i]; scrChatTab.AddCell(ScrollerDataType.Header, i, cellInfo); } scrChatTab.Restart(); } private void CreateScroller(ScrollerController scroller, ChatChannel type) { scroller.Refresh(); if (manager.TryGetTalkData(type, out List datas)) { for (int i = 0; i < datas.Count; i++) { TalkData data = datas[i]; ScrollerDataType scrollerDataType = GetCellType(data); scroller.AddCell(scrollerDataType, i); } } scroller.Restart(); } private void ScrollerJump(ScrollerController scroller, ChatChannel type) { scroller.lockType = EnhanceLockType.LockVerticalBottom; scroller.ResetScrollPos(); int jumpIndex = manager.GetJumpIndex(type); scroller.JumpIndex(jumpIndex); //Debug.Log($"ChatWin JumpIndex {jumpIndex}"); isJumpArea = true; } private void ScrollerJump(ScrollerController scroller, float _offset) { scroller.JumpIndex(-_offset, 0, EnhancedUI.EnhancedScroller.EnhancedScroller.TweenType.immediate); } // 0-系统 1-日期 2-自己 3-其他玩家 private ScrollerDataType GetCellType(TalkData data) { int type = manager.GetTalkDataType(data); if (type == 0) return ScrollerDataType.Tail; else if (type == 1) return ScrollerDataType.Extra1; else if (type == 2) return ScrollerDataType.Header; else return ScrollerDataType.Normal; } public float GetHeight(int type, string content, ArrayList list) { switch (type) { case 0: return m_ChatSysCell.GetHeight(content, list); case 1: return 30; case 2: return m_ChatMineCell.GetHeight(content, list); case 3: return m_ChatOtherCell.GetHeight(content, list); } return 0; } private void UpdateSendButton() { bool isCanSend = manager.IsCanSend(manager.nowChatChannel, out int remainingSeconds); btnSendChat.interactable = isCanSend; imgSendChat.gray = !isCanSend; txtSendChat.text = isCanSend ? Language.Get("Chat02") : Language.Get("Chat14", remainingSeconds); txtSendChat.colorType = isCanSend ? TextColType.NavyBrown : TextColType.LightWhite; } }