从最初的即时战略《沙丘2》开始,战争迷雾的概念开始被引入和正式提出。在沙丘中每一次新开始游戏时,玩家只能观察到自己基地及单位周围极小的范围,而绝大多数地图区域均被黑色遮盖。当他命令单位向黑暗区移动后,经过的区域会被自动打开,地图变得可见,包括该区域的地形/敌人活动情况等等。

红警战争迷雾&小地图添加

战争迷雾的实现方式是通过在地图与相机之间放置一层黑色面片,通过动态修改面片的局部透明度实现。

1.通过动态修改顶点颜色实现:

(1)准备工作:

新建一个用于迷雾的材质球

选择其材质为如下图:

并将Tint Color设置为完全的黑色

新建一个Layer命名ShadowMask(任意与存在Layer名字不同即可)

接下来新建一个Plane,将它延申到覆盖场景,改变其层的名字为shadowMask,勾选其MeshCollider的Convex(不勾选的话后边的射线检测会不起作用)

新建一个ShadowMask脚本,将脚本挂载到ShadowMask物体上,

public class ShadowMask : MonoBehaviour
{
    public GameObject shadowPlane;
    public Transform player;
    public LayerMask shadowMaskLayer;
    public float shadowRadius = 10f;

    private float radiusCircle { get {  return shadowRadius * shadowRadius; } }

    private Mesh mesh;
    private Vector3[] verteies; // 顶点坐标位置
    private Color[] verteiesColors; //顶点坐标的颜色

    void Start()
    {
    }

    void Update()
    {
    }
}

将用到的信息设置好

(2)检测玩家移动后是否能准确与迷雾网格产生碰撞

public class ShadowMask : MonoBehaviour
{
    public GameObject shadowPlane;
    public Transform player;
    public LayerMask shadowMaskLayer;
    public float shadowRadius = 10f;

    private float radiusCircle { get {  return shadowRadius * shadowRadius; } }

    private Mesh mesh;
    private Vector3[] verteies; // 顶点坐标位置
    private Color[] verteiesColors; //顶点坐标的颜色

    void Update()
    {
        Ray r = new Ray(player.position, player.position + Vector3.up * 100);
        Debug.DrawLine(player.position, player.position + Vector3.up * 100);
        RaycastHit hit;
        if (Physics.Raycast(r,out hit,1000,shadowMaskLayer,QueryTriggerInteraction.Collide))
        {
            Debug.Log(hit);
        }

    }
}

 如果一起顺利,则控制台会打印出信息

(3)更换顶点颜色

    void Update()
    {
        Ray r = new Ray(player.position, player.position + Vector3.up * 100);
        Debug.DrawLine(player.position, player.position + Vector3.up * 100);
        RaycastHit hit;
        if (Physics.Raycast(r,out hit,1000,shadowMaskLayer,QueryTriggerInteraction.Collide))
        {
            Debug.Log(hit);
            for (int i = 0; i < verteies.Length; i++)
            {
                //将迷雾平面的顶点坐标变换到世界空间
                Vector3 v = shadowPlane.transform.TransformPoint(verteies[i]);
                //计算变换到世界空间下的顶点坐标与碰撞点的距离
                float distance = Vector3.SqrMagnitude(v - hit.point);
                if(distance < radiusCircle)
                {
                    float alpha = Mathf.Min(verteiesColors[i].a,distance/radiusCircle);
                    verteiesColors[i].a = alpha;
                }
            }
        }

        UpdateVerteiesColors();
    }

    void UpdateVerteiesColors()
    {
        //更换顶点颜色
        mesh.colors = verteiesColors;
    }

实现效果如下图:

如果感觉效果太粗糙,可以将ShadowMask物体的顶点数增加,顶点数越多效果会更好一些

2.通过动态修改贴图像素的颜色实现:

准备工作和通过修改顶点颜色类似,只不过shadowMask的材质球需要更换shader,使用我写的这篇文章中的shader

Unity ShaderLab --- 实现局部透明-CSDN博客

然后需要动态的生成迷雾贴图,代码如下

  Texture2D fogTxtur;
  Color[] pixelColors;

  int width;
  int height;
  float shadowRadius;

  public FogOfWarTexture(int width,int height,float rad)
  {
      this.width = width;
      this.height = height;
      this.shadowRadius = rad;
      fogTxtur = new Texture2D(width,height);
      pixelColors = new Color[width * height];
      for (int i = 0; i < width; i++)
      {
          for (int j = 0; j < height; j++)
          {
              pixelColors[i * height + j] = Color.black;
          }
      }
      this.SetPixels(pixelColors);
  }

   void SetPixels(Color[] colors)
   {
       fogTxtur.SetPixels(pixelColors);
       fogTxtur.Apply();
   }

并且在执行中需要不断改变, 代码如下

   public void HandlerTetureColors(Vector3 point)
   {
       for (int i = 0; i < this.width; i++)
       {
           for (int j = 0; j < this.height; j++)
           {
               if(Mathf.Abs(j - this.width/2 + point.x) < this.shadowRadius && Mathf.Abs(i- this.height/2 + point.z) < this.shadowRadius && pixelColors[i * this.height + j] == Color.black)
               {
                   pixelColors[i * this.height + j] = new Color(0,0,0,0);
               }
           }
       }
       this.SetPixels(pixelColors);
   }

 输入的point值就是玩家移动发出射线与迷雾的物体碰撞点,代码如下:

  void Update()
  {
      Ray r = new Ray(player.position, player.position + Vector3.up * 100);
      Debug.DrawLine(player.position, player.position + Vector3.up * 100);
      RaycastHit hit;
      if (Physics.Raycast(r, out hit, 1000, shadowMaskLayer, QueryTriggerInteraction.Collide))
      {
          Debug.Log(hit);
          fogTexture.HandlerTetureColors(hit.point); 
      }

  }

实现效果如下图

参考链接:

Unity Shadow, Fog Walk Path (Fog of war) (youtube.com)

Unity Fog Of War Tutorial. Dynamic shader based fog of war (youtube.com)

Logo

分享前沿Unity技术干货和开发经验,精彩的Unity活动和社区相关信息

更多推荐