How to calculate next transform location based off distance?

I have a bullet manager keeping track of “bullets” which are just the transforms of when the bullet was spawned. Thus they’re the start location of when they were shot. In my tick of the bullet manager I’m trying to move these bullets forward X distance, but I’m struggling with the math. Does anyone have any tips? I can use my actors transform or location, either one, to deal with this. I’m trying to calculate the Start and End for a sphere sweep so I can detect collisions.

1 Like

I think I got it. Used the math from the below blog for moving bullets.

3 Likes

Could you share your solution? Think this is interesting. :upside_down_face:

1 Like

Basically when generating bullets you add the current actor transform or wherever the bullet is shooting from transform to an array. Then in the Tick for your BP you loop that array then perform a trace between where the bullet started and where it should then be to handle your collision behavior. The math for finding the new location is in the blog under “Moving the bullets” section. If the bullet hit something remove it from the array. If it didn’t hit anything then update the array with its new location (the end point of your trace).

This is basically a continuation of my below topic.

Honestly I’m still not all that impressed. A staff member said my original Niagara implementation is bad performance, but honestly it was the best performance of all 3 things I’ve tried. I’ve tried Niagara, Object Pooling, and now this usage. Niagara still performed better.

2 Likes

Have you tried using event dispatchers?

1 Like

For what? I was originally using Niagara with particle collision events reported back to BP. I’d then sphere trace on collision to know what I hit. This worked great even with 10,000 projectiles flying around. So I’ve no idea why staff told me this was bad. It’s obviously bad for an MP game, but I’m not making an MP game.

1 Like

A sphere overlap seams to perform quite a bit better than sphere trace, but really fast projectiles MIGHT phase through things. My projectiles won’t be going crazy fast though. So far I can get about 1.3k projectiles before seeing any kind of FPS loss.

1 Like

Did you find anything?

I’ve been testing and managed this so far using an array of structs in c++:

Projectiles

Find anything about what? I’ve this fully implemented and working. You just need an array of transforms of when the projectile is shot. Then in your tick (I’m using a timer in a GameplayAbility) you “move” their transforms to where they should be. During that move you run a line or sphere trace for objects and only check for things it can collide with. If it colliders remove its index from the projectiles array. If it doesn’t set its index in the projectiles array to the new location. Below is my projectile handling chopped out. This is done entirely from BP. It of course performs better if C++, but am doing my game almost exclusively with BP and will nativize my gameplay abilities.

Shoot Projectile:

Projectile Tick:

Move Projectile:

It’s 1 tick no matter the amount of projectiles. I don’t notice any form of FPS impact until around 1500 projectiles then it can start to go down. Note that’s 1500 ACTIVE projectiles. It’s VERY unlikely to ever have that many active projectiles. Most will be colliding and being removed.

It’s strictly the collision checks causing the performance decline for me. I’m not sure how to optimize that further yet. Without collisions I can easily have 50,000 projectiles going but kinda useless without collisions. Using Niagara for projectiles performed way better, but it ate through CPU usage like crazy having tons of particle collisions and became crazy inaccurate with its collisions.

How did you go about implementing your solution? Looks like you have collisions, but it’s hard to tell. Do you get collision events so you can apply damage to things?

1 Like

About the math.

That’s very close to what I ended up with minus the math for the bounce on blocking hit and rather than transform I used 2 FVectors: one for world location and another for direction. Don’t think you need scale nor a rotator to have to convert to a direction vector.

Also have * delta time to have a consistent speed regardless of framerate.

How did you manage to get 17k projectiles with collisions at 30 FPS though, lol. That would completely lock my editor up or do the screenshots not have collisions?

Would that improve performance to replace the transform?

I originally had that, but this is inside of a Timer now so I haven’t added checking delta back in.

That depends how you define collision, I just check if the trace hit something, if it did not just continue to next loop. if it did, rotate the direction vector and update location vector for next loop.

They do lol… they are all bunched up inside the box.

For example, this is the function that updates the structs on tick:

// Called every frame
void AProjectileTraceManager::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	AddActorWorldRotation(FRotator(0.f, 100.f * DeltaTime, 0.f));

	TraceManager(DeltaTime);
}

