Server-Side Fog of War: Defending Against Wall Hacks | Unreal Fest Stockholm 2025
https://youtu.be/CK2L7rjp0qk?si=q7nj-mpxdrqa5cvL
Thank you for coming. my name is Alysa Forbes. I work at CCP Games, London as Lead Core Tech Engineer. thank you for waking up early to come to this talk. I'm going to speaking about wall hacks and why we care about them at CCP London and our game and how we solve them on the server side.
CCP Games 런던에서 리드 코어 테크 엔지니어로 일하고 있습니다. 오늘은 월핵에 대해 이야기하고, CCP 런던과 저희 게임에서 월핵을 중요하게 생각하는 이유, 그리고 서버사이드에서 어떻게 해결하는지에 대해 설명드리겠습니다.

So, I'm going to talk about what a whole hack is exactly. why they happen, why we were motivated to solve prevent them in our game. Then I'm going to cover our approach to solving the problem before moving into like a deeper dive into the implementation. Finally, I'm going to wrap up with some remarks on how well it went and some performance data before take any questions you might have.
자, 그럼 핵이 정확히 무엇인지부터 이야기해 보겠습니다. 왜 월핵이 발생하는지, 그리고 저희가 게임에서 핵을 방지하려고 했던 이유는 무엇인지 설명드리겠습니다. 그다음, 문제 해결을 위한 접근 방식을 소개하고 구현 과정에 대해 자세히 살펴보겠습니다. 마지막으로, 핵이 얼마나 효과적이었는지에 대한 소감과 성능 데이터를 공유한 후, 여러분의 질문을 받겠습니다.

So, what is the wall hack exactly? we at CCP London are working on E-Vanguard, an MMO FPS where action takes place in a series of alien worlds. our level designers work hard creating environments for players to explore and fight in, thinking about cover and sight lines, hiding places, loot crates, generally create tactically interesting encounters. Wall hacks essentially undo all that work, exploiting information on the client that the player shouldn't know about, effectively giving cheaters the power to see through walls.
그렇다면 월핵이란 정확히 무엇일까요? 저희 CCP London은 외계 행성들을 배경으로 하는 MMO FPS 게임인 E-Vanguard를 개발하고 있습니다. 저희 레벨 디자이너들은 플레이어들이 탐험하고 전투를 벌일 수 있는 환경을 만들기 위해 엄폐물, 시야, 숨을 곳, 전리품 상자 등 전술적으로 흥미로운 요소들을 세심하게 고려합니다. 그런데 월핵은 플레이어가 알 수 없는 클라이언트 정보를 악용하여 벽 너머를 볼 수 있게 해주는 기술로, 이러한 모든 노력을 무산시킵니다.

here's an example of a simulatable hack in our game. it's taken from a locally running instance and I've put in the player viewport data that the client knows about where enemies are. So, put enemy locations and distance and little green targets in their back. As you can see, kind of reveals where enemies are, giving players like an unfair advantage.
저희 게임에서 시뮬레이션 가능한 핵의 예시를 보여드리겠습니다. 이 예시는 로컬에서 실행 중인 인스턴스에서 가져온 것이며, 플레이어 화면에 적의 위치 정보를 표시했습니다. 적의 위치와 거리, 그리고 적의 등에 작은 초록색 표적을 표시하는 방식입니다. 보시다시피, 이 핵은 적의 위치를 노출시켜 플레이어에게 불공정한 어드밴티지를 제공합니다.

