游戏视野剔除显示FieldOfViewRenderer
中文
简介
游戏中的英雄,小怪等单位的视野显示.因为工作需求做了一个游戏中单位的视野剔除显示的功能,网上相关资源太少,有的可能也不是很适合,比如没有剔除,功能不完善,性能开销太大等,最后用自己的方式实现了策划需求.查了下网上貌似没有这样做的,想到很多游戏项目估计都有这些需求,现在贡献出来,还请斧正.
主要功能
- 通过配档显示敌人视野的范围.
- 有障碍物的地方视野会剔除渲染.
- 自定义shader,通过美术给定的图片采样渲染出不同透明度有渐变效果的纹理.
- 代码简洁易懂,扩展性高.数据类型接口丰富,(如:可通过给定颜色改变视野颜色,以起到显示状态作用.)
- 性能开销可调整,高质量的情况也不会开销太大.
- 提供其他可行方案,供选择对比.
图片预览
下载包体里有详细的说明和录制视频,这里贴出预览图
部分代码
Demo全部代码都在Scripts文件夹里.命名和注释都一目了然,建议下载后直接看源码,这里简单介绍下.
图形绘制器 DrawHelper
主要就是通过Unity封装的GL类库直接绘制相应图形
入口函数是OnPostRender,包含此函数的脚本挂摄像机上,就会被每帧调用.
在其他地方调用AddSector和RemoveSector达到添加移除所需要绘制的扇形视野
当绘制器检测到有图形需要绘制的时候 就绘制,
void OnPostRender()
{
if (allSectors.Count > 0)
DrawTriangle(allSectors);
//绘制其他图形
}
private void DrawTriangle(List<Sector> sectors)
{
if (sectors.Count <= 0) return;
GL.PushMatrix();
mat.SetPass(0);
//GL.LoadOrtho();
GL.Begin(GL.TRIANGLES);
foreach (var sector in sectors)
{
sector.PushGLVertex();
}
GL.End();
GL.PopMatrix();
}
/// <summary>
/// 添加需要绘制扇形(视野)
/// </summary>
/// <param name="sector"></param>
public void AddSector(Sector sector)
{
if (allSectors.Contains(sector)) return;
allSectors.Add(sector);
}
//正式项目中需要用到移除视野(如死亡的时候)
public void RemoveSector(Sector sector)
{
if (allSectors.Contains(sector))
allSectors.Remove(sector);
}
基础扇形类
基础扇形类,就是小怪英雄等单位视野显示的图形.
扇形由许多小三角形拼合而成,三角形由顶点决定,而我们需要把被障碍物挡住的定点剔除, 所以剔除顶点,计算顶点,重构三角形是关键.
绘制出的图形需要和游戏单位保持相对固定的位置关系,所以需要转换坐标系同步更新位置.
最后,显示的图形不能直接显示到单位的眼睛上,而是显示到脚下,所以需要做一个偏移,虽然视野剔除还是从眼睛哪里发起的.
public Transform origin;
public float angleFov = 90; //视野角度
public float R = 2f; //视野最远距离
public float r = 0.3f;
public int quality = 72; //越大质量越高(12的倍数)
public float startAngle = 0;
public Color color = Color.green; //视野颜色(可以通过改变颜色显示不同的状态,如看到敌人变红色)
public Vector3[] vertices;
/// <summary>
/// 视野显示的高度
/// </summary>
public float offsetY = 0.2f;
/// <summary>
/// 将顶点等数据传送给GL
/// </summary>
public void PushGLVertex()
{
GL.Color(color);
int verCount = 0;
var vertices = this.RefreshVertices();
if (null == vertices) return;
int wholeCount = vertices.Length - vertices.Length % 3;
if (0 == this.r)
wholeCount = vertices.Length;
//以下为舍进算法,可能会导致渲染出的FOV和配档差一点点
for (int i = 0; i < wholeCount; i++)
{
verCount++;
vertice = vertices[i];
GL.TexCoord2(GetUVx(vertice), 0.5f);
GL.Vertex3(vertice.x, vertice.y, vertice.z);
if (0 != this.r)
{
if (verCount >= 3 && vertices.Length - i > 3)
{
i = i - 2;
verCount = 0;
}
}
else
{
if (verCount >= 2)
{
if (vertices.Length - i > 1)
{
i--;
}
verCount = 0;
GL.TexCoord2(GetUVx(this.origin.position), 0.5f);
GL.Vertex3(this.origin.position.x, this.origin.position.y, this.origin.position.z);
}
}
}
}
/// <summary>
/// 更新扇形顶点数据
/// </summary>
/// <returns></returns>
public Vector3[] RefreshVertices()
{
startAngle = origin.eulerAngles.y + angleFov / 2;
float angleCurr = startAngle;
offsetVec3 = new Vector3(origin.position.x, offsetY, origin.position.z);
for (int i = 0; i <= quality; i++)
{
Vector3 curVec = new Vector3();
curVec.z = Mathf.Cos(Mathf.Deg2Rad * angleCurr);
curVec.x = Mathf.Sin(Mathf.Deg2Rad * angleCurr);
Vector3 posCurrMax;
RaycastHit hit;
//Debug.DrawRay(origin.position, curVec * R, Color.green,0.1f);//可以取消此行注释显示扫描射线
//if (Physics.Raycast(origin.position, curVec * R, out hit, R, ~(int)Layers.Corpse))//可以过滤掉某些层
if (Physics.Raycast(origin.position, curVec * R, out hit, R))
{
posCurrMax = curVec * Vector3.Distance(hit.point, origin.position) + offsetVec3;
//Debug.DrawLine(origin.position,hit.point,Color.red,0.1f);
}
else
{
posCurrMax = curVec * R + offsetVec3;
}
Vector3 posCurrMin = curVec * r + offsetVec3;
if (0 != r)
{
vertices[2 * i] = posCurrMin;
vertices[2 * i + 1] = posCurrMax;
}
else
{
vertices[i] = posCurrMax;
}
angleCurr -= angleDelta;
}
return vertices;
}
定义shader美观优化
计划的功能都实现出来了,骚策划还不满足,需要再在视觉上优化下,要根据视野远近来个强弱渐变
自定义一个shader实现.
在计算扇形的时候需要抓取UV,以便shader里做渐变渲染.
//获取UV
private float GetUVx(Vector3 vertex)
{
return Mathf.Clamp01(Vector3.SqrMagnitude(vertex - origin.position) / (R * R));
}
// GL.TexCoord2(GetUVx(vertice), 0.5f);
SubShader
{
Tags { "RenderType" = "Transparent" }
LOD 100
Pass
{
Blend SrcAlpha OneMinusSrcAlpha
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float4 color : COLOR;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float4 color : TEXCOORD1;
};
float4 _FieldColor;
sampler2D _AlphaTex;
float4 _AlphaTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
o.color = v.color;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = _FieldColor;
return fixed4(i.color.rgb, tex2D(_AlphaTex, i.uv).r);
}
ENDCG
}
}
使用步骤:
导入下载的包体,打开里面的Demo场景即可预览使用.打上合适的灯光会更好看.若存在问题或者需要修改请执行以下步骤.
1. 将在MainCamera上添加DrawHelper绘制器,并绑定材质FieldOfViewRenderer.
2. 在你的单位的眼睛或者虚拟点上添加Eye脚本(也可以在脚本里面添加一个eyeTrans的变量,这样脚本可以放在单位根节点,只需要初始化eyeTrans即可)
不明白可以看截图和视频
(之前在网上找到了一个通过摄像机剔除而渲染视野的方案,功能满足,但是开销太大,根本没法用到工程中(加入后20帧).于是采用GL底层直接渲染视野.此方案不需要创建mesh,并且所有的视野图形都一批渲染出来,不会像mesh渲染那样可能分批次.之所以不叫FieldOfVisionRenderer是为了和网上的某方案区分)
下载链接
https://download.youkuaiyun.com/download/qq_19646749/11033676
转载请注明出处 (*_^)
(若下载不了 请告诉我)
更新:
2019/03/18 加入了滑动框选功能 详情见FieldOfViewRenderer_andBoxSelect.unitypackage(Unity2018.3.5)
English
FieldOfViewRenderer Description
It is used for the enemy’s vision field in the game.
Main functions:
- Display the enemy’s range of vision.
- The vision field with obstacles won’t be rendered.
- Customize shader to render texture with gradient effect with different transparency by sampling images given by art staff.
- The code is simple and easy to understand, with high expansibility. (for example, the view color can be changed by a given color to display the state).
- It costs little even you set it to high quality. The performance cost can be adjusted.(It is better than some other’s solution.)
- Provide other suitable way for comparison and selection.
Use steps: Importing the package,openning the Demo scene,then you can preview it. If there are problems or need to modify, please perform the following steps.
- Drag the DrawHelper(script) into to the MainCamera and Drag the FieldOfViewRenderer(material) into the Mat.
- Add the Eye(script) to your unit’s eye or virtual point .
Update: I add BoxSelect-module in this project at 03/18/2019 . Inporting FieldOfViewRenderer_andBoxSelect.unitypackage to see detail.
Unity Version:2017.4.2f2 thank u very much !
Download
https://download.youkuaiyun.com/download/qq_19646749/11033676
Reprint please indicate the source