AnimalSimulation/Assets/Scripts/TouchClient.cs

321 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
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<float> progressQueue = new Queue<float>(); // 线程安全的进度队列
private readonly object queueLock = new object(); // 进度队列访问锁
private bool isDraggingSlider = false; // 标记是否正在拖动Slider
private float lastSentProgress = 0f; // 记录最后发送的进度
// 脚本启动时调用
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拖动结束");
}
// 获取当前页面的Slider组件
private Slider GetCurrentPageSlider()
{
if (background == null || currentPage == "首页") return null;
Transform pageTransform = background.Find(currentPage);
if (pageTransform != null)
{
Slider slider = pageTransform.GetComponentInChildren<Slider>();
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;
}
// 切换激活的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}");
}
Debug.Log($"切换到页面: {pageName}, Slider {(slider != 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客户端
}
}