So I’m aware that all this water stuff is still experimental, even here in UE 5.5.1, but let’s see where this leads.
In its current experimental form, a lot of this stuff (PCG, Water Bodies) seems almost actively hostile to terrains generated at runtime. In particular, I’m not using a Landscape as my landform, because so far as I can tell their fundamental geometry/heightmap isn’t something that can be created or altered at runtime. So my base landform is a procedural mesh, into which I’m attempting to create water bodies (initially, a river, I’ll eventually need lakes and ocean).
First off, I verified that this is even theoretically possible:
- Create a new Third-person project (note that the default map is NOT Landscape-based).
- Turn on all the water plugins and restart.
- Drag in a Water Body River (which also implicitly creates a Water Zone).
And there it is, in all its glory. It doesn’t make physical sense, basically being the “water” part of a river floating in the air, but it clearly works. So I’m not entirely crazy to believe I might be able to do this myself.
And I’m close, but I’m hitting a couple significant snags.
I’m trying to create a WaterBodyRiver actor at runtime, as part of a large, tiled, entirely runtime-generated landscape. Since I don’t get the automatic channel-cutting that rivers and the like would give you in the editor, I determine the river’s spline path ahead of time, and manually carve a channel in my terrain for it to “drop into.”
Then I instantiate the actor itself:
riverBody = GetWorld()->SpawnActor<AWaterBodyRiver>(RiverBodyActorClass, GetActorLocation(), FRotator(0.0f, 0.0f, 0.0f));
UWaterSplineComponent* spline = Cast<UWaterSplineComponent>(riverBody->GetDefaultSubobjectByName(FName("WaterSpline")));
if (!spline)
{
UE_LOG(LogTemp, Error, TEXT("Could not find spline subcomponent"));
}
else
{
spline->ClearSplinePoints();
spline->AddPoints(RiverSplinePoints);
}
waterZone = GetWorld()->SpawnActor<AWaterZone>(AWaterZone::StaticClass(), GetActorLocation(), FRotator(0.0f, 0.0f, 0.0f));
waterZone->SetZoneExtent(FVector2D(PatchSize * 200.0f, PatchSize * 200.0f));
(“RiverBodyActorClass” is a blueprint subclass of AWaterBodyRiver so that I can set up some additional debugging and/or have more control, but the code works exactly the same if you replace it with AWaterBodyRiver::StaticClass().)
At runtime, the SpawnActor() call for the River body itself will produce some warnings:
UEDPIE_0_RunelandsMap.RunelandsMap:PersistentLevel.BP_WaterBodyRiver_C_0.SplineMeshComponent_0’ but Mobility is Static.
PIE: Warning: Calling SetStaticMesh on ‘/Game/Maps/UEDPIE_0_RunelandsMap.RunelandsMap:PersistentLevel.BP_WaterBodyRiver_C_0.SplineMeshComponent_1’ but Mobility is Static.
PIE: Warning: Calling SetStaticMesh on ‘/Game/Maps/UEDPIE_0_RunelandsMap.RunelandsMap:PersistentLevel.BP_WaterBodyRiver_C_0.SplineMeshComponent_0’ but Mobility is Static.
PIE: Warning: Calling SetStaticMesh on ‘/Game/Maps/UEDPIE_0_RunelandsMap.RunelandsMap:PersistentLevel.BP_WaterBodyRiver_C_0.SplineMeshComponent_1’ but Mobility is Static.
PIE: Warning: Calling SetStaticMesh on ‘/Game/Maps/UEDPIE_0_RunelandsMap.RunelandsMap:PersistentLevel.BP_WaterBodyRiver_C_0.SplineMeshComponent_0’ but Mobility is Static.
PIE: Warning: Calling SetStaticMesh on ‘/Game/Maps/UEDPIE_0_RunelandsMap.RunelandsMap:PersistentLevel.BP_WaterBodyRiver_C_0.SplineMeshComponent_1’ but Mobility is Static.
Those warnings appear whether or not I actually try to set the location, everything in the actor class is already set to “Movable,” and the Spline Mesh components are created by the actor itself during its initialization, so I have no ability to access them, anyway.
Using ninja-level skills developed over 40 years of software development, I ignored those warnings.
Anyway, back to the code. A few seconds into gameplay (because the water zone apparently needs a little time to make meshes), I try to hook everything up:
UWaterBodyRiverComponent* rivComp = Cast<UWaterBodyRiverComponent>(
riverBody->GetDefaultSubobjectByName(FName("WaterBodyRiverComponent")));
if (!rivComp)
{
rivComp->SetMobility(EComponentMobility::Movable);
rivComp->SetWaterZoneOverride(waterZone);
}
And…nothing happens, except sometimes another couple of those warnings.
But at this point, if I pause the game in the editor, and make basically any change at all to the WaterBodyRiver actor’s details (e.g. move it’s location up a centimeter, or even just change the “Player Can Step Up On” collision setting), suddenly we get a glorious–albeit geographically improbable–river!
Attempts to modify those same parameters from code have no effect, however. It has to be done manually in the editor; something about that process triggers it to build itself.
So I guess there are two questions:
- Any way to get the spawned water body actor not to complain about being static?
- More importantly – how do I trigger the river to actually appear, from code?.
At this point I’m not too worried about physics or post-process effects (although the latter appears to work fine once the river is present; I can drop underwater and see the effects), just the visual appearance of the river.
I prefer the compactness of C++ for my own code, but if anybody has gotten this working from Blueprints, I’d be happy to see that, too.