using UnityEngine; using UnityEngine.UI; using System.Net.Sockets; using System.Net; using System.Text; using System.Threading; using System.Collections.Generic; using DG.Tweening; public class DisplayServer : MonoBehaviour { private TcpListener listener; private Thread listenThread; private CancellationTokenSource listenCts = new CancellationTokenSource(); private volatile bool isRunning = true; private NetworkStream clientStream; public Transform background; [System.Serializable] public class PageModel { public string pageName; public GameObject model; public Vector3 originalPosition; public List singleItemModels; } public List pageModels = new List(); private readonly Queue messageQueue = new Queue(); private readonly object queueLock = new object(); private string currentPage = ""; private string currentSingleItemModel = ""; private Dictionary pageModelCache; private Dictionary textPanelCache; private Dictionary sliderCache; private bool isPlayingAnimation = false; private bool isClientDragging = false; private float lastSendTime = 0f; private float lastSentProgress = 0f; [SerializeField] private int tcpPort = 8888; [SerializeField] private float sendInterval = 0.05f; [SerializeField] private int resolutionWidth = 3328; [SerializeField] private int resolutionHeight = 1352; public Sprite textBackgroundSprite; public Sprite noTextBackgroundSprite; [SerializeField] private float textHiddenOffset = 33f; void Start() { Screen.SetResolution(resolutionWidth, resolutionHeight, false); pageModelCache = new Dictionary(); textPanelCache = new Dictionary(); sliderCache = new Dictionary(); foreach (var pm in pageModels) { if (!string.IsNullOrEmpty(pm.pageName) && pm.model != null) { pm.originalPosition = pm.model.transform.localPosition; pageModelCache[pm.pageName] = pm; if (pm.singleItemModels == null) pm.singleItemModels = new List(); } } CacheUIComponents(); if (background == null) { return; } Image backgroundImage = background.GetComponent(); if (backgroundImage == null) { return; } try { listener = new TcpListener(IPAddress.Any, tcpPort); listener.Start(); } catch { return; } listenThread = new Thread(() => ListenForClients(listenCts.Token)); listenThread.Start(); ShowPage("首页"); SendHasAnimationStatus(); } private void CacheUIComponents() { foreach (Transform child in background) { Transform textPanel = FindDeep(child, "TextPanel"); if (textPanel != null) { textPanelCache[child.name] = textPanel; } Slider slider = child.GetComponentInChildren(); if (slider != null) { sliderCache[child.name] = slider; } } } private void ListenForClients(CancellationToken token) { int retryDelayMs = 2000; while (!token.IsCancellationRequested && isRunning) { try { TcpClient client = listener.AcceptTcpClient(); clientStream = client.GetStream(); Thread clientThread = new Thread(() => HandleClientComm(client, token)); clientThread.Start(); retryDelayMs = 2000; } catch { Thread.Sleep(retryDelayMs); retryDelayMs = Mathf.Min(retryDelayMs * 2, 10000); } } } 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 { break; } } stream.Close(); tcpClient.Close(); clientStream = null; } private void Update() { string msg = null; lock (queueLock) { if (messageQueue.Count > 0) { msg = messageQueue.Dequeue(); } } if (!string.IsNullOrEmpty(msg)) { if (msg.StartsWith("Page:")) { string pageName = msg.Replace("Page:", ""); ShowPage(pageName); isPlayingAnimation = false; isClientDragging = false; lastSentProgress = 0f; SendHasAnimationStatus(); } else if (msg.StartsWith("Model:")) { string modelName = msg.Replace("Model:", ""); PlayModelAnimation(modelName); lastSentProgress = 0f; } else if (msg.StartsWith("SingleItemModel:")) { string modelName = msg.Replace("SingleItemModel:", ""); ShowSingleItemModel(modelName); isPlayingAnimation = false; isClientDragging = false; lastSentProgress = 0f; SendHasAnimationStatus(); } else if (msg.StartsWith("Progress:")) { if (float.TryParse(msg.Replace("Progress:", ""), out float progress)) { SetAnimationProgress(progress); isPlayingAnimation = false; isClientDragging = true; lastSentProgress = progress; } } else if (msg == "EndDrag") { isClientDragging = false; } else if (msg.StartsWith("TextPanel:")) { string command = msg.Replace("TextPanel:", ""); SetTextPanelVisibility(command == "Show"); } else if (msg.StartsWith("Background:")) { string command = msg.Replace("Background:", ""); SetBackgroundSprite(command == "Text"); } else if (msg.StartsWith("ModelMove:")) { string command = msg.Replace("ModelMove:", ""); if (command == "Reset") { ResetModelPosition(); } else if (float.TryParse(command, out float offset)) { MoveModel(offset); } } else if (msg == "RequestHasAnimation") { SendHasAnimationStatus(); } } if (isPlayingAnimation && !isClientDragging && clientStream != null && clientStream.CanWrite) { if (Time.time - lastSendTime >= sendInterval) { if (pageModelCache.TryGetValue(currentPage, out PageModel pm) && pm != null) { ModelController mc = null; if (!string.IsNullOrEmpty(currentSingleItemModel)) { GameObject singleItemModel = pm.singleItemModels.Find(m => m != null && m.name == currentSingleItemModel); if (singleItemModel != null) { mc = singleItemModel.GetComponent(); } } else if (pm.model != null) { mc = pm.model.GetComponent(); } 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); lastSentProgress = progress; lastSendTime = Time.time; } catch { clientStream = null; } } } else { isPlayingAnimation = false; } } } } } private void SendHasAnimationStatus() { if (clientStream == null || !clientStream.CanWrite) return; bool hasAnimation = false; if (pageModelCache.TryGetValue(currentPage, out PageModel pm) && pm != null) { ModelController mc = null; if (!string.IsNullOrEmpty(currentSingleItemModel)) { GameObject singleItemModel = pm.singleItemModels.Find(m => m != null && m.name == currentSingleItemModel); if (singleItemModel != null) { mc = singleItemModel.GetComponent(); } } else if (pm.model != null) { mc = pm.model.GetComponent(); } if (mc != null) { hasAnimation = mc.GetHasAnimation(); } } try { string msg = $"HasAnimation:{(hasAnimation ? "True" : "False")}"; byte[] data = Encoding.UTF8.GetBytes(msg); clientStream.Write(data, 0, data.Length); } catch { clientStream = null; } } private void ShowPage(string pageName) { if (background == null) { return; } foreach (Transform child in background) { bool isActive = child.name == pageName; child.gameObject.SetActive(isActive); } currentPage = pageName; currentSingleItemModel = ""; foreach (var pageModel in pageModels) { bool isActive = pageModel.pageName == pageName; if (pageModel.model != null) { pageModel.model.SetActive(isActive && string.IsNullOrEmpty(currentSingleItemModel)); pageModel.originalPosition = pageModel.model.transform.localPosition; ModelController mc = pageModel.model.GetComponent(); if (mc != null) { mc.OnAnimationFinished -= OnModelAnimationFinished; if (isActive) { mc.OnAnimationFinished += OnModelAnimationFinished; } mc.ResetAnimation(); mc.ResetModelPosition(); } } foreach (var singleItemModel in pageModel.singleItemModels) { if (singleItemModel != null) { singleItemModel.SetActive(false); ModelController mc = singleItemModel.GetComponent(); if (mc != null) { mc.OnAnimationFinished -= OnModelAnimationFinished; mc.ResetAnimation(); mc.ResetModelPosition(); } } } } SetTextPanelVisibility(pageName != "首页"); SetBackgroundSprite(pageName != "首页"); } private void ShowSingleItemModel(string modelName) { if (pageModelCache.TryGetValue(currentPage, out PageModel pm) && pm != null) { if (pm.model != null) { pm.model.SetActive(false); ModelController mc = pm.model.GetComponent(); if (mc != null) { mc.OnAnimationFinished -= OnModelAnimationFinished; mc.ResetAnimation(); } } bool modelFound = false; foreach (var singleItemModel in pm.singleItemModels) { if (singleItemModel != null) { bool isActive = singleItemModel.name == modelName; singleItemModel.SetActive(isActive); ModelController mc = singleItemModel.GetComponent(); if (mc != null) { mc.OnAnimationFinished -= OnModelAnimationFinished; if (isActive) { mc.OnAnimationFinished += OnModelAnimationFinished; mc.ResetAnimation(); mc.ResetModelPosition(); currentSingleItemModel = modelName; modelFound = true; } } else if (isActive) { currentSingleItemModel = modelName; modelFound = true; } } } SetTextPanelVisibility(currentPage != "首页"); SetBackgroundSprite(currentPage != "首页"); } } private void PlayModelAnimation(string modelName) { if (string.IsNullOrEmpty(modelName)) { return; } if (pageModelCache.TryGetValue(currentPage, out PageModel pm) && pm != null) { ModelController mc = null; GameObject targetModel = null; if (!string.IsNullOrEmpty(currentSingleItemModel) && modelName == currentSingleItemModel) { targetModel = pm.singleItemModels.Find(m => m != null && m.name == modelName); if (targetModel != null) { mc = targetModel.GetComponent(); } } else if (pm.model != null && pm.model.name == modelName) { targetModel = pm.model; mc = pm.model.GetComponent(); } if (mc != null) { Animator animator = mc.GetComponent(); if (animator != null && animator.runtimeAnimatorController != null && mc.GetHasAnimation()) { mc.OnAnimationFinished -= OnModelAnimationFinished; mc.OnAnimationFinished += OnModelAnimationFinished; mc.PlayAnimation(); isPlayingAnimation = true; } else { isPlayingAnimation = false; } } else { isPlayingAnimation = false; } } else { isPlayingAnimation = false; } } private void SetAnimationProgress(float progress) { if (pageModelCache.TryGetValue(currentPage, out PageModel pm) && pm != null) { ModelController mc = null; if (!string.IsNullOrEmpty(currentSingleItemModel)) { GameObject singleItemModel = pm.singleItemModels.Find(m => m != null && m.name == currentSingleItemModel); if (singleItemModel != null) { mc = singleItemModel.GetComponent(); } } else if (pm.model != null) { mc = pm.model.GetComponent(); } if (mc != null) { mc.SetAnimationProgress(progress); } } } private void MoveModel(float xOffset) { if (pageModelCache.TryGetValue(currentPage, out PageModel pm) && pm != null) { ModelController mc = null; GameObject targetModel = null; if (!string.IsNullOrEmpty(currentSingleItemModel)) { targetModel = pm.singleItemModels.Find(m => m != null && m.name == currentSingleItemModel); if (targetModel != null) { mc = targetModel.GetComponent(); } } else if (pm.model != null) { targetModel = pm.model; mc = pm.model.GetComponent(); } if (mc != null && targetModel.activeSelf) { mc.MoveModel(xOffset); } else if (targetModel != null && targetModel.activeSelf) { Vector3 originalPosition = pm.originalPosition; Vector3 targetPosition = originalPosition + new Vector3(xOffset, 0f, 0f); targetModel.transform.DOLocalMoveX(targetPosition.x, 1f).SetEase(Ease.InOutSine); } } } private void ResetModelPosition() { if (pageModelCache.TryGetValue(currentPage, out PageModel pm) && pm != null) { ModelController mc = null; GameObject targetModel = null; if (!string.IsNullOrEmpty(currentSingleItemModel)) { targetModel = pm.singleItemModels.Find(m => m != null && m.name == currentSingleItemModel); if (targetModel != null) { mc = targetModel.GetComponent(); } } else if (pm.model != null) { targetModel = pm.model; mc = pm.model.GetComponent(); } if (mc != null && targetModel.activeSelf) { mc.ResetModelPosition(); } else if (targetModel != null && targetModel.activeSelf) { Vector3 originalPosition = pm.originalPosition; targetModel.transform.DOLocalMoveX(originalPosition.x, 1f).SetEase(Ease.InOutSine); } } } private void SetTextPanelVisibility(bool isVisible) { if (background == null || string.IsNullOrEmpty(currentPage) || currentPage == "首页") { return; } if (textPanelCache.TryGetValue(currentPage, out Transform textPanel)) { textPanel.gameObject.SetActive(isVisible); if (isVisible) { ResetModelPosition(); } else { MoveModel(textHiddenOffset); } } } private void SetBackgroundSprite(bool hasText) { if (background == null) { return; } Image backgroundImage = background.GetComponent(); if (backgroundImage != null) { Sprite newSprite = hasText ? textBackgroundSprite : noTextBackgroundSprite; if (newSprite != null) { backgroundImage.sprite = newSprite; } } } private Transform FindDeep(Transform parent, string name) { Transform result = parent.Find(name); if (result != null) return result; foreach (Transform child in parent) { result = FindDeep(child, name); if (result != null) return result; } return null; } private void OnModelAnimationFinished() { isPlayingAnimation = false; if (clientStream != null && clientStream.CanWrite) { try { string progressMsg = "Progress:1.00"; byte[] data = Encoding.UTF8.GetBytes(progressMsg); clientStream.Write(data, 0, data.Length); lastSentProgress = 1f; lastSendTime = Time.time; } catch { 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(); if (mc != null) { mc.OnAnimationFinished -= OnModelAnimationFinished; } } foreach (var singleItemModel in pm.singleItemModels) { if (singleItemModel != null) { ModelController mc = singleItemModel.GetComponent(); if (mc != null) { mc.OnAnimationFinished -= OnModelAnimationFinished; } } } } if (listenThread != null && listenThread.IsAlive) { listenThread.Join(1000); } } private void OnDestroy() { OnApplicationQuit(); } }