Cascade raycast

Hi,

I would like to code a cascading raycast like this:
Source point = position of the emitting object
I choose 4 directions (I could have chosen a different number)
Four raycasts are generated from this source point to strike a first object at 4 impact points, each of which in turn becomes a source of four raycasts to strike a second object, etc.
So I coded a C++ class called RayEmitter2, whose .h:

//RayEmitter2.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "RayEmitter2.generated.h"


UCLASS()
class ESSAI_API ARayEmitter2 : public AActor
{
	GENERATED_BODY()

public:
	ARayEmitter2();

protected:
	virtual void BeginPlay() override;

public:
	virtual void Tick(float DeltaTime) override;

	UPROPERTY(EditAnywhere, Category = "Ray Emission")
	AActor* EmitterActor;

	UPROPERTY(EditAnywhere, Category = "Ray Emission")
	TArray<FVector> EmissionDirections;

	UPROPERTY(EditAnywhere, Category = "Ray Emission")
	float RayLength = 1000.0f;

	UPROPERTY(EditAnywhere, Category = "Ray Emission")
	int32 MaxGenerations = 3;

	UPROPERTY(EditAnywhere, Category = "Ray Emission")
	FLinearColor LineColor = FLinearColor::Yellow;

	UPROPERTY(EditAnywhere, Category = "Ray Emission")
	float LineThickness = 1.0f;

	UPROPERTY(EditAnywhere, Category = "Ray Emission")
	float RayDisplayDuration = 1.0f; // Ajout de la durée d'affichage

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
	int32 nb_appel = 0;


	// Liste des points d’impact pour chaque génération
	TArray<TArray<FVector>> ImpactGenerations;

protected:
	
	// Gère les rayons pour un seul point source
	TArray<FVector> GenerateRaysFromSinglePoint(const FVector& Source);
};


and whose cpp is:

//RayEmitter2.cpp

#include "RayEmitter2.h"
#include "DrawDebugHelpers.h"
#include "Engine/World.h"

//2 problèmes:
//les points d'impact de la 2nd génération sont les mêmes
//le 2nd objet touché est le même que lEr objet touché

// Constructeur
ARayEmitter2::ARayEmitter2()
{
	PrimaryActorTick.bCanEverTick = true;
}

// BeginPlay
void ARayEmitter2::BeginPlay()
{
	Super::BeginPlay();

	if (!EmitterActor)
	{
		UE_LOG(LogTemp, Warning, TEXT("EmitterActor non défini"));
		return;
	}

	ImpactGenerations.Empty();

	// Génération 0 = EmitterActor (point de départ)
	TArray<FVector> CurrentSources;
	CurrentSources.Add(EmitterActor->GetActorLocation());
	ImpactGenerations.Add(CurrentSources);

	UE_LOG(LogTemp, Warning, TEXT("Démarrage des générations de rayons. MaxGenerations: %d"), MaxGenerations); // Ajout de ce log
	UE_LOG(LogTemp, Warning, TEXT("Résumé des impacts par génération :"));
	for (int32 Gen = 0; Gen < MaxGenerations; ++Gen)
	{
		UE_LOG(LogTemp, Warning, TEXT("Génération %d, Nombre de sources: %d"), Gen, CurrentSources.Num()); // Ajout de ce log

		TArray<FVector> NewImpactsThisGeneration;
		TArray<FVector> NextGenerationSources; // Pour stocker les points d'impact de cette génération
		NewImpactsThisGeneration.Empty();
		NextGenerationSources.Empty();
		for (const FVector& Source : CurrentSources)
		{
			// Émettre des rayons depuis la source actuelle
			TArray<FVector> ImpactsFromSource = GenerateRaysFromSinglePoint(Source);
			NewImpactsThisGeneration.Append(ImpactsFromSource);
			NextGenerationSources.Append(ImpactsFromSource); // Préparer les sources pour la prochaine génération
			/*
			// 🔹 Log des points sources (CurrentSources)
			FString SourcesStr;
			for (const FVector& Src : CurrentSources)
			{
				SourcesStr += FString::Printf(TEXT("%s, "), *Src.ToString());
			}
			UE_LOG(LogTemp, Warning, TEXT("Génération %d - Points source: %s"), Gen, *SourcesStr);

			// 🔹 Log des impacts trouvés
			FString ImpactsStr;
			for (const FVector& Impact : ImpactsFromSource)
			{
				ImpactsStr += FString::Printf(TEXT("%s, "), *Impact.ToString());
			}
			UE_LOG(LogTemp, Warning, TEXT("Génération %d - Points d'impact: %s"), Gen, *ImpactsStr);*/
		}


		if (NewImpactsThisGeneration.Num() == 0)
		{
			UE_LOG(LogTemp, Warning, TEXT("Génération %d: Aucun nouvel impact trouvé. Arrêt."), Gen); // Ajout de ce log
			break;
		}

		ImpactGenerations.Add(NewImpactsThisGeneration);
		CurrentSources = NextGenerationSources; // Les nouveaux impacts deviennent les sources pour la prochaine génération

		UE_LOG(LogTemp, Warning, TEXT("Génération %d: %d nouveaux impacts trouvés."), Gen, NewImpactsThisGeneration.Num()); // Ajout de ce log
	}

	UE_LOG(LogTemp, Warning, TEXT("Fin des générations de rayons.")); // Ajout de ce log
}




