mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-04-14 05:08:58 +08:00
5.5 KiB
5.5 KiB
Unreal Bridge Example
This file describes a minimal Unreal-side bridge for clawgo.
Goal:
- render the same world as web
- keep
clawgoauthoritative - let Unreal submit player actions and god-view edits
Recommended Modules
Create three Unreal-side layers:
UClawgoWorldClient
- owns HTTP and WebSocket connections
- fetches
/api/world - subscribes to
/api/runtime - sends
world_player_action - sends
world_entity_update
AClawgoWorldManager
- stores the latest normalized world snapshot
- maps locations / NPCs / entities / rooms to spawned actors
- applies interpolation
UClawgoAssetResolver
- maps logical model keys to Unreal assets
- example:
npc.main->/Game/World/Characters/BP_Mainentity.table->/Game/World/Furniture/BP_Tableroom.task->/Game/World/Rooms/BP_TaskRoom
Recommended UE Data Structures
Use simple USTRUCTs mirroring the web/client contract.
USTRUCT(BlueprintType)
struct FClawgoPlacement
{
GENERATED_BODY()
UPROPERTY(BlueprintReadOnly) FString Model;
UPROPERTY(BlueprintReadOnly) FVector Scale = FVector(1.0, 1.0, 1.0);
UPROPERTY(BlueprintReadOnly) FVector RotationEuler = FVector::ZeroVector;
UPROPERTY(BlueprintReadOnly) FVector Offset = FVector::ZeroVector;
};
USTRUCT(BlueprintType)
struct FClawgoLocation
{
GENERATED_BODY()
UPROPERTY(BlueprintReadOnly) FString Id;
UPROPERTY(BlueprintReadOnly) FString Name;
UPROPERTY(BlueprintReadOnly) FString Description;
UPROPERTY(BlueprintReadOnly) TArray<FString> Neighbors;
};
USTRUCT(BlueprintType)
struct FClawgoEntity
{
GENERATED_BODY()
UPROPERTY(BlueprintReadOnly) FString Id;
UPROPERTY(BlueprintReadOnly) FString Name;
UPROPERTY(BlueprintReadOnly) FString Type;
UPROPERTY(BlueprintReadOnly) FString LocationId;
UPROPERTY(BlueprintReadOnly) FClawgoPlacement Placement;
UPROPERTY(BlueprintReadOnly) TMap<FString, FString> StateText;
};
USTRUCT(BlueprintType)
struct FClawgoNPCState
{
GENERATED_BODY()
UPROPERTY(BlueprintReadOnly) FString NpcId;
UPROPERTY(BlueprintReadOnly) FString CurrentLocation;
UPROPERTY(BlueprintReadOnly) FString CurrentRoomId;
UPROPERTY(BlueprintReadOnly) FString Mood;
UPROPERTY(BlueprintReadOnly) FString Status;
};
USTRUCT(BlueprintType)
struct FClawgoRoom
{
GENERATED_BODY()
UPROPERTY(BlueprintReadOnly) FString Id;
UPROPERTY(BlueprintReadOnly) FString Name;
UPROPERTY(BlueprintReadOnly) FString LocationId;
UPROPERTY(BlueprintReadOnly) FString Status;
UPROPERTY(BlueprintReadOnly) FString TaskSummary;
UPROPERTY(BlueprintReadOnly) TArray<FString> AssignedNpcIds;
};
World Snapshot Normalization
On Unreal side, normalize the payload exactly once.
Recommended normalized snapshot:
struct FClawgoWorldSnapshot
{
FString WorldId;
int64 Tick = 0;
TMap<FString, FClawgoLocation> Locations;
TMap<FString, FClawgoEntity> Entities;
TMap<FString, FClawgoNPCState> NPCStates;
TMap<FString, TArray<FString>> Occupancy;
TMap<FString, TArray<FString>> EntityOccupancy;
TMap<FString, TArray<FString>> RoomOccupancy;
TMap<FString, FClawgoRoom> Rooms;
};
Spawn Rules
Recommended actor ownership:
- location ->
AClawgoLocationAnchor - entity ->
AClawgoEntityActor - npc ->
AClawgoNPCActor - room ->
AClawgoRoomActor
Spawn keys:
- location actor key:
location:<id> - entity actor key:
entity:<id> - npc actor key:
npc:<id> - room actor key:
room:<id>
Placement Rules
Final transform should be resolved from:
- location anchor transform
- plus
entity.placement.offset - plus yaw from
entity.placement.rotation - plus scale from
entity.placement.scale
Recommended interpretation:
rotation[1]is yaw in radians- convert radians to Unreal degrees before applying
Sync Loop
Recommended runtime behavior:
- On boot:
- fetch
/api/world - build initial actors
- On websocket snapshot:
- if
tickunchanged, ignore - if
tickadvanced:- update normalized snapshot
- diff actors
- move actors by interpolation
- If websocket disconnects:
- keep a low-frequency HTTP refresh fallback
Action Writeback
Player action example
{
"action": "world_player_action",
"player_action": "move",
"location_id": "square"
}
God-view entity move example
{
"action": "world_entity_update",
"id": "bench",
"location_id": "commons",
"model": "entity.table",
"rotation_y": 1.57,
"scale": [1.2, 1.2, 1.2],
"offset": [0.8, 0, -0.4]
}
NPC Animation Mapping
Recommended mapping:
status == "working"->Interact- recent
npc_speakevent ->Talk - location changed since last tick ->
Walk - otherwise ->
Idle
Room Rendering
Rooms should not replace the open world.
Recommended behavior:
- world view shows room portals / room pods
- selecting a room opens its isolated sub-scene
- NPCs assigned to the room appear inside while still remaining world-owned
Unreal Asset Resolver
Keep a simple table or UDataAsset.
Example:
TMap<FString, TSoftClassPtr<AActor>> ActorBlueprints;
TMap<FString, TSoftObjectPtr<UStaticMesh>> StaticMeshes;
TMap<FString, TSoftObjectPtr<USkeletalMesh>> SkeletalMeshes;
Logical keys should stay aligned with web:
npc.mainnpc.baseentity.tableentity.chairentity.crateroom.tasklocation.plaza
Deployment Advice
Do not make Unreal the world server.
Keep:
clawgoas authority- Unreal as client
- web as client
This avoids:
- divergent NPC logic
- duplicated quest logic
- different room assignment behavior across clients