Saudações!
Eu sei, e no fundo você sabe quais estão faltando seus jogos de cartas ou três em um. Sistemas furtivos!
E, é claro, qualquer sistema de furtividade que se preze deve poder levar em conta a iluminação do ambiente ao redor do jogador. Fiquei surpreso ao desenterrar o tópico e encontrar uma quantidade anormalmente pequena de informações. Portanto, apresso-me a compartilhar os frutos.
Hoje não desenvolveremos um sistema de furtividade completo para o jogador, consideraremos puramente interações com a iluminação.
Método 1: coletores
Uma maneira simples e com pouco uso de recursos.
Adicionamos um colisor esférico a cada fonte de luz. Tornamos isso um gatilho. Definimos as dimensões aproximadamente iguais ao raio da luz.
O resto é tão claro quanto uma sombra. Estamos escrevendo um script simples, onde OnTriggerEnter () colocamos a ativação do cálculo da luz (para que as fontes de luz não funcionem "ociosas" quando o player não estiver por perto).
O próprio cálculo de iluminação estará localizado em Update (). Em essência, este é o Physics.Raycast usual (). Se atingir o jogador, ele estará na zona de luz. Se não cair, significa que o jogador está atrás de obstáculos e, portanto, na sombra.
Você também pode adicionar aqui o cálculo da distância entre o player e a fonte de luz. Assim, para determinar a iluminação, usaremos um flutuador simples, que variará dependendo da distância das fontes de luz. E você pode usá-lo onde seu coração desejar.
Exemplo
No ponto 1, a iluminação está próxima do máximo. No ponto 2, a iluminação é mínima - há um obstáculo entre a luz e o ponto. No ponto 3, a iluminação é média. E isso não é tudo! Você pode adicionar coletores de gatilho a várias "zonas de sombra" onde o jogador deve se esconder. Nas melhores tradições de Manhunt. Da mesma forma, você pode marcar as zonas brilhantes com o colisor, simulando, por exemplo, a luz de um refletor.
Vantagens:
- Fácil de personalizar Luzes pontuais.
- Bastante econômico em termos de recursos, se você não envia spam para fontes de luz.
Desvantagens:
- A luz do ponto e a luz direcional estão fortemente ajustadas. Se, pela primeira vez, basta posicionar o colisor no campo de luz (para aumentar a visibilidade do jogador na entrada), o segundo é um verdadeiro horror. Você precisa colocar coletores em cada sombra (para reduzir a visibilidade do jogador na entrada) ou verificar constantemente com Physics.Raycast () entre o jogador e o "sol" - ele está localizado sob os raios ou na sombra.
- Um grande número de colecionadores atravessa a cena, complicando a física.
- Cuidados devem ser tomados com fontes de luz que se cruzam.
- A luz dinâmica (movimento ou mudança de intensidade) precisa ser adicionada separadamente por meio de scripts.
Método 2: RenderTexture
O que estamos fazendo aqui? De fato, obtemos uma "captura de tela" da câmera, e não necessariamente da câmera principal. E então analisamos a cor da captura de tela para descobrir como a luz cai sobre o assunto.
Para começar, precisamos de um objeto do qual "leremos" a luz. Crie uma esfera ou plano regular, faça-o pequeno (escala 0.1), coloque-o próximo ao chão, faça-o branco, remova o colisor:
Adicione uma câmera (remova o ouvinte de áudio e verifique se a etiqueta MainCamera não vale a pena). Vincule-o ao nosso objeto. Colocamos um pouco mais alto, direcionamos para baixo. Nós expomos nas configurações não a tela principal. Fazer ortografia é para o seu gosto.
No final, nós o posicionamos de modo que ele olhe para o nosso objeto e apenas para ele.
No final, configure a máscara de descarte das câmeras principal e secundária para que a principal não exiba nossos objetos “leves”, enquanto as secundárias os veem apenas sem desordenar mais nada.
E aqui começa a diversão. Anexamos o script à câmera:
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; }
Na saída, obtemos um float vis, que, em essência, é uma representação numérica do nível de iluminação incidente em nosso objeto. Se a fonte estiver próxima - o objeto é branco - vis é 1. Se estiver escuro - o objeto é preto - vis é ~ 0.
Não precisamos executar a operação acima em todos os quadros, portanto incorporamos um pequeno segundo temporizador:
float interval = 0; void Update () { interval += Time.deltaTime; if (interval < 1) return; interval = 0; // }
Em seguida, amarramos todo o sistema ao jogador para que ele se mova com ele. E nossa variável vis retorna automaticamente a luz ao redor do player!
Este sistema pode ser usado não apenas em conjunto com o reprodutor. Você pode colocá-lo em qualquer lugar e como quiser, criando um tipo de sensor de luz. Como regra, existem maneiras mais eficazes de implementá-las, mas é sempre bom ter alternativas?
As vantagens são óbvias, vamos falar sobre as desvantagens.
Desvantagens
- Cada "detector de luz" (se houver mais de um) requer uma câmera separada.
- Texture2D.ReadPixels () - bem, extremamente lento. Mesmo que você faça isso uma vez por segundo, e nem todos os quadros, mesmo que você divida as funções de escrever e ler texturas em quadros diferentes, ainda existem layouts entre 40 e 110 ms.
- Este sistema não leva em consideração alguns casos raros. Por exemplo, uma lanterna brilha em um personagem. O personagem está bem iluminado, mas a luz cai sobre ele e atrás dele, e não para baixo, respectivamente, nosso detector de luz mostra um baixo nível de iluminação. Você pode resolver o problema, por exemplo, colocando o detector não no chão, mas no nível do peito do personagem. Então você precisa colocar duas câmeras em lados opostos para ler a luz de ambos os lados. O que diminuirá a velocidade do sistema pela metade.