DontDestroyOnLoad 的缺陷和解决办法

缺陷:两个场景中来回跳转时 DontDestroyOnLoad的物体会重复创建  而我们要求场景中只存在一个

解决办法:

    方法一   这个方法直接挂在到一个物体上就可以

public class DestornDemo : MonoBehaviour {

    public static DestornDemo Instance=null ;

  //--------方法一  
    private void Start()
    {
        if (Instance !=null)
        {
            Destroy(this);
            return;
        }

        Instance = this;
        DontDestroyOnLoad(this);       

    }
   
    private void OnGUI()
    {
        if (GUILayout.Button("跳转:1"))
        {
            SceneManager.LoadScene(1);
        }
        if (GUILayout.Button("跳转:0"))
        {
            SceneManager.LoadScene(0);
        }
    }
}   
阿斯顿发

    方法二 此脚本不需要挂载 但是需要调用一下这个脚本中的方法

public class DestornDemo : MonoBehaviour {

    public static DestornDemo Instance=null ;


    static DestornDemo()
    {
        GameObject obj = new GameObject("show");
        Instance = obj.AddComponent<DestornDemo>();
        DontDestroyOnLoad(obj);
        print("Chushihua");
    }
    public void Destroy()
    {
        print("SettingOver");
    }
    private void OnGUI()
    {
        if (GUILayout.Button("跳转:1"))
        {
            SceneManager.LoadScene(1);
        }
        if (GUILayout.Button("跳转:0"))
        {
            SceneManager.LoadScene(0);
        }
    }
}

在另一个脚本中调用

public class Destroy_2 : MonoBehaviour
{


    void Start()
    {
        DestornDemo.Instance.Destroy();
    }

}
    方法三: 在游戏开始时第一个场景中 直接设置好不需要销毁的物体 从此以后再也不跳转到第一个场景