Okay, so to talk about how we fix the problem, I'm going to go into a little bit on why it happens. to cover some basics, the architecture of a multiplayer game like ours is that there exists a central server which contains the game state, it knows everything about the game and coordinates the action amongst multiple game clients. the process by which clients kept up to date about what's happening in the game is called Replication. For example, to simplify a little bit, when a client joins the game, an actor representing that player is replicated across all the clients in the game. And as they move around the world, their position and other attributes are continually replicated as well. And this goes for the enemies, loot crates, and other game objects in the world. and is essentially what drives the immersive multiplayer experience. when an actor should exist on the client and receive updates, we call it relevant for that client. Common relevancy metrics used by Unreal's replication system are to use distance from the player. For example, each actor has like a NetCullDistance outside of which that actor will stop being replicated for that client. And there are other systems such as GridSpatialization2D which apply that concept to optimize for open worlds and actors. what these systems don't take into account is what a player can see or what they should be able to see and hear. So they expose that information unnecessary to the client.
자, 그럼 문제 해결 방법에 대해 이야기하기 전에, 먼저 왜 이런 문제가 발생하는지 간단히 설명드리겠습니다. 기본적인 내용을 살펴보자면, 저희 게임과 같은 멀티플레이어 게임의 아키텍처는 게임 상태를 저장하고 게임에 대한 모든 정보를 알고 있으며 여러 게임 클라이언트 간의 동작을 조율하는 중앙 서버가 존재한다는 것입니다. 클라이언트들이 게임 내 상황을 최신 상태로 유지하는 과정을 Replication이라고 합니다. 예를 들어, 클라이언트가 게임에 접속하면 해당 플레이어를 나타내는 액터가 게임 내 모든 클라이언트에 리플리케이트됩니다. 플레이어가 월드를 이동함에 따라 위치 및 기타 속성도 지속적으로 리플리케이트됩니다. 이는 적, 전리품 상자, 그리고 월드의 다른 오브젝트에도 동일하게 적용됩니다. 바로 이 리플리케이션 시스템이 몰입감 넘치는 멀티플레이어 경험을 제공하는 핵심 요소입니다. 액터가 클라이언트에 존재해야 하고 업데이트를 받아야 할 때, 해당 액터를 'Relevant'라고 합니다. 언리얼 엔진의 리플리케이션 시스템에서 사용하는 일반적인 Relevancy 지표는 플레이어와의 거리입니다. 예를 들어, 각 액터는 NetCullDistance와 같은 값을 가지며, 이 값을 벗어나면 해당 액터는 클라이언트에 대해 더 이상 리플리케이트되지 않습니다. GridSpatialization2D와 같은 다른 시스템들도 이러한 개념을 적용하여 오픈 월드와 액터에 최적화합니다. 하지만 이러한 시스템들은 플레이어가 무엇을 볼 수 있는지, 또는 무엇을 보고 들어야 하는지는 고려하지 않습니다. 따라서 클라이언트에게 불필요한 정보를 노출하게 됩니다.

So to fix this we fix it on the server side by only replicating what the player should be able to see what they should be able to hear. the system was originally conceived as simply visibility based replication. and we called it fog of war because it, well, it was catchy and because it described more generally the approach of using environmental factors to limit what the client should know about.
이 문제를 해결하기 위해 서버 측에서 플레이어가 보고 들을 수 있는 것만 리플리케이트하는 방식으로 수정했습니다. 이 시스템은 원래 가시성 기반 리플리케이션 방식으로 구상되었으며, 전장의 안개(Fog Of War)라고 명명했습니다. 그 이유는 기억하기 쉬웠을 뿐 아니라, 환경적 요소를 활용하여 클라이언트가 알아야 할 정보를 제한하는 접근 방식을 더 포괄적으로 설명했기 때문입니다.

