更新 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 "BloodSystem.h"
#include "Components/BoxComponent.h" #include "NiagaraComponent.h"
#include "NiagaraFunctionLibrary.h"
#include "Components/DecalComponent.h" #include "Components/DecalComponent.h"
#include "Materials/MaterialInterface.h" #include "Kismet/GameplayStatics.h"
#include "Materials/MaterialInstanceDynamic.h" #include "Engine/World.h"
#include "TimerManager.h"
// Sets default values // Sets default values for this component's properties
ABloodPool::ABloodPool() UBloodSystem::UBloodSystem()
{ {
// Set this actor to call Tick() every frame // Set this component to be initialized when the game starts, and to be ticked every frame
PrimaryActorTick.bCanEverTick = false; PrimaryComponentTick.bCanEverTick = true;
// 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);
} }
// Called when the game starts or when spawned // Called when the game starts
void ABloodPool::BeginPlay() void UBloodSystem::BeginPlay()
{ {
Super::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 // Called every frame
UpdatePoolSize(); 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); if (!ActiveBloodEffects[i] || !ActiveBloodEffects[i]->IsActive())
UpdatePoolSize();
}
void ABloodPool::SetPoolColor(const FLinearColor& NewColor)
{ {
PoolColor = NewColor; ActiveBloodEffects.RemoveAt(i);
// Update the material color
if (Decal && Decal->GetMaterial(0))
{
UMaterialInstanceDynamic* DynamicMaterial = Cast<UMaterialInstanceDynamic>(Decal->GetMaterial(0));
if (DynamicMaterial)
{
DynamicMaterial->SetVectorParameterValue(FName("Color"), PoolColor);
} }
} }
} }
void ABloodPool::StartExpansion(float InExpansionRate, float MaxSize) void UBloodSystem::SpawnBloodEffect(const FVector& Location, const FVector& Direction, float Intensity)
{ {
ExpansionRate = InExpansionRate; // Ensure we have a valid blood spray system
MaxPoolSize = MaxSize; if (!BloodSpraySystem)
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)
{ {
return; return;
} }
// Increase the pool size // Manage the number of active blood effects
PoolSize += ExpansionRate * 0.1f; if (ActiveBloodEffects.Num() >= MaxBloodEffects)
// Check if we've reached the maximum size
if (PoolSize >= MaxPoolSize)
{ {
PoolSize = MaxPoolSize; // Remove the oldest blood effect
bIsExpanding = false; if (ActiveBloodEffects[0])
GetWorldTimerManager().ClearTimer(ExpansionTimerHandle); {
ActiveBloodEffects[0]->DestroyComponent();
}
ActiveBloodEffects.RemoveAt(0);
} }
// Update the pool size // Create a rotation from the direction
UpdatePoolSize(); 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;
} }