AnimalSimulation/Assets/Scripts/TouchClient.cs

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();
}
}