taking visibility first, the question becomes of all the game objects in the world, which ones does the player have line of sight on? So we use server-side line traces to establish line of sight. by continually tracing between the player and other actors and finding out where traces get blocked, we can determine whether that actor should be replicated for player. the image here is a screenshot showing some fog of war debug visuals around an NPC. hopefully you can make out the little green dots in the image. They show the location of where line traces are being made around the NPC to equivalent points around the player. we make traces on the visibility channel and they run asynchronously to not block the game thread and we use simple test traces because we don't care about what blocks the trace only whether it is. If any line traces are not blocked, then line of sight is established and the NPC is considered unfogged relative to the player and vice versa if it's a player. for example, since all the line traces here are not blocked, the NPC is being replicated. And here's a short video showing this in action. So, there are some NPCs hiding in this geometry. and marked with bright red line to show when they're being replicated. And as we glimpse them across the halfpipe through the window, we can see that the actors are appearing on the client, but when they move out of view, they stop being replicated. So, it kind of keeps their position hidden unless we can see them.
먼저 가시성을 고려하면, 월드에 있는 모든 오브젝트 중에서 플레이어가 시야를 확보할 수 있는 오브젝트는 무엇일까요? 이를 위해 서버사이드 LineTrace를 사용하여 시야를 확인합니다. 플레이어와 다른 액터 사이의 경로를 지속적으로 추적하고 트레이스가 차단(Blocked)되는 지점을 찾아 해당 액터를 플레이어에 맞게 리플리케이트해야 하는지 판단합니다. 아래 이미지는 NPC 주변의 안개 효과 디버그 시각화를 보여주는 스크린샷입니다. 이미지에서 작은 녹색 점들을 확인할 수 있는데, 이 점들은 NPC 주변의 라인 트레이스와 플레이어 주변의 동일한 지점 사이의 위치를 나타냅니다. Visibility 채널에서 트레이스를 비동기적(asynchronously)으로 실행하여 게임 스레드를 멈추지 않도록 합니다. 또한, 트레이스를 차단하는 요소가 무엇인지는 상관없으므로 차단 여부만 확인하기 위해 간단한 Test Trace를 사용합니다. LineTrace가 차단되지 않으면 시야가 확보된 것으로 간주되어 NPC는 플레이어에 대해 안개 효과가 없는 것으로 판단하고, 반대로 플레이어가 안개 효과에 가려진 경우에는 NPC가 플레이어에 대해 안개 효과가 없는 것으로 판단합니다. 예를 들어, 아래 이미지에서는 모든 라인 트레이스가 차단되지 않았으므로 NPC가 리플리케이트되고 있습니다. 이 기능을 보여주는 짧은 영상이 여기 있습니다. 이 지형지물 안에 몇몇 NPC가 숨어 있는데, 리플리케이트되는 시점을 나타내기 위해 밝은 빨간색 선으로 표시되어 있습니다. 창문을 통해 하프파이프 너머로 NPC들이 보일 때, 클라이언트 측에서는 액터들이 나타나는 것을 볼 수 있지만, 시야에서 벗어나면 리플리케이트가 중단됩니다. 즉, 우리가 볼 수 없을 때만 위치가 숨겨지는 방식입니다.

So what are the what do we have to consider when designing the system? The the two main concerns are fidelity and performance. So there are a couple of ways that the system can fail. it can replicate actors that the player shouldn't know about, which rewards cheaters, or it can fail by not replicating actors that the player should be able to see or replicating them late, which is kind of gameplay destroying. which so it's gameplay for everybody.
actors are not points, they have volume. an actor standing partially obscured by scenery might happen to have a trace point blocked and incorrectly fog that actor. but it would be impractical to trace across a whole area of an actor. So we have to choose points carefully to kind of ensure coverage while not being ridiculous the number of traces we're making. the footage I've shown has shown a pattern where we establish the testing where we have a few widely spaced couple of widely spaced points and points clustered in the middle which in practice tends to work for the kinds of levels that we have. replication lag is another important concern because when you begin replicating an actor to a client it doesn't appear immediately. there can be a lag of up to a second which are again great pop-in.
그렇다면 시스템을 설계할 때 무엇을 고려해야 할까요? 두 가지 주요 고려 사항은 정확도(Fidelity)와 성능(Performance)입니다. 시스템이 실패할 수 있는 방법은 두 가지입니다. 플레이어가 알 필요가 없는 액터를 리플리케이트하여 치터에게 이득을 줄 수도 있고, 플레이어가 볼 수 있어야 하는 액터를 리플리케이트하지 못하거나 늦게 리플리케이트하여 게임 플레이를 망칠 수도 있습니다. 즉, 모든 플레이어의 게임 플레이를 방해하는 것이죠. 액터는 점이 아니라 부피를 가지고 있습니다. 배경에 부분적으로 가려진 액터는 추적 지점이 차단되어 잘못된 안개 효과가 발생할 수 있습니다. 하지만 액터 전체 영역을 추적하는 것은 비현실적입니다. 따라서 추적 지점을 신중하게 선택하여 적절한 범위를 확보하면서도 추적 횟수가 지나치게 많아지지 않도록 해야 합니다. 제가 보여드린 영상에서 보셨듯이, 저희는 테스트를 진행할 때 간격이 넓은 몇 개의 지점과 중앙에 밀집된 지점을 설정하는데, 실제로 이러한 방식이 저희가 개발하는 레벨에서 효과적인 경우가 많습니다. replication latency 또한 중요한 문제입니다. 액터를 클라이언트로 리플리케이트하기 시작해도 즉시 나타나지 않기 때문입니다. 최대 1초까지 지연이 발생할 수 있는데, 이는 팝인(갑자기 튀어나오는) 현상으로 이어질 수 있습니다.
On the flip side of fidelity is server performance. like if you think about a fully loaded server with 18 players and 200 game objects, that's like thousands of of pairs. And if you multiply that by number by say eight or line traces per pair, that would quickly overwhelm the server. And we aim to hit 60 frames per second on the server. So that's we need to try and think about how to avoid that. And we also need to replicate audio. So we want to be able to hear players we can't necessarily see. And we want to make sure that teammates are still unfogged relevant to one another. so the first thing to notice we obviously don't need to perform a line of sight check every frame.
정확도의 이면은 바로 서버 성능입니다. (즉, Fidelity ↔ Server Performance의 Trade-Off 존재) 예를 들어, 플레이어 18명과 게임 오브젝트 200개로 가득 찬 서버를 생각해 보세요. 수천 개의 트레이스가 발생하는데, 각 트레이스 쌍당 8~9개의 트레이스가 처리된다고 가정하면 서버에 과부하가 걸릴 수 있습니다. 저희는 서버에서 초당 60프레임을 목표로 하고 있기 때문에, 이러한 문제를 어떻게 해결해야 할지 고민해야 합니다. 또한 오디오 재현도 중요합니다. 보이지 않는 플레이어의 소리도 들을 수 있어야 하고, 팀원 간의 시야 확보도 중요합니다. 따라서 매 프레임마다 시야 검사를 할 필요는 없다는 점을 명심해야 합니다.

