0
\$\begingroup\$

I'm making a 2D top-down survival game similar to Rimworld, I'm trying to emulate a day-night system.

Many features depend on whether any tile is light (or dark) enough, based on time of day or other light sources such as torches, so I'm wondering if there's an easy way to detect how much light there is on a certain tile without making a whole system that tracks light on a per-tile level (or maybe the latter option is better)?

I am using Unity 2020.3.6f1 and the URP, which means I'm going to be using the 'new' 2D lights.

EXAMPLE: When it's nighttime, I'm going to disable the global light (sun) so those tiles will be pitch black. When it's daytime, the global light will have an intensity of 1 so that tile will have 100% brightness (look normal). When it's nighttime, but there's a torch, the tile where the torch is sitting on will have 100% brightness, and that will slowly fade to 0% over a span of 5 tiles.

Making a system that updates every single tile in the game with information about the light level seems pretty expensive. Is there a way we can observe a block "graphically", or in other words, literally look at the pixel on the screen, and determine it's alpha value to check for light level?

Any suggestion is super welcome.

EDIT: Every tile does not need to know its brightness every frame, but they do need to know it less often. Some examples are:

  • A plant checking every couple of frames to see if it can grow
  • A character checking every couple of frames to see if he's in the dark (and if he is, he needs to start panicking)

More importantly, I need my characters to make decisions based on light amount. For an example, if there is a job that needs to be done at a certain location, a character should first check the light level of that location before going to do it.

\$\endgroup\$
6
  • \$\begingroup\$ Does every single tile care about its brightness every frame, or only certain special tiles or a partial sample of tiles you could check each frame? \$\endgroup\$
    – DMGregory
    May 3 at 14:51
  • 1
    \$\begingroup\$ This is actually a lot harder than it sounds due to the way render pipelines work. The lighting gets calculated on the GPU, which then sends the rendered results directly to the monitor. The CPU, where your game mechanics run, usually does not even know what's on the screen. But you could use a 2nd camera which renders to a render texture and then sample that render texture. \$\endgroup\$
    – Philipp
    May 3 at 15:01
  • \$\begingroup\$ Checking the alpha value of the render output won't tell you the brightness. It tells you the opacity of the pixel. When you want the brightness, then you need to look at the sum of the R, G and B channel of a pixel to get its brightness (and when you want to be completely accurate regarding how humans perceive brightness of different colors, then R * 0.21 + G * 0.72 + B * 0.07). \$\endgroup\$
    – Philipp
    May 3 at 15:08
  • \$\begingroup\$ @DMGregory I've updated the question for more clarity \$\endgroup\$ May 3 at 16:01
  • \$\begingroup\$ @Philipp this sounds a bit tricky to implement but could be faster. I wonder if doing some sort of "grid data structure" like ShoulO suggested is better? \$\endgroup\$ May 3 at 16:01
1
\$\begingroup\$

Sorry I am not bringing "graphical" solution. But as you said every suggestion is welcome.

So this should work also:

A script which is run on each torch, not each tile. Your torch reach is 5 tiles. So that is 5 tile radius. That means that one torch has to update 88 tiles. 88 calculations is not that expensive.

If torch is moving: You can achieve this by simply using Unity 2d circle collider. When new tiles enter collider you add them to the list, when tile leaves the collider you remove it from the list.

Then iterate through this list and check the distance from torch and tile and change its 'lightness' according to distance. And you do not need to run this calculation every frame. Use InvokeRepeating or Coroutines to shoot it every half a second or so.

You do not like Unity colliders? Fine, create 'grid data structure' - basically give each tile some sort of 'address', then update it in your torch based on its speed and moving direction. Then based on current tile address find its nearest neighbors and set their 'lightness' to max. Then find further neighbors and set their 'lightness'. Do it until 5 tile distance.

-- EDIT -- I think based on your comments and edits I now understand what is your situation , and what are you asking.

Your situation (and solution) is like this:

You can get rid idea of tiles all together. To solve this think that tiles just do not exist. What you really have is: several objects that need to know level of 'lightness'. A player, several plants, maybe few random cats. So write a script(s) that updates only these objects.

For light source 'sun' - use a simple timer (either a Coroutine or InvokeRepeating) and set global variable dayLight to different values based on daytime. Throughout a day this timer should tick only 10-20 times, setting the dayLight from 0 to 10, and when day fades - back from 10 to 0;

For your objects - player, plants, cats - create timer that fires function every few seconds or a bit more (or less) frequently. When fired this function first should check variable dayLight, then if day light is insufficient check a distance to nearest light source - a torch. To do this have all tourches in a global list, and iterate this list checking distance to each one. Or if you do not like lists, you can just use Unity 2d colliders to know that this cat or player entered 'torch area', then just check distance to it.

\$\endgroup\$
5
  • \$\begingroup\$ The problem is that I need to track the "lightness" level of all of the tiles in the map. Perhaps not every frame, but that light data needs to be available for reasons mentioned in the question update. Also, there could be tons of light sources so this could get expensive? \$\endgroup\$ May 3 at 16:03
  • \$\begingroup\$ why plant or character can not check its distance from light source? Again no real need to track all tiles. Make character 'panic' when he is far enough from light source. This also can be checked not every frame, but for example every second \$\endgroup\$
    – ShoulO
    May 3 at 16:06
  • \$\begingroup\$ @franticabyss You only need to update the light levels when a light source moves or changes intensity, not every light source every frame. You can easily do that by subtracting the old version of the light source from the lightmap and then adding the new one. That should not be very performance-critical, unless you have a lot of moving/changing light sources in the scene. \$\endgroup\$
    – Philipp
    May 3 at 16:08
  • \$\begingroup\$ I think I might have 20-40 moving light sources in the scene max. But again, I could have maps as big as 500x500. When the sun is rising or the night is falling, that would be updating the "lightness" of 250,000 tiles every single frame until it's completely light or dark. Wouldn't that be a problem? \$\endgroup\$ May 3 at 16:10
  • \$\begingroup\$ It seems that only some objects need to know level of 'lightness', not the tiles, am I correct? I updated my answer \$\endgroup\$
    – ShoulO
    May 3 at 17:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.