556 lines
17 KiB
C#
556 lines
17 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
public class TouchClient : MonoBehaviour
|
|
{
|
|
private TcpClient client;
|
|
private NetworkStream stream;
|
|
private volatile bool isConnected = false;
|
|
|
|
private UdpClient udpListener;
|
|
private Thread udpThread;
|
|
private CancellationTokenSource udpCts = new CancellationTokenSource();
|
|
|
|
private Thread receiveThread;
|
|
private CancellationTokenSource receiveCts = new CancellationTokenSource();
|
|
|
|
public Transform background;
|
|
private string currentPage = "Ê×Ò³";
|
|
private string currentSingleItemModel = "";
|
|
|
|
private readonly Queue<float> progressQueue = new Queue<float>();
|
|
private readonly Queue<bool> hasAnimationQueue = new Queue<bool>();
|
|
private readonly object queueLock = new object();
|
|
private bool isDraggingSlider = false;
|
|
private float lastSentProgress = 0f;
|
|
|
|
private Dictionary<string, Slider> sliderCache;
|
|
private Dictionary<string, Toggle> toggleCache;
|
|
private Dictionary<string, Button> playButtonCache;
|
|
private Dictionary<string, List<Button>> singleItemButtonCache;
|
|
|
|
[System.Serializable]
|
|
public class PageButtonConfig
|
|
{
|
|
public string pageName;
|
|
public List<Button> singleItemButtons;
|
|
public List<string> singleItemModelNames;
|
|
public string defaultModelName;
|
|
}
|
|
[SerializeField] private List<PageButtonConfig> pageButtonConfigs = new List<PageButtonConfig>();
|
|
|
|
[SerializeField] private int udpPort = 9999;
|
|
[SerializeField] private int tcpPort = 8888;
|
|
[SerializeField] private int resolutionWidth = 1920;
|
|
[SerializeField] private int resolutionHeight = 1080;
|
|
public Sprite showSprite;
|
|
public Sprite hideSprite;
|
|
|
|
void Start()
|
|
{
|
|
Screen.SetResolution(resolutionWidth, resolutionHeight, false);
|
|
|
|
sliderCache = new Dictionary<string, Slider>();
|
|
toggleCache = new Dictionary<string, Toggle>();
|
|
playButtonCache = new Dictionary<string, Button>();
|
|
singleItemButtonCache = new Dictionary<string, List<Button>>();
|
|
CacheUIComponents();
|
|
|
|
foreach (var config in pageButtonConfigs)
|
|
{
|
|
if (!string.IsNullOrEmpty(config.pageName) && config.singleItemButtons != null && config.singleItemModelNames != null)
|
|
{
|
|
if (config.singleItemButtons.Count == config.singleItemModelNames.Count)
|
|
{
|
|
singleItemButtonCache[config.pageName] = config.singleItemButtons;
|
|
for (int i = 0; i < config.singleItemButtons.Count; i++)
|
|
{
|
|
if (config.singleItemButtons[i] != null)
|
|
{
|
|
int index = i;
|
|
config.singleItemButtons[i].onClick.RemoveAllListeners();
|
|
config.singleItemButtons[i].onClick.AddListener(() => OnButtonClick_SingleItemModel(config.pageName, config.singleItemModelNames[index]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
udpListener = new UdpClient(udpPort);
|
|
udpThread = new Thread(() => ListenForBroadcast(udpCts.Token));
|
|
udpThread.Start();
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
|
|
ShowPage("Ê×Ò³");
|
|
}
|
|
|
|
private void CacheUIComponents()
|
|
{
|
|
foreach (Transform child in background)
|
|
{
|
|
Slider slider = child.GetComponentInChildren<Slider>();
|
|
if (slider != null)
|
|
{
|
|
sliderCache[child.name] = slider;
|
|
}
|
|
Toggle toggle = child.GetComponentInChildren<Toggle>();
|
|
if (toggle != null)
|
|
{
|
|
toggleCache[child.name] = toggle;
|
|
}
|
|
Button playButton = FindDeep(child, "PlayButton")?.GetComponent<Button>();
|
|
if (playButton != null)
|
|
{
|
|
playButtonCache[child.name] = playButton;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ListenForBroadcast(CancellationToken token)
|
|
{
|
|
IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, udpPort);
|
|
int retryDelayMs = 2000;
|
|
|
|
while (!isConnected && !token.IsCancellationRequested)
|
|
{
|
|
try
|
|
{
|
|
byte[] data = udpListener.Receive(ref endPoint);
|
|
string message = Encoding.UTF8.GetString(data);
|
|
|
|
string serverIp = message.StartsWith("ServerIP:") ? message.Replace("ServerIP:", "").Trim() : message.Trim();
|
|
if (IPAddress.TryParse(serverIp, out IPAddress ipAddress))
|
|
{
|
|
ConnectToServer(serverIp, tcpPort);
|
|
retryDelayMs = 2000;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
Thread.Sleep(retryDelayMs);
|
|
retryDelayMs = Mathf.Min(retryDelayMs * 2, 10000);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ConnectToServer(string ip, int port)
|
|
{
|
|
int retryCount = 0;
|
|
const int maxRetries = 5;
|
|
int retryDelayMs = 2000;
|
|
|
|
while (!isConnected && retryCount < maxRetries)
|
|
{
|
|
try
|
|
{
|
|
client = new TcpClient();
|
|
client.Connect(ip, port);
|
|
stream = client.GetStream();
|
|
isConnected = true;
|
|
udpCts.Cancel();
|
|
udpListener?.Close();
|
|
receiveThread = new Thread(() => ReceiveMessages(receiveCts.Token));
|
|
receiveThread.Start();
|
|
return;
|
|
}
|
|
catch
|
|
{
|
|
retryCount++;
|
|
if (retryCount < maxRetries)
|
|
{
|
|
Thread.Sleep(retryDelayMs);
|
|
retryDelayMs = Mathf.Min(retryDelayMs * 2, 10000);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
lock (queueLock)
|
|
{
|
|
if (data.StartsWith("Progress:") && float.TryParse(data.Replace("Progress:", ""), out float progress))
|
|
{
|
|
progressQueue.Enqueue(progress);
|
|
}
|
|
else if (data.StartsWith("HasAnimation:"))
|
|
{
|
|
bool hasAnimation = data.Replace("HasAnimation:", "") == "True";
|
|
hasAnimationQueue.Enqueue(hasAnimation);
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
stream?.Close();
|
|
client?.Close();
|
|
isConnected = false;
|
|
RestartUdpListener();
|
|
}
|
|
|
|
private void RestartUdpListener()
|
|
{
|
|
try
|
|
{
|
|
udpCts = new CancellationTokenSource();
|
|
udpListener = new UdpClient(udpPort);
|
|
udpThread = new Thread(() => ListenForBroadcast(udpCts.Token));
|
|
udpThread.Start();
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
|
|
private void SendMessageToServer(string msg)
|
|
{
|
|
if (isConnected && stream != null)
|
|
{
|
|
try
|
|
{
|
|
byte[] data = Encoding.UTF8.GetBytes(msg);
|
|
stream.Write(data, 0, data.Length);
|
|
}
|
|
catch
|
|
{
|
|
isConnected = false;
|
|
RestartUdpListener();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnButtonClick_Page(string pageName)
|
|
{
|
|
ShowPage(pageName);
|
|
SendMessageToServer("Page:" + pageName);
|
|
}
|
|
|
|
public void OnButtonClick_PlayModel()
|
|
{
|
|
lock (queueLock)
|
|
{
|
|
progressQueue.Clear();
|
|
}
|
|
|
|
Slider slider = GetCurrentPageSlider();
|
|
if (slider != null)
|
|
{
|
|
var onValueChanged = slider.onValueChanged;
|
|
slider.onValueChanged = new Slider.SliderEvent();
|
|
slider.value = 0f;
|
|
slider.onValueChanged = onValueChanged;
|
|
lastSentProgress = 0f;
|
|
}
|
|
|
|
string modelName = "";
|
|
var config = pageButtonConfigs.Find(c => c.pageName == currentPage);
|
|
if (config != null && !string.IsNullOrEmpty(config.defaultModelName))
|
|
{
|
|
modelName = string.IsNullOrEmpty(currentSingleItemModel) ? config.defaultModelName : currentSingleItemModel;
|
|
}
|
|
else if (currentPage == "Ê×Ò³")
|
|
{
|
|
modelName = "ÕûÌå½á¹¹Õ¹Ê¾";
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(modelName))
|
|
{
|
|
SendMessageToServer("Model:" + modelName);
|
|
}
|
|
}
|
|
|
|
public void OnButtonClick_SingleItemModel(string pageName, string modelName)
|
|
{
|
|
if (pageName != currentPage)
|
|
{
|
|
ShowPage(pageName);
|
|
SendMessageToServer("Page:" + pageName);
|
|
}
|
|
|
|
currentSingleItemModel = modelName;
|
|
lock (queueLock)
|
|
{
|
|
progressQueue.Clear();
|
|
}
|
|
|
|
Slider slider = GetCurrentPageSlider();
|
|
if (slider != null)
|
|
{
|
|
var onValueChanged = slider.onValueChanged;
|
|
slider.onValueChanged = new Slider.SliderEvent();
|
|
slider.value = 0f;
|
|
slider.onValueChanged = onValueChanged;
|
|
lastSentProgress = 0f;
|
|
}
|
|
|
|
SendMessageToServer("SingleItemModel:" + modelName);
|
|
}
|
|
|
|
public void OnSliderChanged(Slider slider)
|
|
{
|
|
isDraggingSlider = true;
|
|
float progress = slider.value / 100f;
|
|
if (progress > 0.01f)
|
|
{
|
|
SendMessageToServer("Progress:" + progress.ToString("F2"));
|
|
lastSentProgress = progress;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
isDraggingSlider = false;
|
|
SendMessageToServer("EndDrag");
|
|
}
|
|
|
|
public void OnTextPanelToggleChanged(Toggle toggle)
|
|
{
|
|
if (toggle == null) return;
|
|
|
|
bool isTextPanelVisible = toggle.isOn;
|
|
Transform backgroundTransform = toggle.transform.Find("Background");
|
|
Image toggleImage = backgroundTransform != null ? backgroundTransform.GetComponent<Image>() : null;
|
|
TextMeshProUGUI toggleText = toggle.GetComponentInChildren<TextMeshProUGUI>();
|
|
|
|
if (toggleImage != null)
|
|
{
|
|
toggleImage.sprite = isTextPanelVisible ? showSprite : hideSprite;
|
|
}
|
|
|
|
if (toggleText != null)
|
|
{
|
|
toggleText.text = isTextPanelVisible ? "ÏÔʾÎÄ×Ö" : "Òþ²ØÎÄ×Ö";
|
|
}
|
|
|
|
string textPanelMessage = isTextPanelVisible ? "TextPanel:Show" : "TextPanel:Hide";
|
|
SendMessageToServer(textPanelMessage);
|
|
|
|
string backgroundMessage = isTextPanelVisible ? "Background:Text" : "Background:NoText";
|
|
SendMessageToServer(backgroundMessage);
|
|
|
|
string modelMoveMessage = isTextPanelVisible ? "ModelMove:Reset" : $"ModelMove:33";
|
|
SendMessageToServer(modelMoveMessage);
|
|
|
|
SendMessageToServer("RequestHasAnimation");
|
|
}
|
|
|
|
private void UpdatePlayButtonVisibility(bool hasAnimation)
|
|
{
|
|
if (playButtonCache.TryGetValue(currentPage, out Button playButton) && playButton != null)
|
|
{
|
|
playButton.gameObject.SetActive(hasAnimation);
|
|
}
|
|
}
|
|
|
|
private Slider GetCurrentPageSlider()
|
|
{
|
|
if (background == null || currentPage == "Ê×Ò³") return null;
|
|
|
|
if (sliderCache.TryGetValue(currentPage, out Slider slider))
|
|
{
|
|
if (!slider.interactable)
|
|
{
|
|
slider.interactable = true;
|
|
}
|
|
return slider;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private Toggle GetCurrentPageTextPanelToggle()
|
|
{
|
|
if (background == null || currentPage == "Ê×Ò³") return null;
|
|
|
|
if (toggleCache.TryGetValue(currentPage, out Toggle toggle))
|
|
{
|
|
if (!toggle.interactable)
|
|
{
|
|
toggle.interactable = true;
|
|
}
|
|
return toggle;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private void ShowPage(string pageName)
|
|
{
|
|
foreach (Transform child in background)
|
|
{
|
|
child.gameObject.SetActive(child.name == pageName);
|
|
}
|
|
currentPage = pageName;
|
|
currentSingleItemModel = "";
|
|
|
|
lock (queueLock)
|
|
{
|
|
progressQueue.Clear();
|
|
hasAnimationQueue.Clear();
|
|
}
|
|
|
|
Slider slider = GetCurrentPageSlider();
|
|
if (slider != null)
|
|
{
|
|
var onValueChanged = slider.onValueChanged;
|
|
slider.onValueChanged = new Slider.SliderEvent();
|
|
slider.value = 0f;
|
|
slider.interactable = true;
|
|
slider.onValueChanged = onValueChanged;
|
|
slider.onValueChanged.RemoveAllListeners();
|
|
slider.onValueChanged.AddListener((value) => OnSliderChanged(slider));
|
|
lastSentProgress = 0f;
|
|
}
|
|
|
|
Toggle toggle = GetCurrentPageTextPanelToggle();
|
|
if (toggle != null)
|
|
{
|
|
var onValueChanged = toggle.onValueChanged;
|
|
toggle.onValueChanged = new Toggle.ToggleEvent();
|
|
toggle.isOn = true;
|
|
toggle.interactable = true;
|
|
Transform backgroundTransform = toggle.transform.Find("Background");
|
|
Image toggleImage = backgroundTransform != null ? backgroundTransform.GetComponent<Image>() : null;
|
|
TextMeshProUGUI toggleText = toggle.GetComponentInChildren<TextMeshProUGUI>();
|
|
if (toggleImage != null)
|
|
{
|
|
toggleImage.sprite = showSprite;
|
|
}
|
|
if (toggleText != null)
|
|
{
|
|
toggleText.text = "ÏÔʾÎÄ×Ö";
|
|
}
|
|
toggle.onValueChanged = onValueChanged;
|
|
toggle.onValueChanged.RemoveAllListeners();
|
|
toggle.onValueChanged.AddListener((value) => OnTextPanelToggleChanged(toggle));
|
|
SendMessageToServer("TextPanel:Show");
|
|
SendMessageToServer("Background:Text");
|
|
SendMessageToServer("ModelMove:Reset");
|
|
}
|
|
|
|
if (singleItemButtonCache.TryGetValue(pageName, out List<Button> buttons))
|
|
{
|
|
foreach (var button in buttons)
|
|
{
|
|
if (button != null)
|
|
{
|
|
button.interactable = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
SendMessageToServer("RequestHasAnimation");
|
|
}
|
|
|
|
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 Update()
|
|
{
|
|
if (isDraggingSlider) return;
|
|
|
|
float progress = -1f;
|
|
bool? hasAnimation = null;
|
|
lock (queueLock)
|
|
{
|
|
if (progressQueue.Count > 0)
|
|
{
|
|
progress = progressQueue.Dequeue();
|
|
}
|
|
if (hasAnimationQueue.Count > 0)
|
|
{
|
|
hasAnimation = hasAnimationQueue.Dequeue();
|
|
}
|
|
}
|
|
|
|
if (progress >= 0f)
|
|
{
|
|
Slider slider = GetCurrentPageSlider();
|
|
if (slider != null)
|
|
{
|
|
float currentProgress = slider.value / 100f;
|
|
if (progress > 0f || Mathf.Abs(progress - 1f) < 0.005f)
|
|
{
|
|
var onValueChanged = slider.onValueChanged;
|
|
slider.onValueChanged = new Slider.SliderEvent();
|
|
slider.value = progress * 100f;
|
|
slider.onValueChanged = onValueChanged;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hasAnimation.HasValue)
|
|
{
|
|
UpdatePlayButtonVisibility(hasAnimation.Value);
|
|
}
|
|
}
|
|
|
|
private void OnApplicationQuit()
|
|
{
|
|
udpCts.Cancel();
|
|
receiveCts.Cancel();
|
|
if (udpThread != null && udpThread.IsAlive)
|
|
{
|
|
udpThread.Join(1000);
|
|
}
|
|
if (receiveThread != null && receiveThread.IsAlive)
|
|
{
|
|
receiveThread.Join(1000);
|
|
}
|
|
udpListener?.Close();
|
|
stream?.Close();
|
|
client?.Close();
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
OnApplicationQuit();
|
|
}
|
|
} |