we can exploit that closer actors probably require more fidelity because pop in would be more obvious and more distant actors they occupy a smaller radius on the screen. So we require fewer visibility points to establish line of sight. We can also be more relaxed about checking actors that are already unfogged. It doesn't really hurt gameplay that much if an actor is visible for a few extra seconds when it goes into cover. and it's probably preferable to not fog and unfogg repeatedly if an actor moves in and out to cover to avoid replication costs of re-replicating repeatedly. So we can exploit these like little these opportunities for optimization and we can establish a trace budget per frame. So, we ran in our game a command that randomly traced across the map repeatedly to kind of figure out a reasonable number of traces we could make every frame that wouldn't were on the server.
가까이 있는 액터는 팝인 현상이 더 두드러지기 때문에 더 높은 정확도가 필요하고, 멀리 있는 액터는 화면에서 차지하는 반경이 작기 때문에 시야 확보에 필요한 가시점 수가 줄어든다는 점을 활용할 수 있습니다. 또한 이미 안개가 제거된 액터에 대해서는 확인 과정을 좀 더 유연하게 진행할 수 있습니다. 액터가 엄폐물로 이동할 때 몇 초 더 보이는 것은 게임 플레이에 큰 영향을 미치지 않습니다. 그리고 액터가 엄폐물 안팎으로 이동할 때 반복적으로 안개로 가려졌다가 걷히는 대신, 반복적인 Re-replicate에 따른 리플리케이션 비용을 줄이기 위해 안개를 반복적으로 생성하고 제거하는 과정(즉, 컬링 ↔ 스폰이 반복되는 과정)을 생략하는 것이 더 효율적입니다. 이러한 작은 최적화 기회를 활용하여 프레임당 트레이스 예산을 설정할 수 있습니다. 저희는 게임에서 맵 전체를 무작위로 트레이스하는 명령어를 반복적으로 실행하여 서버에 과부하가 걸리지 않는 적절한 트레이스 횟수를 찾아냈습니다.

And so these are sort of level of detail parameters that we established. so for very close actors, we want to be able to very accurately tell if we can see a player. so we use line traces and we check every single frame since popping was probably most obvious at that point. But for more distant actors, we can be much more relaxed. like at least on our levels, the line of sight that the the kind of lines of sight we have mean that we can get away with checking like every half a second and using comparatively fewer traces. And for really distant actors, which are essentially points on the screen, we can be even more relaxed again. and when unfogged, we can also just check quite infrequently, too.
그래서 이것들은 우리가 설정한 LOD 파라미터입니다. 아주 가까운 액터의 경우, 플레이어가 보이는지 여부를 매우 정확하게 판단해야 합니다. 따라서 라인 트레이스를 사용하고, 팝인 현상이 가장 두드러지게 나타나는 시점이었기 때문에 매 프레임마다 확인합니다. 하지만 멀리 있는 액터의 경우에는 훨씬 더 유연하게 처리할 수 있습니다. 적어도 우리가 사용하는 레벨에서는 시야각 덕분에 약 0.5초마다 확인하고 비교적 적은 수의 트레이스를 사용해도 됩니다. 화면상의 점으로 표현되는 아주 멀리 있는 액터의 경우에는 더욱 유연하게 처리할 수 있습니다. 컬링되지 않은 경우(unfogged)에는 훨씬 더 드물게 확인해도 됩니다.

