天天看點

Unity MOBA類型遊戲的戰争迷霧效果

基于視野(FOV)的戰争迷霧,例如LOL的視野:滑鼠右鍵點選地闆,目标移動,同時顯示角色周圍視野,滑鼠滾輪可以調節遠近。

Unity MOBA類型遊戲的戰争迷霧效果

Unity版本:2019.4.1f1   

1.建立工程---右鍵3D Object---Terrain,随便刷一個地形,盡量高低錯落,設定地形大小為100*100

Unity MOBA類型遊戲的戰争迷霧效果

2.導入檔案,在Camera上添加Fog Of War Effect腳本,腳本會自動添加<Flare Layer>元件,Unity 2018之前的版本需要添加<GUI Layer>元件,2019以後版本被舍棄,無需添加

Unity MOBA類型遊戲的戰争迷霧效果

 3.右鍵建立一個膠囊,模拟玩家,添加NavMeshAgent元件,添加Fog Of War Explorer腳本

Unity MOBA類型遊戲的戰争迷霧效果
using UnityEngine;
using UnityEngine.AI;

/// <summary>
/// 視野資料-由于計算fov是在子線程操作,通過将視野資料以object類型參數傳入,
/// 使用簡單資料類型或結構體會産生裝箱操作,是以将視野封裝成類
/// </summary>
public class FOWFieldData
{
    public float radiusSquare;
    public Vector3 position;
    public float radius;

    public FOWFieldData(Vector3 position, float radius)
    {
        this.position = position;
        this.radius = radius;
        this.radiusSquare = radius * radius;
    }
}

/// <summary>
/// Player
/// </summary>
public class FogOfWarExplorer : MonoBehaviour
{
    [Range(10, 25)] public float radius = 15;//視野半徑

    private Vector3 m_OriginPosition;
    private FOWMapPos m_FowMapPos;
    private FOWFieldData m_FieldData;
    private bool m_IsInitialized;

    private float distance = 24;//錄影機高度
    private float angle = 45;//錄影機偏移角度
    private Vector3 m_CameraPosition;
    private NavMeshAgent m_Agent;

    void Start()
    {
        m_FieldData = new FOWFieldData(transform.position, radius);

        m_Agent = gameObject.GetComponent<NavMeshAgent>();
        Camera.main.transform.rotation = Quaternion.Euler(angle, 0, 0);
        Camera.main.transform.position = transform.position - Camera.main.transform.forward * distance;
    }

    void Update()
    {
        if (radius <= 0)
            return;
        if (m_OriginPosition != transform.position)
        {
            m_OriginPosition = transform.position;
            var pos = FogOfWarEffect.WorldPositionToFOW(transform.position);
            if (m_FowMapPos.x != pos.x || m_FowMapPos.y != pos.y || !m_IsInitialized)
            {
                m_FowMapPos = pos;
                m_IsInitialized = true;
                m_FieldData.position = transform.position;
                m_FieldData.radius = radius;
                //FogOfWarEffect.SetVisibleAtPosition(m_FieldData);
                FogOfWarEffect.UpdateFOWFieldData(m_FieldData);
            }
        }

        m_CameraPosition = transform.position - Camera.main.transform.forward * distance;
        Camera.main.transform.position = Vector3.Lerp(Camera.main.transform.position, m_CameraPosition, Time.deltaTime * 3f);
        BasicOperation();
    }

    void OnDestroy()
    {
        if (m_FieldData != null)
            FogOfWarEffect.ReleaseFOWFieldData(m_FieldData);
        m_FieldData = null;
    }

    void OnDrawGizmosSelected()
    {
        Gizmos.color = Color.blue;
        Gizmos.DrawWireSphere(transform.position, radius);
    }


    void BasicOperation() 
    {
        if (Input.GetMouseButtonDown(1))//滑鼠右鍵點選地闆
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit))
            {
                m_Agent.SetDestination(hit.point);
            }
        }

        if (Input.GetAxis("Mouse ScrollWheel") > 0)
        {
            if (distance >= 7)
                distance -= 2;
        }

        if (Input.GetAxis("Mouse ScrollWheel") < 0)
        {
            if (distance <= 35)
                distance += 2;
        }

    }

}
           

 4.Scene視角切換成俯視視角,友善調整參數。調整戰争迷霧蒙版類型、寬度、高度、戰争迷霧中心位置,将特效shader、模糊shader、小地圖shader拖入Main Camera上的Fog Of War Effect上

