Download

How to destroy all duplicate beam emitters at once?

I am spawning multiple duplicates of the same beam emitter using a timer and a fire function which is automatic. I cannot delete all of the copies only just the first emitter spawned for some reason when the timer calls the destroy component and deactivate function. How can I delete all of the copies of the same emitter. I have messed with the particle emitter settings and nothing works. Do I have to use an array and how would I use it in this instance? I thought that calling the destroy component and deactivate functions once would delete all of the duplicate emitters in the world space. I am looping the timer in an attempt to continuously delete all the duplicate emitters overtime.

void AWeaponBase::Fire()
{
               FCollisionResponseParams ResponseParams;
		FCollisionQueryParams QueryParams = FCollisionQueryParams::DefaultQueryParam;
		FActorSpawnParameters SpawnParams;
		SpawnParams.SpawnCollisionHandlingOverride =    ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
		QueryParams.AddIgnoredComponent(BaseMesh);
		TArray<FHitResult> Hit;
		const FVector StartTrace = BaseMesh->GetSocketLocation(FName("Socket"));
		FRotator CurrentRotation = BaseMesh->GetSocketRotation(FName("Socket"));
		 FVector AssaultEndTrace = StartTrace + CurrentRotation.Vector() * 10000.0f;
		if (GetWorld()->LineTraceMultiByChannel(Hit, StartTrace, EndTrace, ECollisionChannel::ECC_GameTraceChannel1, QueryParams, ResponseParams))
		{
				for (FHitResult& Results : Hit)
				{
					BeamComp = UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), BeamEmitter, FTransform(Results.ImpactNormal.Rotation(), Results.ImpactPoint));
					BeamComp ->SetBeamSourcePoint(0, StartTrace + CurrentRotation.Vector(), 0);
					BeamComp ->SetBeamTargetPoint(0, EndTrace, 0);
					GetWorldTimerManager().SetTimer(Timer, this, &AWeaponBase::DeactivateEmitter, TrailDelay, true);
				}
}
void AWeaponBase::DeactivateEmitter()
{
		BeamComp->Deactivate();
		BeamComp->SetHiddenInGame(true);
		BeamComp->DestroyComponent();
}

You are spawning X number of BeamEmitters, but only keeping track of the last one spawned (by virtue of only keeping track of it in one variable, BeamComp)

I have no idea if Beam Emitter has any functionality to automatically destroy itself after X amount of time, but that’s what I’d look for first. If there’s nothing to do that, then you can declare a TArray and .Add to that array each BeamComp you spawn, set a single timer, and then for loop on the Array to deactivate/destroy it.

1 Like

How would I for loop through the TArray of Particle emitter components and destroy it? Could you give an example if possible?

Would something like this work?
void AWeaponBase::Fire()
{
               FCollisionResponseParams ResponseParams;
		FCollisionQueryParams QueryParams = FCollisionQueryParams::DefaultQueryParam;
		FActorSpawnParameters SpawnParams;
		SpawnParams.SpawnCollisionHandlingOverride =    ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
		QueryParams.AddIgnoredComponent(BaseMesh);
		TArray<FHitResult> Hit;
		const FVector StartTrace = BaseMesh->GetSocketLocation(FName("Socket"));
		FRotator CurrentRotation = BaseMesh->GetSocketRotation(FName("Socket"));
		 FVector AssaultEndTrace = StartTrace + CurrentRotation.Vector() * 10000.0f;
		if (GetWorld()->LineTraceMultiByChannel(Hit, StartTrace, EndTrace, ECollisionChannel::ECC_GameTraceChannel1, QueryParams, ResponseParams))
		{
		for (FHitResult& Results : Hit)
		{
         //BeamComp is of type UParticleSystemComponent.
      //UParticleSystemComponent* BeamComp; /*Example class*/
BeamComp = UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), BeamEmitter, FTransform(Results.ImpactNormal.Rotation(), Results.ImpactPoint));
  //You have to create a cascade emitter with source point and target point
 //functionality in order to replicate. 
BeamComp ->SetBeamSourcePoint(0, StartTrace + CurrentRotation.Vector(), 0);
BeamComp ->SetBeamTargetPoint(0, EndTrace, 0);
   //This is added every call of the function 
  //using a timer within my character.
    BeamArray.Add(BeamComp);
    //In 1 second this function using the timer will be called.
   //I set the function to false because the
  // for loop should do the job within the 
  //function itself being called with the timer.
	GetWorldTimerManager().SetTimer(Timer, this, &AWeaponBase::DeactivateEmitter, 1.0f, false);
	}
}
void AWeaponBase::DeactivateEmitter()
{
   //Loop
   for(int i = 0; i <= BeamArray.Num(), ++i;)
  {
     //Looping through the array.
     BeamArray[i]->DestroyComponent();
  }
}

/*I have successfully added the beams to the array and called the function
yet the engine crashes. I have also tried to deactivate the system set the visibility of the array 
and still the engine crashes. */

I have looked at all the options in cascade and regardless there is no option to destroy all of the duplicate emitters.

Is your BeamArray UPROPERTY ? also you should probably check that each one IsValid() before accessing it, and make sure you .Empty() the array after.

For crashes, run in debugger, so you can find out what line is causing the crash

1 Like

It is not a UPROPERTY.

Now the engine doesn’t crash but only deletes the first index of the array. It does not deactivate all of the elements in the array. The good news is that the array is valid because every shot fired adds
new elements to the array. I am using this UE_LOG() within the fire function.

UE_LOG(LogTemp, Warning, TEXT("Beam Index Total: %d"), TrailArray.Num());
This is what I am doing 
if (IsValid(TrailArray[BeamIndex]))
	{
		for (int i = 0; i <= TrailArray.Num(); i++)
		{
			TrailArray[i]->Deactivate();
			TrailArray.Empty();
		}
	}

I noticed the engine crashes when I check to see of it is valid but when I don’t only one beam emitter is removed. This is what I am doing now to prevent a crash. Yet only one beam emitter is removed which is the first index in the array for whatever reason.

	for (int i = 0; i <= TrailArray.Num(); i++)
		{
			TrailArray[i]->Deactivate();
			TrailArray.Empty();
		}

I don’t know what I am doing wrong at all. It seems like the logic is correct though.

Empty() erases everything in the Array, do that after the loop, not in the loop

and do your IsValid check inside the loop, not outside the loop

1 Like

I tried and it crashed at the IsValid() check.

for (int i = 0; i <= TrailArray.Num(); i++)
		{	
			if (IsValid(TrailArray[i]))
			{
				TrailArray[i]->DeactivateSystem();
				TrailArray[i]->DestroyComponent();
			}
		}
		TrailArray.Empty();

It crashes at the IsValid() check for some reason.

Oops sorry the trail array is a Uproperty…

i think you mean i < not i <= … Num returns the number of elements, so if there’s 0 elements, it would return 0, and you’d correctly have nothing to loop, but if you have 5 elements, it would return 5, and you access them via 0-4.

Your crash message probably indicates that you’re out of bounds on the array, pay attention to the error messages. :slight_smile:

1 Like

Thank you very much for your help! That was exactly it. This problem is now resolved thank you very much for your help!

for (int i = 0; i < TrailArray.Num(); i++)
		{	
			if (IsValid(TrailArray[i]))
			{
				TrailArray[i]->DeactivateSystem();
				TrailArray[i]->DestroyComponent();
			}
		}
		TrailArray.Empty();