[Solved] Detecting Player in Multiple, Sometimes Overlapping "Light" Areas

tristonhopkins

  • Posts: 69
So for my current project, shadows and light are a huge gameplay aspect.  I need to be able to detect when the player is in an area that is lit up (a radial area defines the light source, so a radial area would work best for detection as well) and some of the lighted areas overlap.

I'm trying to use distance to the center of the light source to define the "light value", because the farther from the center of the light source, the darker the light is.  When the player is within two areas that overlap, I need it to calculate the distance within both areas, again, the light value, and then put that into one number that I can use to determine how visible the player is.

I tried using the basic [absolute value of [(Actor1 X - Actor2 X) ^ 2]] + [absolute value of [(Actor1 Y - Actor2 Y)] ^ 2]] for each light source and then adding them into a game attribute, but the number just continually increases, and I would like to be able to define the detected area the same way I use the draw circle block for the light source (ie: a radius of 32 would be the same in both)

I really hope I'm making some sense, and if not, please let me know where I need to elaborate.  Thanks! :)

« Last Edit: October 09, 2012, 11:45:49 am by tristonhopkins »

Hectate

  • *
  • Posts: 4643
If you used the true Pythagorean Theorem you could get the distance as an exact value to compare to the radius.  Combine that with the For Each Actor of Type block and you can look at the distance to all of them. Just ignore values greater than the radius.
:
:
Patience is a Virtue,
But Haste is my Life.
Proud member of the League of Idiotic Stencylers; doing things in Stencyl that probably shouldn't be done.

tristonhopkins

  • Posts: 69
I'm trying to understand how to implement your advice but I can't figure out how to get it to not add the distance to every light source.  I only want them added to the total if the player is within their radius.  I'm also trying to add in such a way that the center of a light source equals 256, for example, and the edge of the radius of the light source equals 0.  Outside of a light source should always equal 0.  I'm rattling my brain trying to figure out how to get it to work right.

Not sure if it'll be of any help but here's an attempt at showing what I'm talking about.  The area in the middle of the lights, since they're overlapping, should be added together.  At least, when the player is within that area, of course.

« Last Edit: October 08, 2012, 07:52:58 pm by tristonhopkins »

tristonhopkins

  • Posts: 69
Spent almost my entire night trying to figure this out with no luck.  I'm about to slam my head on my desk.

captaincomic

  • *
  • Posts: 6109
So if you want to know the light intensity of some point in the room, you would have to measure the distance to all light sources, convert it somehow to intensity and then add it up.

In your image it looks like the light is 256 at the source and 0 when you are about 64 pixels away.
To get this you can do
intensity = 256 - distance * 256/64 = 256 - distance * 4
or better, to prevent it from getting negative
intensity = larger of (256 - distance * 4, 0)

Something like this:
<set Total Intensity to 0>
<for each actor of type <Light>>
  <set Distance to <sqrt <<<<x-center of <Self>> - <x-center of <actor of type>>>^2>
     + <<<y-center of <Self>> - <y-center of <actor of type>>>^2>>
  <set Intensity to <larger of <256 - <Distance> * 4> and 0>>
  <set Total Intensity to <<Total Intesity> + <Intesity>>>


tristonhopkins

  • Posts: 69
Awesome, thank you.  I just got in bed when I got notified of your reply.  Quite tempted to get up and try this out.

Hectate

  • *
  • Posts: 4643
To help you see why you'd be using that exact math here is a brief explanation of what is happening so you know how/why to use this in the future;

