Unity MOBA类型游戏的战争迷雾效果

基于视野(FOV)的战争迷雾,例如LOL的视野:鼠标右键点击地板,目标移动,同时显示角色周围视野,鼠标滚轮可以调节远近。

Unity版本:2019.4.1f1   

1.新建工程---右键3D Object---Terrain,随便刷一个地形,尽量高低错落,设置地形大小为100*100

2.导入文件,在Camera上添加Fog Of War Effect脚本,脚本会自动添加<Flare Layer>组件,Unity 2018之前的版本需要添加<GUI Layer>组件,2019以后版本被舍弃,无需添加

 3.右键创建一个胶囊,模拟玩家,添加NavMeshAgent组件,添加Fog Of War Explorer脚本

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上

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

6.运行即可查看效果,你还可以添加小地图和右键点击动画等效果。

  如果你的场景和我的不一样,那也没关系,我改了光照设置,不改也没问题,我的光照设置如下:

资源下载:点击下载

网盘下载:访问码:2oeb

参考资料:GitHub

 

  • 8
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值