void AProjectileTraceManager::TraceManager(float DeltaTime)
{
	for (FProjectileStructBase& Projectile : ProjectileStructArray)
	{
		FHitResult HitResult;
		FCollisionQueryParams QueryParams;
		float TraceDistance = ProjectileSpeed * DeltaTime;
		
		GetWorld()->LineTraceSingleByChannel(
			HitResult,
			Projectile.WorldLocation,
			TraceDistance * Projectile.Direction + Projectile.WorldLocation,
			ECollisionChannel::ECC_Visibility, 
			QueryParams, 
			FCollisionResponseParams::DefaultResponseParam
		);

		DrawDebugLine(
			GetWorld(), 
			Projectile.WorldLocation, 
			HitResult.TraceEnd, 
			FColor::Red, 
			false, -1.f, 0, 2.f
		);

		// If no hit, jump to next loop
		if (!HitResult.bBlockingHit)
		{
			Projectile.WorldLocation = HitResult.TraceEnd;
			continue; // Anything after this will never run for this loop
			UE_LOG(LogTemp, Warning, TEXT("This will never run."));
		}

		// Rotate direction vector from hit normal for next loop.
		float dot = HitResult.ImpactNormal | (Projectile.Direction * (-1.f));
		FVector BounceDirection = ((HitResult.ImpactNormal * dot * 2.f) + Projectile.Direction);
		Projectile.Direction = BounceDirection;
		// Update location of struct for next loop
		float DistanceToBlockHit = FVector::Distance(Projectile.WorldLocation, HitResult.ImpactPoint);
		float DistanceAfterBlock = TraceDistance - DistanceToBlockHit;
		Projectile.WorldLocation = HitResult.ImpactPoint + Projectile.Direction * DistanceAfterBlock;
	}
}

idk how will this perform in blueprint… also don’t remember what is the equivalent of continue; in blueprint.

Since this traces from point A to B and next loop from B to C, it should never go through anything unless that moves against the direction of the projectile, but I think you can work around that by playing with the tick groups.

1 Like

Your usage doesn’t look much different from mine. Can’t understand why my performance is so bad. Even shooting 500 projectiles tanks from 100 FPS to 60ish. I think I’m doing something wrong in a loop somewhere. Maybe the loop that shoots the projectiles and not so much the loop that moves them.

As for the continue in BP that’s basically what my Branch checking return value is doing.

I think instead of doing a loop in my Shoot Projectile screenshot I can just flat out insert X number of rows into the array then move on to my delay for cooldown, but doubt that’ll do much since it’s just a basic For loop. That loop is probably what’s causing some bonkery.

Ok, moved the locations behavior out of the initial for loop. Also replaced the Transform with a struct that’s just location and direction. Helps a little, but still having pretty bad performance when shooting 500+ projectiles at once (maybe that’s the problem? they’re all at once).

Did the cpp in blueprint just to compare. This is what I got:

Blueprint:

CPP:

Both do exactly the same, including the bounce on hit.

1 Like

Really weird how BP is performing substantially worse here. Something under the hood in BP doing it then I guess. Wonder if nativizing the BP would fix it. Maybe the way we’re doing it in BP is wrong. Need to further explore what’s going on here as the performance difference shouldn’t be that massive.

Did some quick testing and it’s the looping of the projectiles array during Tick that’s the problem and I mean purely just looping the array. I have it loop then end. No movement calculations. Nothing. Just looping alone tanks my FPS. Tested For Each and For loops. Both are god awful slow in BP.

Agree.

Played with it a bit longer: projectiles now bounce, have life span in seconds (though disabled in the test), cause damage and if they case damage they destory (swap and remove index from array). Moved the function to blueprint library to compare.

Blueprint with CPP function:

CPP:

1 Like

So you moved the tick projectile loop entirely to CPP but left everything else in BP? Interesting. That could be a very solid work around. Still really weird BP loop just goes bonkers. That has got to be a critical bug in UE IMO.

Wonder if we can get a UE dev to chime in here as something is very very wrong.

Edit: Submitted a bug report and linked it to this topic. Something isn’t right.

Do you mind sharing your Trace Manager script that’s exposed to BP? Trying to implement similar, but having a tough time getting it working.

I think I can fix this entirely from BP. Attempting to break down the for each core macro into a more specific loop in my BP.