using UnityEngine.UI;
using System.Collections;
using DG.Tweening;
using System.Collections.Generic;
using UnityEngine.EventSystems;
using UnityEngine;
public class 聊天系统 : MonoBehaviour
{
// UI组件引用
public GameObject 聊天窗口;
public InputField 消息输入框;
public Button 发送按钮;
public Button 关闭按钮;
public Transform 消息容器; // 消息项的父物体(必须是ScrollView的Content)
public GameObject 玩家消息预制体; // 玩家消息预制体(靠右)
public GameObject 朋友消息预制体; // 朋友消息预制体(靠左)
public ScrollRect 聊天滚动视图; // 手动指定滚动视图
public RectTransform 标题栏; // 用于拖拽的标题栏
// 地块信息显示
public GameObject 地块信息预制体; // 用于显示地块信息的预制体
// 聊天设置
public string 玩家名称 = "我";
public string 朋友名称 = "AI助手";
public Color 玩家消息颜色 = Color.blue;
public Color 朋友消息颜色 = Color.green;
public int 消息分割长度 = 20; // 消息分割长度
// 显示相关设置
public RectTransform 聊天内容区域;
public bool 显示关键错误 = true; // 只保留关键错误显示控制
public Color 调试文本颜色 = Color.red;
// 聊天状态
private bool 聊天窗口是否打开 = false;
public 聊天邀请管理器 邀请管理器;
// 字体相关
public Font 聊天字体;
public int 字体大小 = 16;
// UI层级相关
public Canvas 聊天画布;
public int 消息文本层级 = 5;
// 动画设置
public float 动画持续时间 = 0.3f;
public float 缩放比例 = 0.8f;
public float 弹性系数 = 1.1f;
// 消息历史记录
private List<消息数据> 消息历史 = new List<消息数据>();
public int 最大消息数量 = 50; // 限制最大消息数量
// 拖拽相关变量
private bool 正在拖拽 = false;
private Vector2 拖拽偏移量;
private RectTransform 聊天窗口Rect;
// 引用AI管理器
public AIManager ai管理器;
// 当前显示的地块信息
private GameObject 当前地块信息显示 = null;
public void 收到AI消息(string 内容)
{
// 使用朋友的名称和颜色显示AI消息
收到消息(朋友名称, 内容);
}
void Start()
{
聊天窗口.SetActive(false);
聊天窗口是否打开 = false;
邀请管理器 = FindObjectOfType<聊天邀请管理器>();
// 获取聊天窗口的RectTransform用于拖拽
聊天窗口Rect = 聊天窗口.GetComponent<RectTransform>();
// 自动查找AI管理器
if (ai管理器 == null)
{
ai管理器 = FindObjectOfType<AIManager>();
if (ai管理器 != null)
{
ai管理器.聊天系统 = this; // 双向关联
}
else if (显示关键错误)
{
Debug.LogWarning("场景中未找到AIManager组件,AI功能将不可用");
}
}
// 确保标题栏存在,若未指定则尝试查找
if (标题栏 == null)
{
Transform 标题栏Transform = 聊天窗口.transform.Find("标题栏");
if (标题栏Transform != null)
{
标题栏 = 标题栏Transform.GetComponent<RectTransform>();
}
else
{
标题栏 = 聊天窗口Rect;
}
}
// 自动查找滚动视图
if (聊天滚动视图 == null)
聊天滚动视图 = 消息容器.GetComponentInParent<ScrollRect>();
// 验证并修复显示问题
验证消息显示相关组件();
修复输入框();
修复消息容器布局();
修复UI层级问题();
// 绑定事件
发送按钮.onClick.RemoveAllListeners();
发送按钮.onClick.AddListener(发送消息);
关闭按钮.onClick.RemoveAllListeners();
关闭按钮.onClick.AddListener(关闭聊天窗口);
消息输入框.onEndEdit.RemoveAllListeners();
消息输入框.onEndEdit.AddListener(输入框回车发送);
// 添加输入框按键监听,支持Ctrl+Enter换行
消息输入框.onValueChanged.AddListener(OnInputValueChanged);
// 添加拖拽事件监听
添加拖拽事件监听();
StartCoroutine(监控输入框状态());
}
// 添加拖拽事件监听
private void 添加拖拽事件监听()
{
if (标题栏 == null) return;
// 添加事件触发器用于拖拽
EventTrigger trigger = 标题栏.GetComponent<EventTrigger>();
if (trigger == null)
{
trigger = 标题栏.gameObject.AddComponent<EventTrigger>();
}
// 清除现有事件,避免重复添加
trigger.triggers.Clear();
// 鼠标按下事件
EventTrigger.Entry 按下事件 = new EventTrigger.Entry();
按下事件.eventID = EventTriggerType.PointerDown;
按下事件.callback.AddListener((data) => 开始拖拽((PointerEventData)data));
trigger.triggers.Add(按下事件);
// 鼠标拖动事件
EventTrigger.Entry 拖动事件 = new EventTrigger.Entry();
拖动事件.eventID = EventTriggerType.Drag;
拖动事件.callback.AddListener((data) => 执行拖拽((PointerEventData)data));
trigger.triggers.Add(拖动事件);
// 鼠标释放事件
EventTrigger.Entry 释放事件 = new EventTrigger.Entry();
释放事件.eventID = EventTriggerType.PointerUp;
释放事件.callback.AddListener((data) => 结束拖拽());
trigger.triggers.Add(释放事件);
// 鼠标离开事件
EventTrigger.Entry 离开事件 = new EventTrigger.Entry();
离开事件.eventID = EventTriggerType.PointerExit;
离开事件.callback.AddListener((data) => 结束拖拽());
trigger.triggers.Add(离开事件);
}
// 开始拖拽
private void 开始拖拽(PointerEventData 事件数据)
{
正在拖拽 = true;
// 计算鼠标位置与窗口位置的偏移量
RectTransformUtility.ScreenPointToLocalPointInRectangle(
聊天窗口Rect, 事件数据.position, 事件数据.pressEventCamera, out 拖拽偏移量);
}
// 执行拖拽
private void 执行拖拽(PointerEventData 事件数据)
{
if (!正在拖拽 || 聊天窗口Rect == null) return;
Vector2 本地鼠标位置;
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(
聊天窗口Rect.parent.GetComponent<RectTransform>(),
事件数据.position,
事件数据.pressEventCamera,
out 本地鼠标位置))
{
// 根据偏移量调整窗口位置
聊天窗口Rect.localPosition = 本地鼠标位置 - 拖拽偏移量;
}
}
// 结束拖拽
private void 结束拖拽()
{
正在拖拽 = false;
}
// 处理输入框内容变化,支持Ctrl+Enter换行
private void OnInputValueChanged(string input)
{
if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl))
{
if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter))
{
消息输入框.text += "\n";
消息输入框.caretPosition = 消息输入框.text.Length;
}
}
}
// 修复UI层级问题
private void 修复UI层级问题()
{
// 确保有Canvas组件
if (聊天画布 == null)
{
聊天画布 = GetComponentInParent<Canvas>();
if (聊天画布 == null)
{
聊天画布 = 聊天窗口.AddComponent<Canvas>();
聊天窗口.AddComponent<CanvasScaler>();
聊天窗口.AddComponent<GraphicRaycaster>();
}
}
// 确保消息容器在正确层级
if (消息容器 != null)
{
CanvasRenderer cr = 消息容器.GetComponent<CanvasRenderer>();
if (cr == null)
{
cr = 消息容器.gameObject.AddComponent<CanvasRenderer>();
}
// 设置消息容器的层级
RectTransform rt = 消息容器.GetComponent<RectTransform>();
if (rt != null)
{
rt.SetAsLastSibling(); // 确保在最上层
}
}
}
// 验证消息显示相关组件
private void 验证消息显示相关组件()
{
// 验证消息容器
if (消息容器 == null)
{
if (显示关键错误) Debug.LogError("错误:消息容器未赋值!");
return;
}
// 验证滚动视图
if (聊天滚动视图 == null)
{
// 移除布局提示
}
else
{
// 确保消息容器是ScrollView的Content
if (聊天滚动视图.content != 消息容器.GetComponent<RectTransform>())
{
聊天滚动视图.content = 消息容器.GetComponent<RectTransform>();
}
// 修复滚动视图掩码问题
Mask mask = 聊天滚动视图.GetComponent<Mask>();
if (mask != null && !mask.showMaskGraphic)
{
mask.showMaskGraphic = true;
}
// 修复滚动条设置
if (聊天滚动视图.verticalScrollbar != null)
{
聊天滚动视图.verticalScrollbarVisibility = ScrollRect.ScrollbarVisibility.AutoHideAndExpandViewport;
聊天滚动视图.verticalScrollbarSpacing = 2;
}
}
// 验证预制体
if (玩家消息预制体 == null)
{
if (显示关键错误) Debug.LogError("错误:玩家消息预制体未赋值!");
}
if (朋友消息预制体 == null)
{
if (显示关键错误) Debug.LogError("错误:朋友消息预制体未赋值!");
}
// 检查预制体是否激活
if (玩家消息预制体 != null && !玩家消息预制体.activeSelf)
{
玩家消息预制体.SetActive(true);
}
if (朋友消息预制体 != null && !朋友消息预制体.activeSelf)
{
朋友消息预制体.SetActive(true);
}
// 验证文本组件
if (玩家消息预制体 != null)
{
Text 发送者文本 = 玩家消息预制体.transform.Find("发送者文本")?.GetComponent<Text>();
Text 内容文本 = 玩家消息预制体.transform.Find("内容文本")?.GetComponent<Text>();
if (发送者文本 == null || 内容文本 == null)
{
修复消息项预制体(玩家消息预制体);
}
}
if (朋友消息预制体 != null)
{
Text 发送者文本 = 朋友消息预制体.transform.Find("发送者文本")?.GetComponent<Text>();
Text 内容文本 = 朋友消息预制体.transform.Find("内容文本")?.GetComponent<Text>();
if (发送者文本 == null || 内容文本 == null)
{
修复消息项预制体(朋友消息预制体);
}
}
}
// 修复消息项预制体
private void 修复消息项预制体(GameObject 预制体)
{
if (预制体 == null) return;
// 添加发送者文本
if (预制体.transform.Find("发送者文本") == null)
{
GameObject 发送者文本对象 = new GameObject("发送者文本");
发送者文本对象.transform.SetParent(预制体.transform);
Text 发送者文本 = 发送者文本对象.AddComponent<Text>();
发送者文本.font = 聊天字体 ?? Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
发送者文本.fontSize = 字体大小;
发送者文本.color = Color.black;
发送者文本.enabled = true;
发送者文本.raycastTarget = false;
// 设置位置
RectTransform rt = 发送者文本对象.GetComponent<RectTransform>();
rt.anchorMin = new Vector2(0, 1);
rt.anchorMax = new Vector2(0, 1);
rt.pivot = new Vector2(0, 1);
rt.offsetMin = new Vector2(5, -25);
rt.offsetMax = new Vector2(100, 0);
}
// 添加内容文本
if (预制体.transform.Find("内容文本") == null)
{
GameObject 内容文本对象 = new GameObject("内容文本");
内容文本对象.transform.SetParent(预制体.transform);
Text 内容文本 = 内容文本对象.AddComponent<Text>();
内容文本.font = 聊天字体 ?? Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
内容文本.fontSize = 字体大小;
内容文本.color = Color.black;
内容文本.enabled = true;
内容文本.raycastTarget = false;
// 设置位置
RectTransform rt = 内容文本对象.GetComponent<RectTransform>();
rt.anchorMin = new Vector2(0, 1);
rt.anchorMax = new Vector2(1, 1);
rt.pivot = new Vector2(0, 1);
rt.offsetMin = new Vector2(110, -25);
rt.offsetMax = new Vector2(-5, 0);
}
// 移除任何可能存在的时间戳组件
Transform 时间戳Transform = 预制体.transform.Find("时间戳");
if (时间戳Transform != null)
{
DestroyImmediate(时间戳Transform.gameObject);
}
}
// 修复消息容器布局
private void 修复消息容器布局()
{
if (消息容器 == null) return;
// 确保容器激活
消息容器.gameObject.SetActive(true);
// 添加垂直布局组
VerticalLayoutGroup layout = 消息容器.GetComponent<VerticalLayoutGroup>();
if (layout == null)
{
layout = 消息容器.gameObject.AddComponent<VerticalLayoutGroup>();
}
// 修复间距和布局冲突问题
layout.spacing = 20;
layout.childAlignment = TextAnchor.UpperLeft;
layout.childControlWidth = true;
layout.childControlHeight = true;
layout.childForceExpandWidth = false;
layout.childForceExpandHeight = false;
layout.padding = new RectOffset(10, 10, 10, 10);
// 添加内容大小适配
ContentSizeFitter fitter = 消息容器.GetComponent<ContentSizeFitter>();
if (fitter == null)
{
fitter = 消息容器.gameObject.AddComponent<ContentSizeFitter>();
}
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
fitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
// 滚动视图设置优化
if (聊天滚动视图 != null)
{
聊天滚动视图.vertical = true;
聊天滚动视图.horizontal = false;
聊天滚动视图.movementType = ScrollRect.MovementType.Clamped;
聊天滚动视图.content.sizeDelta = new Vector2(0, 0);
聊天滚动视图.scrollSensitivity = 40f;
// 确保滚动条正确关联
if (聊天滚动视图.verticalScrollbar != null)
{
聊天滚动视图.verticalScrollbar.value = 0; // 初始位置在底部
}
}
// 修复两个预制体的RectTransform
修复预制体RectTransform(玩家消息预制体, true);
修复预制体RectTransform(朋友消息预制体, false);
}
// 修复预制体的RectTransform
private void 修复预制体RectTransform(GameObject 预制体, bool 是玩家消息)
{
if (预制体 == null) return;
RectTransform rt = 预制体.GetComponent<RectTransform>();
if (rt != null)
{
rt.anchorMin = new Vector2(0, 1);
rt.anchorMax = new Vector2(1, 1);
rt.pivot = new Vector2(0.5f, 1);
rt.offsetMin = new Vector2(0, 0);
rt.offsetMax = new Vector2(0, 0);
rt.localScale = Vector3.one;
}
// 确保文本可见
Text[] texts = 预制体.GetComponentsInChildren<Text>();
foreach (var text in texts)
{
text.enabled = true;
text.font = 聊天字体 ?? Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
text.fontSize = 字体大小;
text.horizontalOverflow = HorizontalWrapMode.Wrap;
text.verticalOverflow = VerticalWrapMode.Overflow;
}
// 移除预制体中可能存在的时间戳
Transform 时间戳Transform = 预制体.transform.Find("时间戳");
if (时间戳Transform != null)
{
DestroyImmediate(时间戳Transform.gameObject);
}
}
// 分割消息的方法
private List<string> 分割消息(string 内容)
{
List<string> 分割后的消息 = new List<string>();
// 如果消息长度小于等于分割长度,直接返回原消息
if (string.IsNullOrEmpty(内容) || 内容.Length <= 消息分割长度)
{
分割后的消息.Add(内容);
return 分割后的消息;
}
// 否则按指定长度分割消息
for (int i = 0; i < 内容.Length; i += 消息分割长度)
{
int 截取长度 = Mathf.Min(消息分割长度, 内容.Length - i);
string 子消息 = 内容.Substring(i, 截取长度);
分割后的消息.Add(子消息);
}
return 分割后的消息;
}
// 显示消息项
private void 显示消息项(string 发送者, string 内容, Color 颜色, bool 是玩家消息, bool 显示发送者 = true)
{
if (消息容器 == null)
{
if (显示关键错误) Debug.LogError("消息容器未赋值!");
return;
}
// 根据是否是玩家消息选择不同的预制体
GameObject 消息预制体 = 是玩家消息 ? 玩家消息预制体 : 朋友消息预制体;
if (消息预制体 == null)
{
if (显示关键错误) Debug.LogError("消息项预制体未赋值!");
return;
}
// 实例化消息项
GameObject 消息项 = Instantiate(消息预制体, 消息容器);
消息项.name = $"{(是玩家消息 ? "玩家" : "朋友")}_消息_{System.DateTime.Now.Ticks}";
消息项.SetActive(true);
消息项.transform.SetAsLastSibling();
// 初始状态设置为缩放为0,为动画做准备
消息项.transform.localScale = Vector3.zero;
// 设置发送者文本
Text 发送者文本 = 消息项.transform.Find("发送者文本")?.GetComponent<Text>();
if (发送者文本 != null)
{
发送者文本.text = 发送者 + ":";
发送者文本.color = 颜色;
发送者文本.font = 聊天字体 ?? Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
发送者文本.fontSize = 字体大小;
发送者文本.enabled = 显示发送者;
发送者文本.rectTransform.sizeDelta = new Vector2(100, 发送者文本.preferredHeight);
}
else if (显示关键错误)
{
Debug.LogError("未找到'发送者文本'组件!");
}
// 设置内容文本
Text 内容文本 = 消息项.transform.Find("内容文本")?.GetComponent<Text>();
if (内容文本 != null)
{
内容文本.text = 内容;
内容文本.font = 聊天字体 ?? Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
内容文本.fontSize = 字体大小;
内容文本.enabled = true;
// 计算最大宽度
float 最大宽度 = 聊天滚动视图 != null ?
聊天滚动视图.GetComponent<RectTransform>().rect.width - 120 : 300;
内容文本.rectTransform.sizeDelta = new Vector2(最大宽度, 内容文本.preferredHeight);
// 强制更新文本尺寸
Canvas.ForceUpdateCanvases();
LayoutRebuilder.ForceRebuildLayoutImmediate(消息项.GetComponent<RectTransform>());
}
else if (显示关键错误)
{
Debug.LogError("未找到'内容文本'组件!");
}
// 强制刷新布局
LayoutRebuilder.ForceRebuildLayoutImmediate(消息容器.GetComponent<RectTransform>());
if (聊天内容区域 != null)
LayoutRebuilder.ForceRebuildLayoutImmediate(聊天内容区域);
// 应用Q弹动画效果
应用消息动画(消息项.transform);
// 保存消息到历史记录
消息历史.Add(new 消息数据(发送者, 内容, System.DateTime.Now, 是玩家消息));
// 检查并移除超出最大数量的消息
清理旧消息();
// 滚动到最新消息
StartCoroutine(滚动到最新消息());
}
// 应用消息弹出的Q弹动画
private void 应用消息动画(Transform 消息变换)
{
// 确保DOTween可用
if (消息变换 == null) return;
// 重置缩放
消息变换.localScale = Vector3.one * 缩放比例;
// 使用DOTween创建Q弹效果
消息变换.DOScale(Vector3.one * 弹性系数, 动画持续时间 / 2)
.SetEase(Ease.OutQuad)
.OnComplete(() => {
// 回弹效果
消息变换.DOScale(Vector3.one, 动画持续时间 / 2)
.SetEase(Ease.OutElastic);
});
}
// 滚动到最新消息 - 优化滚动效果
private IEnumerator 滚动到最新消息()
{
// 等待布局刷新
yield return new WaitForEndOfFrame();
if (聊天滚动视图 != null)
{
// 强制更新内容大小
ContentSizeFitter fitter = 消息容器.GetComponent<ContentSizeFitter>();
if (fitter != null)
{
fitter.SetLayoutVertical();
}
// 使用动画平滑滚动到底部
float targetPos = 0; // 0表示滚动到底部
float startPos = 聊天滚动视图.verticalNormalizedPosition;
float elapsedTime = 0;
float duration = 0.2f;
while (elapsedTime < duration)
{
elapsedTime += Time.deltaTime;
聊天滚动视图.verticalNormalizedPosition = Mathf.Lerp(startPos, targetPos, elapsedTime / duration);
yield return null;
}
// 确保最终位置正确
聊天滚动视图.verticalNormalizedPosition = targetPos;
}
}
// 清理超出最大数量的旧消息
private void 清理旧消息()
{
if (消息历史.Count <= 最大消息数量) return;
int 要删除的数量 = 消息历史.Count - 最大消息数量;
// 从容器中移除最早的消息对象
for (int i = 0; i < 要删除的数量 && 消息容器.childCount > 0; i++)
{
Destroy(消息容器.GetChild(0).gameObject);
}
// 从历史记录中移除
消息历史.RemoveRange(0, 要删除的数量);
}
// 验证必要组件
private void 验证必要组件()
{
if (消息输入框 == null && 显示关键错误)
Debug.LogError("错误:消息输入框未赋值!");
if (发送按钮 == null && 显示关键错误)
Debug.LogError("错误:发送按钮未赋值!");
if (关闭按钮 == null && 显示关键错误)
Debug.LogError("错误:关闭按钮未赋值!");
if (聊天字体 == null)
{
聊天字体 = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
if (聊天字体 == null && 消息输入框 != null && 消息输入框.textComponent != null)
聊天字体 = 消息输入框.textComponent.font;
}
}
private void 修复输入框()
{
if (消息输入框 == null) return;
消息输入框.gameObject.SetActive(true);
消息输入框.enabled = true;
消息输入框.interactable = true;
if (消息输入框.textComponent == null)
{
Text textComp = 消息输入框.GetComponentInChildren<Text>();
if (textComp == null) textComp = 消息输入框.gameObject.AddComponent<Text>();
消息输入框.textComponent = textComp;
}
if (消息输入框.textComponent != null)
{
if (聊天字体 != null) 消息输入框.textComponent.font = 聊天字体;
消息输入框.textComponent.fontSize = 字体大小;
消息输入框.textComponent.color = Color.black;
}
if (消息输入框.placeholder != null)
{
Text placeholder = 消息输入框.placeholder.GetComponent<Text>();
if (placeholder != null)
{
if (聊天字体 != null) placeholder.font = 聊天字体;
placeholder.text = "输入消息...(Ctrl+Enter换行)";
}
}
}
private IEnumerator 监控输入框状态()
{
while (true)
{
if (聊天窗口是否打开 && 消息输入框 != null && !消息输入框.interactable)
消息输入框.interactable = true;
yield return new WaitForSeconds(0.5f);
}
}
public void 打开聊天窗口()
{
if (聊天窗口是否打开) return;
聊天窗口.SetActive(true);
聊天窗口是否打开 = true;
if (消息输入框 != null) 消息输入框.text = "";
StartCoroutine(延迟聚焦输入框(0.1f));
if (邀请管理器 != null) 邀请管理器.设置正在聊天(true);
}
// 新增:带地块信息的打开聊天窗口方法
public void 打开聊天窗口(地块Data 地块信息)
{
打开聊天窗口(); // 先调用无参数版本打开窗口
// 清除旧的地块信息
if (当前地块信息显示 != null)
{
Destroy(当前地块信息显示);
}
if (地块信息预制体 != null && 地块信息 != null)
{
当前地块信息显示 = Instantiate(地块信息预制体, 消息容器);
当前地块信息显示.name = $"地块信息_{地块信息.序号}";
Text 地块描述文本 = 当前地块信息显示.transform.Find("内容文本")?.GetComponent<Text>();
if (地块描述文本 != null)
{
地块描述文本.text = $@"地块信息:
- 序号:{地块信息.序号}
- 坐标:({地块信息.X}, {地块信息.Y})
- 生态类型:{地块信息.生态类型}
- 描述:{地块信息.描述}
- 生物列表:{string.Join(", ", 地块信息.生物列表)}";
}
// 强制布局刷新
LayoutRebuilder.ForceRebuildLayoutImmediate(消息容器.GetComponent<RectTransform>());
StartCoroutine(滚动到最新消息());
}
// 发送AI请求
if (ai管理器 != null && 地块信息 != null)
{
string 提示词 = $@"你是一个生态科考AI助手,现在要生成一个新的地块信息。
该地块序号为 {地块信息.序号},坐标为 ({地块信息.X}, {地块信息.Y}),生态类型为:{地块信息.生态类型}。
请根据以下要求生成内容:
- 描述:简洁的环境描述(1~2句话)
- 生物列表:至少3种生物(可以是动物、植物、菌类)";
ai管理器.发送消息给AI(提示词);
}
}
private IEnumerator 延迟聚焦输入框(float 延迟时间)
{
yield return new WaitForSeconds(延迟时间);
if (消息输入框 != null)
{
消息输入框.Select();
消息输入框.ActivateInputField();
}
}
public void 关闭聊天窗口()
{
if (!聊天窗口是否打开) return;
聊天窗口.SetActive(false);
聊天窗口是否打开 = false;
if (邀请管理器 != null) 邀请管理器.设置正在聊天(false);
}
public void 发送消息()
{
if (!聊天窗口是否打开 || 消息输入框 == null) return;
string 消息内容 = 消息输入框.text.Trim();
if (string.IsNullOrEmpty(消息内容)) return;
// 分割消息
List<string> 分割后的消息 = 分割消息(消息内容);
// 发送分割后的消息
for (int i = 0; i < 分割后的消息.Count; i++)
{
// 只有第一条消息显示发送者,后续分割的消息隐藏发送者
bool 显示发送者 = (i == 0);
显示消息项(玩家名称, 分割后的消息[i], 玩家消息颜色, true, 显示发送者);
}
// 发送消息给AI
if (ai管理器 != null)
{
ai管理器.发送消息给AI(消息内容);
}
else if (显示关键错误)
{
Debug.LogWarning("AI管理器未找到,无法发送消息给AI");
}
消息输入框.text = "";
消息输入框.ActivateInputField();
}
private void 输入框回车发送(string 内容)
{
if (消息输入框 == null) return;
// 只有在没有按下Ctrl键时,回车才发送消息
if ((Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter)) &&
!(Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)))
{
if (聊天窗口是否打开) 发送消息();
消息输入框.DeactivateInputField();
消息输入框.ActivateInputField();
}
}
// 收到消息方法
public void 收到消息(string 发送者, string 内容)
{
// 分割收到的消息
List<string> 分割后的消息 = 分割消息(内容);
// 显示分割后的消息
for (int i = 0; i < 分割后的消息.Count; i++)
{
// 只有第一条消息显示发送者,后续分割的消息隐藏发送者
bool 显示发送者 = (i == 0);
显示消息项(发送者, 分割后的消息[i], 朋友消息颜色, false, 显示发送者);
}
}
// 在场景视图中可视化消息容器区域
void OnDrawGizmosSelected()
{
// 移除场景视图中的调试可视化
}
// 消息数据类,用于存储消息历史
private class 消息数据
{
public string 发送者;
public string 内容;
public System.DateTime 时间;
public bool 是玩家消息;
public 消息数据(string 发送者, string 内容, System.DateTime 时间, bool 是玩家消息)
{
this.发送者 = 发送者;
this.内容 = 内容;
this.时间 = 时间;
this.是玩家消息 = 是玩家消息;
}
}
private static 聊天系统 _instance;
public static 聊天系统 Instance
{
get
{
if (_instance == null)
{
_instance = FindObjectOfType<聊天系统>();
if (_instance == null)
{
GameObject go = new GameObject("聊天系统");
_instance = go.AddComponent<聊天系统>();
DontDestroyOnLoad(go);
}
}
return _instance;
}
}
}
最新发布