bump, would really help for AI sight, shotgun like abilities, or to trace for collision like a strong gust blowing a player away coming out of a small hole
FYI, most shotgun traces are done with scattered line traces, much like how the projectiles would really move.
Anyway, I finally decided to build a crappy cone trace. Should be performant enough if you don’t abuse it.
Just replace my custom trace method in the middle there with the default Unreal Multi Sphere Trace.
Ok, image was shrinked, try this blueprint pastebin linky.
For those interested, all I did was cut the path into segments, trace a small size in the first segment, then calculate the next segment’s size with basic trigonometry and trace at that size, and so on and so forth using a bigger sphere every time till we reach the end.
Keep the is Multitrace boolean false if you only want to find the first hit object and like to optimise stuff.
Still having this issue, but we dont really need a cone shape, we just need to be able to increase the sphere radius over time.
For anyone who comes across this, I have a tutorial here for performing efficient cone-shaped traces.
is there a blueprint version of this?
You can generate a cone shaped mesh using a ProceduralMeshComponent and use the OnComponentBeginOverlap delegate.
.h
#pragma once
#include "CoreMinimal.h"
#include "ProceduralMeshComponent.h"
#include "ProceduralConeComponent.generated.h"
USTRUCT(BlueprintType)
struct FConeDimensions
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cone Config", meta = (ExposeOnSpawn=true))
float ConeHeight = 100.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cone Config", meta = (ExposeOnSpawn=true, ClampMin = "0.0", ClampMax = "89.0", UIMin = "0.0", UIMax = "89.0"))
float ConeHalfAngle = 30.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Cone Config", meta = (ExposeOnSpawn=true))
int32 Segments = 16;
};
UCLASS(ClassGroup="Collision", Blueprintable, editinlinenew, meta=(DisplayName="Procedural Cone", BlueprintSpawnableComponent))
class MODULENAME_API UProceduralConeComponent : public UProceduralMeshComponent
{
GENERATED_BODY()
public:
UProceduralConeComponent(const FObjectInitializer& ObjectInitializer);
UFUNCTION()
void GenerateCone();
UFUNCTION(BlueprintCallable, Category = "Cone Config")
void SetConeDimensions(FConeDimensions InDimensions);
#if WITH_EDITOR
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif
protected:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Cone Config", meta = (ExposeOnSpawn=true))
FConeDimensions ConeDimensions;
virtual void OnComponentCreated() override;
};
.cpp
#include "ActorComponents/ProceduralConeComponent.h"
#include "KismetProceduralMeshLibrary.h"
UProceduralConeComponent::UProceduralConeComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
PrimaryComponentTick.bCanEverTick = false;
SetCollisionEnabled(ECollisionEnabled::QueryOnly);
SetCollisionObjectType(ECollisionChannel::ECC_WorldDynamic);
SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
CastShadow = false;
bUseAsyncCooking = true;
bUseComplexAsSimpleCollision = false;
/*BodyInstance.bUseCCD = true;
BodyInstance.SetUseMACD(true);
SetAllUseCCD(true);
SetAllUseMACD(true);*/
}
void UProceduralConeComponent::OnComponentCreated()
{
Super::OnComponentCreated();
GenerateCone();
}
void UProceduralConeComponent::GenerateCone()
{
ConeDimensions.ConeHalfAngle = FMath::Clamp(ConeDimensions.ConeHalfAngle, 0.f, 89.f);
TArray<FVector> Vertices;
TArray<int32> Triangles;
TArray<FVector> Normals;
TArray<FVector2D> UVs;
TArray<FColor> Colors;
TArray<FProcMeshTangent> Tangents;
const float BaseRadius = ConeDimensions.ConeHeight * FMath::Tan(FMath::DegreesToRadians(ConeDimensions.ConeHalfAngle));
// Apex [0] (origin)
const int32 ApexIndex = 0;
Vertices.Add(FVector::ZeroVector);
// Base ring [1 .. Segments+1]
for (int32 i = 0; i <= ConeDimensions.Segments; i++)
{
const float Angle = 2.f * PI * i / ConeDimensions.Segments;
Vertices.Add(FVector(ConeDimensions.ConeHeight, FMath::Cos(Angle) * BaseRadius, FMath::Sin(Angle) * BaseRadius));
}
// Base center [Segments+2]
const int32 BaseCenterIndex = Vertices.Num();
Vertices.Add(FVector(ConeDimensions.ConeHeight, 0.f, 0.f));
auto RingIndex = [](int32 i) { return i + 1; };
for (int32 i = 0; i < ConeDimensions.Segments; i++)
{
const int32 Curr = RingIndex(i);
const int32 Next = RingIndex(i + 1);
// Cone surface
Triangles.Add(ApexIndex);
Triangles.Add(Curr);
Triangles.Add(Next);
// Base cap
Triangles.Add(BaseCenterIndex);
Triangles.Add(Next);
Triangles.Add(Curr);
}
UKismetProceduralMeshLibrary::CalculateTangentsForMesh(Vertices, Triangles, UVs, Normals, Tangents);
ClearMeshSection(0);
CreateMeshSection(0, Vertices, Triangles, Normals, UVs, Colors, Tangents, true);
ClearCollisionConvexMeshes();
AddCollisionConvexMesh(Vertices);
}
void UProceduralConeComponent::SetConeDimensions(FConeDimensions InDimensions)
{
ConeDimensions = InDimensions;
GenerateCone();
}
#if WITH_EDITOR
void UProceduralConeComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
const FName PropertyName = PropertyChangedEvent.MemberProperty
? PropertyChangedEvent.MemberProperty->GetFName()
: NAME_None;
if (PropertyName == GET_MEMBER_NAME_CHECKED(UProceduralConeComponent, ConeDimensions))
{
GenerateCone();
}
}
#endif
