Page 1 sur 1

raycast en cascade

Publié : 16 avr. 2025, 16:18
par Bobasptal
Bonjour,

Je voudrais coder un raycast en cascade comme ceci :
Point source = position de l'objet émetteur
Je choisis 4 directions (j'aurais pu choisir un autre nombre).
Quatre raycasts sont générés à partir de ce point source pour frapper un premier objet en 4 points d'impact, chacun devenant à son tour une source de quatre raycasts pour frapper un second objet, etc.
J'ai donc codé une classe C++ appelée RayEmitter2, dont le .h :

Code : Tout sélectionner

//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);
};
et le cpp sont :

Code : Tout sélectionner

//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;
}
Voici le résultat du journal :
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 problèmes que je ne parviens pas à résoudre :
Les points d'impact de la deuxième génération sont les mêmes
Le deuxième objet touché est le même que le premier.

Voici ce que j'obtiens :
Capture d’écran 2025-04-12 à 23.22.47.png
Capture d’écran 2025-04-12 à 23.22.47.png (150.9 Kio) Consulté 193 fois
Voici ce que je devrais obtenir  concernant le 1er plan touché:
2.png
2.png (178.88 Kio) Consulté 193 fois
Pouvez-vous m'aider?
Inutile de remplacerLineTraceSingleByChannel par LineTraceMultiByChannel car l'utilité de LineTraceMultiByChannel ne répond pas à la cascade

merci

Re: raycast en cascade

Publié : 18 avr. 2025, 10:26
par Bender
Salut,

Problème 1 : Les points d’impact de la 2e génération sont les mêmes
Problème 2 : Le 2e objet touché est le même que le 1er

Ces deux problèmes ont une même origine probable : les rayons de la génération suivante partent de points situés à l’intérieur ou trop près de la même surface que celle qu’ils viennent de toucher — donc ils la re-tapent. Même si tu as tenté un décalage du point d’impact, tu l’as commenté. Résultat : la nouvelle génération de rayons frappe la même surface/objet à chaque fois.

voici la version corrigée de GenerateRaysFromSinglePoint, avec le décalage du point d’impact et les visuels de debug conservés :

Fonction mise à jour : GenerateRaysFromSinglePoint

Code : Tout sélectionner

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());

	for (const FVector& Direction : EmissionDirections)
	{
		UE_LOG(LogTemp, Warning, TEXT("  Direction: %s"), *Direction.ToString());
		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)
		{
			FVector AdjustedImpactPoint = Hit.ImpactPoint + Hit.ImpactNormal * 1.0f;
			ImpactPoints.Add(AdjustedImpactPoint);

			UE_LOG(LogTemp, Warning, TEXT("    Normale: %s"), *Hit.ImpactNormal.ToString());
			UE_LOG(LogTemp, Warning, TEXT("    Impact ajusté à: %s"), *AdjustedImpactPoint.ToString());
			UE_LOG(LogTemp, Warning, TEXT("    Touche: %s (ptr: %p)"),
				*GetNameSafe(Hit.GetActor()),
				Hit.GetActor());

			DrawDebugLine(GetWorld(), Source, Hit.ImpactPoint, LineColor.ToFColor(true), false, RayDisplayDuration, 0, LineThickness);
			DrawDebugPoint(GetWorld(), Hit.ImpactPoint, 8.0f, FColor::Red, false, RayDisplayDuration);
		}
		else
		{
			UE_LOG(LogTemp, Warning, TEXT("    Pas d'impact. Fin du rayon: %s"), *End.ToString());
			DrawDebugLine(GetWorld(), Source, End, FColor::Red, false, RayDisplayDuration, 0, LineThickness);
		}
	}

	UE_LOG(LogTemp, Warning, TEXT("GenerateRaysFromSinglePoint terminé. %d impacts trouvés."), ImpactPoints.Num());
	return ImpactPoints;
}
Ce que ça change :
• Décalage vers l’avant du point d’impact via Hit.ImpactNormal * 1.0f pour éviter que les prochains rayons refrappent le même plan.
• Toujours le DrawDebugLine vers le point réel touché, pour un visuel clair et propre.
• Toujours un retour d’un point ajusté, pour servir de nouvelle source fiable dans les générations suivantes.

:geek:

Re: raycast en cascade

Publié : 18 avr. 2025, 14:04
par Bobasptal
merci !
mais ça ne fonctionne toujours pas !
rendu =1ère image

Re: raycast en cascade

Publié : 19 avr. 2025, 09:35
par Bender
Ajoute dans ton .h

Code : Tout sélectionner

// Nouvelle version avec passage du set d'acteurs déjà frappés
TArray<FVector> GenerateRaysFromSinglePoint(const FVector& Source, TSet<AActor*>& AlreadyHitActors);
Remplace l’ancienne fonction par celle-ci dans le .cpp

Code : Tout sélectionner

TArray<FVector> ARayEmitter2::GenerateRaysFromSinglePoint(const FVector& Source, TSet<AActor*>& AlreadyHitActors)
{
	nb_appel++;
	TArray<FVector> ImpactPoints;
	ImpactPoints.Empty();

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

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

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

		// Ignorer tous les acteurs déjà touchés dans les générations précédentes
		for (AActor* Ignored : AlreadyHitActors)
		{
			if (IsValid(Ignored))
			{
				Params.AddIgnoredActor(Ignored);
			}
		}

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

		if (bHit)
		{
			AActor* HitActor = Hit.GetActor();
			if (IsValid(HitActor))
			{
				AlreadyHitActors.Add(HitActor); // On l’ajoute aux ignorés pour les prochaines générations
				UE_LOG(LogTemp, Warning, TEXT("    Touche: %s (ptr: %p)"), *HitActor->GetName(), HitActor);
			}

			// Décalage plus marqué pour éviter les doubles hits
			FVector AdjustedImpactPoint = Hit.ImpactPoint + Hit.ImpactNormal * 10.0f;
			ImpactPoints.Add(AdjustedImpactPoint);

			DrawDebugLine(GetWorld(), Source, Hit.ImpactPoint, LineColor.ToFColor(true), false, RayDisplayDuration, 0, LineThickness);
			DrawDebugPoint(GetWorld(), Hit.ImpactPoint, 8.0f, FColor::Red, false, RayDisplayDuration);
		}
		else
		{
			UE_LOG(LogTemp, Warning, TEXT("    Pas d'impact. Fin du rayon: %s"), *End.ToString());
			DrawDebugLine(GetWorld(), Source, End, FColor::Red, false, RayDisplayDuration, 0, LineThickness);
		}
	}

	UE_LOG(LogTemp, Warning, TEXT("GenerateRaysFromSinglePoint terminé. %d impacts trouvés."), ImpactPoints.Num());
	return ImpactPoints;
}
Modifie ton BeginPlay()
(Juste au début )

Code : Tout sélectionner

TSet<AActor*> AlreadyHitActors;
Et à l’intérieur de la boucle de génération (dans BeginPlay) :

Code : Tout sélectionner

TArray<FVector> ImpactsFromSource = GenerateRaysFromSinglePoint(Source, AlreadyHitActors);


Cette version évite que les rayons frappent le même objet plusieurs fois, décale assez les points pour ne pas rester “collés” à une surface, et affiche des visuels de debug