diff --git a/Example/README.md b/Example/README.md
new file mode 100644
index 0000000..0820e6d
--- /dev/null
+++ b/Example/README.md
@@ -0,0 +1,63 @@
+# Unity UDP Example
+
+This example demonstrates how to use the UnityUDP Flutter app to send commands to a Unity game.
+
+## Setup
+
+### Unity Setup
+
+1. Copy `UDPCommandListener.cs` into your Unity project's `Assets/Scripts/` folder
+2. Create a new empty GameObject in your scene (GameObject → Create Empty)
+3. Rename it to "UDP Manager"
+4. Attach the `UDPCommandListener` script to the UDP Manager GameObject
+5. Set the port to 7777 (or any port you prefer) in the inspector
+
+### Example Scene Setup
+
+For the example commands to work, create a simple test scene:
+
+1. Create a Cube (GameObject → 3D Object → Cube)
+2. Name it "TestCube"
+3. Create a UI Canvas with a Text element showing a timer (optional, for the timer command)
+
+### Testing the Example
+
+1. Run your Unity game
+2. Open the UnityUDP Flutter app
+3. Copy/move the `unityudp_projects.json` file to your Documents folder
+4. Select a command and send it to your Unity game (make sure the IP address matches your computer's local IP and port is 7777)
+
+## Sample Commands
+
+The example includes these simple commands:
+
+- **Toggle Cube**: Enables/Disables the TestCube GameObject
+- **Start Timer**: Starts a 10-second countdown timer
+- **Reset Scene**: Reloads the current scene
+
+## UDP Protocol
+
+The UDP commands are sent as JSON strings with the following format:
+
+```json
+{
+ "command": "command_name",
+ "value": "optional_value"
+}
+```
+
+The Unity script parses these JSON commands and executes the corresponding action.
+
+## Customizing
+
+You can add your own commands by:
+
+1. Adding a new case in the `ProcessCommand` method in `UDPCommandListener.cs`
+2. Creating a corresponding JSON entry in your project file
+
+## Troubleshooting
+
+- **Commands not working**: Check that your firewall allows UDP traffic on the specified port
+- **Can't connect**: Verify the IP address is correct (use `ipconfig` on Windows or `ifconfig` on Mac/Linux)
+- **Unity crashes**: Make sure all GameObject names match the ones in the script
+
diff --git a/Example/UDPCommandListener.cs b/Example/UDPCommandListener.cs
new file mode 100644
index 0000000..4b0b368
--- /dev/null
+++ b/Example/UDPCommandListener.cs
@@ -0,0 +1,419 @@
+using UnityEngine;
+using UnityEngine.SceneManagement;
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+
+///
+/// Simple UDP Command Listener for Unity
+/// Listens for JSON commands on a specified UDP port and executes them
+///
+public class UDPCommandListener : MonoBehaviour
+{
+ [Header("UDP Settings")]
+ [Tooltip("Port to listen on for UDP commands")]
+ public int port = 7777;
+
+ [Header("References")]
+ [Tooltip("GameObject to toggle/manipulate (e.g., a cube)")]
+ public GameObject testObject;
+
+ [Header("Debug")]
+ public bool showDebugLogs = true;
+
+ // UDP client for receiving messages
+ private UdpClient udpClient;
+ private Thread receiveThread;
+ private bool isRunning = false;
+
+ // Timer state
+ private float timerDuration = 0f;
+ private float timerRemaining = 0f;
+ private bool timerActive = false;
+
+ // Rotation state
+ private float rotationSpeed = 0f;
+
+ void Start()
+ {
+ // Find the test object if not assigned
+ if (testObject == null)
+ {
+ testObject = GameObject.Find("TestCube");
+ if (testObject == null)
+ {
+ LogMessage("Warning: TestCube not found in scene. Create a GameObject named 'TestCube' or assign one manually.");
+ }
+ }
+
+ // Start listening for UDP messages
+ StartUDPListener();
+ }
+
+ void Update()
+ {
+ // Update timer
+ if (timerActive && timerRemaining > 0)
+ {
+ timerRemaining -= Time.deltaTime;
+ if (timerRemaining <= 0)
+ {
+ timerRemaining = 0;
+ timerActive = false;
+ LogMessage("Timer finished!");
+ }
+ }
+
+ // Update rotation
+ if (rotationSpeed != 0 && testObject != null)
+ {
+ testObject.transform.Rotate(Vector3.up, rotationSpeed * Time.deltaTime);
+ }
+ }
+
+ void OnGUI()
+ {
+ // Display timer on screen
+ if (timerActive && timerRemaining > 0)
+ {
+ GUIStyle style = new GUIStyle();
+ style.fontSize = 30;
+ style.normal.textColor = Color.white;
+ style.alignment = TextAnchor.UpperCenter;
+
+ GUI.Label(new Rect(0, 10, Screen.width, 50),
+ $"Timer: {timerRemaining:F1}s", style);
+ }
+
+ // Display UDP status
+ GUIStyle statusStyle = new GUIStyle();
+ statusStyle.fontSize = 14;
+ statusStyle.normal.textColor = isRunning ? Color.green : Color.red;
+ statusStyle.alignment = TextAnchor.UpperLeft;
+
+ GUI.Label(new Rect(10, 10, 300, 30),
+ $"UDP Listener: {(isRunning ? "Running" : "Stopped")} (Port {port})",
+ statusStyle);
+ }
+
+ ///
+ /// Starts the UDP listener thread
+ ///
+ void StartUDPListener()
+ {
+ try
+ {
+ udpClient = new UdpClient(port);
+ receiveThread = new Thread(new ThreadStart(ReceiveData));
+ receiveThread.IsBackground = true;
+ receiveThread.Start();
+ isRunning = true;
+
+ LogMessage($"UDP Listener started on port {port}");
+ }
+ catch (Exception e)
+ {
+ LogMessage($"Error starting UDP listener: {e.Message}");
+ }
+ }
+
+ ///
+ /// Receives data on a separate thread
+ ///
+ void ReceiveData()
+ {
+ while (isRunning)
+ {
+ try
+ {
+ IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
+ byte[] data = udpClient.Receive(ref remoteEndPoint);
+ string message = Encoding.UTF8.GetString(data);
+
+ LogMessage($"Received UDP message: {message}");
+
+ // Process the command on the main thread
+ UnityMainThreadDispatcher.Instance().Enqueue(() => ProcessCommand(message));
+ }
+ catch (Exception e)
+ {
+ if (isRunning)
+ {
+ LogMessage($"Error receiving UDP data: {e.Message}");
+ }
+ }
+ }
+ }
+
+ ///
+ /// Processes a received command
+ ///
+ void ProcessCommand(string jsonCommand)
+ {
+ try
+ {
+ // Parse the JSON command
+ UDPCommand command = JsonUtility.FromJson(jsonCommand);
+
+ if (command == null || string.IsNullOrEmpty(command.command))
+ {
+ LogMessage("Invalid command format");
+ return;
+ }
+
+ LogMessage($"Processing command: {command.command}");
+
+ // Execute the command
+ switch (command.command.ToLower())
+ {
+ case "toggle_cube":
+ ToggleObject();
+ break;
+
+ case "set_object":
+ SetObjectState(command.target, command.value);
+ break;
+
+ case "start_timer":
+ StartTimer(command.value);
+ break;
+
+ case "stop_timer":
+ StopTimer();
+ break;
+
+ case "reset_scene":
+ ResetScene();
+ break;
+
+ case "rotate_cube":
+ SetRotation(command.value);
+ break;
+
+ case "change_color":
+ ChangeColor(command.value);
+ break;
+
+ default:
+ LogMessage($"Unknown command: {command.command}");
+ break;
+ }
+ }
+ catch (Exception e)
+ {
+ LogMessage($"Error processing command: {e.Message}");
+ }
+ }
+
+ // ========== Command Implementations ==========
+
+ void ToggleObject()
+ {
+ if (testObject != null)
+ {
+ testObject.SetActive(!testObject.activeSelf);
+ LogMessage($"TestCube toggled to: {testObject.activeSelf}");
+ }
+ else
+ {
+ LogMessage("TestCube not found!");
+ }
+ }
+
+ void SetObjectState(string targetName, string state)
+ {
+ GameObject target = string.IsNullOrEmpty(targetName) ? testObject : GameObject.Find(targetName);
+
+ if (target != null)
+ {
+ bool active = state.ToLower() == "true" || state == "1";
+ target.SetActive(active);
+ LogMessage($"{target.name} set to: {active}");
+ }
+ else
+ {
+ LogMessage($"GameObject '{targetName}' not found!");
+ }
+ }
+
+ void StartTimer(string durationStr)
+ {
+ if (float.TryParse(durationStr, out float duration))
+ {
+ timerDuration = duration;
+ timerRemaining = duration;
+ timerActive = true;
+ LogMessage($"Timer started for {duration} seconds");
+ }
+ else
+ {
+ LogMessage("Invalid timer duration");
+ }
+ }
+
+ void StopTimer()
+ {
+ timerActive = false;
+ timerRemaining = 0;
+ LogMessage("Timer stopped");
+ }
+
+ void ResetScene()
+ {
+ LogMessage("Resetting scene...");
+ Scene currentScene = SceneManager.GetActiveScene();
+ SceneManager.LoadScene(currentScene.name);
+ }
+
+ void SetRotation(string speedStr)
+ {
+ if (float.TryParse(speedStr, out float speed))
+ {
+ rotationSpeed = speed;
+ LogMessage($"Rotation speed set to: {speed}");
+ }
+ else
+ {
+ LogMessage("Invalid rotation speed");
+ }
+ }
+
+ void ChangeColor(string colorName)
+ {
+ if (testObject != null)
+ {
+ Renderer renderer = testObject.GetComponent();
+ if (renderer != null)
+ {
+ Color newColor = Color.white;
+
+ switch (colorName.ToLower())
+ {
+ case "red":
+ newColor = Color.red;
+ break;
+ case "blue":
+ newColor = Color.blue;
+ break;
+ case "green":
+ newColor = Color.green;
+ break;
+ case "yellow":
+ newColor = Color.yellow;
+ break;
+ case "white":
+ newColor = Color.white;
+ break;
+ case "black":
+ newColor = Color.black;
+ break;
+ default:
+ LogMessage($"Unknown color: {colorName}");
+ return;
+ }
+
+ renderer.material.color = newColor;
+ LogMessage($"Color changed to: {colorName}");
+ }
+ else
+ {
+ LogMessage("TestCube has no Renderer component!");
+ }
+ }
+ else
+ {
+ LogMessage("TestCube not found!");
+ }
+ }
+
+ // ========== Utility Methods ==========
+
+ void LogMessage(string message)
+ {
+ if (showDebugLogs)
+ {
+ Debug.Log($"[UDP] {message}");
+ }
+ }
+
+ void OnApplicationQuit()
+ {
+ StopUDPListener();
+ }
+
+ void OnDestroy()
+ {
+ StopUDPListener();
+ }
+
+ void StopUDPListener()
+ {
+ isRunning = false;
+
+ if (receiveThread != null && receiveThread.IsAlive)
+ {
+ receiveThread.Abort();
+ }
+
+ if (udpClient != null)
+ {
+ udpClient.Close();
+ }
+
+ LogMessage("UDP Listener stopped");
+ }
+}
+
+///
+/// Data class for UDP commands
+///
+[System.Serializable]
+public class UDPCommand
+{
+ public string command;
+ public string target;
+ public string value;
+}
+
+///
+/// Helper class to execute actions on the main Unity thread
+/// This is necessary because UDP receives data on a separate thread
+///
+public class UnityMainThreadDispatcher : MonoBehaviour
+{
+ private static UnityMainThreadDispatcher _instance;
+ private static readonly System.Collections.Generic.Queue _executionQueue = new System.Collections.Generic.Queue();
+
+ public static UnityMainThreadDispatcher Instance()
+ {
+ if (_instance == null)
+ {
+ GameObject go = new GameObject("MainThreadDispatcher");
+ _instance = go.AddComponent();
+ DontDestroyOnLoad(go);
+ }
+ return _instance;
+ }
+
+ public void Enqueue(Action action)
+ {
+ lock (_executionQueue)
+ {
+ _executionQueue.Enqueue(action);
+ }
+ }
+
+ void Update()
+ {
+ lock (_executionQueue)
+ {
+ while (_executionQueue.Count > 0)
+ {
+ _executionQueue.Dequeue().Invoke();
+ }
+ }
+ }
+}
+
diff --git a/Example/unityudp_projects.json b/Example/unityudp_projects.json
new file mode 100644
index 0000000..6b2c5fe
--- /dev/null
+++ b/Example/unityudp_projects.json
@@ -0,0 +1,81 @@
+{
+ "id": "example-project-001",
+ "name": "Unity Example Project",
+ "ipAddress": "127.0.0.1",
+ "port": 7777,
+ "packages": [
+ {
+ "id": "cmd-001",
+ "name": "Toggle Cube",
+ "data": "{\"command\":\"toggle_cube\"}",
+ "ipAddress": "127.0.0.1"
+ },
+ {
+ "id": "cmd-002",
+ "name": "Enable Cube",
+ "data": "{\"command\":\"set_object\",\"target\":\"TestCube\",\"value\":\"true\"}",
+ "ipAddress": "127.0.0.1"
+ },
+ {
+ "id": "cmd-003",
+ "name": "Disable Cube",
+ "data": "{\"command\":\"set_object\",\"target\":\"TestCube\",\"value\":\"false\"}",
+ "ipAddress": "127.0.0.1"
+ },
+ {
+ "id": "cmd-004",
+ "name": "Start Timer (10s)",
+ "data": "{\"command\":\"start_timer\",\"value\":\"10\"}",
+ "ipAddress": "127.0.0.1"
+ },
+ {
+ "id": "cmd-005",
+ "name": "Stop Timer",
+ "data": "{\"command\":\"stop_timer\"}",
+ "ipAddress": "127.0.0.1"
+ },
+ {
+ "id": "cmd-006",
+ "name": "Reset Scene",
+ "data": "{\"command\":\"reset_scene\"}",
+ "ipAddress": "127.0.0.1"
+ },
+ {
+ "id": "cmd-007",
+ "name": "Rotate Cube Fast",
+ "data": "{\"command\":\"rotate_cube\",\"value\":\"100\"}",
+ "ipAddress": "127.0.0.1"
+ },
+ {
+ "id": "cmd-008",
+ "name": "Rotate Cube Slow",
+ "data": "{\"command\":\"rotate_cube\",\"value\":\"20\"}",
+ "ipAddress": "127.0.0.1"
+ },
+ {
+ "id": "cmd-009",
+ "name": "Stop Rotation",
+ "data": "{\"command\":\"rotate_cube\",\"value\":\"0\"}",
+ "ipAddress": "127.0.0.1"
+ },
+ {
+ "id": "cmd-010",
+ "name": "Change Color Red",
+ "data": "{\"command\":\"change_color\",\"value\":\"red\"}",
+ "ipAddress": "127.0.0.1"
+ },
+ {
+ "id": "cmd-011",
+ "name": "Change Color Blue",
+ "data": "{\"command\":\"change_color\",\"value\":\"blue\"}",
+ "ipAddress": "127.0.0.1"
+ },
+ {
+ "id": "cmd-012",
+ "name": "Change Color Green",
+ "data": "{\"command\":\"change_color\",\"value\":\"green\"}",
+ "ipAddress": "127.0.0.1"
+ }
+ ]
+}
+