From 5c6b03818881c7661ef942a807b24d1204fbca52 Mon Sep 17 00:00:00 2001 From: Joshua Deville Date: Thu, 9 Jul 2026 22:32:38 -0400 Subject: [PATCH] Complete Phase 2b: block placement/removal, ship-relative snapping, mouse-look, visual polish Place/remove (task #21): all four block types (Hull/Thruster/Power/Storage) now have working place chains on BP_PlayerCharacter (keys 1-4), plus a remove key (X) that raycasts to a targeted block, verifies it via Array_Contains against PlacedBlocks before destroying it (guards against deleting terrain/ship/other actors), and calls RemoveBlockFromArray. Stat aggregation (task #22): rewrote RecalculateStats on BP_ShipPawn to iterate PlacedBlocks with a ForEachLoop, casting each element to its block class and summing bonuses onto base values, instead of reading four fixed Slot_* variables. Multiple blocks of the same type now correctly stack (e.g. two Hull blocks = +100, not capped at one). Also fixed a real bug found during manual playtesting: AddBlockToArray/RemoveBlockFromArray never actually called RecalculateStats (dead-end exec pins), so stats never updated after the initial BeginPlay call regardless of what was placed. Ship-relative placement: raycast hit location is now converted into the ship's local space, clamped to a 600-unit radius, snapped to the 200-unit grid, then converted back to world space. Recomputed every tick from the ship's current transform, so placement always stays near/aligned with the ship instead of landing at arbitrary world coordinates. Also fixed a related bug: the raycast never checked bBlockingHit, so a miss (e.g. aiming at open sky) silently fell back to world origin (0,0,0) -- coincidentally the ship's spawn point -- making blocks appear to "teleport" there and then trail behind once the ship moved. Added bHasValidBuildTarget, gating all four placement branches on an actual hit. Mouse-look: extended the UnrealMCPython plugin itself (hot-patched via Live Coding) with a new "InputAxisKey" node type wrapping UK2Node_InputAxisKeyEvent::Initialize(FKey), since the existing "InputKey" node type only supports press/release button semantics, not continuous axis values. Wired Mouse X/Y to AddControllerYawInput/AddControllerPitchInput (Y inverted), switched bUseControllerRotationYaw/Pitch back on, and repurposed the old A/D actor-rotation logic into A/D strafe via AddMovementInput(RightVector). Also added mouse-wheel zoom on the ship's CameraBoom (TargetArmLength, clamped 300-2000). Visual polish: ship mesh was a single scaled Cone; added a cylinder fuselage and two wing panels for a recognizable ship silhouette. Added distinct colored materials per block type (Hull/Thruster/Power/Storage) plus a translucent ghost-preview material, replacing the default gray checker look. Added a SkyAtmosphere actor to fix the "will render black" sky light warning that made the level unreadable. Co-Authored-By: Claude Sonnet 5 --- Content/Blueprints/BP_PlayerCharacter.uasset | 4 ++-- Content/Blueprints/BP_ShipPawn.uasset | 4 ++-- Content/Maps/L_TestFlight.umap | 4 ++-- Content/Materials/M_Block_Hull.uasset | 3 +++ Content/Materials/M_Block_Power.uasset | 3 +++ Content/Materials/M_Block_Storage.uasset | 3 +++ Content/Materials/M_Block_Thruster.uasset | 3 +++ Content/Materials/M_Ghost_Preview.uasset | 3 +++ Content/Materials/M_Ship_Hull.uasset | 3 +++ .../UnrealMCPython/Private/MCPythonHelper.cpp | 19 ++++++++++++++++++- 10 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 Content/Materials/M_Block_Hull.uasset create mode 100644 Content/Materials/M_Block_Power.uasset create mode 100644 Content/Materials/M_Block_Storage.uasset create mode 100644 Content/Materials/M_Block_Thruster.uasset create mode 100644 Content/Materials/M_Ghost_Preview.uasset create mode 100644 Content/Materials/M_Ship_Hull.uasset diff --git a/Content/Blueprints/BP_PlayerCharacter.uasset b/Content/Blueprints/BP_PlayerCharacter.uasset index 3fb7dc3..88d2ceb 100644 --- a/Content/Blueprints/BP_PlayerCharacter.uasset +++ b/Content/Blueprints/BP_PlayerCharacter.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:afa8c7d45ec17ce5561ee97fbdb1cbd580887503188333841d6d69462eba84be -size 217853 +oid sha256:a37d49802cbe7efa204da973444a7038fb6c1cdcf60e633f443a6dc462d6772b +size 434928 diff --git a/Content/Blueprints/BP_ShipPawn.uasset b/Content/Blueprints/BP_ShipPawn.uasset index c9a5734..3305d17 100644 --- a/Content/Blueprints/BP_ShipPawn.uasset +++ b/Content/Blueprints/BP_ShipPawn.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d9cf111284cfa21fde9d5645c4c53e07ebe63339dc577b437b4907a28f61c251 -size 330185 +oid sha256:2350e63ceae7de303840628858475e548d4faac738874d7355aa58ec97fc910f +size 400892 diff --git a/Content/Maps/L_TestFlight.umap b/Content/Maps/L_TestFlight.umap index 256ab29..b8ebd98 100644 --- a/Content/Maps/L_TestFlight.umap +++ b/Content/Maps/L_TestFlight.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5625baac3e1e6936272d275d391f72f6f07db600332d619889180c4a02170a05 -size 56274 +oid sha256:1d70e6f53bd0dcd74284fca69077b2964d0c3652bd02cb19adc3016ef8dfaad6 +size 60191 diff --git a/Content/Materials/M_Block_Hull.uasset b/Content/Materials/M_Block_Hull.uasset new file mode 100644 index 0000000..5c67133 --- /dev/null +++ b/Content/Materials/M_Block_Hull.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bea55fae75ec60e94223995c053bc2d017e5e5875f46642c3733e26d5a1ab7a5 +size 9163 diff --git a/Content/Materials/M_Block_Power.uasset b/Content/Materials/M_Block_Power.uasset new file mode 100644 index 0000000..0d80839 --- /dev/null +++ b/Content/Materials/M_Block_Power.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:14a0c9d368c079fe987a77ef6dac3d89ba4c4c0fe493c28bbc8b09f026c383ab +size 9454 diff --git a/Content/Materials/M_Block_Storage.uasset b/Content/Materials/M_Block_Storage.uasset new file mode 100644 index 0000000..0869072 --- /dev/null +++ b/Content/Materials/M_Block_Storage.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ef5503a11e936ab4acc39e6c597dcaf3663a07956487e14d3fcd2b6012af3200 +size 9475 diff --git a/Content/Materials/M_Block_Thruster.uasset b/Content/Materials/M_Block_Thruster.uasset new file mode 100644 index 0000000..285c806 --- /dev/null +++ b/Content/Materials/M_Block_Thruster.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ccc903e894b2b9e9f8fc9acdac8098946487fbb5d2d7177948df6fd6f4296d01 +size 9484 diff --git a/Content/Materials/M_Ghost_Preview.uasset b/Content/Materials/M_Ghost_Preview.uasset new file mode 100644 index 0000000..4209fa4 --- /dev/null +++ b/Content/Materials/M_Ghost_Preview.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aaa90a06cb03e0a29bbad66bc5ecf06132622b650137edf5216206ad4942a0ff +size 9894 diff --git a/Content/Materials/M_Ship_Hull.uasset b/Content/Materials/M_Ship_Hull.uasset new file mode 100644 index 0000000..088911a --- /dev/null +++ b/Content/Materials/M_Ship_Hull.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:54847a7078d3be73af230a68f6623fb5bee79843fd8e240a49e9e064a455f303 +size 10111 diff --git a/Plugins/UnrealMCPython/Source/UnrealMCPython/Private/MCPythonHelper.cpp b/Plugins/UnrealMCPython/Source/UnrealMCPython/Private/MCPythonHelper.cpp index 6bb59a8..6b767b9 100644 --- a/Plugins/UnrealMCPython/Source/UnrealMCPython/Private/MCPythonHelper.cpp +++ b/Plugins/UnrealMCPython/Source/UnrealMCPython/Private/MCPythonHelper.cpp @@ -50,6 +50,7 @@ #include "K2Node_MacroInstance.h" #include "K2Node_DynamicCast.h" #include "K2Node_InputKey.h" +#include "K2Node_InputAxisKeyEvent.h" #include "K2Node_SpawnActorFromClass.h" #include "K2Node_CallArrayFunction.h" #include "EdGraphSchema_K2.h" @@ -660,6 +661,22 @@ static UEdGraphNode* CreateBPNodeFromJson(UEdGraph* Graph, UBlueprint* Blueprint Creator.Finalize(); NewNode = KeyNode; } + else if (NodeType == TEXT("InputAxisKey")) + { + FString KeyName; + if (!NodeJson->TryGetStringField(TEXT("key_name"), KeyName)) + { + OutError = TEXT("InputAxisKey node missing 'key_name'."); + return nullptr; + } + FGraphNodeCreator Creator(*Graph); + UK2Node_InputAxisKeyEvent* AxisKeyNode = Creator.CreateNode(false); + AxisKeyNode->Initialize(FKey(*KeyName)); + AxisKeyNode->NodePosX = PosX; + AxisKeyNode->NodePosY = PosY; + Creator.Finalize(); + NewNode = AxisKeyNode; + } else if (NodeType == TEXT("SpawnActor")) { FGraphNodeCreator Creator(*Graph); @@ -678,7 +695,7 @@ static UEdGraphNode* CreateBPNodeFromJson(UEdGraph* Graph, UBlueprint* Blueprint } else { - OutError = FString::Printf(TEXT("Unknown node type '%s'. Supported: CallFunction, Event, CustomEvent, CastTo, Branch, Sequence, VariableGet, VariableSet, MacroInstance, InputKey, SpawnActor."), *NodeType); + OutError = FString::Printf(TEXT("Unknown node type '%s'. Supported: CallFunction, Event, CustomEvent, CastTo, Branch, Sequence, VariableGet, VariableSet, MacroInstance, InputKey, InputAxisKey, SpawnActor."), *NodeType); return nullptr; }