So we solve replication lag by projecting visibility points ahead of where the players moving. So if an actor has a movement component then we look at the velocity component of that of their movement and move the trace points correspondingly. This shows that in action. This NPC is running back and forth and we can see that the points being projected ahead of them. which essentially gives us time when they run out from behind the wall. We've already seen them at the point that they appear. So, they're fully replicated. Without that projection, they would just sort of pop into existence sort of like past the end of the wall.
또한 우리는 플레이어의 이동 경로보다 더 앞쪽에 라인 트레이스하여 리플리케이션 지연 문제를 해결합니다. 액터에 Movement Component가 있는 경우, 해당 무브 컴포넌트의 Velocity 요소를 확인하고 그에 따라 LineTrace 대상 위치를 이동시킵니다. 이 영상에서 그 작동 방식을 확인할 수 있습니다. 이 NPC가 앞 뒤로 뛰어다니는 것을 보면, 트레이스 포인트가 NPC보다 앞쪽에 투영되는 것을 볼 수 있습니다. 이는 NPC가 벽 뒤에서 나타날 때, 이미 그 지점에서 NPC를 볼 수 있기 때문에 리플리케이션이 완료되는 시간적 여유를 제공합니다. 이러한 투영이 없다면, NPC는 벽 끝에서 갑자기 나타나는 것처럼 보일 것입니다.
보충 설명
거리가 멀수록 굳이 9개의 포인트에 대해 LineTrace를 수행할 필요가 없다. 극단적으로 멀리있다고 생각해보자. 9개의 포인트에 대해 전부 LineTrace하는 것과, 하나의 포인트에 대해서만 LineTrace 하는 것의 정확도 차이는 0에 수렴할 것이다.또한 LineTrace 대상이 속도를 갖거나, Network Latency를 갖는 경우 기존 라인 트레이스 대상 위치보다 미래의 위치에 예측하여 라인 트레이스를 수행하도록 변경한다.

so to handle audio we use the idea of exposure events. So the fog of war system can aware when an audio event plays. We log an event of a certain radius over a certain time. and within that radius, that player or NPC will be unfogged for players around them. I can hopefully demonstrate this here. So, this tirelessly running NPC is running back and forth. And we can hear within this boundary, this yellow boundary, their audio is being replicated. And we use that radius as well as line of sight to determine where to replicate them. So when they fall outside that boundary, they stop being replicated.
오디오를 처리하기 위해 Exposure Event라는 개념을 사용합니다. 전장의 안개 시스템은 오디오 이벤트가 재생될 때 이를 감지할 수 있습니다. 특정 반경 내에서 특정 시간 동안 이벤트를 기록하고, 해당 반경 내에서는 플레이어나 NPC가 주변 플레이어에게 안개가 제거된 것처럼 보이게 됩니다. 여기서 이를 보여드리겠습니다. 이 NPC는 끊임없이 앞뒤로 뛰어다니고 있습니다. 노란색 경계선 안에서 해당 NPC의 오디오가 리플리케이트되고 있는 것을 볼 수 있습니다. 이 반경과 시야를 기준으로 리플리케이션 위치를 결정합니다. NPC가 경계선 밖으로 나가면 리플리케이션이 중단됩니다.

