692 lines
22 KiB
C#
692 lines
22 KiB
C#
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<GameObject> singleItemModels;
|
||
}
|
||
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 string currentSingleItemModel = "";
|
||
private Dictionary<string, PageModel> pageModelCache;
|
||
private Dictionary<string, Transform> textPanelCache;
|
||
private Dictionary<string, Slider> 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<string, PageModel>();
|
||
textPanelCache = new Dictionary<string, Transform>();
|
||
sliderCache = new Dictionary<string, Slider>();
|
||
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<GameObject>();
|
||
}
|
||
}
|
||
|
||
CacheUIComponents();
|
||
|
||
if (background == null)
|
||
{
|
||
return;
|
||
}
|
||
Image backgroundImage = background.GetComponent<Image>();
|
||
if (backgroundImage == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
listener = new TcpListener(IPAddress.Any, tcpPort);
|
||
listener.Start();
|
||
}
|
||
catch
|
||
{
|
||
return;
|
||
}
|
||
|
||
listenThread = new Thread(() => ListenForClients(listenCts.Token));
|
||
listenThread.Start();
|
||
|
||
ShowPage("<22><>ҳ");
|
||
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<Slider>();
|
||
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<ModelController>();
|
||
}
|
||
}
|
||
else if (pm.model != null)
|
||
{
|
||
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);
|
||
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<ModelController>();
|
||
}
|
||
}
|
||
else if (pm.model != null)
|
||
{
|
||
mc = pm.model.GetComponent<ModelController>();
|
||
}
|
||
|
||
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<ModelController>();
|
||
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<ModelController>();
|
||
if (mc != null)
|
||
{
|
||
mc.OnAnimationFinished -= OnModelAnimationFinished;
|
||
mc.ResetAnimation();
|
||
mc.ResetModelPosition();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
SetTextPanelVisibility(pageName != "<22><>ҳ");
|
||
SetBackgroundSprite(pageName != "<22><>ҳ");
|
||
}
|
||
|
||
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<ModelController>();
|
||
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<ModelController>();
|
||
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 != "<22><>ҳ");
|
||
SetBackgroundSprite(currentPage != "<22><>ҳ");
|
||
}
|
||
}
|
||
|
||
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<ModelController>();
|
||
}
|
||
}
|
||
else if (pm.model != null && pm.model.name == modelName)
|
||
{
|
||
targetModel = pm.model;
|
||
mc = pm.model.GetComponent<ModelController>();
|
||
}
|
||
|
||
if (mc != null)
|
||
{
|
||
Animator animator = mc.GetComponent<Animator>();
|
||
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<ModelController>();
|
||
}
|
||
}
|
||
else if (pm.model != null)
|
||
{
|
||
mc = pm.model.GetComponent<ModelController>();
|
||
}
|
||
|
||
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<ModelController>();
|
||
}
|
||
}
|
||
else if (pm.model != null)
|
||
{
|
||
targetModel = pm.model;
|
||
mc = pm.model.GetComponent<ModelController>();
|
||
}
|
||
|
||
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<ModelController>();
|
||
}
|
||
}
|
||
else if (pm.model != null)
|
||
{
|
||
targetModel = pm.model;
|
||
mc = pm.model.GetComponent<ModelController>();
|
||
}
|
||
|
||
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 == "<22><>ҳ")
|
||
{
|
||
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<Image>();
|
||
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<ModelController>();
|
||
if (mc != null)
|
||
{
|
||
mc.OnAnimationFinished -= OnModelAnimationFinished;
|
||
}
|
||
}
|
||
foreach (var singleItemModel in pm.singleItemModels)
|
||
{
|
||
if (singleItemModel != null)
|
||
{
|
||
ModelController mc = singleItemModel.GetComponent<ModelController>();
|
||
if (mc != null)
|
||
{
|
||
mc.OnAnimationFinished -= OnModelAnimationFinished;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (listenThread != null && listenThread.IsAlive)
|
||
{
|
||
listenThread.Join(1000);
|
||
}
|
||
}
|
||
|
||
private void OnDestroy()
|
||
{
|
||
OnApplicationQuit();
|
||
}
|
||
} |