Unity MOBA類型遊戲的戰争迷霧效果
using ASL.FogOfWar;
using System.Collections.Generic;
using UnityEngine;

public struct FOWMapPos
{
    public int x;
    public int y;

    public FOWMapPos(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}

/// <summary>
/// 螢幕空間戰争迷霧
/// </summary>
[RequireComponent(typeof(FlareLayer))]
public class FogOfWarEffect : MonoBehaviour
{

    //迷霧蒙版類型
    public enum FogMaskType
    {
        /// <summary>
        /// 精确計算的FOV
        /// </summary>
        AccurateFOV,
        /// <summary>
        /// 基礎FOV
        /// </summary>
        BasicFOV,
        /// <summary>
        /// 簡單圓形
        /// </summary>
        Circular,
    }

    //執行個體化
    public static FogOfWarEffect Instance
    {
        get
        {
            if (instance == null)
                instance = FindObjectOfType<FogOfWarEffect>();
            return instance;
        }
    }

    private static FogOfWarEffect instance;

    /// <summary>
    /// 迷霧蒙版類型
    /// </summary>
    public FogMaskType fogMaskType { get { return m_FogMaskType; } }

    /// <summary>
    /// 戰争迷霧顔色(RGB迷霧顔色,Alpha已探索區域透明度)
    /// </summary>
    public Color fogColor { get { return m_FogColor; } }

    /// <summary>
    /// 迷霧區域寬度
    /// </summary>
    public float xSize { get { return m_XSize; } }

    /// <summary>
    /// 迷霧區域高度
    /// </summary>
    public float zSize { get { return m_ZSize; } }

    /// <summary>
    /// 迷霧貼圖寬度
    /// </summary>
    public int texWidth { get { return m_TexWidth; } }

    /// <summary>
    /// 迷霧貼圖高度
    /// </summary>
    public int texHeight { get { return m_TexHeight; } }

    /// <summary>
    /// 迷霧區域中心坐标
    /// </summary>
    public Vector3 centerPosition { get { return m_CenterPosition; } }

    public float heightRange { get { return m_HeightRange; } }

    public Texture2D fowMaskTexture
    {
        get
        {
            if (m_Map != null)
                return m_Map.GetFOWTexture();
            return null;
        }
    }

    public RenderTexture minimapMask
    {
        get
        {
            if (!m_GenerateMinimapMask)
                return null;
            return m_Renderer.GetMimiMapMask();
        }
    }

    [Header("戰争迷霧蒙版類型")]
    [SerializeField]
    private FogMaskType m_FogMaskType;
    [Header("戰争迷霧顔色")]
    [SerializeField]
    private Color m_FogColor = Color.black;
    [Header("戰争迷霧寬度")]
    [SerializeField]
    private float m_XSize = 77f;
    [Header("戰争迷霧高度")]
    [SerializeField]
    private float m_ZSize = 77f;
    [Header("戰争迷霧貼圖寬度")]
    [SerializeField]
    private int m_TexWidth = 66;
    [Header("戰争迷霧貼圖高度")]
    [SerializeField]
    private int m_TexHeight = 66;
    [Header("戰争迷霧中心位置")]
    [SerializeField]
    private Vector3 m_CenterPosition;
    [Header("高度随機值")]
    [SerializeField]
    private float m_HeightRange = 1.23f;
    /// <summary>
    /// 模糊偏移量
    /// 
    /// </summary>
    [Header("模糊偏移量")]
    [SerializeField]
    private float m_BlurOffset = 0.003f;
    /// <summary>
    /// 模糊疊代次數
    /// </summary>
    [Header("模糊疊代次數")]
    [SerializeField]
    private int m_BlurInteration = 2;
    /// <summary>
    /// 是否生成小地圖蒙版
    /// </summary>
    private bool m_GenerateMinimapMask;
    /// <summary>
    /// 迷霧特效shader
    /// </summary>
    [Header("戰争特效Shader")] public Shader effectShader;
    /// <summary>
    /// 模糊shader
    /// </summary>
    [Header("模糊Shader")] public Shader blurShader;
    /// <summary>
    /// 小地圖蒙版渲染shader
    /// </summary>
    [Header("小地圖蒙版渲染Shader")] public Shader minimapRenderShader;
    [Header("更新周期")][Range(0.0f,1.0f)]public float UpdateTime = 1.0f;