Wall overlap: Verification Traces
- Visibility 트레이스 포인트가 벽밖으로 돌출될 수 있음
- 각 잠재적인 시야가 액터 뒤쪽에 트레이스되는지 테스트
- 액터가 벽에 가까워질 때 갑작스럽게 가려지는 것을 감소
It's worth bringing up the need to perform extra checks because to bring up example of this guy standing in this little half pipe. You can see that some of the visibility points that we're using are actually outside of the the walls that they're standing in, they overlap, which means for players standing next to them, next to the wall, this player would be this NPC would always be unfogged. So, we add a second check to verify that any point that establishes a line of sight can be traced back to the parent actor. so every line of sight check actually requires two traces to confirm that they should be unfogged, it's very helpful for reducing accidental unfogging.
추가적인 검사가 필요한 이유를 언급할 필요가 있습니다. 예를 들어, 이 작은 하프파이프 안에 서 있는 NPC를 생각해 보세요. 우리가 사용하는 비저빌리티 포인트 중 일부가 실제로 NPC가 서 있는 벽 바깥쪽에 있고, 서로 겹치는 부분이 있습니다. 즉, 벽 옆에 서 있는 플레이어의 경우, 이 NPC는 항상 보이게 됩니다. 따라서 시야를 확보하는 모든 지점이 부모 액터까지 트레이스 가능한지 확인하는 두 번째 검사를 추가합니다. 모든 시야 검사는 두 번의 트레이스를 통해 가려지지 말아야 하는지(unfogged) 확인해야 합니다. 이는 의도치 않은 시야 가리기를 줄이는 데 매우 효과적입니다.
And here's an example of the system running in Vanguard. You can see the behind this ridge. This is the wall hack simulation that we had earlier. So we can kind of see where everyone is. When we enable fog of war, it hides everything that we can't that we're not supposed to see. This guy we can still see cuz he's on top of the ridge. And then we can see the people down again there when we reach the top. as you'd expect.
여기 뱅가드에서 시스템이 작동하는 예시가 있습니다. 이 능선 뒷편이 보이시죠? 이건 아까 보여드렸던 월핵 시뮬레이션입니다. 그래서 모두의 위치를 대략적으로 파악할 수 있습니다. 전장의 안개를 활성화하면, 우리가 볼 수 없어야 하는 모든 것이 가려집니다. 이 사람은 능선 위에 있어서 여전히 볼 수 있죠. 그리고 정상에 오르면 아래쪽에 있는 사람들을 다시 볼 수 있습니다. 예상대로죠.


So how do we do this? what I can run through a high level overview of what we implemented to do make this happen. the work consists of two main components a subsystem fog of war subsystem which is a game engine subsystem running whenever the game is and a custom replication node because we use rep graph. So we implemented a node that uses the fog of war status to decide what to replicate. The subsystem tracks fogful actors. It performs physically checks, handles audio exposure events and emits events as appropriate. A fog of war event is just a map of player actors. the lists of actors that have been fogged or unfogged. So it's only sending information when things change.
그럼 어떻게 구현했을까요? 이 작업을 위해 구현한 내용을 간략하게 설명해 드리겠습니다. 이 작업은 크게 두 가지 구성 요소로 이루어져 있습니다. 하나는 게임 엔진 서브시스템인 '전장의 안개(Fog of War)' 서브시스템으로, 게임이 실행될 때마다 작동합니다. 다른 하나는 Replication Graph를 사용하기 때문에 구현한 커스텀 리플리케이션 그래프 노드입니다. 전장의 안개 상태를 기반으로 리플리케이트 대상을 결정하는 노드를 구현했습니다. 이 서브시스템은 안개에 가려진 액터를 트레이스하고, 물리적 검사를 수행하며, audio exposure events를 처리하고, 필요에 따라 적절히 이벤트를 발생시킵니다. 전장의 안개 이벤트는 플레이어 액터의 맵, 즉 안개에 가려졌거나 가려지지 않은 액터 리스트를 나타냅니다. 따라서 상황이 변경될 때만 정보를 전송합니다.

