一、效果图

二、实现原理

        RawImage组件可以调整图片的UV坐标的四个值,Shader可以根据UV坐标去把图片进行截取,并融合成一个新的纹理。这样我们就可以通过C#脚本,动态的去改变RawImage的UV Rect的Vector4数值,然后设置给Shader,实时获取到截取的小地图

三、脚本

        1、UnityShader脚本

  顶点函数部分:

v2f vert (a2v v)
{
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = (v.uv * _UVScaleOffset.xy) + _UVScaleOffset.zw;//添加Uv偏移和大小
    return o;
}

片元函数部分:

fixed4 frag (v2f i) : SV_Target
{
    //使用修改后的UV坐标从主纹理采样颜色
    fixed4 color = tex2D(_MainTex, i.uv);//获取原图颜色
    color.rgb =  color.rgb 
    return color;
}

全部Shader代码:

Shader "Leh1ng01/MapShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _UVScaleOffset ("UV Scale Offset", Vector) = (1, 1, 0, 0)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            struct a2v
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
            };

            sampler2D _MainTex;
            fixed4 _UVScaleOffset;

            v2f vert (a2v v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = (v.uv * _UVScaleOffset.xy) + _UVScaleOffset.zw;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 color = tex2D(_MainTex, i.uv);
                color.rgb = color.rgb;
                return color;
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

这样我们的Shader脚本就完成了,主要的逻辑都在顶点和片元函数中。

2、C#脚本

using UnityEngine;

public class MapShaderController : MonoBehaviour
{
    public Material myMaterial;
    public Vector4 uvScaleOffset = new Vector4(1, 1, 0, 0);

    void Start()
    {
        if (myMaterial != null)
        {
            // 修改UV缩放和偏移
            myMaterial.SetVector("_UVScaleOffset", uvScaleOffset);
        }
    }
}

在C#脚本中,我们只需要把uv的四个值,在获取到挂载上边的Shader的材质球后,直接对Shader中的属性进行赋值。然后把图片也进行一个设置即可。

场景中实现步骤:

上边的代码完成后,挂载到空物体上,挂载RawImage和C#脚本,再把上边Shader的材质球挂载到RawImage上,其实就已经可以通过手动改变这里的X、Y值,看到小地图的移动了。

当然,我们肯定是想要他动态的刷新地图的。所以我用简单的代码进行了一个实例:
 

void Update()
{
    if (Input.GetAxis("Horizontal") != 0 || Input.GetAxis("Vertical") != 0)
    {
        Player.transform.Translate(Vector3.forward * Time.deltaTime *             Input.GetAxis("Vertical") * 5);

        Player.transform.Rotate(Vector3.up * Time.deltaTime * Input.GetAxis("Horizontal") * 150);

        playerPoint.transform.localEulerAngles = new Vector3(0, 0, -Player.transform.eulerAngles.y);
        
        
        SetUpValue();
        
    }
}
private void SetUpValue()
{
    float x = Player.transform.position.x / texture.width * 20;
    float y = Player.transform.position.z / texture.height * 20;
    myImage.uvRect = new Rect(x + 0.5f, y + 0.5f,1, 1);
    myMaterial.SetVector("_UVScaleOffset", uvScaleOffset);
}

上边部分就是通过玩家在场景中的位置,计算了一下偏移,然后让UVRect的值发生改变并赋值给Shader。

四、场景搭建

UI的map是空对象,上边说过怎么挂载。他的子对象就是一个图片,用来显示玩家的。最后挂完是这样:

下边的map也是一个空对象,就是为了直接把小地图放到3D场景里:

五、完成

        这样我们就完成了通过UV坐标,改变小地图显示的demo。应该还有优化的地方,在知道了大概逻辑后,就会有很多的可玩性,可以加上很多自己的逻辑。

        这个不用遮罩,不用把整个小地图图片放到UI上。只用通过计算偏移,然后改变UV,在通过Shader根据UV坐标渲染一部分就可以,个人认为很不错。

Logo

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

更多推荐