许多RTS游戏通过在所选单元周围画一个圆圈来显示距离(攻击距离,移动距离,视线等)。如果地形是平坦的,这可以通过使用圆形纹理拉伸四轴来完成。如果不是这种情况,那么四轴很可能会被夹在山丘或其它几何体后面。那么怎么样我们可以在复杂的对象周围绘制圆圈。
a.尽管处理了每一块几何体,但这种技术低面向地形。因此,第一步是在Unity 种设置地形,但不是使用模型,我们将在Unity编辑器中创建一个。
b.创建一个shader命名为RadiusShader,并创建相关材质为RadiusMat.
c.创建一个terrains或者下载一个,terrains是Unity中的特殊对象,纹理映射的方式与传统的3D模型不同,我们无法从shader提供_MainTex,因为它需要直接从地形本身提供,为此,选择"Paint Texture",然后单击 "Add Texture....";
d.双击打开shader移走_Glossiness和_Metallc属性同时加入下面四个属性
Properties {
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Center("Center",Vector) = (200,0,200,0)
_Radius("Radius",Float) = 100
_RadiusColor("Radius Color",Color) = (1,0,0,1)
_RadiusWidth("Radius Width",Float) = 10
}
e.在CGPROGRAM部分加入下列相关变量,同时移走_Glossiness和 _Metallic
float3 _Center;
float _Radius;
fixed4 _RadiusColor;
float _RadiusWidth;
h.对surface function函数的输入不仅需要纹理的UV,还需要地形的每个点的位置(在世界坐标中).我们可以通过更改struct Input来检索此参数,如下所示:
struct Input
{
float2 uv_MainTex;//the UV of the terrain texture
float3 worldPos;//The in-world position
};
i.最后我们使用下面的surface function:
void surf (Input IN, inout SurfaceOutputStandard o) {
//Get the distance between the center of the place we wish to draw from and the input"s world position
float d = distance(_Center, IN.worldPos);
//if the distance is larger than the radius and it is less than our radius + width change the color
if ((d > _Radius) && (d < (_Radius + _RadiusWidth)))
{
o.Albedo = _RadiusColor;
}
//Otherwise,use the normal color
else
{
o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
}
}
返回到游戏场景可看到效果图如下:
源码如下:
Shader "Custom/RadiusShader" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Center("Center",Vector) = (200,0,200,0)
_Radius("Radius",Float) = 100
_RadiusColor("Radius Color",Color) = (1,0,0,1)
_RadiusWidth("Radius Width",Float) = 10
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input
{
float2 uv_MainTex;//the UV of the terrain texture
float3 worldPos;//The in-world position
};
float3 _Center;
float _Radius;
fixed4 _RadiusColor;
float _RadiusWidth;
fixed4 _Color;
void surf (Input IN, inout SurfaceOutputStandard o) {
//Get the distance between the center of the place we wish to draw from and the input"s world position
float d = distance(_Center, IN.worldPos);
//if the distance is larger than the radius and it is less than our radius + width change the color
if ((d > _Radius) && (d < (_Radius + _RadiusWidth)))
{
o.Albedo = _RadiusColor;
}
//Otherwise,use the normal color
else
{
o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
}
}
ENDCG
}
FallBack "Diffuse"
}
移动圆圈
上面已经实现了很好的效果,但是你可能还想下运行时更改圆圈的位置,我们可以通过代码执行此操作.如果你希望圆圈跟随你的角色,则需要执行其它步骤:
a. 创建一个新的C#脚本命名为SetRadiusPropertied.
b.由于我们可能希望在游戏中和外部都看到这种变化,我们可以在类的外部添加一个标记,表示在编辑器中执行此代码,此外还有Game正在播放时,添加以下内容标签:
[ExecuteInEditMode]
public class SetRadiusPropertied : MonoBehaviour
c.在脚本中添加以下变量
public Material radiusMaterial;
public float radius = 1;
public Color color = Color.white;
e.在Update()方法中,添加如下代码
if(radiusMaterial != null)
{
radiusMaterial.SetVector("_Center", transform.position);
radiusMaterial.SetFloat("_Radius", radius);
radiusMaterial.SetColor("_RadiusColor", color);
}
h.把脚本附属到一个物体上面,然后移动物体.你会发现圆圈也会跟着移动.
它是如何运行的
绘制圆的相关参数是其中心,半径和颜色.它们在Shader中均可,名称为 _Center,_Radius和_RadiusColor.通过将worldPos变量添加到Input结构,我们要求Unity向我们提供我们绘制的以世界坐标表示的像素的位置.这是编辑器中对象的实际位置.
surf()函数是实际绘制圆的位置.它计算从绘制点到半径中心的距离,然后检查它是否在 _Radius和_Radius + _RadiusWidth之间,如果是这种情况,它会使用所选的颜色.在另一种情况下,它只是像我们目前所见的所有其它shader一样对纹理贴图进行采样.
以上均基于Unity2018.1.0f,源码可见我的GitHub:
https://github.com/xiaoshuivv/ShadersUnity2018.1.0f.git