Skip to main content
Glama

Unreal Engine MCP Bridge

by gingerol
create_simple_game.py26.2 kB
#!/usr/bin/env python """ Example script to create a simple game using MCP. This script demonstrates how to use the Unreal MCP to create a simple game with: - A player character that can move around - Collectible items that disappear when the player touches them - A score counter displayed on screen """ import logging import sys import os import time # Add the parent directory to the path so we can import the unreal_mcp_server module sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from unreal_mcp_server import get_unreal_connection # Configure logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.StreamHandler(sys.stdout) ] ) logger = logging.getLogger("SimpleGameExample") def create_simple_game(): """Create a simple game with a player character, collectibles, and score. This function creates: 1. A player character Blueprint with movement controls 2. A collectible item Blueprint 3. A HUD with score display 4. Input mappings for player movement 5. A GameMode to tie everything together Returns: The response from the final operation, or None if there was an error. """ # Get the connection to Unreal Engine conn = get_unreal_connection() if not conn: logger.error("Failed to connect to Unreal Engine") return None # Step 1: Create a new level logger.info("Creating a new level") response = conn.send_command("create_level", { "level_name": "SimpleGameLevel", "save_path": "/Game/Levels" }) if response.get("status") == "error": logger.error(f"Error creating level: {response.get('error')}") return response # Step 2: Create input mappings logger.info("Creating input mappings") response = conn.send_command("create_input_mapping", { "mapping_name": "IM_SimpleGame", "save_path": "/Game/Input", "axis_mappings": [ { "axis_name": "MoveForward", "key": "W", "scale": 1.0 }, { "axis_name": "MoveForward", "key": "S", "scale": -1.0 }, { "axis_name": "MoveRight", "key": "D", "scale": 1.0 }, { "axis_name": "MoveRight", "key": "A", "scale": -1.0 } ], "action_mappings": [ { "action_name": "Jump", "key": "SpaceBar" } ] }) if response.get("status") == "error": logger.error(f"Error creating input mappings: {response.get('error')}") return response # Step 3: Create the player character Blueprint logger.info("Creating player character Blueprint") response = conn.send_command("create_blueprint", { "name": "BP_SimplePlayer", "parent_class": "Character", "path": "/Game/Blueprints" }) if response.get("status") == "error": logger.error(f"Error creating player Blueprint: {response.get('error')}") return response player_bp_path = "/Game/Blueprints/BP_SimplePlayer" # Step 4: Set up the player character components logger.info("Setting up player character components") # Add a camera component response = conn.send_command("add_component_to_blueprint", { "blueprint_name": player_bp_path, "component_type": "CameraComponent", "component_name": "PlayerCamera", "location": [0, 0, 50], # Position the camera above the character "rotation": [0, 0, 0], "parent_component": "CapsuleComponent" }) if response.get("status") == "error": logger.error(f"Error adding camera component: {response.get('error')}") return response # Set the camera as the view target response = conn.send_command("set_component_property", { "blueprint_name": player_bp_path, "component_name": "PlayerCamera", "property_name": "bUsePawnControlRotation", "property_value": True }) if response.get("status") == "error": logger.error(f"Error setting camera property: {response.get('error')}") return response # Step 5: Add a score variable to the player logger.info("Adding score variable to player") response = conn.send_command("add_blueprint_variable", { "blueprint_name": player_bp_path, "variable_name": "Score", "variable_type": "Integer", "default_value": 0, "is_exposed": True, "category": "Gameplay", "tooltip": "Player's current score" }) if response.get("status") == "error": logger.error(f"Error adding score variable: {response.get('error')}") return response # Step 6: Set up player movement input logger.info("Setting up player movement input") # Add MoveForward input action response = conn.send_command("add_blueprint_input_action_node", { "blueprint_name": player_bp_path, "input_name": "MoveForward", "is_action": False # This is an axis mapping }) if response.get("status") == "error": logger.error(f"Error adding MoveForward input: {response.get('error')}") return response move_forward_node_id = response.get("node_id") # Add Add Movement Input function response = conn.send_command("add_blueprint_function_node", { "blueprint_name": player_bp_path, "function_name": "AddMovementInput", "target": "self" }) if response.get("status") == "error": logger.error(f"Error adding AddMovementInput function: {response.get('error')}") return response add_movement_forward_node_id = response.get("node_id") # Add Get Forward Vector function response = conn.send_command("add_blueprint_function_node", { "blueprint_name": player_bp_path, "function_name": "GetActorForwardVector", "target": "self" }) if response.get("status") == "error": logger.error(f"Error adding GetActorForwardVector function: {response.get('error')}") return response get_forward_vector_node_id = response.get("node_id") # Connect MoveForward to AddMovementInput response = conn.send_command("connect_blueprint_nodes", { "blueprint_name": player_bp_path, "source_node_id": move_forward_node_id, "source_pin": "OutputDelegate", "target_node_id": add_movement_forward_node_id, "target_pin": "execute" }) if response.get("status") == "error": logger.error(f"Error connecting nodes: {response.get('error')}") return response # Connect MoveForward value to AddMovementInput ScaleValue response = conn.send_command("connect_blueprint_nodes", { "blueprint_name": player_bp_path, "source_node_id": move_forward_node_id, "source_pin": "AxisValue", "target_node_id": add_movement_forward_node_id, "target_pin": "ScaleValue" }) if response.get("status") == "error": logger.error(f"Error connecting nodes: {response.get('error')}") return response # Connect GetForwardVector to AddMovementInput WorldDirection response = conn.send_command("connect_blueprint_nodes", { "blueprint_name": player_bp_path, "source_node_id": get_forward_vector_node_id, "source_pin": "ReturnValue", "target_node_id": add_movement_forward_node_id, "target_pin": "WorldDirection" }) if response.get("status") == "error": logger.error(f"Error connecting nodes: {response.get('error')}") return response # Repeat for MoveRight input response = conn.send_command("add_blueprint_input_action_node", { "blueprint_name": player_bp_path, "input_name": "MoveRight", "is_action": False # This is an axis mapping }) if response.get("status") == "error": logger.error(f"Error adding MoveRight input: {response.get('error')}") return response move_right_node_id = response.get("node_id") # Add Add Movement Input function for right movement response = conn.send_command("add_blueprint_function_node", { "blueprint_name": player_bp_path, "function_name": "AddMovementInput", "target": "self" }) if response.get("status") == "error": logger.error(f"Error adding AddMovementInput function: {response.get('error')}") return response add_movement_right_node_id = response.get("node_id") # Add Get Right Vector function response = conn.send_command("add_blueprint_function_node", { "blueprint_name": player_bp_path, "function_name": "GetActorRightVector", "target": "self" }) if response.get("status") == "error": logger.error(f"Error adding GetActorRightVector function: {response.get('error')}") return response get_right_vector_node_id = response.get("node_id") # Connect MoveRight to AddMovementInput response = conn.send_command("connect_blueprint_nodes", { "blueprint_name": player_bp_path, "source_node_id": move_right_node_id, "source_pin": "OutputDelegate", "target_node_id": add_movement_right_node_id, "target_pin": "execute" }) if response.get("status") == "error": logger.error(f"Error connecting nodes: {response.get('error')}") return response # Connect MoveRight value to AddMovementInput ScaleValue response = conn.send_command("connect_blueprint_nodes", { "blueprint_name": player_bp_path, "source_node_id": move_right_node_id, "source_pin": "AxisValue", "target_node_id": add_movement_right_node_id, "target_pin": "ScaleValue" }) if response.get("status") == "error": logger.error(f"Error connecting nodes: {response.get('error')}") return response # Connect GetRightVector to AddMovementInput WorldDirection response = conn.send_command("connect_blueprint_nodes", { "blueprint_name": player_bp_path, "source_node_id": get_right_vector_node_id, "source_pin": "ReturnValue", "target_node_id": add_movement_right_node_id, "target_pin": "WorldDirection" }) if response.get("status") == "error": logger.error(f"Error connecting nodes: {response.get('error')}") return response # Step 7: Create the collectible item Blueprint logger.info("Creating collectible item Blueprint") response = conn.send_command("create_blueprint", { "name": "BP_Collectible", "parent_class": "Actor", "path": "/Game/Blueprints" }) if response.get("status") == "error": logger.error(f"Error creating collectible Blueprint: {response.get('error')}") return response collectible_bp_path = "/Game/Blueprints/BP_Collectible" # Add a static mesh component to the collectible response = conn.send_command("add_component_to_blueprint", { "blueprint_name": collectible_bp_path, "component_type": "StaticMeshComponent", "component_name": "CollectibleMesh", "location": [0, 0, 0], "rotation": [0, 0, 0], "scale": [0.5, 0.5, 0.5] # Make it smaller than default }) if response.get("status") == "error": logger.error(f"Error adding static mesh component: {response.get('error')}") return response # Set the static mesh to a sphere response = conn.send_command("set_static_mesh_properties", { "blueprint_name": collectible_bp_path, "component_name": "CollectibleMesh", "static_mesh": "/Engine/BasicShapes/Sphere.Sphere", "materials": ["/Engine/BasicShapes/BasicShapeMaterial.BasicShapeMaterial"] }) if response.get("status") == "error": logger.error(f"Error setting static mesh: {response.get('error')}") return response # Add a sphere collision component response = conn.send_command("add_component_to_blueprint", { "blueprint_name": collectible_bp_path, "component_type": "SphereComponent", "component_name": "CollisionSphere", "location": [0, 0, 0], "rotation": [0, 0, 0], "scale": [1, 1, 1], "parent_component": "CollectibleMesh" }) if response.get("status") == "error": logger.error(f"Error adding collision component: {response.get('error')}") return response # Set the collision sphere radius response = conn.send_command("set_component_property", { "blueprint_name": collectible_bp_path, "component_name": "CollisionSphere", "property_name": "SphereRadius", "property_value": 50.0 }) if response.get("status") == "error": logger.error(f"Error setting collision sphere radius: {response.get('error')}") return response # Add OnComponentBeginOverlap event response = conn.send_command("add_blueprint_event_node", { "blueprint_name": collectible_bp_path, "event_type": "ComponentBeginOverlap", "component_name": "CollisionSphere" }) if response.get("status") == "error": logger.error(f"Error adding overlap event: {response.get('error')}") return response overlap_node_id = response.get("node_id") # Add Cast to BP_SimplePlayer node response = conn.send_command("add_blueprint_function_node", { "blueprint_name": collectible_bp_path, "function_name": "Cast", "category": "Utilities|Casting", "target_type": "BP_SimplePlayer" }) if response.get("status") == "error": logger.error(f"Error adding cast node: {response.get('error')}") return response cast_node_id = response.get("node_id") # Connect overlap event to cast node response = conn.send_command("connect_blueprint_nodes", { "blueprint_name": collectible_bp_path, "source_node_id": overlap_node_id, "source_pin": "OutputDelegate", "target_node_id": cast_node_id, "target_pin": "execute" }) if response.get("status") == "error": logger.error(f"Error connecting nodes: {response.get('error')}") return response # Connect Other Actor to cast node response = conn.send_command("connect_blueprint_nodes", { "blueprint_name": collectible_bp_path, "source_node_id": overlap_node_id, "source_pin": "OtherActor", "target_node_id": cast_node_id, "target_pin": "Object" }) if response.get("status") == "error": logger.error(f"Error connecting nodes: {response.get('error')}") return response # Add Branch node (to check if cast succeeded) response = conn.send_command("add_blueprint_function_node", { "blueprint_name": collectible_bp_path, "function_name": "Branch", "category": "Flow Control" }) if response.get("status") == "error": logger.error(f"Error adding branch node: {response.get('error')}") return response branch_node_id = response.get("node_id") # Connect cast node to branch response = conn.send_command("connect_blueprint_nodes", { "blueprint_name": collectible_bp_path, "source_node_id": cast_node_id, "source_pin": "then", "target_node_id": branch_node_id, "target_pin": "execute" }) if response.get("status") == "error": logger.error(f"Error connecting nodes: {response.get('error')}") return response # Connect cast success to branch condition response = conn.send_command("connect_blueprint_nodes", { "blueprint_name": collectible_bp_path, "source_node_id": cast_node_id, "source_pin": "CastSucceeded", "target_node_id": branch_node_id, "target_pin": "Condition" }) if response.get("status") == "error": logger.error(f"Error connecting nodes: {response.get('error')}") return response # Add Get Score node response = conn.send_command("add_blueprint_get_variable_node", { "blueprint_name": collectible_bp_path, "variable_name": "Score", "target": "BP_SimplePlayer" }) if response.get("status") == "error": logger.error(f"Error adding Get Score node: {response.get('error')}") return response get_score_node_id = response.get("node_id") # Add integer + integer node response = conn.send_command("add_blueprint_function_node", { "blueprint_name": collectible_bp_path, "function_name": "Add_IntInt", "category": "Math|Integer", "inputs": { "B": 1 # Add 1 to the score } }) if response.get("status") == "error": logger.error(f"Error adding Add_IntInt node: {response.get('error')}") return response add_score_node_id = response.get("node_id") # Connect Get Score to Add response = conn.send_command("connect_blueprint_nodes", { "blueprint_name": collectible_bp_path, "source_node_id": get_score_node_id, "source_pin": "ReturnValue", "target_node_id": add_score_node_id, "target_pin": "A" }) if response.get("status") == "error": logger.error(f"Error connecting nodes: {response.get('error')}") return response # Add Set Score node response = conn.send_command("add_blueprint_set_variable_node", { "blueprint_name": collectible_bp_path, "variable_name": "Score", "target": "BP_SimplePlayer" }) if response.get("status") == "error": logger.error(f"Error adding Set Score node: {response.get('error')}") return response set_score_node_id = response.get("node_id") # Connect Branch True to Set Score response = conn.send_command("connect_blueprint_nodes", { "blueprint_name": collectible_bp_path, "source_node_id": branch_node_id, "source_pin": "True", "target_node_id": set_score_node_id, "target_pin": "execute" }) if response.get("status") == "error": logger.error(f"Error connecting nodes: {response.get('error')}") return response # Connect Add Score to Set Score response = conn.send_command("connect_blueprint_nodes", { "blueprint_name": collectible_bp_path, "source_node_id": add_score_node_id, "source_pin": "ReturnValue", "target_node_id": set_score_node_id, "target_pin": "Value" }) if response.get("status") == "error": logger.error(f"Error connecting nodes: {response.get('error')}") return response # Connect Cast result to Set Score target response = conn.send_command("connect_blueprint_nodes", { "blueprint_name": collectible_bp_path, "source_node_id": cast_node_id, "source_pin": "As BP Simple Player", "target_node_id": set_score_node_id, "target_pin": "Target" }) if response.get("status") == "error": logger.error(f"Error connecting nodes: {response.get('error')}") return response # Add Destroy Actor node response = conn.send_command("add_blueprint_function_node", { "blueprint_name": collectible_bp_path, "function_name": "DestroyActor", "target": "self" }) if response.get("status") == "error": logger.error(f"Error adding DestroyActor node: {response.get('error')}") return response destroy_node_id = response.get("node_id") # Connect Set Score to Destroy Actor response = conn.send_command("connect_blueprint_nodes", { "blueprint_name": collectible_bp_path, "source_node_id": set_score_node_id, "source_pin": "then", "target_node_id": destroy_node_id, "target_pin": "execute" }) if response.get("status") == "error": logger.error(f"Error connecting nodes: {response.get('error')}") return response # Step 8: Create a HUD Widget Blueprint logger.info("Creating HUD Widget Blueprint") response = conn.send_command("create_umg_widget_blueprint", { "widget_name": "WBP_GameHUD", "save_path": "/Game/UI" }) if response.get("status") == "error": logger.error(f"Error creating HUD widget: {response.get('error')}") return response hud_widget_path = "/Game/UI/WBP_GameHUD" # Add a Text Block for the score response = conn.send_command("add_umg_text_block", { "widget_blueprint": hud_widget_path, "text_block_name": "ScoreText", "text": "Score: 0", "position": [50, 50], "size": [200, 50], "color": [1, 1, 1, 1], # White "font_size": 24 }) if response.get("status") == "error": logger.error(f"Error adding text block: {response.get('error')}") return response # Step 9: Create a GameMode Blueprint logger.info("Creating GameMode Blueprint") response = conn.send_command("create_gamemode_blueprint", { "gamemode_name": "BP_SimpleGameMode", "save_path": "/Game/Blueprints", "default_pawn_class": "/Game/Blueprints/BP_SimplePlayer", "hud_class": "/Game/UI/WBP_GameHUD" }) if response.get("status") == "error": logger.error(f"Error creating GameMode: {response.get('error')}") return response # Step 10: Set the default GameMode logger.info("Setting default GameMode") response = conn.send_command("set_default_gamemode", { "gamemode_class": "/Game/Blueprints/BP_SimpleGameMode" }) if response.get("status") == "error": logger.error(f"Error setting default GameMode: {response.get('error')}") return response # Step 11: Spawn some collectibles in the level logger.info("Spawning collectibles in the level") for i in range(10): # Calculate a position in a circle angle = i * 36 # 360 degrees / 10 items radius = 500 x = radius * math.cos(math.radians(angle)) y = radius * math.sin(math.radians(angle)) response = conn.send_command("spawn_blueprint_actor", { "blueprint_path": collectible_bp_path, "actor_name": f"Collectible_{i}", "location": [x, y, 100], # Positioned in a circle "rotation": [0, 0, 0], "scale": [1, 1, 1] }) if response.get("status") == "error": logger.error(f"Error spawning collectible: {response.get('error')}") return response # Step 12: Add a floor to the level logger.info("Adding a floor to the level") response = conn.send_command("spawn_actor", { "actor_name": "Floor", "actor_type": "StaticMeshActor", "location": [0, 0, 0], "rotation": [0, 0, 0], "scale": [10, 10, 0.1] # Make it wide and flat }) if response.get("status") == "error": logger.error(f"Error spawning floor: {response.get('error')}") return response floor_actor_name = response.get("actor_name") # Set the floor's static mesh to a cube response = conn.send_command("set_actor_property", { "actor_name": floor_actor_name, "property_name": "StaticMeshComponent.StaticMesh", "property_value": "/Engine/BasicShapes/Cube.Cube" }) if response.get("status") == "error": logger.error(f"Error setting floor mesh: {response.get('error')}") return response # Step 13: Spawn the player character logger.info("Spawning player character") response = conn.send_command("spawn_blueprint_actor", { "blueprint_path": player_bp_path, "actor_name": "Player", "location": [0, 0, 200], # Start above the floor "rotation": [0, 0, 0], "scale": [1, 1, 1] }) if response.get("status") == "error": logger.error(f"Error spawning player: {response.get('error')}") return response # Step 14: Save the level logger.info("Saving the level") response = conn.send_command("save_level", { "level_path": "/Game/Levels/SimpleGameLevel" }) if response.get("status") == "error": logger.error(f"Error saving level: {response.get('error')}") return response # Step 15: Start Play-in-Editor mode logger.info("Starting Play-in-Editor mode") response = conn.send_command("start_play_in_editor", {}) if response.get("status") == "error": logger.error(f"Error starting Play-in-Editor: {response.get('error')}") return response logger.info("Successfully created a simple game!") return response if __name__ == "__main__": # Create the simple game result = create_simple_game() if result and result.get("status") != "error": print("Successfully created a simple game!") print("The game is now running in Play-in-Editor mode.") print("Use WASD to move the character and collect the spheres.") # Keep the script running for a while to allow playing print("Press Ctrl+C to exit...") try: time.sleep(300) # Run for 5 minutes except KeyboardInterrupt: # Stop Play-in-Editor mode conn = get_unreal_connection() if conn: conn.send_command("stop_play_in_editor", {}) print("Exiting...") else: print("Failed to create the game. Make sure Unreal Engine is running.")

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/gingerol/UnrealEngine-ai-mcp'

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