# Lumine Generalist Agents 3d Worlds

> Build generalist agents for 3D open worlds using vision-language models with human-like interaction patterns and adaptive reasoning—achieving zero-shot transfer across games without fine-tuning through pixel-level perception and high-frequency action generation.

- Skill: `adu2021/lumine-generalist-agents-3d-worlds` (Agent Skill)
- Install (CLI): `npx skillmds@latest add adu2021/lumine-generalist-agents-3d-worlds`
- Raw SKILL.md: https://api.skillmd.com/api/skills/adu2021/lumine-generalist-agents-3d-worlds/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: MIT
- Author: adu2021 (https://skillmd.com/u/adu2021)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/adu2021/lumine-generalist-agents-3d-worlds

---


# Build Generalist Agents for Open-World 3D Environments

Game-playing agents typically require game-specific training or scripted logic. Lumine demonstrates that pretrained vision-language models can play complex 3D open-world games by mimicking human interaction patterns: processing visual frames at 5 Hz, generating keyboard-mouse actions at 30 Hz, and adaptively invoking reasoning only when necessary. A single agent trained on one game transfers zero-shot to others without fine-tuning.

The approach completes five-hour story sequences on par with human efficiency, establishing a concrete step toward generalist embodied agents for open-ended environments.

## Core Concept

Lumine treats game-playing as a straightforward vision-to-action problem: given pixels, generate human-like actions (keyboard/mouse). The key innovations are:

1. **Human-like interaction frequency** - Process at 5 Hz (human perception) for perception but generate actions at 30 Hz (motor frequency)
2. **Adaptive reasoning** - Invoke chain-of-thought only when needed (complex decisions), not constantly
3. **Zero-shot transfer** - Train once on one game; use directly on others with no game-specific tuning

This approach relies on strong vision-language models' inherent understanding of physics, game mechanics, and strategy—learned during general pretraining.

## Architecture Overview

- **Vision Encoder**: Processes game frames to understand state and environment
- **Perception Buffer**: Maintains frame history for context (last 4-5 frames at 5 Hz)
- **Reasoning Module**: VLM chain-of-thought for complex decisions (adaptive invocation)
- **Action Generator**: Converts reasoning to keyboard/mouse commands
- **Action Executor**: Sends actions to game at 30 Hz motor frequency
- **Adaptive Control**: Routes between reactive (fast) and deliberative (slow) modes

## Implementation Steps

**Step 1: Frame Capture and Perception Pipeline**

Capture game frames and feed them to vision-language model.

```python
import cv2
import numpy as np
from collections import deque
import time

class PerceptionPipeline:
    """
    Handles frame capture and perception preprocessing.
    """

    def __init__(self, game_process, perception_freq: int = 5,
                 frame_size: Tuple[int, int] = (1280, 720)):
        """
        Args:
            game_process: Handle to running game process
            perception_freq: Perception frequency (Hz), typically 5
            frame_size: Resolution to capture at
        """
        self.game = game_process
        self.perception_freq = perception_freq
        self.frame_size = frame_size
        self.perception_interval = 1.0 / perception_freq

        # Frame buffer for temporal context
        self.frame_buffer = deque(maxlen=4)
        self.last_perception_time = 0

    def capture_frame(self) -> np.ndarray:
        """
        Capture game screenshot.

        Returns:
            frame: RGB image [H, W, 3]
        """
        # Capture window (use Windows API or PyGetWindow)
        screenshot = self.game.get_screenshot()
        frame = cv2.resize(screenshot, self.frame_size)
        return frame

    def get_perception_observation(self) -> Dict[str, Any]:
        """
        Retrieve frame at perception frequency.

        Returns:
            observation: {frame, history, timestamp}
        """
        current_time = time.time()

        # Check if enough time has passed
        if current_time - self.last_perception_time < self.perception_interval:
            return None  # Too soon; skip

        # Capture new frame
        frame = self.capture_frame()
        self.frame_buffer.append(frame)
        self.last_perception_time = current_time

        return {
            'current_frame': frame,
            'frame_history': list(self.frame_buffer),
            'timestamp': current_time
        }
```

**Step 2: Adaptive Reasoning Module**

Decide when to invoke chain-of-thought reasoning vs. reactive response.

```python
class AdaptiveReasoningModule:
    """
    Routes between fast reactive actions and slow reasoning-based actions.
    """

    def __init__(self, vlm, reasoning_threshold: float = 0.3):
        """
        Args:
            vlm: Vision-language model (e.g., GPT-4o, Claude)
            reasoning_threshold: Confidence below which triggers reasoning
        """
        self.vlm = vlm
        self.reasoning_threshold = reasoning_threshold

    def analyze_game_state(self, frame: np.ndarray) -> Dict[str, Any]:
        """
        Quick analysis of current game state without full reasoning.

        Args:
            frame: Current game frame

        Returns:
            analysis: {situation, confidence, needs_reasoning}
        """
        # Fast perception-only prompt (no reasoning)
        prompt = f"""Analyze this game screenshot briefly:

What is the player's immediate situation?
- Location/context in 1-2 words
- Immediate threat level (none/low/medium/high)

Keep response brief (under 20 tokens)."""

        response = self.vlm.generate(
            prompt, frame=frame, max_tokens=50,
            temperature=0.0  # Deterministic for speed
        )

        # Parse response
        threat_level = self._parse_threat_level(response)
        confidence = self._estimate_confidence(response)

        needs_reasoning = threat_level == 'high' or confidence < self.reasoning_threshold

        return {
            'situation': response,
            'threat_level': threat_level,
            'confidence': confidence,
            'needs_reasoning': needs_reasoning
        }

    def invoke_reasoning(self, frame: np.ndarray, frame_history: List[np.ndarray],
                        situation: str) -> str:
        """
        Invoke full chain-of-thought reasoning for complex decisions.

        Args:
            frame: Current frame
            frame_history: Recent frame history for context
            situation: Quick situation analysis

        Returns:
            reasoning: Chain-of-thought explanation
        """
        prompt = f"""You are playing a complex 3D action RPG. Current situation:
{situation}

Given the game state in these screenshots, analyze:
1. What is happening in the game world?
2. What are the major threats/opportunities?
3. What is the optimal strategy for the next 5 seconds?
4. What specific controls should be executed?

Provide detailed reasoning."""

        # Use frame history for temporal context
        reasoning = self.vlm.generate(
            prompt,
            frames=frame_history,
            max_tokens=500,
            temperature=0.5
        )

        return reasoning

    def _parse_threat_level(self, response: str) -> str:
        """Extract threat level from response."""
        lower = response.lower()
        if 'high' in lower:
            return 'high'
        elif 'medium' in lower:
            return 'medium'
        elif 'low' in lower:
            return 'low'
        else:
            return 'none'

    def _estimate_confidence(self, response: str) -> float:
        """Estimate model confidence from response uncertainty."""
        # Heuristic: presence of uncertainty words
        uncertain_words = ['maybe', 'unclear', 'hard to tell', 'uncertain']
        uncertainty_count = sum(1 for word in uncertain_words
                               if word in response.lower())
        confidence = max(0.0, 1.0 - 0.2 * uncertainty_count)
        return confidence
```

**Step 3: Action Generation and Execution**

Convert reasoning to keyboard/mouse commands.

```python
import pydirectinput  # For sending inputs to Windows games

class ActionGenerator:
    """
    Converts VLM reasoning to game actions.
    """

    def __init__(self, action_freq: int = 30):
        """
        Args:
            action_freq: Action frequency (Hz), typically 30 (motor frequency)
        """
        self.action_freq = action_freq
        self.action_interval = 1.0 / action_freq
        self.last_action_time = 0

        # Key mapping: action names to keyboard keys
        self.key_map = {
            'move_forward': 'w',
            'move_back': 's',
            'move_left': 'a',
            'move_right': 'd',
            'jump': 'space',
            'crouch': 'ctrl',
            'interact': 'e',
            'attack': 'mouse1',
            'special': 'q',
            'inventory': 'i',
        }

    def parse_reasoning_to_actions(self, reasoning: str) -> List[str]:
        """
        Extract action commands from reasoning text.

        Args:
            reasoning: Chain-of-thought explanation

        Returns:
            actions: List of action codes (e.g., ['move_forward', 'attack'])
        """
        actions = []

        # Simple parsing: look for action mentions in reasoning
        reasoning_lower = reasoning.lower()

        action_keywords = {
            'move forward': 'move_forward',
            'move_forward': 'move_forward',
            'move back': 'move_back',
            'move left': 'move_left',
            'move right': 'move_right',
            'jump': 'jump',
            'crouch': 'crouch',
            'interact': 'interact',
            'attack': 'attack',
            'cast': 'special',
            'inventory': 'inventory',
        }

        for keyword, action in action_keywords.items():
            if keyword in reasoning_lower:
                if action not in actions:
                    actions.append(action)

        return actions if actions else ['wait']

    def execute_actions(self, actions: List[str], duration: float = 0.1):
        """
        Send action commands to game.

        Args:
            actions: List of actions to execute
            duration: How long to hold each action
        """
        current_time = time.time()

        # Throttle to action frequency
        if current_time - self.last_action_time < self.action_interval:
            return

        for action in actions:
            if action == 'wait':
                continue

            key = self.key_map.get(action)
            if key:
                if action.startswith('move_') or action in ['jump', 'crouch']:
                    # Hold movement keys
                    pydirectinput.press(key)
                    time.sleep(duration)
                    pydirectinput.release(key)
                else:
                    # Toggle actions
                    pydirectinput.press(key)
                    time.sleep(0.05)
                    pydirectinput.release(key)

        self.last_action_time = current_time
```

**Step 4: Game Agent Main Loop**

Integrate perception, reasoning, and action generation into agent loop.

```python
class LumineGameAgent:
    """
    Generalist game-playing agent for open-world 3D games.
    """

    def __init__(self, game_process, vlm):
        """
        Args:
            game_process: Handle to running game
            vlm: Vision-language model (GPT-4o, Claude, etc.)
        """
        self.game = game_process
        self.vlm = vlm

        self.perception = PerceptionPipeline(game_process, perception_freq=5)
        self.reasoning = AdaptiveReasoningModule(vlm)
        self.actions = ActionGenerator(action_freq=30)

        self.episode_steps = 0
        self.reasoning_invocations = 0

    def run_episode(self, max_steps: int = 10000):
        """
        Run game episode with agent control.

        Args:
            max_steps: Maximum steps before episode ends
        """
        print("Starting game episode...")
        start_time = time.time()

        for step in range(max_steps):
            # Step 1: Perception (5 Hz)
            perception_obs = self.perception.get_perception_observation()
            if perception_obs is None:
                continue  # Not enough time passed

            frame = perception_obs['current_frame']
            frame_history = perception_obs['frame_history']

            # Step 2: Analyze game state (fast)
            state_analysis = self.reasoning.analyze_game_state(frame)

            # Step 3: Decide on action (adaptive)
            if state_analysis['needs_reasoning']:
                self.reasoning_invocations += 1
                reasoning_output = self.reasoning.invoke_reasoning(
                    frame, frame_history, state_analysis['situation']
                )
            else:
                # Use fast heuristic response
                reasoning_output = state_analysis['situation']

            # Step 4: Generate actions
            action_list = self.actions.parse_reasoning_to_actions(reasoning_output)

            # Step 5: Execute actions (30 Hz)
            self.actions.execute_actions(action_list, duration=0.05)

            self.episode_steps += 1

            # Optional: check for episode termination
            if self._check_episode_complete():
                print(f"Episode complete in {self.episode_steps} steps")
                break

            # Progress report
            if (step + 1) % 100 == 0:
                elapsed = time.time() - start_time
                print(f"Step {step + 1}: {elapsed:.1f}s elapsed, "
                      f"{self.reasoning_invocations} reasonings")

    def _check_episode_complete(self) -> bool:
        """Check if current objective is complete."""
        # Game-specific check: look for completion UI
        # For now, return False (continue playing)
        return False
```

## Practical Guidance

**When to Use Lumine Approach:**
- Complex 3D game environments (VLMs understand physics and strategy)
- Scenarios allowing long-horizon episodes (minutes to hours)
- Games where zero-shot transfer is valuable (no fine-tuning per game)

**When NOT to Use:**
- Fast-paced arcade games (latency from VLM inference problematic)
- Games requiring frame-perfect timing (5 Hz perception insufficient)
- Fully open-ended exploration (no clear objectives)

**Hyperparameters and Configuration:**
- Perception frequency: 5 Hz for most games; increase to 10 Hz for fast-paced games
- Action frequency: 30 Hz (standard game input frequency); match game engine updates
- Reasoning threshold: 0.3-0.5 (trigger reasoning when uncertain)
- Reasoning invocation frequency: ~10% of steps (too frequent = latency, too rare = poor decisions)

**Pitfalls to Avoid:**
1. **Latency sensitivity** - VLM inference takes 2-5 seconds; plan ahead rather than react immediately
2. **Input buffering** - Queue actions during reasoning latency; don't let agent freeze
3. **Frame processing overhead** - Compress/resize frames; avoid loading full resolution
4. **Game-specific skills** - Some games have unique mechanics; agent needs examples or adaptation

---

Reference: https://arxiv.org/abs/2511.08892