Looking a bit deeper into the subsystem, it maintains an array of unique visibility pairs, which are pairs of actors that can be fogged relative to one another. And the subsystem hooks into actor creation destroy delegates and it looks for specific classes of actors to decide what to include in the fog of war pairs. A unique visibility pair consists of a player and any other object which is foggable in the game. And the main thing that this subsystem does is iterate through those visibility pairs. I put some pseudo code for the tick function that's executed every frame. Essentially it checks to see whether because traces are asynchronous. We look for the results of the previous frames checks first and if anything's changed then we emit the foggable event and then we add any scheduled visibility checks to a list and sort that by lateness and then run through all of the checks up to our trace budget forming line of sight checks. I've added at the bottom you can see there's an sort of escape hatch condition which where if we exceed the trace budget for a frame we make all close pairs visible which it's like a reduces load on the system because they're the most burdensome check and avoids minimize hopefully any pop-in caused by pairs not getting to be checked in time and sort of falling behind and popping in a bit late replicating late.
이 서브시스템을 좀 더 자세히 살펴보면, 서로 상대적으로 안개에 가려질 수 있는 액터 쌍인 고유한 가시성 쌍(unique visibility pairs) 배열을 유지합니다. 이 서브시스템은 액터 스폰 및 파괴 델리게이트에 연결되어 특정 액터 클래스를 찾아 전장의 안개 쌍에 포함할 대상을 결정합니다. 고유한 가시성 쌍은 플레이어와 게임 내에서 안개에 가려질 수 있는 다른 모든 객체로 구성됩니다. 이 서브시스템의 주요 기능은 이러한 가시성 쌍을 순회하는 것입니다. 매 프레임마다 실행되는 틱 함수에 대한 의사 코드를 아래에 첨부했습니다. 기본적으로 트레이스가 비동기적으로 이루어지기 때문에 이전 프레임의 검사 결과를 먼저 확인하고, 변경 사항이 있으면 "foggable" 이벤트를 발생시킨 다음, 예약된 가시성 검사를 리스트에 추가하고 지연 시간 순으로 정렬한 후, 트레이스 예산 내에서 모든 검사를 실행하여 시야 확보 검사를 수행합니다. 아래쪽에 일종의 비상 탈출 조건을 추가했는데, 프레임에 대한 트레이스 예산을 초과하면 모든 가까운 쌍을 Visible로 만들도록 되어 있습니다. 이는 시스템 부하를 줄이는 데 도움이 됩니다. 가장 비용 부담이 큰 쌍을 검사하고, 쌍이 제때 검사되지 않아 뒤쳐지거나 리플리케이트가 늦어져 발생하는 팝인 현상을 최소화하기 위함입니다.
(→ 가까울수록 9개의 포인트에 대해 트레이스를 해야하므로 트레이스 예산을 초과했을 때 모든 가까운 쌍을 Visible로 만든다는 듯 하다)

The visibility pair handles or contains a state of all the state required for checking to objects. the time last checked when the next check should occur. It has functions for performing the checks themselves, taking into account level of distance and also handles audio exposure events.
가시성 쌍은 객체 확인에 필요한 모든 상태를 관리하거나 포함합니다. 마지막 확인 시간과 다음 확인 시간을 기록합니다. 또한 거리 수준을 고려하여 확인 자체를 수행하는 함수와 오디오 노출 이벤트도 처리합니다.

- FogOfWarExposureComponent는 모든 foggable 액터에 부착될 수 있습니다.
- AddExposure 이벤트를 통해 소유자가 지정된 반경 내에서 지정된 시간 동안 안개를 제거(unfogged)했음을 알립니다.
- Exposure 컴포넌트는 시야 트레이스와 함께 서브시스템 틱에서 가시성 쌍에 의해 확인됩니다.
- Exposure는 시야 트레이스와 달리 비대칭적으로 안개를 제거(unfog)합니다.
★ fog, unfog 용어가 계속 사용되는데,
fog : culled, destroy
unfog : spawn으로 바꿔 생각해도 된다.
audio exposure events we implement with a component that can be attached to any actor and it has a a function for adding an event which declares that owner unfogged for the appropriate time. exposure events unfog just one member of the pair and line of sight which unfogs both and the custom replication node that we implemented it's based on epics dynamic spatial frequency rep graph node it which dynamically modifies replication frequency of actors depending on how far they away from the player. we implemented that node and moved the dynamic spatial frequency calculations to a background task because which saves a fair bit of time on the game thread and it also looks for fog of war events and uses them in deciding whether or not to replicate an actor.
audio exposure 이벤트는 모든 액터에 부착할 수 있는 컴포넌트를 통해 구현했으며, 해당 액터가 적절한 시간 동안 안개가 제거되었음을 선언하는 이벤트를 추가하는 함수를 제공합니다. exposure 이벤트는 쌍의 멤버 중 하나만 안개를 제거하고, 가시성 이벤트는 2개의 멤버 모두의 안개를 제거합니다. 또한, 저희가 구현한 커스텀 리플리케이션 그래프 노드는 Epic Games의 Dynamic Spatial Frequency 리플리케이션 그래프 노드를 기반으로 하며, 플레이어와의 거리에 따라 액터의 NetUpdateFrequency(= RepPeriod?)를 동적으로 수정합니다. 저희는 이 노드를 구현하고 Dynamic Spatial Frequency 계산을 백그라운드 작업(= Asynchronous Task)으로 이동시켜 게임 스레드에서 상당한 시간을 절약했습니다. 또한, 전장의 안개 이벤트를 감지하고 이를 액터 리플리케이트 여부를 결정하는 데 사용합니다.

