Greetings
I know, and you know deep down, what your card games or “three in a row” games are missing. Stealth systems!
And of course, any self-respecting stealth system must be able to take into account the ambient light around the player. I was amazed to dig up the topic and find an abnormally small amount of information. Therefore, I hasten to share the fruits.
')
Today we will not develop a full-fledged system of secrecy for the player, consider purely interaction with lighting.
Method 1: colliders
Simple and not very resource-intensive way.
To each light source we add a spherical collider. Making it a trigger. We set the dimensions approximately equal to the radius of light.
The rest is clear as a shadow. We write a simple script, where in OnTriggerEnter () we place the activation of the calculation of illumination (so that the light sources do not work "idle" when there is no player nearby).
The light calculation itself will be located in Update (). In fact, this is the usual Physics.Raycast (). If it hits the player, the player is in the light zone. If it doesn't, then the player is behind the obstacles and, therefore, in the shadows.
Also here you can add the calculation of the distance between the player and the light source. Thus, to determine the illumination we will have a simple float, which will vary depending on the distance to the light sources. And you can use it where your heart desires.
Example
At point 1, the illumination is close to the maximum. At point 2, the illumination is minimal - between the light and the point of the obstacle. At point 3, the illumination is average. And that's not it! You can add trigger colliders to various “shadow zones” where the player must hide. In the best traditions of Manhunt. Similarly, you can mark bright zones with a collider, simulating, for example, a spotlight.
Benefits:
- Easy to set up point lights.
- Quite economical in terms of resources, if not spam light sources.
Disadvantages:
- Spot light and Directional light are heavily tuned. If for the first it is enough to rationalize the collider in the field of light (to increase the visibility of the player at the entrance), then the second seems to be a real horror. It is necessary either to place colliders at each shadow (to reduce the player’s visibility at the entrance), or to constantly check with Physics.Raycast () between the player and the “sun” - the one under the rays or in the shade.
- A large number of colliders litter the scene, complicating physics.
- Care must be taken with intersecting light sources.
- Dynamic light (moving or changing intensity) should be added separately through scripts.
Method 2: RenderTexture
What are we doing here? In fact, we get a “screenshot” from the camera, and not necessarily from the main camera. And then analyze the color of the screenshot to find out how bright the light falls on the subject.
First we need an object from which we will “read” the light. Create a normal sphere or plane, make a small one (scale 0.1), place it close to the floor, make it white, remove the collider:
Add a camera (be sure to remove the audio listener and check that the MainCamera tag is not worth it). Bind it to our object. We put it a little higher, we direct it down. We set in the settings not the main display. Whether to make it orthographic is to your taste.
At the end we position it so that it looks at our object and only at it.
At the end, we set up the Culling mask of the main and secondary cameras so that the main one does not display our “light” objects, and the secondary ones only see them without being littered with anything else.
And here the most interesting begins. We tie a script to the camera:
public Camera cam; // RenderTexture tex; Texture2D _tex; void Start () { // "". // , - . // Depth 0 - . tex = new RenderTexture (1, 1, 8); // RenderTexture "" , // , . _tex = new Texture2D (1, 1, TextureFormat.RGB24, false); } void Update () { // "" cam.targetTexture = tex; cam.Render (); // RenderTexture.active = tex; // Texture2D _tex.ReadPixels (new Rect (0, 0, 1, 1), 0, 0); _tex.Apply (); Color col = _tex.GetPixel (0, 0); float vis = (col.r + col.g + col.b) / 3; }
At the output, we obtain a float vis, which, in essence, is a numerical representation of the level of illumination falling on our object. If the source is close - the object is white - vis is 1. If it is dark - the object is black - vis is ~ 0.
We do not need to perform the above operation every frame, so we embed a small second timer:
float interval = 0; void Update () { interval += Time.deltaTime; if (interval < 1) return; interval = 0; // }
Next, we tie our entire system to the player so that it moves with it. And our vis variable automatically provides the illumination around the player!
This system can be used not only in conjunction with the player. You can place it anywhere and in any way, creating a kind of light sensors. As a rule, there are more effective ways to implement them, but is it always nice to have alternatives?
The advantages are obvious, let's talk about the disadvantages.
disadvantages
- Each “light detector” (if there are more than one) requires a separate camera.
- Texture2D.ReadPixels () is extremely slow. Even if you do it once a second, and not every frame, even if you break the functions of writing and reading textures into different frames, you still have 40-110ms of spans.
- This system does not take into account some rare cases. For example, on the character shine a flashlight. The character is well lit, but the light falls on him and behind him, not down, respectively, our light detector shows a low level of illumination. You can solve the problem, for example, by placing the detector not at the floor, but at the level of the character’s chest. Then you need to put two cameras on opposite sides to read the light from either side. That will slow the system even twice.