// Tick
void ARayEmitter2::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);


}

TArray<FVector> ARayEmitter2::GenerateRaysFromSinglePoint(const FVector& Source)
{
	nb_appel++;
	TArray<FVector> ImpactPoints;
	ImpactPoints.Empty();

	UE_LOG(LogTemp, Warning, TEXT("GenerateRaysFromSinglePoint %d fois appelé depuis: %s"),nb_appel, *Source.ToString()); // Ajout de ce log

	for (const FVector& Direction : EmissionDirections)
	{
		UE_LOG(LogTemp, Warning, TEXT("  Direction: %s"), *Direction.ToString()); // Ajout de ce log
		FVector DirNormalized = Direction.GetSafeNormal();
		FVector End = Source + DirNormalized * RayLength;

		FHitResult Hit;
		FCollisionQueryParams Params;
		Params.AddIgnoredActor(this);
		if (EmitterActor)
			Params.AddIgnoredActor(EmitterActor);

		bool bHit = GetWorld()->LineTraceSingleByChannel(
			Hit,
			Source,
			End,
			ECollisionChannel::ECC_Visibility,
			Params
		);

		if (bHit)
		{
			UE_LOG(LogTemp, Warning, TEXT("    Normale: %s"), *Hit.ImpactNormal.ToString());

			ImpactPoints.Add(Hit.ImpactPoint);//avant
			// Décale légèrement le point d'impact dans la direction de la normale pour éviter de toucher la même surface
			//FVector AdjustedImpactPoint = Hit.ImpactPoint + Hit.ImpactNormal * 1.0f;
		   //ImpactPoints.Add(AdjustedImpactPoint);

			UE_LOG(LogTemp, Warning, TEXT("    Impact à: %s"), *Hit.ImpactPoint.ToString()); // Ajout de ce log
			/*UE_LOG(LogTemp, Warning, TEXT("    Touche: %s à %s"),
				*Hit.GetActor()->GetName(),
				*Hit.ImpactPoint.ToString());*///avant
			UE_LOG(LogTemp, Warning, TEXT("    Touche: %s (ptr: %p) à %s"),
				*Hit.GetActor()->GetName(),
				Hit.GetActor(),
				*Hit.ImpactPoint.ToString());
			// Debug visual
			//le 3ème paramètre de DrawDebugLine est un point(pas une direction)
			//DrawDebugLine(GetWorld(), Source, AdjustedImpactPoint, LineColor.ToFColor(true), false, RayDisplayDuration, 0, LineThickness);
			//DrawDebugPoint(GetWorld(), AdjustedImpactPoint, 8.0f, FColor::Red, false, RayDisplayDuration);
			DrawDebugLine(GetWorld(), Source, Hit.ImpactPoint, LineColor.ToFColor(true), false, RayDisplayDuration, 0, LineThickness);//avant
			DrawDebugPoint(GetWorld(), Hit.ImpactPoint, 8.0f, FColor::Red, false, RayDisplayDuration);//avant
		}
		else
		{
			UE_LOG(LogTemp, Warning, TEXT("    Pas d'impact. Fin du rayon: %s"), *End.ToString()); // Ajout de ce log
			DrawDebugLine(GetWorld(), Source, End, FColor::Red, false, 1.0f, 0, LineThickness);
		}
	}

	UE_LOG(LogTemp, Warning, TEXT("GenerateRaysFromSinglePoint terminé. %d impacts trouvés."), ImpactPoints.Num()); // Ajout de ce log
	return ImpactPoints;
}

