My original stealth system for Spamocalypse was based around the navigation system. Each Node has a variable for illumination, which is calculated using raycasts and then baked in. For point, area and spot lights, this is inversely proportional to the distance from that light; for the sun/moon, the value of the sun’s intensity is just added without any processing. When an NPC or the player is trying to check if they can be seen at a player, they query the NavigationMesh to get the closest Node, and then check the illumination of that Node.
What’s the problem?
This is expensive! As I covered in previous post, this causes a performance impact. However, the fix I made for that causes another issue while trying to calculate the effect that street lamps would have on the lighting. They had absolutely no effect – it was possible to stand underneath one, and the NPCs would simply ignore you if there were no other lights around. I suppose I could have rewritten that as a “Refuge In Audacity” thing, where the spammers wouldn’t see you because they wouldn’t believe someone would stand under a street lamp in full view of them…but that’s another game, I think.
The street lamps I have are essentially a long, narrow box which acts as a parent for a point light. When the light was casting rays to check a line of sight to different nodes, it just hit the inside of the collider. So, the next thing I tried was disabling the parent collider for those lights, but I ended up accidentally disabling other scenery colliders. When some other point lights were casting rays around themselves, they ended up hitting points that were outside the navigation mesh, which caused the BuildNavMesh class to throw an exception.
WTF was I thinking?
I just didn’t know of any better way to do this. My first thought was to try using Unity’s LightProbes, but I started Spamocalypse the free version of Unity 4.6, and they weren’t available to free users at the time. I also don’t know if the API for LightProbes allows the intensity to be retrieved as a float, which is what I was looking for. I also had some experience at trying to generate a pathfinding system for my Master’s thesis, so I decided to give that a try, as I knew the general concept would work.
The actual solution
After looking around the Unity forums, I’ve created a physics-based solution that uses trigger colliders and raycasts. Each spot, area and point light has a trigger collider attached, which tracks instances of a CalculateLight component that are inside the collider. If the light has a “line of sight” to that CalculateLight, the light contributes to it’s total intensity. The amount contributed is the intensity of the light, divided by the distance between the light and the object it’s tracking, and at the moment is clamped to a maximum of 1.
There is only ever 1 directional light in a scene, so the Sun component handles this. It has a list of CalculateLight components, and tracks each of them using FixedUpdate. It basically casts a ray to check if the object can be seen, and toggles whether or not it “visible” to the sun/moon. If a CalculateLight component suddenly is in the sun, it’s total intensity is immediately increased by the intensity of the sun, and stays that way until the character can no longer be seen.
Does it work?
Yes, it does. I haven’t done a full test over the City Inbound level yet, but it doesn’t take as long to calculate, and it actually works in real-time – so, I could possibly use this for things like spotlights or torches in future projects. I will update this with the full test results.