I am trying to achieve dynamic line-of-sight field of view from a character in a top-down perspective, like in the game Among Us.
After trying various things, I found something that works but is very demanding on the system:
- Every tick, on the local player’s pawn, send out between 100-300 line traces in a circle, and create a procedural mesh (or move its vertices if the mesh already exists) forming a disc whose outer edges conform to where the line traces are blocked. This disc now defines the area the character would be able to see around them if the camera were centered on them and obstructed by walls, etc., instead of looking down above them.
- Add 3 Scene Capture 2d components to the Player Pawn class. Remove them for any pawns that are not locally controlled. I do this because SceneCapture2d components allow you to set lists of actors that are rendered for them or which are not rendered for them:
- Main Scene capture: Can see everything in the level and the player pawn that owns it.
- Vision Disc capture: Can see the procedural field of view mesh generated each tick, but nothing else.
- OtherPawns Capture: Can see other player pawns but nothing else.
- In my Post Process Material, Sample all 3 scene capture2d components’ Render Target textures. Lerp between Main Scene Capture and OtherPawns Capture using Vision Disc Capture as the Alpha of the Lerp node.
This works nearly perfectly, though doesn’t looks as sharp and smooth as it does in Among Us.
There is a problem, though: The device the game is running on now has to render 3 (4 if your Pawn also has a Camera, which it does so I can deproject traces for Mouse Clicks on the screen into the world, though the player does not see anything from the Camera) entire 2048x2048 render targets (adjusted in the PPM to fit to the screen’s aspect ratio and accurately depict the synchornized top-down view) every tick. AS you can imagine, this slows it to about 1-3 FPS on many mobile devices, and causes the game to take up most of the CPU or GPU resources on faster devices in order to maintain a good framerate.
I think there must be a better way that isn’t so resource-intensive, which would allow the game to be played on a broader range of devices.
How does Among Us do it, and can that be done in Unreal?
I already tried using a Material Parameter Collection to set a Player Pawn Location vector that other actors’ material could use to give a per-pixel visibility effect like this, and that worked for distance but not for line-of-sight occlusion. That procedural mesh I’m using for LOS occlusion effect, I can’t figure out how to mathematically use that in an Actor’s material.
Some people have said just turn visibility on or off depending on whether the line traces hit, but that would affect the entire actor, so you could see the whole actor if only a part of them should be visible behind a corner that is between your pawn and theirs. That’s not visually nor gameplay-wise what I need.