    /// <summary>
    /// 預生成的地圖FOV資料(如果為空則使用實時計算FOV)
    /// </summary>
    //public FOWPregenerationFOVMapData pregenerationFOVMapData;

    /// <summary>
    /// 戰争迷霧地圖對象
    /// </summary>
    private FOWMap m_Map;
    /// <summary>
    /// 戰争迷霧渲染器
    /// </summary>
    private FOWRenderer m_Renderer;

    private bool m_IsInitialized;

    private float m_MixTime = 0.0f;
    private float m_RefreshTime = 0.0f;

    private float m_DeltaX;
    private float m_DeltaZ;
    private float m_InvDeltaX;
    private float m_InvDeltaZ;

    private Camera m_Camera;

    private const float kDispearSpeed = 3f;
    private const float kRefreshTextureSpeed = 4.0f;

    private Vector3 m_BeginPos;

    private List<FOWFieldData> m_FieldDatas;

    private bool m_IsFieldDatasUpdated;

    void Awake()
    {
        m_IsInitialized = Init();

    }

    void OnDestroy()
    {
        if (m_Renderer != null)
            m_Renderer.Release();
        if (m_Map != null)
            m_Map.Release();
        if (m_FieldDatas != null)
            m_FieldDatas.Clear();
        m_FieldDatas = null;
        m_Renderer = null;
        m_Map = null;
        instance = null;
    }

    void FixedUpdate()
    {
        /*
        更新迷霧紋理
        */
        if (m_MixTime >= UpdateTime)
        {
            if (m_RefreshTime >= UpdateTime)
            {
                m_RefreshTime = 0.0f;
                if (m_Map.RefreshFOWTexture())
                {

                    m_Renderer.SetFogFade(0);
                    m_MixTime = 0;
                    m_IsFieldDatasUpdated = false;
                    //m_Renderer.SetFogTexture(m_Map.GetFOWTexture());
                }
            }
            else
            {
                m_RefreshTime += Time.deltaTime * kRefreshTextureSpeed;
            }
        }
        else
        {
            m_MixTime += Time.deltaTime * kDispearSpeed;
            m_Renderer.SetFogFade(m_MixTime);
        }
    }

    private bool Init()
    {
        if (m_XSize <= 0 || m_ZSize <= 0 || m_TexWidth <= 0 || m_TexHeight <= 0)
            return false;
        if (effectShader == null || !effectShader.isSupported)
            return false;
        m_Camera = gameObject.GetComponent<Camera>();
        if (m_Camera == null)
            return false;
        m_Camera.depthTextureMode |= DepthTextureMode.Depth;
        m_DeltaX = m_XSize / m_TexWidth;
        m_DeltaZ = m_ZSize / m_TexHeight;
        m_InvDeltaX = 1.0f / m_DeltaX;
        m_InvDeltaZ = 1.0f / m_DeltaZ;
        m_BeginPos = m_CenterPosition - new Vector3(m_XSize * 0.5f, 0, m_ZSize * 0.5f);
        m_Renderer = new FOWRenderer(effectShader, blurShader, minimapRenderShader, m_CenterPosition, m_XSize, m_ZSize, m_FogColor, m_BlurOffset, m_BlurInteration);
        m_Map = new FOWMap(m_FogMaskType, m_BeginPos, m_XSize, m_ZSize, m_TexWidth, m_TexHeight, m_HeightRange);
        IFOWMapData md = gameObject.GetComponent<IFOWMapData>();
        if (md != null)
            m_Map.SetMapData(md);
        else
        {
            m_Map.SetMapData(new FOWMapData(m_TexHeight, m_TexHeight));
            m_Map.GenerateMapData(m_HeightRange);
        }
        if (minimapRenderShader != null)
            m_GenerateMinimapMask = true;
        return true;
    }