Here’s what the output log gives me:
LogTemp: Warning: Démarrage des générations de rayons. MaxGenerations: 2
LogTemp: Warning: Résumé des impacts par génération :
LogTemp: Warning: Génération 0, Nombre de sources: 1
LogTemp: Warning: GenerateRaysFromSinglePoint 1 fois appelé depuis: X=1881.093 Y=2823.205 Z=75.063
LogTemp: Warning: Direction: X=1.000 Y=1.000 Z=1.000
LogTemp: Warning: Normale: X=0.000 Y=0.000 Z=-1.000
LogTemp: Warning: Impact à: X=2075.531 Y=3017.643 Z=269.500
LogTemp: Warning: Touche: StaticMeshActor_UAID_0042C04F8026175D02_1540545753 (ptr: 00000718B7BCD700) à X=2075.531 Y=3017.643 Z=269.500
LogTemp: Warning: Direction: X=1.000 Y=-1.000 Z=1.000
LogTemp: Warning: Normale: X=0.000 Y=0.000 Z=-1.000
LogTemp: Warning: Impact à: X=2075.531 Y=2628.768 Z=269.500
LogTemp: Warning: Touche: StaticMeshActor_UAID_0042C04F8026175D02_1540545753 (ptr: 00000718B7BCD700) à X=2075.531 Y=2628.768 Z=269.500
LogTemp: Warning: Direction: X=-1.000 Y=1.000 Z=1.000
LogTemp: Warning: Normale: X=0.000 Y=0.000 Z=-1.000
LogTemp: Warning: Impact à: X=1686.656 Y=3017.643 Z=269.500
LogTemp: Warning: Touche: StaticMeshActor_UAID_0042C04F8026175D02_1540545753 (ptr: 00000718B7BCD700) à X=1686.656 Y=3017.643 Z=269.500
LogTemp: Warning: Direction: X=-1.000 Y=-1.000 Z=1.000
LogTemp: Warning: Normale: X=0.000 Y=0.000 Z=-1.000
LogTemp: Warning: Impact à: X=1686.656 Y=2628.768 Z=269.500
LogTemp: Warning: Touche: StaticMeshActor_UAID_0042C04F8026175D02_1540545753 (ptr: 00000718B7BCD700) à X=1686.656 Y=2628.768 Z=269.500
LogTemp: Warning: GenerateRaysFromSinglePoint terminé. 4 impacts trouvés.
LogTemp: Warning: Génération 0: 4 nouveaux impacts trouvés.
LogTemp: Warning: Génération 1, Nombre de sources: 4
LogTemp: Warning: GenerateRaysFromSinglePoint 2 fois appelé depuis: X=2075.531 Y=3017.643 Z=269.500
LogTemp: Warning: Direction: X=1.000 Y=1.000 Z=1.000
LogTemp: Warning: Normale: X=-0.577 Y=-0.577 Z=-0.577
LogTemp: Warning: Impact à: X=2075.531 Y=3017.643 Z=269.500
LogTemp: Warning: Touche: StaticMeshActor_UAID_0042C04F8026175D02_1540545753 (ptr: 00000718B7BCD700) à X=2075.531 Y=3017.643 Z=269.500
LogTemp: Warning: Direction: X=1.000 Y=-1.000 Z=1.000
LogTemp: Warning: Normale: X=-0.577 Y=0.577 Z=-0.577
LogTemp: Warning: Impact à: X=2075.531 Y=3017.643 Z=269.500
LogTemp: Warning: Touche: StaticMeshActor_UAID_0042C04F8026175D02_1540545753 (ptr: 00000718B7BCD700) à X=2075.531 Y=3017.643 Z=269.500
LogTemp: Warning: Direction: X=-1.000 Y=1.000 Z=1.000
LogTemp: Warning: Normale: X=0.577 Y=-0.577 Z=-0.577
LogTemp: Warning: Impact à: X=2075.531 Y=3017.643 Z=269.500
LogTemp: Warning: Touche: StaticMeshActor_UAID_0042C04F8026175D02_1540545753 (ptr: 00000718B7BCD700) à X=2075.531 Y=3017.643 Z=269.500
LogTemp: Warning: Direction: X=-1.000 Y=-1.000 Z=1.000
LogTemp: Warning: Normale: X=0.577 Y=0.577 Z=-0.577
LogTemp: Warning: Impact à: X=2075.531 Y=3017.643 Z=269.500
LogTemp: Warning: Touche: StaticMeshActor_UAID_0042C04F8026175D02_1540545753 (ptr: 00000718B7BCD700) à X=2075.531 Y=3017.643 Z=269.500
LogTemp: Warning: GenerateRaysFromSinglePoint terminé. 4 impacts trouvés.
LogTemp: Warning: GenerateRaysFromSinglePoint 3 fois appelé depuis: X=2075.531 Y=2628.768 Z=269.500
LogTemp: Warning: Direction: X=1.000 Y=1.000 Z=1.000
LogTemp: Warning: Normale: X=-0.577 Y=-0.577 Z=-0.577
LogTemp: Warning: Impact à: X=2075.531 Y=2628.768 Z=269.500
LogTemp: Warning: Touche: StaticMeshActor_UAID_0042C04F8026175D02_1540545753 (ptr: 00000718B7BCD700) à X=2075.531 Y=2628.768 Z=269.500
LogTemp: Warning: Direction: X=1.000 Y=-1.000 Z=1.000
LogTemp: Warning: Normale: X=-0.577 Y=0.577 Z=-0.577
LogTemp: Warning: Impact à: X=2075.531 Y=2628.768 Z=269.500
LogTemp: Warning: Touche: StaticMeshActor_UAID_0042C04F8026175D02_1540545753 (ptr: 00000718B7BCD700) à X=2075.531 Y=2628.768 Z=269.500
LogTemp: Warning: Direction: X=-1.000 Y=1.000 Z=1.000
LogTemp: Warning: Normale: X=0.577 Y=-0.577 Z=-0.577
LogTemp: Warning: Impact à: X=2075.531 Y=2628.768 Z=269.500
LogTemp: Warning: Touche: StaticMeshActor_UAID_0042C04F8026175D02_1540545753 (ptr: 00000718B7BCD700) à X=2075.531 Y=2628.768 Z=269.500
LogTemp: Warning: Direction: X=-1.000 Y=-1.000 Z=1.000
LogTemp: Warning: Normale: X=0.577 Y=0.577 Z=-0.577
LogTemp: Warning: Impact à: X=2075.531 Y=2628.768 Z=269.500
LogTemp: Warning: Touche: StaticMeshActor_UAID_0042C04F8026175D02_1540545753 (ptr: 00000718B7BCD700) à X=2075.531 Y=2628.768 Z=269.500
LogTemp: Warning: GenerateRaysFromSinglePoint terminé. 4 impacts trouvés.
LogTemp: Warning: GenerateRaysFromSinglePoint 4 fois appelé depuis: X=1686.656 Y=3017.643 Z=269.500
LogTemp: Warning: Direction: X=1.000 Y=1.000 Z=1.000
LogTemp: Warning: Normale: X=-0.577 Y=-0.577 Z=-0.577
LogTemp: Warning: Impact à: X=1686.656 Y=3017.643 Z=269.500
LogTemp: Warning: Touche: StaticMeshActor_UAID_0042C04F8026175D02_1540545753 (ptr: 00000718B7BCD700) à X=1686.656 Y=3017.643 Z=269.500
LogTemp: Warning: Direction: X=1.000 Y=-1.000 Z=1.000
LogTemp: Warning: Normale: X=-0.577 Y=0.577 Z=-0.577
LogTemp: Warning: Impact à: X=1686.656 Y=3017.643 Z=269.500
LogTemp: Warning: Touche: StaticMeshActor_UAID_0042C04F8026175D02_1540545753 (ptr: 00000718B7BCD700) à X=1686.656 Y=3017.643 Z=269.500
LogTemp: Warning: Direction: X=-1.000 Y=1.000 Z=1.000
LogTemp: Warning: Normale: X=0.577 Y=-0.577 Z=-0.577
LogTemp: Warning: Impact à: X=1686.656 Y=3017.643 Z=269.500
LogTemp: Warning: Touche: StaticMeshActor_UAID_0042C04F8026175D02_1540545753 (ptr: 00000718B7BCD700) à X=1686.656 Y=3017.643 Z=269.500
LogTemp: Warning: Direction: X=-1.000 Y=-1.000 Z=1.000
LogTemp: Warning: Normale: X=0.577 Y=0.577 Z=-0.577
LogTemp: Warning: Impact à: X=1686.656 Y=3017.643 Z=269.500
LogTemp: Warning: Touche: StaticMeshActor_UAID_0042C04F8026175D02_1540545753 (ptr: 00000718B7BCD700) à X=1686.656 Y=3017.643 Z=269.500
LogTemp: Warning: GenerateRaysFromSinglePoint terminé. 4 impacts trouvés.
LogTemp: Warning: GenerateRaysFromSinglePoint 5 fois appelé depuis: X=1686.656 Y=2628.768 Z=269.500
LogTemp: Warning: Direction: X=1.000 Y=1.000 Z=1.000
LogTemp: Warning: Normale: X=-0.577 Y=-0.577 Z=-0.577
LogTemp: Warning: Impact à: X=1686.656 Y=2628.768 Z=269.500
LogTemp: Warning: Touche: StaticMeshActor_UAID_0042C04F8026175D02_1540545753 (ptr: 00000718B7BCD700) à X=1686.656 Y=2628.768 Z=269.500
LogTemp: Warning: Direction: X=1.000 Y=-1.000 Z=1.000
LogTemp: Warning: Normale: X=-0.577 Y=0.577 Z=-0.577
LogTemp: Warning: Impact à: X=1686.656 Y=2628.768 Z=269.500
LogTemp: Warning: Touche: StaticMeshActor_UAID_0042C04F8026175D02_1540545753 (ptr: 00000718B7BCD700) à X=1686.656 Y=2628.768 Z=269.500
LogTemp: Warning: Direction: X=-1.000 Y=1.000 Z=1.000
LogTemp: Warning: Normale: X=0.577 Y=-0.577 Z=-0.577
LogTemp: Warning: Impact à: X=1686.656 Y=2628.768 Z=269.500
LogTemp: Warning: Touche: StaticMeshActor_UAID_0042C04F8026175D02_1540545753 (ptr: 00000718B7BCD700) à X=1686.656 Y=2628.768 Z=269.500
LogTemp: Warning: Direction: X=-1.000 Y=-1.000 Z=1.000
LogTemp: Warning: Normale: X=0.577 Y=0.577 Z=-0.577
LogTemp: Warning: Impact à: X=1686.656 Y=2628.768 Z=269.500
LogTemp: Warning: Touche: StaticMeshActor_UAID_0042C04F8026175D02_1540545753 (ptr: 00000718B7BCD700) à X=1686.656 Y=2628.768 Z=269.500
LogTemp: Warning: GenerateRaysFromSinglePoint terminé. 4 impacts trouvés.
LogTemp: Warning: Génération 1: 16 nouveaux impacts trouvés.
LogTemp: Warning: Fin des générations de rayons.

2 problems I can’t solve:
The impact points of the second generation are the same
The second object hit is the same as the first object hit

Here’s what I get:

Here’s what I should get:

Can you help me?

thanks

It seems to me that you are not ignoring the origin plane when you launch the second set of rays.

I’m not sure why is that because I can clearly see Params.AddIgnoredActor(this); but I’m not fully aware of your whole setup so double check if you are ignoring the correct actor in your second round of ray-traces or weather there is another actor that might be blocking the ray-casts. (some loose plane maybe)

If you are unable to resolve that or you just need a quick and dirty solution:

  1. Start your second set of points a set distance after past the hit point.
  2. Use LineTraceMultiByChannel() and ignore the first hit.