Projectile trajectory and ground level

Hello !
Could someone tell me how can we set a projectile to follow the ground level ; for example a projectile that would follow the blue line and not the red one on this screen :

Hey there @Eichel456! Welcome to the community! So the answer I’ll give will be defined by how you want the projectile to behave.

Do you want it to always hug the ground? i.e. Like abilities in Mobas like for example Dota 2? Or did you want it to just spawn at your feet, and fly in the direction you want while maintaining at least a certain distance from the ground?

Do you want it to collide with the terrain or float above it?

Hey ! thx =)

So I’d like the projectile to fly in the direction I want (this part is already done) while always maintaining a certain distance from the ground ; floating above the terrain.

UnrealEditor_2022-10-23_18-06-26

Got a bit of progress for you, all that’s left is needing to implement it going down.

Are you using a tracer to check the distance with the ground and then change it according to the projectile height ? That’s ingenious :+1:
projectilegroundlevel
I managed to make it up/down thanks to that solution !
But would it be optimal for ~100 of those projectiles simultaneously in the level ?

No. The number of traces would get expensive. Movement would also get jittery on lower FPS.

1 Like

Normal unreal movement components use traces to check their surroundings, so maybe a conversion to c++ would be enough to make this feasible?

CMC and PMC do use a decent amount of traces, but nowhere near the amount yours would need.

Test it. Take your current build and spawn 100.
Retest at 30, 60, 120 FPS.

Absolutely, totally forgot to post the BP but it seems you got the gist. Yes this will work for relatively small amount of projectiles but doing anything on tick is rough on the performance so many more than just a couple would be a problem. Polling the height above ground is going to remain expensive, there’s a couple of ways around this, like making a preprocessor like how navmesh is handled. Though that’s a good bit more complex for this situation.

The alternative is probably to fake it, best as possible.

Interesting.
Would using a character as a projectile (since they stay on ground) with a move location order be a better solution performance wise ?

The character classes do more than that in most cases. It’s lighter weight due to mostly being based on the collision capsule and can go to sleep and be dormant. I don’t know how suitable it’d be for your projectile. Give the one you have a solid stress test and see how bad it is. Worst case scenario we go back to the drawing board with a more complex model. Say if your terrain isn’t quite random we could probably “scan” the terrain and shift the processing to frontload. This is also rather overengineered. There’s definitely a better way to handle this but it’s eluding me.

okay, thanks for help !
Please let me know if you ever find the way to handle this that is currently eluding you :smiley:

1 Like

I’d still like to improve my projectile (currently using a character projectile which isnt optimal).
My terrain isn’t random : it’s just flat floor, 90° cliffs and ramps.
So you could please elaborate more on " “scan” the terrain and shift the processing to frontload." or any other ideas that you might have ?

Hello again! Future me here to say, I probably should have used a better word than scan. But to give you the high level gist, if your terrain doesn’t change, you can project your projectiles path, and then just move it along the path if no issue.

This requires knowing how your projectile it going to move. If it’s only going forward, you just project a straight line until it makes a collision, then plot a point X units above that, and then continue projecting forward from there. I’ll do a bit of a visual mockup, let me know if it makes sense.

Red: Trace forward until a hit or for fixed distance, then poll downward if no hit, poll upward if hit, then capture that location for the next trace location and a spline point. After that it repeats the same and makes a spline point every fixed distance. Then when all the projection is done, a spline is generated from those points and that should be much cheaper, but you’ll have to set up the projection beforehand.

If your game is closer to how MOBAs function this can be even less accurate because the hitboxing is 2 dimensional.

2 Likes

It makes perfect sense
Just wondering how can I make the projectile follow the spline ?

So I’ve got 2 resources for it, one of them isn’t a tutorial but more of an overview of someone using splines for projectiles, the other shows movement along a spline (but it’s time based so you may have to do distance calculations to make it always move the correct speed).

Disclaimer: One or more of these links are unaffiliated with Epic Games. Epic Games is not liable for anything that may occur outside of this Unreal Engine domain. Please exercise your best judgment when following links outside of the forums.

Overview:

More like a tutorial:

Thanks !
But both tutos use tick (well event tick and timeline update), it looks like your first idea with traces to the ground with extra steps.
Also the spline would work great when the projectile needs to go up, but what about going down ? The trace wouldn’t hit anything.

They were more reference to understand how it remained on the spline in two different possible ways, neither one was meant to represent the full implementation. Bit of a unique use case haha. The same goes for it when it travels a certain distance/time. If the trace hits nothing down, just like in the other example you could just have it larger or extend. In your case the name of the game is using less and less resources.

Another alternative is if it’s a fixed map you can do a ton of traces outside of runtime (in editor as an editor utility) and get an array/tileset of heights much like the Environmental Query System AI uses to determine values, and just lock the projectile’s to the plain based on it’s world position. However the implementation of such would be a bit over my head to explain/work out in just a thread, at least in any simple regard.

I see thx
It’s definitely a lot more complex than what I would have imagined.

You could scan the level once and store height data in a 2D map so you can move unlimited projectiles around without having to line trace at all.

  1. Create a GameInstanceSubsystem manager class which holds a height map (TMap of 2D int vector (near absolute world coordinates) to a float height). GameInstanceSubsystem is nice cause you can access it from any projectile at any time, if it’s not accessible in BP use a normal subsystem or actor.
  2. On BeginPlay (or when you need to scan) let the manager class perform line traces from top to bottom over the bounds of the level, every X units (your preferred scan resolution). Let the trace report traces to the relevant landscape.
  3. Store each line trace as a height value in the manager class its height map.
  4. Now you can access a height by a 2D X Y coordinate from the manager whenever you want, so for example on tick on a projectile. See how it performs and if you can reduce this tick speed and perhaps interpolate between the height values you retrieve.

Question is if this is more performant than an actual line trace per projectile. It depends on how many values you store on the managing class. It’s a special case really it would work fine if you’d use it to for example pick a few landscape coordinates for birds to fly above and land on, but it would not be suitable for a sonic or racing game.

Something like

	const int32 HeightMapResolution = 100;
	const int32 LevelSize = 2000;
	TMap<FIntVector2, float> HeightMap;
	for (int32 x = 0; x <= LevelSize; x += HeightMapResolution) {
		for (int32 y = 0; y <= LevelSize; y += HeightMapResolution) {
			FHitResult OutHit;
			const FVector StartTrace = FVector((float)x, (float)y, (float)(LevelSize + HeightMapResolution));
			const FVector EndTrace = FVector((float)x, (float)y, 0.f);
			TArray<AActor*> IgnoreTraceActors;
			if (UKismetSystemLibrary::LineTraceSingle(this, StartTrace, EndTrace, UEngineTypes::ConvertToTraceType(ECC_Visibility), false, IgnoreTraceActors, EDrawDebugTrace::None, OutHit, true)) {
				HeightMap.Add(FIntVector2(x, y), OutHit.ImpactPoint.Z);
			}
			else {
				HeightMap.Add(FIntVector2(x, y), 0.f);
			}
		}	
	}

Whenever you need a height value for a projectile you get the absolute world position of the projectile and round its X,Y values to the nearest key in HeightMap.

HeightMap.FindChecked(FIntVector2(InX, InY));
2 Likes