Делаем «туман войны» как в стратегии

Туман войны – это закрашенное черным цветом поле карты. Используется для того чтобы скрыть действия игроков друг от друга, в том случае если «туман войны» динамический, то есть, видимым остается только определенный радиус, вокруг юнита. Но не только в стратегии может пригодиться такая штука, например, статический «туман» отлично подойдет к ролевой игре с видом сверху, когда нужно чтобы открытые области, более не скрывались. Впрочем, можно сочетать оба типа и добиться неплохого эффекта. В этом уроке попробуем разобраться, как вообще можно это сделать. Затронем основы, так сказать.


Так, делаем всё по порядку, а затем разберемся в логике работы.

Нарисуем маску, чистого зеленого цвета, на прозрачном фоне, в разрешении 250х250:


Примерно вот такого вида. И сохраняем в формате png.

Затем в редакторе импортируем как текстуру и ставим Wrap Mode > Clamp, всё остальное по умолчанию.

Создаем обычный Plane, делаем его дочерним к персонажу, потом создаем материал и выбираем шейдер Particles > Alpha Blended и кидаем в него нашу текстуру маски. Должно получится примерно вот так:

Делаем «туман войны» как в стратегии

Создадим еще один слой FogOfWar_Mask и назначим маске его:


Окей, с этим всё. Маска готова.

Создаем новый шейдер FogOfWar:

Shader "Custom/FogOfWar" {

	Properties 
	{
		_Color("Main Color", Color) = (1,1,1,1)
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_Blur("Blur", Range(0,0.003)) = 0.0015
	}
	
	SubShader 
	{
		Tags { "Queue"="Transparent" "RenderType"="Transparent" "LightMode"="ForwardBase" }
		Blend SrcAlpha OneMinusSrcAlpha
		Lighting Off
		LOD 200
		
		CGPROGRAM
		#pragma surface surf Lambert alpha
		#pragma target 3.0
		
		fixed4 LightingNoLighting(SurfaceOutput s, fixed3 lightDir, float aten)
		{
			fixed4 color;
			color.rgb = s.Albedo;
			color.a = s.Alpha;
			return color;
		}

		fixed4 _Color;
		sampler2D _MainTex;
		float _Blur;

		struct Input 
		{
			float2 uv_MainTex;
		};

		void surf (Input IN, inout SurfaceOutput o) 
		{
		        half4 baseColor1 = tex2D (_MainTex, IN.uv_MainTex + float2(-_Blur, 0));
			half4 baseColor2 = tex2D (_MainTex, IN.uv_MainTex + float2(0, -_Blur));
			half4 baseColor3 = tex2D (_MainTex, IN.uv_MainTex + float2(_Blur, 0));
			half4 baseColor4 = tex2D (_MainTex, IN.uv_MainTex + float2(0, _Blur));
			half4 baseColor = 0.25 * (baseColor1 + baseColor2 + baseColor3 + baseColor4);
			o.Albedo = _Color.rgb * baseColor.b;
			o.Alpha = _Color.a - baseColor.g;
		}
		
		ENDCG
	} 
	Fallback "Diffuse"
}

И сразу создаем материал с таким же именем и выберем в нем наш шейдер, который будет по адресу Custom > FogOfWar.

Далее, добавим на сцену Plane, это будет "туман". Растягиваем его так, чтобы он покрывал карту (сохраняя форму квадрата). По высоте настраиваем, чтобы он был выше зданий, игрока и т.п., но не слишком высоко, главное - скрыть объекты. Кидаем на Plane материал FogOfWar, который мы создали ранее. А в качестве текстуры к нашему материалу у нас будет Render Texture.

Создаем ее с параметрами, как на скриншоте:


Переименуем в FogOfWarRT и кидаем в материал "тумана":


Настраиваем цвет и прозрачность по вкусу.

Настроим главную камеру, Depth = 0, а Culling Mask выбираем всё кроме маски:


Добавим еще одну камеру и делаем всё - как показано ниже:


Суть в том, что она должна смотреть на карту сверху вниз, а параметр Size увеличиваем, чтоб в поле камеры уместился "туман" и карта соответственно. Не стоит переживать за дополнительную нагрузку, потому как камера будет обрабатывать только один слой с масками.

Вешаем на эту камеру скрипт FogOfWarMode:

using UnityEngine;
using System.Collections;

[RequireComponent (typeof(Camera))]

public class FogOfWarMode : MonoBehaviour {

	public bool isDynamic;
	private Camera _camera;

	void Start () 
	{
		_camera = GetComponent<Camera>();
		_camera.clearFlags = CameraClearFlags.Color;
	}

	void OnPostRender () 
	{
		if(!isDynamic)
		{
			_camera.clearFlags = CameraClearFlags.Depth;
		}
	}
}

Если поставить галочку isDynamic, то туман будет динамический.

Собственно, если всё сделано правильно, то должно работать.

Теперь о том как это работает. Дополнительная камера обновляет Render Texture, фон рисует синий, а на нем зеленую маску. Эту текстуру мы используем на Plane, где шейдер вырезает маску и мы получаем прозрачную область. Как-то так...