1. If you divide the current distance by the max distance you get a result expressed as a decimal.
Code: [Select]
Distance / MaxDistance = DistanceFactorFor example, at a distance of 16 pixels with a max distance of 64...
Code: [Select]
16 / 64 = .25
2. Since your DistanceFactor is now a decimal, it is functionally a percentage of any value that is multiplied by it.
Code: [Select]
DistanceFactor * MaxIntensity = IntensityExcept if we do this calculation we get a wrong value!
Code: [Select]
.25 * 256 = 64 intensity //we need it to be 192!
3. Why? Because .25 is 25% but the percent you actually want is 75% (1/4 distance is 3/4 intensity). We need to reverse the DistanceFactor for the value to increase to the center and decrease to the outside.
Now, if you know your DistanceFactor is never going to be over 1 (by not using those values or setting them to 1 if they're greater than 1) you can just do a quick subtraction to reverse the value.
Code: [Select]
1 - DistanceFactor = ReversedFactorSo this becomes
Code: [Select]
1 - .25 = .75
4. Now if we recombine our two pieces of the puzzle we can get the intensity value for any light.
Code: [Select]
Intensity = ReversedFactor * MaxIntensityor to break it down again...
Code: [Select]
Intensity = (1 - DistanceFactor) * MaxIntensityor to break it down again...
Code: [Select]
Intensity = (1 - (Distance / MaxDistance)) * MaxIntensityand to show the numbers...
Code: [Select]
Intensity = (1 - (16 / 64)) * 256 //value is 192!

Note: captaincomic's approach is legitimate as well but it is less flexible. He noticed that there was a close relationship between your MaxIntensity and your MaxDistance (256 = 64*4) and used that as a shortcut. BUT if you were ever to have variations in your lights, such as lights with lower/higher intensity values or different distances (perhaps a candle that only had 32 max intensity over 16 pixels distance?) you wouldn't be able to use the same code. Using the method above, however, you could get the intensity for any "light" actor by assigning different MaxIntensity and MaxDistance values to each one and just using the "get attribute" block inside of your "for each actor" loop when checking each one.
:
:
Patience is a Virtue,
But Haste is my Life.
Proud member of the League of Idiotic Stencylers; doing things in Stencyl that probably shouldn't be done.

captaincomic

  • *
  • Posts: 6109
Actually I think your method and mine are the same, it's just from a different perspective. :)

Your formula is:
Code: [Select]
Intensity = (1 - (Distance / MaxDistance)) * MaxIntensity
My formula is:
Code: [Select]
Intensity = 256 - Distance * 256/64(where 256 is the maximum intensity and 64 is the maximum distance)

So that's
Code: [Select]
Intestity = MaxIntensity - Distance * MaxIntensity/MaxDistanceIf you expand the product in your formula it wil get you the same.

But yeah, it's better to keep it flexible by using MaxIntesity and MaxDistance variables instead of hardcoded values.

Hectate

  • *
  • Posts: 4643
Ah yes, now that you've replaced the values with variables I see what you mean. I do like mine because the order of operations is explicit which works well with the math blocks in design mode (since the way you nest them affects the order of operation) but I can see how handy your approach can be if you're just typing it out in code.
:
:
Patience is a Virtue,
But Haste is my Life.
Proud member of the League of Idiotic Stencylers; doing things in Stencyl that probably shouldn't be done.

ipe 369

  • Posts: 1001
may i ask how you drew the shadows? did you just draw actors? because you can't just draw light onto darkness, that doesn't work

tristonhopkins

  • Posts: 69
@ Hectate and captaincomic
Thank you for all of your help!  I have it working perfectly now.  I can't thank you enough for explaining how to do the math step-by-step (and why).  Math was never my strong subject, hence why Stencyl appeals to my desire to program.

@ ipe369
It would probably be easier to just show you my behavior that I'm using.  I probably could be doing this MUCH better than I already am, but it works and that's good enough for right now.


« Last Edit: October 09, 2012, 12:31:10 pm by tristonhopkins »

ipe 369

  • Posts: 1001
You draw the light on ?!?! I've no idea how that works! I presumed drawing the light on would just obscure your vision more :) I presume you couldn't do a full darkness :)

tristonhopkins

  • Posts: 69
I'm fairly certain the only reason it works is because of the various opacity levels and blend modes for the shadows and lights, respectively.

Also, the lights are drawn multiple times to make them much more intense in appearance.  Drawing them only once makes them not show up much at all.

I've uploaded what I have working so you can see what it looks like in action.
http://www.stencyl.com/game/play/14957

« Last Edit: October 09, 2012, 11:58:32 am by tristonhopkins »

ipe 369

  • Posts: 1001
you mean it would work with black ?
also i congratulate you on a spectacular looking game!

tristonhopkins

  • Posts: 69
you mean it would work with black ?
also i congratulate you on a spectacular looking game!
Thank you!

When I draw the darkness I'm using blend mode multiply, so it's not entirely black.  The closest I could get was using a loop to draw the darkness twice, but it dulled the effect of the lights.  I tried increasing the amount of times the lights were drawn and the light just looked blown out and over-saturated.  I might try fiddling with it to see if I can yield a better result.