    /// <summary>
    /// 世界坐标轉戰争迷霧坐标
    /// </summary>
    /// <param name="position"></param>
    /// <returns></returns>
    public static FOWMapPos WorldPositionToFOW(Vector3 position)
    {
        if (!Instance)
            return default(FOWMapPos);
        if (!Instance.m_IsInitialized)
            return default(FOWMapPos);

        int x = Mathf.FloorToInt((position.x - Instance.m_BeginPos.x) * Instance.m_InvDeltaX);
        int z = Mathf.FloorToInt((position.z - Instance.m_BeginPos.z) * Instance.m_InvDeltaZ);

        return new FOWMapPos(x, z);
    }

    public static Vector2 WorldPositionTo2DLocal(Vector3 position)
    {
        if (!Instance)
            return default(Vector2);
        if (!Instance.m_IsInitialized)
            return default(Vector2);

        Vector2 pos = default(Vector2);
        pos.x = (position.x - Instance.m_BeginPos.x) / Instance.m_XSize;
        pos.y = (position.z - Instance.m_BeginPos.z) / Instance.m_ZSize;

        return pos;
    }

    / <summary>
    / 将指定位置設定為可見
    / </summary>
    / <param name="fieldData">視野</param>
    //public static void SetVisibleAtPosition(FOWFieldData fieldData)
    //{
    //    if (!Instance)
    //        return;
    //    if (!Instance.m_IsInitialized)
    //        return;
    //    if (fieldData == null)
    //        return;

    //    Instance.m_Map.SetVisible(fieldData);

    //}

    public static void UpdateFOWFieldData(FOWFieldData data)
    {
        if (!Instance)
            return;
        if (!Instance.m_IsInitialized)
            return;
        if (Instance.m_FieldDatas == null)
            Instance.m_FieldDatas = new List<FOWFieldData>();
        if (!Instance.m_FieldDatas.Contains(data))
        {
            Instance.m_FieldDatas.Add(data);
        }
        if (!Instance.m_IsFieldDatasUpdated)
        {
            //lock (Instance.m_FieldDatas)
            {
                Instance.m_Map.SetVisible(Instance.m_FieldDatas);
                Instance.m_IsFieldDatasUpdated = true;
            }
        }
    }

    public static void ReleaseFOWFieldData(FOWFieldData data)
    {
        if (!instance)
            return;
        if (!instance.m_IsInitialized)
            return;
        //lock (instance.m_FieldDatas)
        {
            if (instance.m_FieldDatas != null && instance.m_FieldDatas.Contains(data))
                instance.m_FieldDatas.Remove(data);
        }
    }

    /// <summary>
    /// 是否在地圖中可見
    /// </summary>
    /// <param name="position"></param>
    /// <returns></returns>
    public static bool IsVisibleInMap(Vector3 position)
    {
        if (!Instance)
            return true;
        if (!Instance.m_IsInitialized)
            return true;
        int x = Mathf.FloorToInt((position.x - Instance.m_BeginPos.x) * Instance.m_InvDeltaX);
        int z = Mathf.FloorToInt((position.z - Instance.m_BeginPos.z) * Instance.m_InvDeltaZ);

        return Instance.m_Map.IsVisibleInMap(x, z);

    }

    void OnRenderImage(RenderTexture src, RenderTexture dst)
    {
        if (!m_IsInitialized)
            Graphics.Blit(src, dst);
        else
        {
            m_Renderer.RenderFogOfWar(m_Camera, m_Map.GetFOWTexture(), src, dst);
        }
    }

    void OnDrawGizmosSelected()
    {
        FOWUtils.DrawFogOfWarGizmos(m_CenterPosition, m_XSize, m_ZSize, m_TexWidth, m_TexHeight, m_HeightRange);
    }
}
           

 5.單擊Terrain 右上角勾選靜态Static,Windows----AI---Navigation,Bake---Bake

Unity MOBA類型遊戲的戰争迷霧效果
Unity MOBA類型遊戲的戰争迷霧效果

6.運作即可檢視效果,你還可以添加小地圖和右鍵點選動畫等效果。

  如果你的場景和我的不一樣,那也沒關系,我改了光照設定,不改也沒問題,我的光照設定如下:

Unity MOBA類型遊戲的戰争迷霧效果

資源下載下傳:點選下載下傳

網盤下載下傳:通路碼:2oeb

參考資料:GitHub

繼續閱讀