--- title: Unity Environment Server emoji: 🌐 colorFrom: blue colorTo: green sdk: docker pinned: false app_port: 8000 base_path: /web tags: - openenv - Unity - MlAgents - MlAgentsUnity - MlAgentsEnv ---
# Unity ML-Agents Environment OpenEnv wrapper for [Unity ML-Agents](https://github.com/Unity-Technologies/ml-agents) environments. This environment provides access to Unity's reinforcement learning environments through a standardized HTTP/WebSocket interface. ## Supported Environments | Environment | Action Type | Description | |------------|-------------|-------------| | **PushBlock** | Discrete (7) | Push a block to a goal position | | **3DBall** | Continuous (2) | Balance a ball on a platform | | **3DBallHard** | Continuous (2) | Harder version of 3DBall | | **GridWorld** | Discrete (5) | Navigate a grid to find goals | | **Basic** | Discrete (3) | Simple left/right movement | More environments may be available depending on the ML-Agents registry version. ## Installation ### Option 1: Non-Docker Installation (Local Development) #### Prerequisites - Python 3.10+ - [uv](https://docs.astral.sh/uv/) (recommended) or pip #### Install from OpenEnv Repository ```bash # Clone the OpenEnv repository (if not already done) git clone https://github.com/your-org/OpenEnv.git cd OpenEnv # Install the unity_env package with dependencies cd envs/unity_env uv pip install -e . # Or with pip pip install -e . ``` #### Install Dependencies Only ```bash cd envs/unity_env # Using uv (recommended) uv sync # Or using pip pip install -r requirements.txt # if available pip install mlagents-envs numpy pillow fastapi uvicorn pydantic ``` #### Verify Installation ```bash # Test the installation cd envs/unity_env python -c "from server.unity_environment import UnityMLAgentsEnvironment; print('Installation successful!')" ``` **Note:** The first run will download Unity environment binaries (~500MB). These are cached in `~/.mlagents-cache/` for future use. ### Option 2: Docker Installation #### Prerequisites - Docker installed and running - Python 3.10+ (for running the client) #### Build the Docker Image ```bash cd envs/unity_env # Build the Docker image docker build -f server/Dockerfile -t unity-env:latest . # Verify the build docker images | grep unity-env ``` **Note for Apple Silicon (M1/M2/M3/M4) users:** Docker mode is **not supported** on Apple Silicon because Unity's Mono runtime crashes under x86_64 emulation. Use **direct mode** (`--direct`) or **server mode** (`--url`) instead, which run native macOS binaries. See [Troubleshooting](#docker-mode-fails-on-apple-silicon-m1m2m3m4) for details. #### Run the Docker Container ```bash # Run with default settings (graphics enabled, 800x600) docker run -p 8000:8000 unity-env:latest # Run with custom settings docker run -p 8000:8000 \ -e UNITY_NO_GRAPHICS=0 \ -e UNITY_WIDTH=1280 \ -e UNITY_HEIGHT=720 \ -e UNITY_TIME_SCALE=1.0 \ unity-env:latest # Run in headless mode (faster for training) docker run -p 8000:8000 \ -e UNITY_NO_GRAPHICS=1 \ -e UNITY_TIME_SCALE=20 \ unity-env:latest # Run with persistent cache (avoid re-downloading binaries) docker run -p 8000:8000 \ -v ~/.mlagents-cache:/root/.mlagents-cache \ unity-env:latest ``` #### Install Client Dependencies To connect to the Docker container, install the client on your host machine: ```bash cd envs/unity_env pip install requests websockets ``` ## Quick Start ### Option 1: Direct Mode (Fastest for Testing) Run the Unity environment directly without a server: ```bash cd envs/unity_env # Run with graphics (default: 1280x720) python example_usage.py --direct # Run with custom window size python example_usage.py --direct --width 800 --height 600 # Run headless (faster for training) python example_usage.py --direct --no-graphics --time-scale 20 # Run 3DBall environment python example_usage.py --direct --env 3DBall --episodes 5 ``` ### Option 2: Server Mode Start the server and connect with a client: ```bash # Terminal 1: Start the server (graphics enabled by default) cd envs/unity_env uv run uvicorn server.app:app --host 0.0.0.0 --port 8000 # Terminal 2: Run the example client python example_usage.py --url http://localhost:8000 python example_usage.py --url http://localhost:8000 --env 3DBall --episodes 5 ``` ### Option 3: Docker Mode Run via Docker container (auto-starts and connects): ```bash cd envs/unity_env # Run with default settings python example_usage.py --docker # Run with custom window size python example_usage.py --docker --width 1280 --height 720 # Run headless (faster for training) python example_usage.py --docker --no-graphics --time-scale 20 # Run 3DBall for 10 episodes python example_usage.py --docker --env 3DBall --episodes 10 # Use a custom Docker image python example_usage.py --docker --docker-image my-unity-env:v1 ``` ## Example Scripts ### Basic Usage Examples #### 1. Direct Mode - Quick Testing ```bash # Run PushBlock with graphics (default) python example_usage.py --direct # Output: # ============================================================ # Unity ML-Agents Environment - Direct Mode # ============================================================ # Environment: PushBlock # Episodes: 3 # Max steps: 500 # Window size: 1280x720 # Graphics: Enabled # ... ``` #### 2. Direct Mode - Training Configuration ```bash # Headless mode with fast simulation (20x speed) python example_usage.py --direct --no-graphics --time-scale 20 --episodes 10 --max-steps 1000 # This is ideal for training - no graphics overhead, faster simulation ``` #### 3. Direct Mode - 3DBall with Custom Window ```bash # Run 3DBall (continuous actions) with larger window python example_usage.py --direct --env 3DBall --width 1280 --height 720 --episodes 5 ``` #### 4. Docker Mode - Production-like Testing ```bash # Build the image first docker build -f server/Dockerfile -t unity-env:latest . # Run via Docker with graphics python example_usage.py --docker --width 1280 --height 720 # Run via Docker in headless mode for training python example_usage.py --docker --no-graphics --time-scale 20 --episodes 20 ``` #### 5. Server Mode - Separate Server and Client ```bash # Terminal 1: Start server with specific settings UNITY_WIDTH=1280 UNITY_HEIGHT=720 uv run uvicorn server.app:app --port 8000 # Terminal 2: Connect and run episodes python example_usage.py --url http://localhost:8000 --env PushBlock --episodes 5 python example_usage.py --url http://localhost:8000 --env 3DBall --episodes 5 ``` #### 6. Alternating Environments ```bash # Run alternating episodes between PushBlock and 3DBall python example_usage.py --direct --env both --episodes 6 # Episodes 1,3,5 = PushBlock; Episodes 2,4,6 = 3DBall ``` ### Command Line Options | Option | Default | Description | |--------|---------|-------------| | `--direct` | - | Run environment directly (no server) | | `--docker` | - | Run via Docker container | | `--url` | localhost:8000 | Server URL for server mode | | `--docker-image` | unity-env:latest | Docker image name | | `--env` | PushBlock | Environment: PushBlock, 3DBall, both | | `--episodes` | 3 | Number of episodes | | `--max-steps` | 500 | Max steps per episode | | `--width` | 1280 | Window width in pixels | | `--height` | 720 | Window height in pixels | | `--no-graphics` | - | Headless mode (faster) | | `--time-scale` | 1.0 | Simulation speed multiplier | | `--quality-level` | 5 | Graphics quality 0-5 | | `--quiet` | - | Reduce output verbosity | ## Python Client Usage ### Connect to Server ```python from envs.unity_env import UnityEnv, UnityAction # Connect to the server with UnityEnv(base_url="http://localhost:8000") as client: # Reset to PushBlock environment result = client.reset(env_id="PushBlock") print(f"Observation dims: {len(result.observation.vector_observations)}") # Take actions for _ in range(100): # PushBlock actions: 0=noop, 1=forward, 2=backward, # 3=rotate_left, 4=rotate_right, 5=strafe_left, 6=strafe_right action = UnityAction(discrete_actions=[1]) # Move forward result = client.step(action) print(f"Reward: {result.reward}, Done: {result.done}") if result.done: result = client.reset() ``` ### Connect via Docker ```python from envs.unity_env import UnityEnv, UnityAction # Automatically start Docker container and connect client = UnityEnv.from_docker_image( "unity-env:latest", environment={ "UNITY_NO_GRAPHICS": "0", "UNITY_WIDTH": "1280", "UNITY_HEIGHT": "720", } ) try: result = client.reset(env_id="PushBlock") for _ in range(100): action = UnityAction(discrete_actions=[1]) result = client.step(action) finally: client.close() ``` ### Switch Environments Dynamically ```python # Start with PushBlock result = client.reset(env_id="PushBlock") # ... train on PushBlock ... # Switch to 3DBall (continuous actions) result = client.reset(env_id="3DBall") action = UnityAction(continuous_actions=[0.5, -0.3]) result = client.step(action) ``` ### Direct Environment Usage (No Server) ```python from envs.unity_env.server.unity_environment import UnityMLAgentsEnvironment from envs.unity_env.models import UnityAction # Create environment directly env = UnityMLAgentsEnvironment( env_id="PushBlock", no_graphics=False, # Show graphics window width=1280, height=720, time_scale=1.0, ) try: obs = env.reset() print(f"Observation: {len(obs.vector_observations)} dimensions") for step in range(100): action = UnityAction(discrete_actions=[1]) # Move forward obs = env.step(action) print(f"Step {step}: reward={obs.reward}, done={obs.done}") if obs.done: obs = env.reset() finally: env.close() ``` ## Action Spaces ### PushBlock (Discrete) 7 discrete actions: - `0`: No operation - `1`: Move forward - `2`: Move backward - `3`: Rotate left - `4`: Rotate right - `5`: Strafe left - `6`: Strafe right ```python action = UnityAction(discrete_actions=[1]) # Move forward ``` ### 3DBall (Continuous) 2 continuous actions in range [-1, 1]: - Action 0: X-axis rotation - Action 1: Z-axis rotation ```python action = UnityAction(continuous_actions=[0.5, -0.3]) ``` ## Observations All environments provide vector observations. The size depends on the environment: - **PushBlock**: 70 dimensions (14 ray-casts detecting walls/goals/blocks) - **3DBall**: 8 dimensions (rotation and ball position/velocity) - **GridWorld**: Visual observations (grid view) ```python result = client.reset() obs = result.observation # Access observations print(f"Vector obs: {obs.vector_observations}") print(f"Behavior: {obs.behavior_name}") print(f"Action spec: {obs.action_spec_info}") ``` ### Visual Observations (Optional) Some environments support visual observations. Enable with `include_visual=True`: ```python result = client.reset(include_visual=True) if result.observation.visual_observations: # Base64-encoded PNG images for img_b64 in result.observation.visual_observations: # Decode and use the image import base64 img_bytes = base64.b64decode(img_b64) ``` ## Configuration ### Constructor Arguments When creating `UnityMLAgentsEnvironment` directly: ```python from envs.unity_env.server.unity_environment import UnityMLAgentsEnvironment env = UnityMLAgentsEnvironment( env_id="PushBlock", # Unity environment to load no_graphics=False, # False = show graphics window width=1280, # Window width in pixels height=720, # Window height in pixels time_scale=1.0, # Simulation speed (20.0 for fast training) quality_level=5, # Graphics quality 0-5 ) ``` ### Environment Variables For Docker deployment, configure via environment variables: | Variable | Default | Description | |----------|---------|-------------| | `UNITY_ENV_ID` | PushBlock | Default Unity environment | | `UNITY_NO_GRAPHICS` | 0 | Set to 1 for headless mode | | `UNITY_WIDTH` | 1280 | Window width in pixels | | `UNITY_HEIGHT` | 720 | Window height in pixels | | `UNITY_TIME_SCALE` | 1.0 | Simulation speed multiplier | | `UNITY_QUALITY_LEVEL` | 5 | Graphics quality 0-5 | | `UNITY_CACHE_DIR` | ~/.mlagents-cache | Binary cache directory | ## Environment State Access detailed environment information: ```python state = client.state() print(f"Environment: {state.env_id}") print(f"Episode ID: {state.episode_id}") print(f"Step count: {state.step_count}") print(f"Available envs: {state.available_envs}") print(f"Action spec: {state.action_spec}") print(f"Observation spec: {state.observation_spec}") ``` ## Troubleshooting ### Docker Mode Fails on Apple Silicon (M1/M2/M3/M4) **Symptom:** When running with `--docker` on Apple Silicon Macs, you see an error like: ``` Error running with Docker: Server error: The Unity environment took too long to respond... ``` Or in Docker logs: ``` * Assertion: should not be reached at tramp-amd64.c:605 Environment shut down with return code -6 (SIGABRT) ``` **Cause:** Unity ML-Agents binaries are x86_64 (Intel) only. When Docker runs the x86_64 Linux container on Apple Silicon, it uses QEMU emulation. The Mono runtime inside Unity has architecture-specific code that crashes under emulation. **Solutions:** 1. **Use Direct Mode** (recommended for macOS): ```bash python example_usage.py --direct --no-graphics ``` Direct mode downloads native macOS binaries which work on Apple Silicon. 2. **Use Server Mode** with a local server: ```bash # Terminal 1: Start server (uses native macOS binaries) uvicorn server.app:app --host 0.0.0.0 --port 8000 # Terminal 2: Run client python example_usage.py --url http://localhost:8000 ``` 3. **Use an x86_64 Linux machine** for Docker mode: The Docker image works correctly on native x86_64 Linux machines (cloud VMs, dedicated servers, etc.). ### First Run is Slow The first run downloads Unity binaries (~500MB). This is normal and only happens once. Binaries are cached in `~/.mlagents-cache/`. ### Graphics Not Showing - Ensure `--no-graphics` is NOT set - On Linux, ensure X11 is available - For Docker, you may need to set up X11 forwarding ### Docker Container Fails to Start ```bash # Check Docker logs docker logs # Ensure the image is built docker images | grep unity-env # Rebuild if necessary docker build -f server/Dockerfile -t unity-env:latest . ``` ### Import Errors ```bash # Ensure you're in the correct directory cd envs/unity_env # Install dependencies uv sync # or pip install -e . ``` ### mlagents-envs Installation Issues The `mlagents-envs` and `mlagents` packages are installed from source by default (via the GitHub repository). If you encounter issues or want to install manually: ```bash # Clone the ml-agents repository git clone https://github.com/Unity-Technologies/ml-agents.git cd ml-agents # Install mlagents-envs from source pip install -e ./ml-agents-envs # Install the full ml-agents package pip install -e ./ml-agents ``` This approach is useful when: - You need to modify the mlagents source code - You want to use a specific branch or commit - The git dependency in pyproject.toml is causing issues ## Caveats 1. **First Run Download**: Unity binaries (~500MB) are downloaded on first use 2. **Platform-Specific**: Binaries are platform-specific (macOS, Linux, Windows) 3. **Apple Silicon + Docker**: Docker mode does not work on Apple Silicon Macs due to x86_64 emulation issues with Unity's Mono runtime. Use direct mode or server mode instead. 4. **Single Worker**: Unity environments are not thread-safe; use `workers=1` 5. **Graphics Mode**: Some features require X11/display for graphics mode 6. **Multi-Agent**: Currently uses first agent only; full multi-agent support planned ## Dependencies - `mlagents-envs` (installed from source via git) - `mlagents` (installed from source via git) - `numpy>=1.20.0` - `pillow>=9.0.0` (for visual observations) - `openenv-core[core]>=0.2.0` ## References - [Unity ML-Agents Documentation](https://unity-technologies.github.io/ml-agents/) - [ML-Agents GitHub](https://github.com/Unity-Technologies/ml-agents) - [Example Environments](https://unity-technologies.github.io/ml-agents/Learning-Environment-Examples/)