Skip to main content
Glama
PlayModeHandler.cs10 kB
using UnityEngine; using UnityEditor; using System; using System.Collections; using System.Collections.Generic; namespace UnityMCP { /// <summary> /// Handles Unity Play Mode testing and automation /// Enables automated testing workflows for game development /// </summary> public static class PlayModeHandler { private static float testStartTime; #pragma warning disable 0414 private static bool isTestRunning = false; #pragma warning restore 0414 private static List<string> testLog = new List<string>(); public static string HandleCommand(string path, string requestBody) { try { var data = JsonUtility.FromJson<CommandData>(requestBody); switch (path) { case "/playmode/enter": return EnterPlayMode(data); case "/playmode/exit": return ExitPlayMode(data); case "/playmode/status": return GetPlayModeStatus(); case "/playmode/test": return RunTest(data); case "/playmode/pause": return PausePlayMode(); case "/playmode/step": return StepFrame(); case "/playmode/timescale": return SetTimeScale(data); case "/playmode/screenshot": return CaptureScreenshot(data); default: return Error("Unknown playmode command"); } } catch (Exception e) { return Error(e.Message); } } public static string EnterPlayMode(CommandData data) { if (EditorApplication.isPlaying) { return Error("Already in play mode"); } EditorApplication.delayCall += () => { testStartTime = Time.realtimeSinceStartup; testLog.Clear(); EditorApplication.isPlaying = true; if (data.pauseOnEnter) { EditorApplication.playModeStateChanged += PauseOnEnter; } LogTest("Entered play mode"); }; return Success("Entering play mode"); } public static void PauseOnEnter(PlayModeStateChange state) { if (state == PlayModeStateChange.EnteredPlayMode) { EditorApplication.isPaused = true; EditorApplication.playModeStateChanged -= PauseOnEnter; LogTest("Paused on enter"); } } public static string ExitPlayMode(CommandData data) { if (!EditorApplication.isPlaying) { return Error("Not in play mode"); } EditorApplication.delayCall += () => { EditorApplication.isPlaying = false; float duration = Time.realtimeSinceStartup - testStartTime; LogTest($"Exited play mode after {duration:F2}s"); }; return Success("Exiting play mode"); } public static string GetPlayModeStatus() { var status = new { isPlaying = EditorApplication.isPlaying, isPaused = EditorApplication.isPaused, isCompiling = EditorApplication.isCompiling, timeSinceStartup = Time.realtimeSinceStartup - testStartTime, frameCount = Time.frameCount, timeScale = Time.timeScale, testLog = testLog.ToArray() }; return JsonUtility.ToJson(status); } public static string RunTest(CommandData data) { if (!EditorApplication.isPlaying) { // Enter play mode first EditorApplication.delayCall += () => { testStartTime = Time.realtimeSinceStartup; testLog.Clear(); isTestRunning = true; EditorApplication.isPlaying = true; EditorApplication.playModeStateChanged += (state) => { if (state == PlayModeStateChange.EnteredPlayMode) { ExecuteTest(data); } }; }; return Success($"Starting test: {data.testName}"); } else { EditorApplication.delayCall += () => ExecuteTest(data); return Success($"Executing test: {data.testName}"); } } public static void ExecuteTest(CommandData data) { try { LogTest($"Test started: {data.testName}"); // Find test GameObject if specified if (!string.IsNullOrEmpty(data.targetObject)) { var target = GameObject.Find(data.targetObject); if (target != null) { LogTest($"Found target: {data.targetObject}"); // Perform test action if (data.action == "move") { target.transform.position = new Vector3(data.x ?? 0, data.y ?? 0, data.z ?? 0); LogTest($"Moved to ({data.x}, {data.y}, {data.z})"); } else if (data.action == "destroy") { GameObject.Destroy(target); LogTest($"Destroyed {data.targetObject}"); } else if (data.action == "activate") { target.SetActive(data.value ?? true); LogTest($"Set active: {data.value}"); } } else { LogTest($"ERROR: Target not found: {data.targetObject}"); } } // Wait for duration if specified if (data.duration > 0) { LogTest($"Waiting for {data.duration}s"); EditorApplication.delayCall += () => { System.Threading.Thread.Sleep((int)(data.duration * 1000)); if (data.exitAfter) { EditorApplication.isPlaying = false; LogTest("Test complete, exiting play mode"); } }; } else if (data.exitAfter) { EditorApplication.delayCall += () => { EditorApplication.isPlaying = false; LogTest("Test complete, exiting play mode"); }; } } catch (Exception e) { LogTest($"ERROR: {e.Message}"); } } public static string PausePlayMode() { EditorApplication.isPaused = !EditorApplication.isPaused; LogTest($"Pause: {EditorApplication.isPaused}"); return Success($"Play mode paused: {EditorApplication.isPaused}"); } public static string StepFrame() { if (!EditorApplication.isPlaying) { return Error("Not in play mode"); } EditorApplication.Step(); LogTest("Stepped one frame"); return Success("Stepped one frame"); } public static string SetTimeScale(CommandData data) { Time.timeScale = data.timeScale; LogTest($"Time scale set to {data.timeScale}"); return Success($"Time scale: {data.timeScale}"); } public static string CaptureScreenshot(CommandData data) { string path = data.screenshotPath ?? "Assets/Screenshots/test_screenshot.png"; EditorApplication.delayCall += () => { ScreenCapture.CaptureScreenshot(path); LogTest($"Screenshot saved: {path}"); }; return Success($"Capturing screenshot to {path}"); } public static void LogTest(string message) { testLog.Add($"[{Time.realtimeSinceStartup - testStartTime:F2}s] {message}"); Debug.Log($"[Unity MCP Test] {message}"); } public static string Success(string message) { return $"{{\"success\":true,\"message\":\"{message}\"}}"; } public static string Error(string message) { return $"{{\"success\":false,\"error\":\"{message}\"}}"; } [Serializable] public class CommandData { public bool pauseOnEnter; public string testName; public string targetObject; public string action; public float? x; public float? y; public float? z; public bool? value; public float duration; public bool exitAfter; public float timeScale; public string screenshotPath; } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/muammar-yacoob/unity-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server