AnimalSimulation/Assets/Scripts/DisplayServer.cs

346 lines
12 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 UnityEngine;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading;
using System.Collections.Generic;
using System;
public class DisplayServer : MonoBehaviour
{
private TcpListener listener; // 用于接受客户端连接的TCP监听器
private Thread listenThread; // 用于接受客户端连接的线程
private CancellationTokenSource listenCts = new CancellationTokenSource(); // 控制线程取消
private volatile bool isRunning = true; // 线程安全的服务器循环标志
private NetworkStream clientStream; // 当前客户端的网络流,用于发送消息
public Transform background; // UI页面的父级变换
[System.Serializable]
public class PageModel
{
public string pageName; // 页面名称
public GameObject model; // 与页面关联的模型挂载ModelController
}
public List<PageModel> pageModels = new List<PageModel>(); // 页面-模型映射列表
private readonly Queue<string> messageQueue = new Queue<string>(); // 线程安全的消息队列
private readonly object queueLock = new object(); // 消息队列访问锁
private string currentPage = ""; // 跟踪当前显示的页面
private Dictionary<string, PageModel> pageModelCache; // 页面-模型映射的缓存
private bool isPlayingAnimation = false; // 跟踪动画播放状态
private bool isClientDragging = false; // 跟踪客户端是否正在拖动Slider
private float lastSendTime = 0f; // 上次发送进度的时间
private float sendInterval = 0.05f; // 进度发送间隔(秒)
private float lastSentProgress = 0f; // 记录上次发送的进度
// 脚本启动时调用
void Start()
{
// 设置显示端分辨率为 3328x1352
Screen.SetResolution(3328, 1352, false);
// 初始化页面-模型缓存以加快查找
pageModelCache = new Dictionary<string, PageModel>();
foreach (var pm in pageModels)
{
if (!string.IsNullOrEmpty(pm.pageName) && pm.model != null)
{
pageModelCache[pm.pageName] = pm;
}
}
// 在端口8888上启动TCP监听
listener = new TcpListener(IPAddress.Any, 8888);
listener.Start();
// 启动线程以监听客户端连接
listenThread = new Thread(() => ListenForClients(listenCts.Token));
listenThread.Start();
Debug.Log("显示端服务器已启动,等待触摸端连接...");
// 显示初始页面
ShowPage("首页");
}
// 监听传入的TCP客户端连接
private void ListenForClients(CancellationToken token)
{
while (!token.IsCancellationRequested && isRunning)
{
try
{
TcpClient client = listener.AcceptTcpClient();
Debug.Log("触摸端已连接!");
clientStream = client.GetStream(); // 保存客户端流以发送消息
// 为每个客户端启动新线程处理通信
Thread clientThread = new Thread(() => HandleClientComm(client, token));
clientThread.Start();
}
catch (Exception ex)
{
Debug.LogError($"接受客户端连接错误: {ex.Message}");
}
}
}
// 处理与单个客户端的通信
private void HandleClientComm(TcpClient tcpClient, CancellationToken token)
{
NetworkStream stream = tcpClient.GetStream();
byte[] message = new byte[4096];
int bytesRead;
// 在未取消或未断开连接的情况下持续读取
while (!token.IsCancellationRequested && isRunning)
{
try
{
bytesRead = stream.Read(message, 0, 4096);
if (bytesRead == 0) break; // 客户端断开连接
string data = Encoding.UTF8.GetString(message, 0, bytesRead);
lock (queueLock)
{
messageQueue.Enqueue(data); // 将消息加入队列以供处理
}
}
catch (Exception ex)
{
Debug.LogError($"读取客户端消息错误: {ex.Message}");
break;
}
}
// 清理客户端资源
stream.Close();
tcpClient.Close();
clientStream = null; // 清空客户端流
Debug.Log("客户端断开连接");
}
// 在主线程中处理排队的消息并发送动画进度
private void Update()
{
// 处理接收到的消息
string msg = null;
lock (queueLock)
{
if (messageQueue.Count > 0)
{
msg = messageQueue.Dequeue(); // 获取下一条消息
}
}
if (!string.IsNullOrEmpty(msg))
{
Debug.Log($"收到消息: {msg}");
if (msg.StartsWith("Page:"))
{
string pageName = msg.Replace("Page:", "");
ShowPage(pageName);
isPlayingAnimation = false; // 重置播放状态
isClientDragging = false; // 重置拖动状态
lastSentProgress = 0f; // 重置进度
}
else if (msg.StartsWith("Model:"))
{
string modelName = msg.Replace("Model:", "");
PlayModelAnimation(modelName);
lastSentProgress = 0f; // 重置进度
}
else if (msg.StartsWith("Progress:"))
{
if (float.TryParse(msg.Replace("Progress:", ""), out float progress))
{
SetAnimationProgress(progress);
isPlayingAnimation = false; // 拖动时停止播放
isClientDragging = true; // 标记客户端正在拖动
lastSentProgress = progress; // 更新最后发送的进度
Debug.Log($"处理进度消息: Progress:{progress:F2}");
}
}
else if (msg == "EndDrag")
{
isClientDragging = false; // 客户端拖动结束
Debug.Log("客户端Slider拖动结束");
}
}
// 仅当动画播放且客户端未拖动时发送进度,且限制发送频率
if (isPlayingAnimation && !isClientDragging && clientStream != null && clientStream.CanWrite)
{
if (Time.time - lastSendTime >= sendInterval)
{
if (pageModelCache.TryGetValue(currentPage, out PageModel pm) && pm != null && pm.model != null)
{
ModelController mc = pm.model.GetComponent<ModelController>();
if (mc != null && mc.IsPlayingAnimation())
{
float progress = mc.GetAnimationProgress();
// 仅当进度有效且变化显著时发送
if (progress > 0f && Mathf.Abs(progress - lastSentProgress) > 0.005f)
{
try
{
string progressMsg = $"Progress:{progress:F2}";
byte[] data = Encoding.UTF8.GetBytes(progressMsg);
clientStream.Write(data, 0, data.Length);
Debug.Log($"发送动画进度: {progressMsg}");
lastSentProgress = progress; // 更新最后发送的进度
lastSendTime = Time.time; // 更新发送时间
}
catch (Exception ex)
{
Debug.LogError($"发送动画进度失败: {ex.Message}");
clientStream = null; // 标记流失效
}
}
}
else
{
// 动画已停止,更新状态
isPlayingAnimation = false;
}
}
}
}
}
// 切换激活的UI页面并更新模型
private void ShowPage(string pageName)
{
foreach (Transform child in background)
{
child.gameObject.SetActive(child.name == pageName); // 仅激活目标页面
}
currentPage = pageName;
// 更新模型可见性并重置动画
foreach (var pageModel in pageModels)
{
if (pageModel.model != null)
{
bool isActive = pageModel.pageName == pageName;
pageModel.model.SetActive(isActive);
ModelController mc = pageModel.model.GetComponent<ModelController>();
if (mc != null)
{
// 取消订阅旧事件
mc.OnAnimationFinished -= OnModelAnimationFinished;
// 订阅动画结束事件
if (isActive)
{
mc.OnAnimationFinished += OnModelAnimationFinished;
}
mc.ResetAnimation();
Debug.Log($"模型 {pageModel.model.name} 重置,页面: {pageName}");
}
}
}
Debug.Log($"显示端切换到页面: {pageName}");
}
// 为指定模型播放动画
private void PlayModelAnimation(string modelName)
{
PageModel pm = pageModels.Find(p => p.model != null && p.model.name == modelName);
if (pm != null)
{
ModelController mc = pm.model.GetComponent<ModelController>();
if (mc != null)
{
// 取消旧订阅,添加新订阅
mc.OnAnimationFinished -= OnModelAnimationFinished;
mc.OnAnimationFinished += OnModelAnimationFinished;
mc.PlayAnimation();
isPlayingAnimation = true; // 确认动画开始播放
Debug.Log($"播放模型动画: {modelName}");
}
else
{
isPlayingAnimation = false; // 动画未启动
Debug.LogWarning($"模型 {modelName} 未找到ModelController");
}
}
else
{
isPlayingAnimation = false; // 动画未启动
Debug.LogWarning($"未找到模型 {modelName}");
}
}
// 设置当前页面模型的动画进度
private void SetAnimationProgress(float progress)
{
if (pageModelCache.TryGetValue(currentPage, out PageModel pm) && pm != null && pm.model != null)
{
ModelController mc = pm.model.GetComponent<ModelController>();
if (mc != null)
{
mc.SetAnimationProgress(progress);
Debug.Log($"设置模型 {pm.model.name} 进度: {progress:F2}");
}
}
else
{
Debug.LogWarning($"未找到页面 {currentPage} 的模型或ModelController");
}
}
// 处理动画结束事件
private void OnModelAnimationFinished()
{
isPlayingAnimation = false;
Debug.Log("模型动画播放完成,停止发送进度");
// 发送最终进度 1.0
if (clientStream != null && clientStream.CanWrite)
{
try
{
string progressMsg = "Progress:1.00";
byte[] data = Encoding.UTF8.GetBytes(progressMsg);
clientStream.Write(data, 0, data.Length);
Debug.Log($"发送最终动画进度: {progressMsg}");
lastSentProgress = 1f;
lastSendTime = Time.time;
}
catch (Exception ex)
{
Debug.LogError($"发送最终进度失败: {ex.Message}");
clientStream = null;
}
}
}
// 应用程序退出时清理资源
private void OnApplicationQuit()
{
isRunning = false;
listenCts.Cancel();
listener.Stop();
clientStream?.Close();
// 取消所有动画结束事件订阅
foreach (var pm in pageModels)
{
if (pm.model != null)
{
ModelController mc = pm.model.GetComponent<ModelController>();
if (mc != null)
{
mc.OnAnimationFinished -= OnModelAnimationFinished;
}
}
}
if (listenThread != null && listenThread.IsAlive)
{
listenThread.Join(1000); // 等待线程结束
}
}
}