using UnityEngine; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Collections.Generic; using System; using System.Collections; using System.Globalization; public class UnityRecieve_FACEMOTION3D_and_iFacialMocap : MonoBehaviour { // broadcast address public bool gameStartWithConnect = true; public string iOS_IPAddress = "255.255.255.255"; private UdpClient client; private bool StartFlag = true; //object references public SkinnedMeshRenderer[] faceMeshRenderers; public Transform headBone; public Transform rightEyeBone; public Transform leftEyeBone; public Transform headPositionObject; private UdpClient udp; private Thread thread; private string messageString = ""; public int LOCAL_PORT = 49983; // Start is called void StartFunction() { if (StartFlag == true) { StartFlag = false; FindGameObjectsInsideUnitySettings(); //Send to iOS if (gameStartWithConnect == true) { Connect_to_iOS_App(); } //Recieve udp from iOS CreateUdpServer(); } } void Start() { StartFunction(); } void CreateUdpServer() { try { udp = new UdpClient(LOCAL_PORT); udp.Client.ReceiveTimeout = 5; thread = new Thread(new ThreadStart(ThreadMethod)); thread.Start(); } catch (Exception e) { Debug.LogError($"[iFacialMocap] UDP 서버 생성 실패: {e.Message}"); } } IEnumerator WaitProcess(float WaitTime) { yield return new WaitForSeconds(WaitTime); } void Connect_to_iOS_App() { try { //iFacialMocap SendMessage_to_iOSapp("iFacialMocap_sahuasouryya9218sauhuiayeta91555dy3719", 49983); //Facemotion3d SendMessage_to_iOSapp("FACEMOTION3D_OtherStreaming", 49993); } catch (Exception e) { Debug.LogError($"[iFacialMocap] iOS 앱 연결 실패: {e.Message}"); } } void StopStreaming_iOS_App() { SendMessage_to_iOSapp("StopStreaming_FACEMOTION3D", 49993); } //iOSアプリに通信開始のメッセージを送信 //Send a message to the iOS application to start streaming void SendMessage_to_iOSapp(string sendMessage, int send_port) { try { client = new UdpClient(); client.Connect(iOS_IPAddress, send_port); byte[] dgram = Encoding.UTF8.GetBytes(sendMessage); // 메시지 전송 시도 for (int i = 0; i < 5; i++) { client.Send(dgram, dgram.Length); } } catch (Exception e) { Debug.LogError($"[iFacialMocap] 메시지 전송 실패: {e.Message}"); } } // Update is called once per frame void Update() { try { SetAnimation_inside_Unity_settings(); } catch { } } //BlendShapeの設定 //set blendshapes void SetBlendShapeWeightFromStrArray(string[] strArray2) { string mappedShapeName = strArray2[0].Replace("_R", "Right").Replace("_L", "Left"); float weight = float.Parse(strArray2[1], CultureInfo.InvariantCulture); if (faceMeshRenderers != null) { foreach (var meshRenderer in faceMeshRenderers) { if (meshRenderer != null && meshRenderer.sharedMesh != null) { int index = meshRenderer.sharedMesh.GetBlendShapeIndex(mappedShapeName); if (index > -1) { meshRenderer.SetBlendShapeWeight(index, weight); } } } } } //BlendShapeとボーンの回転の設定 //set blendshapes & bone rotation void SetAnimation_inside_Unity_settings() { try { string[] strArray1 = messageString.Split('='); if (strArray1.Length >= 2) { //blendShapes foreach (string message in strArray1[0].Split('|')) { string[] strArray2 = new string[3]; if (message.Contains("&")) { strArray2 = message.Split('&'); } else { strArray2 = message.Split('-'); } if (strArray2.Length == 2) { SetBlendShapeWeightFromStrArray(strArray2); } } foreach (string message in strArray1[1].Split('|')) { string[] strArray2 = message.Split('#'); if (strArray2.Length == 2) { string[] commaList = strArray2[1].Split(','); if (strArray2[0] == "head" && headBone != null) { headBone.localRotation = Quaternion.Euler(float.Parse(commaList[0], CultureInfo.InvariantCulture), -float.Parse(commaList[1], CultureInfo.InvariantCulture), -float.Parse(commaList[2], CultureInfo.InvariantCulture)); if (headPositionObject != null) { headPositionObject.localPosition = new Vector3(-float.Parse(commaList[3], CultureInfo.InvariantCulture), float.Parse(commaList[4], CultureInfo.InvariantCulture), float.Parse(commaList[5], CultureInfo.InvariantCulture)); } } else if (strArray2[0] == "rightEye" && rightEyeBone != null) { rightEyeBone.localRotation = Quaternion.Euler(float.Parse(commaList[0], CultureInfo.InvariantCulture), -float.Parse(commaList[1], CultureInfo.InvariantCulture), float.Parse(commaList[2], CultureInfo.InvariantCulture)); } else if (strArray2[0] == "leftEye" && leftEyeBone != null) { leftEyeBone.localRotation = Quaternion.Euler(float.Parse(commaList[0], CultureInfo.InvariantCulture), -float.Parse(commaList[1], CultureInfo.InvariantCulture), float.Parse(commaList[2], CultureInfo.InvariantCulture)); } } } } } catch { } } void FindGameObjectsInsideUnitySettings() { // 모든 Transform 참조는 이미 인스펙터에서 할당되므로 추가 초기화가 필요 없음 } void ThreadMethod() { //Process once every 5ms long next = DateTime.Now.Ticks + 50000; long now; while (true) { try { IPEndPoint remoteEP = null; byte[] data = udp.Receive(ref remoteEP); messageString = Encoding.ASCII.GetString(data); } catch (SocketException e) { if (e.SocketErrorCode != SocketError.TimedOut) { Debug.LogError($"[iFacialMocap] 데이터 수신 오류: {e.Message}"); } } catch (Exception e) { Debug.LogError($"[iFacialMocap] 예상치 못한 오류: {e.Message}"); } do { now = DateTime.Now.Ticks; } while (now < next); next += 50000; } } public string GetMessageString() { return messageString; } void OnEnable() { StartFunction(); } void OnDisable() { try { OnApplicationQuit(); } catch { } } void OnApplicationQuit() { if (StartFlag == false) { StartFlag = true; StopUDP(); } } public void StopUDP() { if (gameStartWithConnect == true) { StopStreaming_iOS_App(); } udp.Dispose(); thread.Abort(); } private bool HasBlendShapes(SkinnedMeshRenderer skin) { if (!skin.sharedMesh) { return false; } if (skin.sharedMesh.blendShapeCount <= 0) { return false; } return true; } } public static class FM3D_and_iFacialMocap_GetAllChildren { public static List GetAll(this GameObject obj) { List allChildren = new List(); allChildren.Add(obj); GetChildren(obj, ref allChildren); return allChildren; } public static void GetChildren(GameObject obj, ref List allChildren) { Transform children = obj.GetComponentInChildren(); if (children.childCount == 0) { return; } foreach (Transform ob in children) { allChildren.Add(ob.gameObject); GetChildren(ob.gameObject, ref allChildren); } } }