Quick answer: The most common cause is that the Water plugin's water mesh actor is not generating correctly at runtime. The WaterMeshActor must exist in the level and be configured with the correct extent and tile size.
Here is how to fix Unreal water body not rendering runtime. You have placed a water body in your Unreal Engine level using the Water plugin, it looks perfect in the editor viewport, and then you package the project or test in a standalone game window and the water is completely invisible. The landscape beneath shows through, the water material is gone, and the surface mesh simply does not render. The Water plugin's rendering pipeline has several runtime requirements that differ from editor preview behavior, and missing any one of them causes the water to vanish.
The Symptom
A water body (Lake, River, Ocean, or Custom) renders correctly in the Unreal Editor viewport but is invisible when running the game as a standalone build or in a packaged project. The water surface is completely absent — you can see the landscape or terrain below where the water should be. There are no rendering errors in the log, no shader compilation warnings, and no indication that anything is wrong.
In some cases, the water appears briefly on startup and then disappears, or it renders at the wrong height. In other cases, the water renders as a solid black or white surface instead of with its intended material. You might also see the water render in PIE (Play In Editor) mode but not in a standalone game launch from the same editor session. These variants point to different stages of the water rendering pipeline failing.
What Causes This
1. The WaterMeshActor is missing or misconfigured. The Water plugin uses a dedicated AWaterMeshActor to generate the tessellated surface mesh for water bodies. This actor must exist in the level and its extent must be large enough to cover all water bodies. If the WaterMeshActor is missing (it is normally auto-created but can be accidentally deleted), no water surface mesh is generated at runtime. In the editor, a preview mesh is shown regardless, masking the problem.
2. Water body type does not generate a mesh automatically. WaterBodyCustom does not use the plugin's automatic mesh generation system. It requires you to provide a static mesh for the water surface. If you placed a WaterBodyCustom expecting it to work like WaterBodyLake, the water surface will be missing because no mesh was assigned. WaterBodyLake, WaterBodyRiver, and WaterBodyOcean all generate their meshes through the WaterMeshActor system.
3. The water material is not compatible with the target rendering path. The default water materials use features like single-layer water rendering, scene depth sampling, and custom translucency passes. If your project settings disable these features, or if you are targeting a platform or rendering level that does not support them (like mobile ES3.1), the water material fails to render and produces either an invisible surface or a solid color fallback.
4. The Water plugin is not packaged with the build. If the Water plugin is not explicitly enabled in your .uproject file or if its content is excluded from packaging, the water system's runtime components are missing. The water bodies exist as actors but the rendering infrastructure is absent, resulting in invisible water.
The Fix
Step 1: Verify the WaterMeshActor exists and covers your water bodies. Check your level for the presence of a AWaterMeshActor. If one does not exist, the water surface mesh will not be generated. Here is a diagnostic component you can attach to any actor to validate the water setup at runtime:
// WaterDiagnosticComponent.h
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "WaterDiagnosticComponent.generated.h"
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class UWaterDiagnosticComponent : public UActorComponent
{
GENERATED_BODY()
public:
virtual void BeginPlay() override;
UFUNCTION(BlueprintCallable, Category = "Water Debug")
void DiagnoseWaterSetup();
UFUNCTION(BlueprintCallable, Category = "Water Debug")
void ValidateWaterBodies();
};
// WaterDiagnosticComponent.cpp
#include "WaterDiagnosticComponent.h"
#include "WaterBodyActor.h"
#include "WaterMeshActor.h"
#include "WaterSubsystem.h"
#include "EngineUtils.h"
#include "Kismet/GameplayStatics.h"
void UWaterDiagnosticComponent::BeginPlay()
{
Super::BeginPlay();
DiagnoseWaterSetup();
}
void UWaterDiagnosticComponent::DiagnoseWaterSetup()
{
UWorld* World = GetWorld();
if (!World) return;
// Check for WaterMeshActor
AWaterMeshActor* WaterMesh = nullptr;
for (TActorIterator<AWaterMeshActor>
It(World); It; ++It)
{
WaterMesh = *It;
break;
}
if (!WaterMesh)
{
UE_LOG(LogTemp, Error,
TEXT("WATER: No WaterMeshActor in level! "
"Water surfaces will not render."));
return;
}
UE_LOG(LogTemp, Log,
TEXT("WATER: WaterMeshActor found: %s"),
*WaterMesh->GetName());
// Check for Water Subsystem
UWaterSubsystem* WaterSub =
World->GetSubsystem<UWaterSubsystem>();
if (!WaterSub)
{
UE_LOG(LogTemp, Error,
TEXT("WATER: WaterSubsystem not found! "
"Is the Water plugin enabled?"));
}
else
{
UE_LOG(LogTemp, Log,
TEXT("WATER: WaterSubsystem active"));
}
ValidateWaterBodies();
}
void UWaterDiagnosticComponent::ValidateWaterBodies()
{
UWorld* World = GetWorld();
if (!World) return;
int32 BodyCount = 0;
for (TActorIterator<AWaterBody>
It(World); It; ++It)
{
AWaterBody* WB = *It;
BodyCount++;
UWaterBodyComponent* WBComp =
WB->GetWaterBodyComponent();
if (!WBComp)
{
UE_LOG(LogTemp, Error,
TEXT("WATER: %s has no WaterBodyComponent"),
*WB->GetName());
continue;
}
// Check if water body has a valid material
UMaterialInterface* WaterMat =
WBComp->GetWaterMaterial();
if (!WaterMat)
{
UE_LOG(LogTemp, Warning,
TEXT("WATER: %s has no water material"),
*WB->GetName());
}
else
{
UE_LOG(LogTemp, Log,
TEXT("WATER: %s material: %s"),
*WB->GetName(),
*WaterMat->GetName());
}
UE_LOG(LogTemp, Log,
TEXT("WATER: %s type=%d visible=%s"),
*WB->GetName(),
(int32)WBComp->GetWaterBodyType(),
WBComp->IsVisible()
? TEXT("YES") : TEXT("NO"));
}
UE_LOG(LogTemp, Log,
TEXT("WATER: Total water bodies: %d"),
BodyCount);
}
Run this diagnostic at startup to immediately identify whether the WaterMeshActor is present. If it reports no WaterMeshActor, add one to your level manually from the Place Actors panel under the Water category. Ensure its extent covers all water bodies in the level.
Step 2: Verify project settings for water rendering. The Water plugin requires certain rendering features to be enabled. Check these settings programmatically or in the Project Settings UI:
// Check render settings relevant to water
void UWaterDiagnosticComponent::CheckRenderSettings()
{
// Single Layer Water rendering must be enabled
IConsoleVariable* SLWVar = IConsoleManager::Get().
FindConsoleVariable(
TEXT("r.Water.SingleLayerWater"));
if (SLWVar)
{
UE_LOG(LogTemp, Log,
TEXT("WATER: SingleLayerWater = %d"),
SLWVar->GetInt());
if (SLWVar->GetInt() == 0)
{
UE_LOG(LogTemp, Error,
TEXT("WATER: SingleLayerWater is OFF! "
"Water will not render correctly."));
}
}
// Check if the water mesh is enabled
IConsoleVariable* WMVar = IConsoleManager::Get().
FindConsoleVariable(
TEXT("r.Water.WaterMesh.Enabled"));
if (WMVar)
{
UE_LOG(LogTemp, Log,
TEXT("WATER: WaterMesh.Enabled = %d"),
WMVar->GetInt());
}
// Verify depth prepass for translucency
IConsoleVariable* DepthVar = IConsoleManager::Get().
FindConsoleVariable(
TEXT("r.DepthOfFieldQuality"));
if (DepthVar)
{
UE_LOG(LogTemp, Log,
TEXT("WATER: DepthOfFieldQuality = %d"),
DepthVar->GetInt());
}
}
Step 3: Ensure the Water plugin is correctly packaged. Verify that both the Water and WaterExtras plugins are enabled in your .uproject file. If you are using the water system's landscape integration features, the LandscapeEditorUtilities module must also be available. In your Build.cs, add "Water" to PublicDependencyModuleNames if you reference any water classes in C++.
Related Issues
If water renders but with no reflections, verify that screen-space reflections or planar reflections are enabled for the water surface. If the water surface flickers or z-fights with the landscape, adjust the water body's height offset or the landscape sculpting depth beneath the water. If rivers render with gaps or discontinuous surfaces, increase the spline point density and verify that the river width is consistent across points.
If you are targeting mobile platforms, be aware that the default water materials use SM5 features. You will need to create simplified water materials for mobile that avoid scene depth reads and complex translucency. The WaterBodyCustom type with a simple translucent material is often the most reliable approach for mobile water rendering.