AnimalSimulation/Assets/Scripts/DisplayServer.cs

256 lines
8.0 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; // 页面-模型映射的缓存
// 脚本启动时调用
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);
}
else if (msg.StartsWith("Model:"))
{
string modelName = msg.Replace("Model:", "");
PlayModelAnimation(modelName);
}
else if (msg.StartsWith("Progress:"))
{
if (float.TryParse(msg.Replace("Progress:", ""), out float progress))
{
SetAnimationProgress(progress);
Debug.Log($"处理进度消息: Progress:{progress:F2}");
}
}
}
// 每帧发送动画进度到客户端
if (clientStream != null && clientStream.CanWrite)
{
if (pageModelCache.TryGetValue(currentPage, out PageModel pm) && pm != null && pm.model != null)
{
ModelController mc = pm.model.GetComponent<ModelController>();
if (mc != null)
{
float progress = mc.GetAnimationProgress();
try
{
string progressMsg = $"Progress:{progress:F2}";
byte[] data = Encoding.UTF8.GetBytes(progressMsg);
clientStream.Write(data, 0, data.Length);
Debug.Log($"发送动画进度: {progressMsg}");
}
catch (Exception ex)
{
Debug.LogError($"发送动画进度失败: {ex.Message}");
clientStream = null; // 标记流失效
}
}
}
}
}
// 切换激活的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.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.PlayAnimation();
Debug.Log($"播放模型动画: {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 OnApplicationQuit()
{
isRunning = false;
listenCts.Cancel();
listener.Stop();
clientStream?.Close();
if (listenThread != null && listenThread.IsAlive)
{
listenThread.Join(1000); // 等待线程结束
}
}
}