It also we also have a a custom replication graph which instantiates this node and we found it we had to filter RPC events on the on the fog status of an actor prevent issues where sending an RPC event to an actor that is fogged would briefly create that actor on the client and then uninstantiate them after the all have been processed.

so we implemented the system in our game midway through development. So we already had some levels of gameplay written and we sort of added the system and we discovered that the main issue was that actors would be written on the client not sort of expecting actors to persist on the client. They weren't really written for actors considering actors would disappear or like due to fogging. For example, projectile owners would disappear being fogged while the projectile was in mid combat. so essentially the work was to add a lot of safeguards around that. we also had issues around the the visibility channel occasionally like not being configured properly. so, actors with fog in full view of the player. so it's like a long process of adapting what we had to work with the system. turrets were particularly troublesome because they were embedded into scenery often and this would hide visibility points within the scenery and cause them to fog erratically.
그래서 저희는 개발 중간에 게임에 해당 시스템을 구현했습니다. 이미 일부 게임플레이 레벨이 작성된 상태였는데, 시스템을 추가하면서 가장 큰 문제점을 발견했습니다. 바로 액터가 클라이언트 측에서 작성되었지만, 클라이언트 측에서 액터가 유지될 것이라고 예상하지 못했다는 점입니다. 액터가 사라지거나 안개로 인해 사라지는 상황을 고려하지 않고 작성된 것이었습니다. 예를 들어, 전투 중에 Projectile이 안개에 가려지면 Projectile의 Owner가 사라지는 문제가 있었습니다. 따라서 기본적으로 이러한 문제를 해결하기 위한 여러 가지 안전장치를 추가하는 것이 작업이었습니다. 또한 Visibility Channel이 제대로 설정되지 않아 플레이어가 안개에 가려진 액터를 완전히 볼 수 있는 문제도 있었습니다. 기존 시스템을 시스템에 맞게 수정하는 데 시간이 오래 걸렸습니다. 특히 포탑은 문제가 심각했는데, 지형에 매립되는 경우가 많아 지형 내의 visibility point를 가리고 안개가 불규칙적으로(erratically) 발생하는 문제가 있었습니다.

And performance-wise it runs quite well. you can see on this trace graph this real insights graph there's the at the bottom there is the background task containing all the the raycasts of the system and at the top left a tick for the fog system where it's checking results and performing fogging and unfogging runs like it's it tends to run under half a millisecond on the game thread and perhaps up to a millisecond on the in the background task which is acceptable for our purposes. We we have it running with all players, NPCs and loot boxes in the game and in testing it's generally proven to be good enough for for our for our purposes.
성능 면에서도 상당히 잘 작동합니다. 이 Real Insights 트레이스 그래프에서 볼 수 있듯이, 맨 아래에는 시스템의 모든 레이캐스트를 포함하는 백그라운드 작업이 있고, 왼쪽 위에는 안개 시스템이 표시되어 결과를 확인하고 안개 생성 및 제거 작업을 수행합니다. 게임 스레드에서는 0.5ms 미만, 백그라운드 작업(= Asynchronous Task)에서는 최대 1ms초 정도 소요되는데, 이는 저희 목적에 충분히 만족스러운 수준입니다. 모든 플레이어, NPC, 전리품 상자를 게임에 추가한 상태로 테스트해 본 결과, 전반적으로 저희 목적에 충분히 부합하는 성능을 보여주었습니다.
'UE5 > Unreal Fest' 카테고리의 다른 글
| [UE5] Navigating Enhanced Input | Unreal Fest Gold Coast 2024 (0) | 2025.04.12 |
|---|