using System.Collections.Generic; using UnityEngine; using TMPro; using System.Collections; using UnityEngine.UI; using UnityEngine.SceneManagement; using UnityEngine.EventSystems; public class DialogueManager : MonoBehaviour { public enum DialogueState { Before, Win, Lost } public static DialogueManager Instance; [Header("UI Elements")] public TextMeshProUGUI tmpText; public float typingSpeed = 0.05f; public TMP_FontAsset fallbackFont; // 备用字体 [Header("Dialogue Content")] public List<string> beforeBattleDialogues = new List<string>(); public List<string> winDialogues = new List<string>(); public List<string> lostDialogues = new List<string>(); [Header("场景布置")] public GameObject image; public GameObject image1; public GameObject image2; public TextMeshProUGUI text; [Header("按钮设置")] public Button startButton; public Button loseButton; public Button winButton; [Header("UI 元素路径")] public string tmpTextPath = "Canvas/DialoguePanel/TMPText"; public string startButtonPath = "Canvas/Buttons/StartButton"; public string winButtonPath = "Canvas/Buttons/WinButton"; public string loseButtonPath = "Canvas/Buttons/LoseButton"; public string imagePath = "Canvas/Background"; private bool isTyping = false; private Coroutine typingCoroutine; private string currentSentence; public DialogueState currentState = DialogueState.Before; public int currentDialogueIndex = 0; private List<string> currentDialogueList; private TextMeshProUGUI _currentTmpText; private Button _currentStartButton; private Button _currentWinButton; private Button _currentLoseButton; private GameObject _currentImage; void Awake() { // 单例模式实现 if (Instance == null) { Instance = this; DontDestroyOnLoad(gameObject); SceneManager.sceneLoaded += OnSceneLoaded; Debug.Log("对话管理器初始化完成"); } else { Destroy(gameObject); return; } } void Start() { // 初始状态加载 LoadInitialState(); } void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; } private void LoadInitialState() { // 确保对话列表初始化 InitializeDialogueLists(); Debug.Log("加载初始对话状态..."); // 检查存档中的对话状态 if (SaveSystem.Instance != null && SaveSystem.Instance.SaveExists()) { DialogueState savedState = SaveSystem.Instance.GetDialogueState(); int savedIndex = SaveSystem.Instance.GetDialogueIndex(); // 使用SetDialogueState确保正确初始化 SetDialogueState(savedState); currentDialogueIndex = savedIndex; Debug.Log($"从存档加载对话状态: {savedState} 索引: {savedIndex}"); } else { // 检查战斗结果 int result = PlayerPrefs.GetInt("LastBattleResult", -1); // 使用状态设置方法确保列表初始化 if (result == 1) SetDialogueState(DialogueState.Win); else if (result == 0) SetDialogueState(DialogueState.Lost); else SetDialogueState(DialogueState.Before); } // 清除状态 PlayerPrefs.DeleteKey("LastBattleResult"); } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { // 仅初始化UI但不启动对话 StartCoroutine(InitializeUIWithoutDialogue()); } private IEnumerator InitializeUIWithoutDialogue() { yield return null; Debug.Log("初始化UI组件但不启动对话..."); // 仅执行UI初始化逻辑 // 不包含StartStateDialogue()调用 } private IEnumerator ReinitializeUIAfterFrame() { yield return null; // 等待一帧确保场景完全加载 Debug.Log("重新初始化UI组件..."); // 1. 重新获取当前场景的UI引用 _currentTmpText = GameObject.Find(tmpTextPath)?.GetComponent<TextMeshProUGUI>(); _currentStartButton = GameObject.Find(startButtonPath)?.GetComponent<Button>(); _currentWinButton = GameObject.Find(winButtonPath)?.GetComponent<Button>(); _currentLoseButton = GameObject.Find(loseButtonPath)?.GetComponent<Button>(); _currentImage = GameObject.Find(imagePath); // 2. 更新公共引用 if (_currentTmpText != null) { tmpText = _currentTmpText; EnsureFontLoaded(); // 确保字体加载 } if (_currentStartButton != null) startButton = _currentStartButton; if (_currentWinButton != null) winButton = _currentWinButton; if (_currentLoseButton != null) loseButton = _currentLoseButton; if (_currentImage != null) image = _currentImage; // 3. 重新绑定按钮事件 if (startButton != null) { startButton.onClick.RemoveAllListeners(); startButton.onClick.AddListener(OnStartClick); startButton.gameObject.SetActive(false); } if (loseButton != null) { loseButton.onClick.RemoveAllListeners(); loseButton.onClick.AddListener(OnLoseClick); loseButton.gameObject.SetActive(false); } if (winButton != null) { winButton.onClick.RemoveAllListeners(); winButton.onClick.AddListener(OnWinClick); winButton.gameObject.SetActive(false); } // 4. 确保事件系统存在 EnsureEventSystemExists(); // 5. 恢复UI状态 if (tmpText != null) { tmpText.gameObject.SetActive(false); } // 6. 继续之前的对话状态 StartStateDialogue(); } // 确保字体加载 private void EnsureFontLoaded() { if (tmpText == null) return; // 如果主字体丢失,使用备用字体 if (tmpText.font == null || tmpText.font.name == "Fallback") { if (fallbackFont != null) { tmpText.font = fallbackFont; Debug.Log("使用备用字体"); } else { Debug.LogError("主字体备用字体均不可用!"); } } // 确保材质正确 if (tmpText.fontSharedMaterial == null) { Debug.LogWarning("字体材质丢失,使用默认材质"); tmpText.fontSharedMaterial = fallbackFont.material; } } void Update() { if (tmpText == null) { Debug.LogWarning("tmpText引用为空!"); return; } // 添加跳过功能:按E键跳过当前打字效果 if (Input.GetKeyDown(KeyCode.E) && isTyping) { SkipTyping(); } // 按E键继续下一句对话 else if (Input.GetKeyDown(KeyCode.E) && !isTyping && tmpText.gameObject.activeSelf) { NextSentence(); } } // 设置当前对话状态 public void SetDialogueState(DialogueState newState) { currentState = newState; currentDialogueIndex = 0; switch (newState) { case DialogueState.Before: currentDialogueList = beforeBattleDialogues; break; case DialogueState.Win: currentDialogueList = winDialogues; break; case DialogueState.Lost: currentDialogueList = lostDialogues; break; } Debug.Log($"状态已切换至: {newState}"); // 保存状态 SaveDialogueState(); } // 保存对话状态 private void SaveDialogueState() { if (SaveSystem.Instance != null) { SaveSystem.Instance.SaveDialogueState(currentState, currentDialogueIndex); } } private void InitializeDialogueLists() { // 确保所有对话列表都已初始化 if (beforeBattleDialogues == null) beforeBattleDialogues = new List<string>(); if (winDialogues == null) winDialogues = new List<string>(); if (lostDialogues == null) lostDialogues = new List<string>(); // 添加默认对话防止空列表 if (beforeBattleDialogues.Count == 0) beforeBattleDialogues.Add("默认对话:请添加战斗前对话内容"); if (winDialogues.Count == 0) winDialogues.Add("默认对话:请添加胜利后对话内容"); if (lostDialogues.Count == 0) lostDialogues.Add("默认对话:请添加失败后对话内容"); } // 启动当前状态的对话 public void StartStateDialogue() { if (currentDialogueList == null || currentDialogueList.Count == 0) { Debug.LogWarning($"当前对话列表为空! 状态: {currentState}"); // 自动恢复默认列表 SetDialogueState(currentState); } // 确保索引在有效范围内 currentDialogueIndex = Mathf.Clamp(currentDialogueIndex, 0, currentDialogueList.Count - 1); currentSentence = currentDialogueList[currentDialogueIndex]; Debug.Log($"开始对话: {currentSentence}"); // 暂停玩家移动 playercontrol player = FindObjectOfType<playercontrol>(); if (player != null) player.canMove = false; // 锁定玩家输入 Cursor.lockState = CursorLockMode.None; Cursor.visible = true; // 显示对话框UI tmpText.gameObject.SetActive(true); BringDialogueToFront(); // 确保在最前层 // 启动打字效果协程 if (isTyping) { StopCoroutine(typingCoroutine); } typingCoroutine = StartCoroutine(TypeSentence(currentSentence)); } // 确保对话框在最前层 private void BringDialogueToFront() { if (tmpText != null) { tmpText.transform.SetAsLastSibling(); Canvas canvas = tmpText.GetComponentInParent<Canvas>(); if (canvas != null) { canvas.sortingOrder = 100; // 设为最高层级 Debug.Log($"设置Canvas层级: {canvas.sortingOrder}"); } } } // 显示下一句对话 public void NextSentence() { currentDialogueIndex++; if (currentDialogueIndex < currentDialogueList.Count) { StartStateDialogue(); // 保存当前进度 SaveDialogueState(); } else { // 对话结束 EndDialogue(); } } IEnumerator TypeSentence(string sentence) { isTyping = true; tmpText.text = ""; // 确保文本颜色可见 tmpText.color = new Color(tmpText.color.r, tmpText.color.g, tmpText.color.b, 1f); // 逐字显示 foreach (char letter in sentence.ToCharArray()) { tmpText.text += letter; yield return new WaitForSeconds(typingSpeed); } isTyping = false; } // 跳过当前打字效果 public void SkipTyping() { if (isTyping && typingCoroutine != null) { StopCoroutine(typingCoroutine); tmpText.text = currentSentence; isTyping = false; } } public void EndDialogue() { Debug.Log("对话结束"); if (isTyping && typingCoroutine != null) { StopCoroutine(typingCoroutine); isTyping = false; } // 恢复玩家控制 playercontrol player = FindObjectOfType<playercontrol>(); if (player != null) player.canMove = true; // 隐藏对话框 if (tmpText != null) { tmpText.gameObject.SetActive(false); } // 根据状态显示对应按钮 switch (currentState) { case DialogueState.Before: if (startButton != null) { startButton.gameObject.SetActive(true); EventSystem.current.SetSelectedGameObject(startButton.gameObject); } break; case DialogueState.Win: if (winButton != null) { winButton.gameObject.SetActive(true); EventSystem.current.SetSelectedGameObject(winButton.gameObject); } break; case DialogueState.Lost: if (loseButton != null) { loseButton.gameObject.SetActive(true); EventSystem.current.SetSelectedGameObject(loseButton.gameObject); } break; } // 确保事件系统可用 EnsureEventSystemExists(); // 保存完成状态 SaveDialogueState(); } private void EnsureEventSystemExists() { EventSystem eventSystem = FindObjectOfType<EventSystem>(); if (eventSystem == null) { GameObject eventSystemObj = new GameObject("EventSystem"); eventSystem = eventSystemObj.AddComponent<EventSystem>(); eventSystemObj.AddComponent<StandaloneInputModule>(); Debug.Log("创建新事件系统"); } else if (eventSystem.GetComponent<StandaloneInputModule>() == null) { eventSystem.gameObject.AddComponent<StandaloneInputModule>(); Debug.Log("添加输入模块到事件系统"); } } public void OnMainMenuClick() { Debug.Log("主菜单按钮点击"); SceneManager.LoadScene("MainMenu"); } public void OnStartClick() { Debug.Log("开始作战"); SceneManager.LoadScene("Fight"); } public void OnLoseClick() { Debug.Log("失败按钮点击"); SceneManager.LoadScene("Fight"); } public void OnWinClick() { if (currentState != DialogueState.Win) { Debug.LogWarning("尝试在非胜利状态执行胜利逻辑"); return; } playercontrol player = FindObjectOfType<playercontrol>(); if (player != null) player.canMove = true; Debug.Log("执行胜利逻辑"); if (image != null) image.SetActive(false); if (image1 != null) image1.SetActive(false); if (image2 != null) image2.SetActive(false); if (text != null) text.gameObject.SetActive(false); if (tmpText != null) tmpText.gameObject.SetActive(false); if (winButton != null) winButton.gameObject.SetActive(false); // 清除对话存档 if (SaveSystem.Instance != null) { SaveSystem.Instance.SaveDialogueState(DialogueState.Before, 0); } } } 我怀疑是装换sence之后导致检测不到之前的组件
08-09
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; } } }
最新发布
08-31
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值