Gloo Blob Visuals
I wanted to recreate the visual effect for the gloo as well which has the core aspects
Shape: Gelatinous blob shape
Color: Fades from dark to light (drying effect)
Size: Grows in size from initial spawn
Splatter: A 'splatter' effect on surfaces
Shape
I don't have much 3D modelling skill (I leave that to the professionals) but I was able to get a reasonably 'gelatinous blob shape' in blender and import it into the project

Gloo Size
It looks organic if the Gloo grows a bit when it hits something like its almost "alive", and in the intrest of not looking the same everytime we'll use a random range of sizes so there's a bit of diversity.
The logic for size (and color to come) will be done completely in blueprint by making use if Timelines, but its good practice to define the range as editable properties so we can change them during testing
.h
// GlooBlob.h
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Visuals")
float GlooSizeMin;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Visuals")
float GlooSizeMax;
As well as a private member which we will cache the final Gloo Size in (needed for later)
// GlooBlob.h
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
float GlooSize;
Then the blueprint logic will be to choose a random number from within the range, cache the value in our private member GlooSize, and scale the Static Mesh based off the timeline
UE Timelines are a useful tool that allow you to map values based off time

Gloo Color
First I made a simple material which has two base colors that are lerped by a variable ColorLerp

Then in Blueprint I added to the BeginPlay() sequence using another Timeline to set the value for ColorLerp which starts the color a dark then lightens over time giving it a "drying effect"


Splatter
The last visual effect is a splatter, since the Gloo is wet when fired, it makes sense for some of it to splatter on contact.
We'll use the same strategy as before and setup some blueprint editable properties defining our Splatter.
.h
// GlooBlob.h
void AddSplatter(const FHitResult& Hit);
UPROPERTY(EditDefaultsOnly, Category="Visuals")
UMaterial* SplatterDecal;
UPROPERTY(EditDefaultsOnly, Category="Visuals")
float SplatterDepth;
UPROPERTY(EditDefaultsOnly, Category="Visuals")
float SplatterSizeMin;
UPROPERTY(EditDefaultsOnly, Category="Visuals")
float SplatterSizeMax;
I mentioned needing to cache the GlooSize which would come in handy later, and now it finally comes in hand for determining the right Splatter size.
The reason being, if we just simply do the same random size selection for the splatter given a range we will very quickly get cases where the Gloo is dwarfed by the size of the Splatter, or the Splatter is all together hidden by the size of the Gloo

Therefor we want the Splatter size to be relative to the size of the Gloo, while adding some random rotation still for visual diversity.
This is achieved by just range mapping from the GlooSize's range to the Splatter's range which keeps both sizes consistent relative to one another
Map X in [A,B] to [C,D] -> (X - A) / B - A) * (D - C) + C
.cpp
// GlooBlob.cpp
void AGlooBlob::AddSplatter(const FHitResult& Hit)
{
const FVector SplatterLocation= Hit.ImpactPoint;
FRotator SplatterRotation = Hit.ImpactNormal.Rotation();
SplatterRotation.Roll = FMath::FRandRange(-180.f, 180.f);
const float SplatterSize = ((GlooSize - GlooSizeMin) / (GlooSizeMax - GlooSizeMin)) * (SplatterSizeMax - SplatterSizeMin) + SplatterSizeMin;
const FVector SplatterBounds = FVector(SplatterDepth, SplatterSize, SplatterSize);
UDecalComponent* DecalComponent = UGameplayStatics::SpawnDecalAttached(SplatterDecal, SplatterBounds, Hit.GetComponent(), NAME_None, SplatterLocation, SplatterRotation, EAttachLocation::KeepWorldPosition);
if (DecalComponent)
{
DecalComponent->SetFadeIn(0.25f, 0.4f);
}
}
Then we add a call to create a Splatter in Projectile passing in the HitResult so we know where to spawn it.
.cpp
// GlooCannonProjectile
void AGlooCannonProjectile::CreateGlooBlob(AActor* OtherActor, UPrimitiveComponent* OtherComp, const FHitResult& Hit)
{
//...
GlooBlob->AddSplatter(Hit);
//...
}
Lastly we want to give the Splatter the same "drying effect" as the Gloo.
I created a texture from an image online and hooked it up to a new material very similar to the Gloo Blob color material, only here the texture sample is what determines where the color is painted.

.h
// GlooBlob.h
UFUNCTION(BlueprintImplementableEvent)
void ApplySplatterColorEffect(const UDecalComponent* SceneComponent);
and add a call to the new Blueprint function after we make the decal
.cpp
// GlooBloo.cpp
UDecalComponent* DecalComponent = UGameplayStatics::SpawnDecalAttached(SplatterDecal, SplatterBounds, Hit.GetComponent(), NAME_None, SplatterLocation, SplatterRotation, EAttachLocation::KeepWorldPosition);
if (DecalComponent)
{
//...
ApplySplatterColorEffect(DecalComponent);
}
finally all we have to do is hook up the new Blueprint function within the GlooBlob_BP


Last updated