先放上效果图,吸引眼球
不吹不黑,下载了官网上面的一款同样效果的插件,发现实用起来相当麻烦,它需要额外的一层平面来遮挡相机,和本人写的动态光相比大家一眼就懂哪一个更优质实用。此插件实现其特效只需下面当中的前三个组件就可以。
下面进入正题:
首先说明思路,想实现2D动态光照并对光照范围内的Collider2D组件发生反应,这里的重点就是如下几部分
1:Mask遮罩原理(mask组件效果如下图)
2:射线检测生成特定区
首先我们知道,mask组件针对于图片类型的,但是如果想动态的改变一个image或是sprite的形状难以做到,不过我们知道mesh是可以动态变化的,所以我们这里就将思路转化为,使用mask原理写一个Shader加入到一个动态的Mesh上面,就形成了一个变化的光照区域,在加上向四周发射射线检测去模拟光线的发射,获取到光线在一定范围内遇到的障碍点,就达到了如此的效果。
那么下面开始我们的代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
public class DynamicLighting : MonoBehaviour {
/// <summary>
/// 光照半径
/// </summary>
public float Range = 3;
/// <summary>
/// 灯光范围光滑度,组成网络的三角的个数(必须>3)
/// </summary>
public int SmoothnessOfLighting = 15;
/// <summary>
/// 光照阴影层
/// </summary>
public LayerMask[] LayerMask;
/// <summary>
/// Layer层的值
/// </summary>
private int layerMaskValue;
/// <summary>
/// 光照产生图形的点的数组,共外圈点+中心点个
/// </summary>
private Vector3[] vectorsOfLightMesh;
/// <summary>
/// 此物体的网络
/// </summary>
private Mesh mesh;
/// <summary>
/// 每个点与中心点组成的方向变量
/// </summary>
private Vector3 direction;
/// <summary>
/// 射线碰撞的信息
/// </summary>
private RaycastHit2D hit;
/// <summary>
/// 三角形的索引
/// </summary>
private int triangleIndex;
/// <summary>
/// 三角形点序号数组
/// </summary>
private int[] triangles;
/// <summary>
/// uv贴图数组
/// </summary>
private Vector2[] uvArray;
// Use this for initialization
void Start () {
mesh = gameObject.GetComponent<MeshFilter>().mesh;
vectorsOfLightMesh = new Vector3[SmoothnessOfLighting + 1];
triangles = new int[SmoothnessOfLighting * 3];
uvArray = new Vector2[SmoothnessOfLighting + 1];
SetLayerMaskValue(LayerMask);
}
// Update is called once per frame
void Update () {
GenerateMesh();
}
/// <summary>
/// 将层级信息赋值给layerMaskValue
/// </summary>
/// <param name="layerMask"></param>
private void SetLayerMaskValue(LayerMask[] layerMask)
{
for (int i = 0; i
下面为什么是Zero以及下面为什么对每个点都要减去Transform.Position。我做一下解释;首先大家可以将Transform.Position去掉进行测试,会发现每当此物体移动10点距离的时候,他的光照mesh就移动了20点距离,这个bug我想了一天,最终验证了我的猜想:当绘制一个图形的时候对于Vector3.zero这个点就已经是物体在世界坐标的Transfrom。所以绘制mesh的时候,我们的点集要是Vector3.zero为中心的。(国内的另一篇文章并没有解决这个问题,因此为了避开问题它设置了一个Transform变量,使用的时候需要将此变量赋值表示光源跟随的物体,而且和unity商城所卖的组件用的都是加遮罩层遮挡相机的原理,这种做法在实际开发中有一些麻烦的地方)
vectorsOfLightMesh[0] = Vector3.zero;
//生成三角形mesh时,逆时针生成的点被摄相机认为背对,所以为了顺时针这里我们用i--让他顺时针方向加入点集
for (int i = SmoothnessOfLighting; i > 0; i--)
{
direction = new Vector3(Mathf.Cos(Mathf.Deg2Rad * i * 360 / SmoothnessOfLighting), Mathf.Sin(Mathf.Deg2Rad * i * 360 / SmoothnessOfLighting), 0);
direction = direction * Range;
if (Physics2D.Raycast(transform.position, direction, Range, layerMaskValue))
{
Debug.DrawLine(transform.position, transform.position + direction, Color.red);
hit = Physics2D.Raycast(transform.position, direction, Range, layerMaskValue);
// 如果被射线探中,则将探测到的点赋值给顶点集
//vectorsOfLightMesh[i] = new Vector3(hit.point.x, hit.point.y, BoundTransform.position.z);
vectorsOfLightMesh[SmoothnessOfLighting - i + 1] = (Vector3)hit.point + Vector3.forward * transform.position.z - transform.position;
}
else
{
Debug.DrawLine(transform.position, transform.position + direction, Color.white);
// 否则将射线的末端作为网格的顶点
vectorsOfLightMesh[SmoothnessOfLighting - i + 1] = direction;
}
}
triangleIndex = 0;
// 根据网格顶点组合三角形
for (int i = 1; i
下面上Shader的代码:
第一个shader也是最重要的shader,另一个只是为了好看所写的光源贴图;
这个shader实现了Mask的原理,对与渲染的物体来说其区域值是1,剩余区域全部为0,将其加入到上方脚本添加的物体的Mesh组件中,下一步再将只在mask内部显示的物体的sprite进行如下属性修改使其只在mask中显示便可达到上图的效果
Shader "Custom/Masked" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" "Queue"="Geometry-100"} ColorMask 0 ZWrite off LOD 200 Stencil { Ref 1 Pass replace } CGPROGRAM #pragma surface surf Standard fullforwardshadows #pragma target 3.0 sampler2D _MainTex; struct Input { float2 uv_MainTex; }; fixed4 _Color; void surf (Input IN, inout SurfaceOutputStandard o) { // Albedo comes from a texture tinted by color fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; // Metallic and smoothness come from slider variables o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }
由于第二个shader不是非常重要这里根据自己的项目需求在制作一个光源贴图即可。一些重点知识在此文章中已经提到了,那么大家开始尝试把~对了打一播广告本人制作的模仿塞尔达传说的卡通shader马上就要上架了,在官网商店上同样的东西价值15美金,而我上架的是免费的!是免费的!欢迎大家搜索CartoonShader进行下载!
这个很叼。。。顶顶顶
看过一篇类似的纹章,对于遮挡物边缘的判断有一个更好的解决办法,效果还是很不错的推荐看看。
https://indienova.com/indie-game-development/sight-light-how-to-create-2d-visibility-shadow-effects-for-your-game/