Скачать этот проект можно тут:

У вас нет доступа!

Внимание! У Вас нет прав для просмотра скрытого текста.

Комментариев 25

Офлайн
DayFall 20 октября 2015
В целом неплохо, но не хватает статичного тумана (затемнение посещенных областей, которые затем покинул игрок) и в шейдере есть лишний код:

fixed4 LightingNoLighting(SurfaceOutput s, fixed3 lightDir, float aten)
{
fixed4 color;
color.rgb = s.Albedo;
color.a = s.Alpha;
return color;
}

Функцию целиком можно убрать, не используется.
Офлайн
Light 21 октября 2015
Цитата: DayFall
затемнение посещенных областей, которые затем покинул игрок

Опция public bool isDynamic;
Офлайн
DayFall 24 октября 2015
Цитата: Light
Цитата: DayFall
затемнение посещенных областей, которые затем покинул игрок

Опция public bool isDynamic;

Не работает. И хватит тереть мои каменты.
Офлайн
Light 24 октября 2015
Цитата: DayFall
Не работает.

Работает. Но если речь идет о постепенном затемнение, то его нет.

Цитата: DayFall
И хватит тереть мои каменты.

Я как нибудь сам решу, что мне делать.
Офлайн
отличный туман войны огромное спасибо!!!
Офлайн
скажие а можно как нибдь сохранить раскрытые области в сейв?
Офлайн
Light 1 ноября 2015
Цитата: Виталий Дёмин
скажие а можно как нибдь сохранить раскрытые области в сейв?

Хм, ну возможно...
Надо сделать снимок RenderTexture, чтобы сохранить текущий рисунок, открытых областей. Затем, на старте сцены, поставить этот скриншот как текстуру, вместо RenderTexture. Камера по прежнему будет рендрить в RenderTexture и получится что изображение со скриншота будет передано туда. После, вернуть всё как было. Как-то так наверно...
Офлайн
GreatPasta 11 марта 2016
Failed to import package with error: Couldn't decompress package =(

unity 5.3.3f1 Personal
Офлайн
Light 12 марта 2016
GreatPasta, проверил на той же версии, проблем нет.
Офлайн
And8 3 октября 2016
Подскажи, почему когда туман динамический, вокруг персонажа плавный контур, а когда нет - рваные края, как это исправить?
Офлайн
Light 3 октября 2016
And8, можно попробовать покрутить Blur в настройках шейдера.
Офлайн
Svvrvch 30 декабря 2016
Сделал всё в точности по инструкции, но почему-то возникла такая проблема(на снимке):
http://savepic.ru/12521632.png
Свет находится в дали от игрока. Если нужно то скину демо сцену.
Офлайн
Light 30 декабря 2016
Svvrvch, скинь сцену, посмотрим...
Офлайн
Svvrvch 30 декабря 2016
Вот сцена:
https://drive.google.com/file/d/0Bw8XK-ofukLnTm5Wd21ycXItX3M/view
Офлайн
Light 30 декабря 2016
Svvrvch, во первых, нужно сделать так, чтобы маска не вращалась вместе с персом. Во вторых, позиция FogOfWarCamera по Х и Z, должна быть такой же как и у Plane. Далее, Orthographic Size камеры FogOfWarCamera высчитывается по формуле: Х * 0.5f * 10f где Х это Scale одной из сторон, квадрата Plane. Кстати эта формула годится только для стандартного объекта Plane в Юнити.

Главную камеру тоже можно перевести в режим Orthographic, раз она строго под прямым углом.
Офлайн
Svvrvch 30 декабря 2016
Light, спасибо.
Офлайн
cry_san 16 января 2017
А что насчет 2D? Такое возможно, если на карте только плоские спрайты?
Офлайн
Light 16 января 2017
cry_san, конечно, всё тоже самое делать, а меш тумана выдвинуть на камеру, чтобы спрайты были на заднем плане.
Офлайн
cry_san 19 января 2017
Light,
Помаялся и плюнул. Вспомнил высшую математику и сделал расчеты самостоятельно ))
Офлайн
Light 19 января 2017
cry_san, что тут маятся?) Меш поверх спрайтов, камеру "тумана" разворачиваем на меш, в остальном тоже самое, рендер текстура и маска без изменений.
Офлайн
cry_san 25 января 2017
А как у тумана сделать четкие границы?
Офлайн
Light 25 января 2017
cry_san, блюр выкрутить на ноль в шейдере.
Офлайн
ArturST 2 мая 2017
не хочет работать на андроиде (черный екран) что может быть причиной?
Офлайн
ArturST 2 мая 2017
в редакторе все четко работает , но после установки на планшет после загрузки уровня черный екран и только UI, почему-то шейдер не вырезает маску, в шейдере возможно под андроид нужно чет менять. В шейдерах не силен может есть какое решение или мысли по этому поводу , был бы благодарен за подсказку .
Офлайн
Light 3 мая 2017
ArturST, на юнити 5.6.0 делал сборку, запускал на андройде 4.1.2... вроде работает. Но в любом случаи, я шейдеры не пишу пока...
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Дешевый хостинг
  • Яндекс.Метрика