更新 Source/FLESH/Private/BloodPool.cpp

This commit is contained in:
2025-04-18 18:12:18 +08:00
parent cfc784f4f4
commit 37b715b346

View File

@@ -1,114 +1,166 @@
#include "BloodPool.h"
#include "Components/BoxComponent.h"
#include "BloodSystem.h"
#include "NiagaraComponent.h"
#include "NiagaraFunctionLibrary.h"
#include "Components/DecalComponent.h"
#include "Materials/MaterialInterface.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "TimerManager.h"
#include "Kismet/GameplayStatics.h"
#include "Engine/World.h"
// Sets default values
ABloodPool::ABloodPool()
// Sets default values for this component's properties
UBloodSystem::UBloodSystem()
{
// Set this actor to call Tick() every frame
PrimaryActorTick.bCanEverTick = false;
// Create and set up the collision component
Collision = CreateDefaultSubobject<UBoxComponent>(TEXT("Collision"));
RootComponent = Collision;
Collision->SetCollisionProfileName(TEXT("NoCollision"));
Collision->SetBoxExtent(FVector(1.0f, 100.0f, 100.0f));
// Create and set up the decal component
Decal = CreateDefaultSubobject<UDecalComponent>(TEXT("Decal"));
Decal->SetupAttachment(RootComponent);
Decal->SetRelativeRotation(FRotator(90.0f, 0.0f, 0.0f));
Decal->DecalSize = FVector(10.0f, 100.0f, 100.0f);
// Set this component to be initialized when the game starts, and to be ticked every frame
PrimaryComponentTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void ABloodPool::BeginPlay()
// Called when the game starts
void UBloodSystem::BeginPlay()
{
Super::BeginPlay();
// Set the initial decal material
if (DecalMaterial)
{
UMaterialInstanceDynamic* DynamicMaterial = UMaterialInstanceDynamic::Create(DecalMaterial, this);
if (DynamicMaterial)
{
// Set the color parameter if it exists
DynamicMaterial->SetVectorParameterValue(FName("Color"), PoolColor);
Decal->SetMaterial(0, DynamicMaterial);
}
}
// Update the pool size
UpdatePoolSize();
}
// Called every frame
void UBloodSystem::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
void ABloodPool::SetPoolSize(float NewSize)
// Clean up finished blood effects
for (int32 i = ActiveBloodEffects.Num() - 1; i >= 0; --i)
{
PoolSize = FMath::Max(0.1f, NewSize);
UpdatePoolSize();
}
void ABloodPool::SetPoolColor(const FLinearColor& NewColor)
if (!ActiveBloodEffects[i] || !ActiveBloodEffects[i]->IsActive())
{
PoolColor = NewColor;
// Update the material color
if (Decal && Decal->GetMaterial(0))
{
UMaterialInstanceDynamic* DynamicMaterial = Cast<UMaterialInstanceDynamic>(Decal->GetMaterial(0));
if (DynamicMaterial)
{
DynamicMaterial->SetVectorParameterValue(FName("Color"), PoolColor);
ActiveBloodEffects.RemoveAt(i);
}
}
}
void ABloodPool::StartExpansion(float InExpansionRate, float MaxSize)
void UBloodSystem::SpawnBloodEffect(const FVector& Location, const FVector& Direction, float Intensity)
{
ExpansionRate = InExpansionRate;
MaxPoolSize = MaxSize;
bIsExpanding = true;
// Start the expansion timer
GetWorldTimerManager().SetTimer(ExpansionTimerHandle, this, &ABloodPool::ExpandPool, 0.1f, true);
}
void ABloodPool::UpdatePoolSize()
{
// Update the decal size
if (Decal)
{
Decal->DecalSize = FVector(10.0f, 100.0f * PoolSize, 100.0f * PoolSize);
}
// Update the collision box size
if (Collision)
{
Collision->SetBoxExtent(FVector(1.0f, 100.0f * PoolSize, 100.0f * PoolSize));
}
}
void ABloodPool::ExpandPool()
{
if (!bIsExpanding)
// Ensure we have a valid blood spray system
if (!BloodSpraySystem)
{
return;
}
// Increase the pool size
PoolSize += ExpansionRate * 0.1f;
// Check if we've reached the maximum size
if (PoolSize >= MaxPoolSize)
// Manage the number of active blood effects
if (ActiveBloodEffects.Num() >= MaxBloodEffects)
{
PoolSize = MaxPoolSize;
bIsExpanding = false;
GetWorldTimerManager().ClearTimer(ExpansionTimerHandle);
// Remove the oldest blood effect
if (ActiveBloodEffects[0])
{
ActiveBloodEffects[0]->DestroyComponent();
}
ActiveBloodEffects.RemoveAt(0);
}
// Update the pool size
UpdatePoolSize();
// Create a rotation from the direction
FRotator Rotation = Direction.Rotation();
// Spawn the blood effect
UNiagaraComponent* BloodEffect = UNiagaraFunctionLibrary::SpawnSystemAtLocation(
GetWorld(),
BloodSpraySystem,
Location,
Rotation,
FVector(Intensity),
true,
true,
ENCPoolMethod::AutoRelease
);
if (BloodEffect)
{
// Set the intensity parameter if it exists
BloodEffect->SetFloatParameter(FName("Intensity"), Intensity);
// Add to active blood effects
ActiveBloodEffects.Add(BloodEffect);
}
}
AActor* UBloodSystem::CreateBloodPool(const FVector& Location, float Size)
{
// Ensure we have a valid blood pool class
if (!BloodPoolClass)
{
return nullptr;
}
// Manage the number of active blood pools
if (ActiveBloodPools.Num() >= MaxBloodPools)
{
// Remove the oldest blood pool
if (ActiveBloodPools[0])
{
ActiveBloodPools[0]->Destroy();
}
ActiveBloodPools.RemoveAt(0);
}
// Spawn the blood pool
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = GetOwner();
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
// Trace downward to find the floor
FHitResult HitResult;
FVector TraceStart = Location;
FVector TraceEnd = Location - FVector(0, 0, 200.0f);
FCollisionQueryParams QueryParams;
QueryParams.AddIgnoredActor(GetOwner());
if (GetWorld()->LineTraceSingleByChannel(HitResult, TraceStart, TraceEnd, ECC_Visibility, QueryParams))
{
// Spawn at the hit location
AActor* BloodPool = GetWorld()->SpawnActor<AActor>(BloodPoolClass, HitResult.Location, FRotator::ZeroRotator, SpawnParams);
if (BloodPool)
{
// Set the size of the blood pool
BloodPool->SetActorScale3D(FVector(Size));
// Add to active blood pools
ActiveBloodPools.Add(BloodPool);
return BloodPool;
}
}
return nullptr;
}
UDecalComponent* UBloodSystem::ApplyBloodDecal(UPrimitiveComponent* Component, const FVector& Location, const FVector& Direction, float Size)
{
// Ensure we have a valid blood decal material
if (!BloodDecalMaterial || !Component)
{
return nullptr;
}
// Manage the number of active blood decals
if (ActiveBloodDecals.Num() >= MaxBloodDecals)
{
// Remove the oldest blood decal
if (ActiveBloodDecals[0])
{
ActiveBloodDecals[0]->DestroyComponent();
}
ActiveBloodDecals.RemoveAt(0);
}
// Create a rotation from the direction (opposite to the direction for the decal to face outward)
FRotator Rotation = (-Direction).Rotation();
// Create the decal component
UDecalComponent* DecalComponent = NewObject<UDecalComponent>(Component);
DecalComponent->SetupAttachment(Component);
DecalComponent->SetRelativeLocation(Component->GetComponentTransform().InverseTransformPosition(Location));
DecalComponent->SetRelativeRotation(Component->GetComponentTransform().InverseTransformRotation(Rotation.Quaternion()));
DecalComponent->SetDecalMaterial(BloodDecalMaterial);
DecalComponent->DecalSize = FVector(10.0f, Size * 100.0f, Size * 100.0f);
DecalComponent->RegisterComponent();
// Add to active blood decals
ActiveBloodDecals.Add(DecalComponent);
return DecalComponent;
}