using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using TMPro; using UnityEngine; using UnityEngine.UI; public class TouchClient : MonoBehaviour { private TcpClient client; // 用于与显示端通信的TCP客户端 private NetworkStream stream; // 用于发送/接收数据的流 private volatile bool isConnected = false; // 线程安全的连接状态标志 private UdpClient udpListener; // 用于接收广播IP的UDP客户端 private Thread udpThread; // 监听广播的线程 private CancellationTokenSource udpCts = new CancellationTokenSource(); // 控制UDP线程取消 private Thread receiveThread; // 接收服务器消息的线程 private CancellationTokenSource receiveCts = new CancellationTokenSource(); // 控制接收线程取消 public Transform background; // UI页面的父级变换 private string currentPage = "首页"; // 跟踪当前显示的页面 private readonly Queue progressQueue = new Queue(); // 线程安全的进度队列 private readonly object queueLock = new object(); // 进度队列访问锁 private bool isDraggingSlider = false; // 标记是否正在拖动Slider private float lastSentProgress = 0f; // 记录最后发送的进度 [Header("Text Panel Toggle Sprites")] public Sprite showSprite; // 显示状态的Sprite public Sprite hideSprite; // 隐藏状态的Sprite // 脚本启动时调用 void Start() { // 设置触摸控制端分辨率为 1920x1080 Screen.SetResolution(1920, 1080, false); // 在端口9999上初始化UDP监听 udpListener = new UdpClient(9999); udpThread = new Thread(() => ListenForBroadcast(udpCts.Token)); udpThread.Start(); Debug.Log("触摸端等待显示端广播..."); // 显示初始页面 ShowPage("首页"); } // 监听UDP广播以发现显示端IP private void ListenForBroadcast(CancellationToken token) { IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 9999); // 在未连接或未取消的情况下持续监听 while (!isConnected && !token.IsCancellationRequested) { try { // 接收广播数据 byte[] data = udpListener.Receive(ref endPoint); string serverIp = Encoding.UTF8.GetString(data); Debug.Log("发现显示端IP: " + serverIp); ConnectToServer(serverIp, 8888); // 尝试连接到服务器 } catch (Exception ex) { Debug.LogError($"UDP接收错误: {ex.Message}"); Thread.Sleep(1000); // 避免快速重试 } } } // 通过TCP连接到显示端服务器 private void ConnectToServer(string ip, int port) { try { client = new TcpClient(); client.Connect(ip, port); // 连接到服务器 stream = client.GetStream(); isConnected = true; udpCts.Cancel(); // 连接成功后停止UDP监听线程 udpListener?.Close(); // 关闭UDP监听器 Debug.Log("触摸端已连接到显示端: " + ip); // 启动接收线程以处理服务器消息 receiveThread = new Thread(() => ReceiveMessages(receiveCts.Token)); receiveThread.Start(); } catch (Exception ex) { Debug.LogError($"连接显示端失败: {ex.Message}"); } } // 接收服务器发送的消息 private void ReceiveMessages(CancellationToken token) { byte[] message = new byte[4096]; int bytesRead; while (!token.IsCancellationRequested && isConnected) { try { bytesRead = stream.Read(message, 0, 4096); if (bytesRead == 0) break; // 服务器断开连接 string data = Encoding.UTF8.GetString(message, 0, bytesRead); if (data.StartsWith("Progress:") && float.TryParse(data.Replace("Progress:", ""), out float progress)) { lock (queueLock) { progressQueue.Enqueue(progress); // 将进度加入队列 Debug.Log($"收到进度: {progress:F2}"); } } } catch (Exception ex) { Debug.LogError($"接收服务器消息错误: {ex.Message}"); isConnected = false; break; } } } // 向显示端服务器发送消息 private void SendMessageToServer(string msg) { if (isConnected && stream != null) { try { byte[] data = Encoding.UTF8.GetBytes(msg); stream.Write(data, 0, data.Length); Debug.Log("发送指令: " + msg); } catch (Exception ex) { Debug.LogError($"发送消息失败: {ex.Message}"); isConnected = false; // 标记连接失效 } } } // 页面按钮点击时调用 public void OnButtonClick_Page(string pageName) { ShowPage(pageName); // 切换到指定页面 SendMessageToServer("Page:" + pageName); // 通知服务器 } // 模型播放按钮点击时调用 public void OnButtonClick_PlayModel(string modelName) { // 清空进度队列,防止旧进度干扰 lock (queueLock) { progressQueue.Clear(); } // 重置Slider到0 Slider slider = GetCurrentPageSlider(); if (slider != null) { var onValueChanged = slider.onValueChanged; slider.onValueChanged = new Slider.SliderEvent(); slider.value = 0f; slider.onValueChanged = onValueChanged; lastSentProgress = 0f; // 重置最后发送的进度 Debug.Log($"播放模型 {modelName} 前重置Slider到0"); } SendMessageToServer("Model:" + modelName); // 通知服务器播放模型 } // 滑块值改变时调用 public void OnSliderChanged(Slider slider) { isDraggingSlider = true; // 标记正在拖动 float progress = slider.value / 100f; // 转换为0-1 if (progress > 0.01f) // 忽略接近0的值以避免异常 { SendMessageToServer("Progress:" + progress.ToString("F2")); // 发送进度 lastSentProgress = progress; // 记录最后发送的进度 Debug.Log($"Slider拖动到: {progress:F2}"); } } // 滑块拖动结束时调用(需在Inspector中绑定) public void OnSliderEndDrag() { Slider slider = GetCurrentPageSlider(); if (slider != null) { float progress = slider.value / 100f; if (progress > 0.01f) { SendMessageToServer("Progress:" + progress.ToString("F2")); // 发送最终进度 lastSentProgress = progress; // 更新最后发送的进度 Debug.Log($"Slider拖动结束,发送最终进度: {progress:F2}"); } } isDraggingSlider = false; // 结束拖动 SendMessageToServer("EndDrag"); // 通知服务器拖动结束 Debug.Log("Slider拖动结束"); } // 文字面板Toggle状态改变时调用 public void OnTextPanelToggleChanged(Toggle toggle) { if (toggle == null) return; bool isTextPanelVisible = toggle.isOn; Image toggleImage = toggle.graphic as Image; // 使用Toggle的graphic字段 TextMeshProUGUI toggleText = toggle.GetComponentInChildren(); if (toggleImage != null) { toggleImage.sprite = isTextPanelVisible ? showSprite : hideSprite; Debug.Log($"Toggle sprite 切换: {currentPage}, isOn={isTextPanelVisible}, sprite={(isTextPanelVisible ? showSprite : hideSprite).name}"); } else { Debug.LogWarning($"Toggle {toggle.name} 未找到graphic Image组件"); } if (toggleText != null) { toggleText.text = isTextPanelVisible ? "显示文字" : "隐藏文字"; Debug.Log($"Toggle 文本更新: {currentPage}, text={toggleText.text}"); } else { Debug.LogWarning($"Toggle {toggle.name} 未找到TextMeshProUGUI组件"); } // 发送显隐消息到显示端 string message = isTextPanelVisible ? "TextPanel:Show" : "TextPanel:Hide"; SendMessageToServer(message); Debug.Log($"文字面板Toggle切换: {message}"); } // 获取当前页面的Slider组件 private Slider GetCurrentPageSlider() { if (background == null || currentPage == "首页") return null; Transform pageTransform = background.Find(currentPage); if (pageTransform != null) { Slider slider = pageTransform.GetComponentInChildren(); if (slider != null) { if (!slider.interactable) { slider.interactable = true; // 确保Slider可交互 Debug.Log($"启用Slider交互: {currentPage}"); } return slider; } Debug.LogWarning($"未在页面 {currentPage} 找到Slider"); return null; } Debug.LogWarning($"未找到页面 {currentPage}"); return null; } // 获取当前页面的TextPanelToggle组件 private Toggle GetCurrentPageTextPanelToggle() { if (background == null || currentPage == "首页") return null; Transform pageTransform = background.Find(currentPage); if (pageTransform != null) { Toggle toggle = pageTransform.GetComponentInChildren(); if (toggle != null) { if (!toggle.interactable) { toggle.interactable = true; // 确保Toggle可交互 Debug.Log($"启用TextPanelToggle交互: {currentPage}"); } return toggle; } Debug.LogWarning($"未在页面 {currentPage} 找到TextPanelToggle"); return null; } Debug.LogWarning($"未找到页面 {currentPage}"); return null; } // 切换激活的UI页面 private void ShowPage(string pageName) { foreach (Transform child in background) { child.gameObject.SetActive(child.name == pageName); // 仅激活目标页面 } currentPage = pageName; // 清空进度队列以避免旧页面数据干扰 lock (queueLock) { progressQueue.Clear(); } // 初始化当前页面的Slider Slider slider = GetCurrentPageSlider(); if (slider != null) { // 临时禁用OnValueChanged以防止初始化触发 var onValueChanged = slider.onValueChanged; slider.onValueChanged = new Slider.SliderEvent(); slider.value = 0f; // 重置Slider到0 slider.interactable = true; // 确保可交互 // 恢复OnValueChanged并绑定 slider.onValueChanged = onValueChanged; slider.onValueChanged.RemoveAllListeners(); slider.onValueChanged.AddListener((value) => OnSliderChanged(slider)); lastSentProgress = 0f; // 重置最后发送的进度 Debug.Log($"初始化Slider: {pageName}, 值: {slider.value}"); } // 初始化当前页面的TextPanelToggle Toggle toggle = GetCurrentPageTextPanelToggle(); if (toggle != null) { // 临时禁用OnValueChanged以防止初始化触发 var onValueChanged = toggle.onValueChanged; toggle.onValueChanged = new Toggle.ToggleEvent(); toggle.isOn = true; // 默认显示文字面板 toggle.interactable = true; // 确保可交互 // 更新Sprite和文本 Image toggleImage = toggle.graphic as Image; // 使用Toggle的graphic字段 TextMeshProUGUI toggleText = toggle.GetComponentInChildren(); if (toggleImage != null) { toggleImage.sprite = showSprite; Debug.Log($"初始化Toggle sprite: {pageName}, sprite={showSprite.name}"); } else { Debug.LogWarning($"Toggle {toggle.name} 未找到graphic Image组件"); } if (toggleText != null) { toggleText.text = "显示文字"; Debug.Log($"初始化Toggle 文本: {pageName}, text={toggleText.text}"); } else { Debug.LogWarning($"Toggle {toggle.name} 未找到TextMeshProUGUI组件"); } // 恢复OnValueChanged并绑定 toggle.onValueChanged = onValueChanged; toggle.onValueChanged.RemoveAllListeners(); toggle.onValueChanged.AddListener((value) => OnTextPanelToggleChanged(toggle)); // 发送初始显隐状态 SendMessageToServer("TextPanel:Show"); Debug.Log($"初始化TextPanelToggle: {pageName}, isOn: {toggle.isOn}"); } Debug.Log($"切换到页面: {pageName}, Slider {(slider != null ? "找到" : "未找到")}, TextPanelToggle {(toggle != null ? "找到" : "未找到")}"); } // 在主线程中更新Slider进度 private void Update() { if (isDraggingSlider) return; // 拖动时忽略服务器进度 float progress = -1f; lock (queueLock) { if (progressQueue.Count > 0) { progress = progressQueue.Dequeue(); // 获取最新进度 } } if (progress >= 0f) { Slider slider = GetCurrentPageSlider(); if (slider != null) { // 仅当进度与当前Slider值差异较大或接近1.0时更新 float currentProgress = slider.value / 100f; if (progress > 0f || Mathf.Abs(progress - 1f) < 0.005f) { // 临时禁用OnValueChanged以防止循环触发 var onValueChanged = slider.onValueChanged; slider.onValueChanged = new Slider.SliderEvent(); slider.value = progress * 100f; // 更新Slider值(0-100) slider.onValueChanged = onValueChanged; // 恢复事件 Debug.Log($"更新Slider进度: {progress:F2}, Slider值: {slider.value}"); } } } } // 应用程序退出时清理资源 private void OnApplicationQuit() { udpCts.Cancel(); // 通知UDP线程停止 receiveCts.Cancel(); // 通知接收线程停止 if (udpThread != null && udpThread.IsAlive) { udpThread.Join(1000); // 等待UDP线程结束 } if (receiveThread != null && receiveThread.IsAlive) { receiveThread.Join(1000); // 等待接收线程结束 } udpListener?.Close(); // 关闭UDP监听器 stream?.Close(); // 关闭TCP流 client?.Close(); // 关闭TCP客户端 } }