1. Project Configuration
Ensure your project structure includes the Core DLL and the Godot Adapter layer.
1
2
3
4
5
6
| your_project/
├── /
│ ├── .csproj
│ └── bin/
│ └── .Core.dll # The Pure Logic Core
│ └── .Godot.dll # The Godot Adapter Layer
|
2. Scene Setup
2.1 Composition Root
Add a GPUserScopeRoot node to your main scene. This node handles service registration and input routing.
1
2
3
4
5
6
7
8
9
10
11
12
| // In your main scene or autoload
public partial class MainScene : Node
{
[Export] public GPUserScopeRoot UserScopeRoot { get; set; }
public override void _Ready()
{
// The GPUserScopeRoot automatically configures services
// Access the registry for dependency injection
var registry = UserScopeRoot.Registry;
}
}
|
Create a CoreInputActionSettingsResource and assign it to the GPUserScopeRoot.
1
2
3
4
5
6
7
| // Configure input actions
var inputSettings = new CoreInputActionSettingsResource();
inputSettings.PlaceAction = "ui_accept";
inputSettings.CancelAction = "ui_cancel";
inputSettings.RotateAction = "ui_rotate";
UserScopeRoot.ActionSettings = inputSettings;
|
2.3 Service Registration
The GPUserScopeRoot automatically registers core services:
IPlacementService - Placement business logicITargetingService - Grid targeting and validationIIndicatorService - Visual feedback and previewsIEventBus - Domain event publishing
3. Basic Gameplay Integration (C#)
3.1 Placement Workflow
Use the PlacementWorkflow for placement operations:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
| using GridPlacement.Core.Workflows;
using GridPlacement.Core.Types;
using GridPlacement.Core.Sessions;
public partial class PlayerController : Node
{
private ServiceRegistry _registry;
private PlacementWorkflow _placementWorkflow;
private TargetingWorkflow _targetingWorkflow;
public override void _Ready()
{
// Get the service registry from the composition root
var userScopeRoot = GetNode<GPUserScopeRoot>("/root/GPUserScopeRoot");
_registry = userScopeRoot.Registry;
// Resolve workflows from the registry
_placementWorkflow = _registry.GetService<PlacementWorkflow>();
_targetingWorkflow = _registry.GetService<TargetingWorkflow>();
}
public void StartPlacement(string placeableId)
{
// Create a placeable (normally loaded from configuration)
var placeable = new Placeable
{
Id = placeableId,
Name = "Building Block",
Description = "A basic building block",
Position = new CoreVector2I(0, 0)
};
// Start the placement session
var result = _placementWorkflow.StartSession(placeableId, placeable);
// Handle the orchestrator output (visual effects, UI updates, etc.)
ProcessOrchestratorOutput(result);
}
public void UpdatePlacement(Vector2 mousePosition)
{
if (!_placementWorkflow.Session.IsActive)
return;
// Convert mouse position to grid coordinates
var gridPos = ConvertToGridCoordinates(mousePosition);
var targetingSnapshot = new TargetingSnapshot2D
{
GridPosition = gridPos,
IsValid = true
};
// Update the placement workflow
var result = _placementWorkflow.Update(targetingSnapshot);
ProcessOrchestratorOutput(result);
}
public void RotatePlacement()
{
if (_placementWorkflow.Session.IsActive)
{
var result = _placementWorkflow.Rotate();
ProcessOrchestratorOutput(result);
}
}
public void ConfirmPlacement()
{
if (_placementWorkflow.Session.IsActive)
{
var result = _placementWorkflow.Confirm();
ProcessOrchestratorOutput(result);
}
}
private void ProcessOrchestratorOutput(OrchestratorOutput output)
{
// Apply visual effects, update UI, handle events
// This is where you would:
// - Update preview meshes
// - Show/hide placement indicators
// - Play sounds or animations
// - Update UI state
}
}
|
Connect to the input service for reactive input handling:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| using GridPlacement.Core.Systems.Input;
public partial class InputController : Node
{
private CoreInputService _inputService;
private PlayerController _playerController;
public override void _Ready()
{
var userScopeRoot = GetNode<GPUserScopeRoot>("/root/GPUserScopeRoot");
var registry = userScopeRoot.Registry;
_inputService = registry.GetService<CoreInputService>();
_playerController = GetNode<PlayerController>("../PlayerController");
// Subscribe to input events
_inputService.PlaceActionTriggered += OnPlaceAction;
_inputService.CancelActionTriggered += OnCancelAction;
_inputService.RotateActionTriggered += OnRotateAction;
}
private void OnPlaceAction()
{
if (_playerController.IsPlacementActive)
{
_playerController.ConfirmPlacement();
}
else
{
_playerController.StartPlacement("basic_building");
}
}
private void OnCancelAction()
{
_playerController.CancelPlacement();
}
private void OnRotateAction()
{
_playerController.RotatePlacement();
}
}
|
4. Reactive UI (GDScript)
Connect to the adapter events for live UI updates:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| extends Control
# Reference to the placement adapter
var placement_adapter: PlacementAdapter
func _ready():
# Get the placement adapter from the service registry
var user_scope_root = get_node("/root/GPUserScopeRoot")
var registry = user_scope_root.get_registry()
placement_adapter = registry.get_service("PlacementAdapter")
# Connect to adapter events for live updates
placement_adapter.output_ready.connect(_on_placement_updated)
func _on_placement_updated(output: OrchestratorOutput):
# Update UI based on placement state
var session = placement_adapter.orchestrator.session
if session.is_active:
$PlacementPanel.show()
$CoordinateLabel.text = "Position: " + str(session.last_snapshot.grid_position)
$ValidityLabel.text = "Valid: " + str(session.last_snapshot.is_valid)
else:
$PlacementPanel.hide()
func _on_build_button_pressed():
# Start placement with a specific placeable
placement_adapter.start_session("basic_building", load("res://resources/basic_building.tres"))
func _on_rotate_button_pressed():
# Rotate the current placement
placement_adapter.rotate()
func _on_cancel_button_pressed():
# Cancel the current placement
placement_adapter.cancel_session()
|
5. Configuration and Data
5.1 Placeable Configuration
Create placeable resources for your game objects:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // Example: Create a placeable from a configuration
public Placeable CreatePlaceableFromConfig(string configPath)
{
var config = ResourceLoader.Load<PlaceableConfig>(configPath);
return new Placeable
{
Id = config.Id,
Name = config.Name,
Description = config.Description,
Size = config.Size,
Properties = config.Properties
};
}
|
5.2 Grid Configuration
Configure grid properties for your game:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| using GridPlacement.Core.State.Grid;
public void ConfigureGrid()
{
var gridState = _registry.GetService<IGridState>();
// Set grid dimensions
gridState.SetGridSize(new CoreVector2I(50, 50));
// Configure cell size (in world units)
gridState.SetCellSize(new CoreVector2(1.0f, 1.0f));
// Set grid origin
gridState.SetOrigin(new CoreVector2(0, 0));
}
|
6. Common Patterns
6.1 Session Management
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| public class PlacementManager : Node
{
private PlacementWorkflow _workflow;
public bool IsPlacementActive => _workflow.Session.IsActive;
public Placeable CurrentPlaceable => _workflow.Session.Placeable;
public CoreVector2I CurrentPosition => _workflow.Session.LastSnapshot.GridPosition;
public void CancelPlacement()
{
if (IsPlacementActive)
{
_workflow.Cancel();
}
}
}
|
6.2 Event-Driven Updates
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| public class UIController : Node
{
public override void _Ready()
{
var eventBus = _registry.GetService<IEventBus>();
eventBus.Subscribe<PlaceableSelectedEvent>(OnPlaceableSelected);
eventBus.Subscribe<PlacementCompletedEvent>(OnPlacementCompleted);
}
private void OnPlaceableSelected(PlaceableSelectedEvent evt)
{
UpdateUIForPlacement(evt.Placeable);
}
private void OnPlacementCompleted(PlacementCompletedEvent evt)
{
ShowPlacementSuccess(evt.Position);
}
}
|
Next Steps
Troubleshooting
Q: Services not found in registry
- Ensure
GPUserScopeRoot is properly initialized before accessing services - Check that the Core DLL is loaded and Godot adapter is properly configured
Q: Placement workflow not responding
- Verify that a placement session is active before calling
Update() or Confirm() - Check that the targeting snapshot contains valid grid coordinates
Q: UI not updating
- Connect to adapter events instead of polling for state changes
- Use the
OrchestratorOutput to drive visual updates and UI state changes