教你3个步骤实现Unity小地图(知识点:RenderTexture、Mask、shader)

一、前言

小地图功能在很多游戏中都有,特别是RPG游戏,在Unity中如何实现小地图功能呢,我来教你。

二、思路

专门创建一个顶视角的摄像机,然后摄像机有个Target Texture属性,可以将渲染的图形输出到Render Texture中,我们使用UGUIRaw Image来显示这张Render Texture即可。
在这里插入图片描述

三、GitHub工程

本文的Demo工程我已上传到GitHub,感兴趣的同学可以自行下载进行学习。注:我用的Unity版本是:Unity 2020.1.2f1c1 (64-bit)
GitHub工程地址:https://github.com/linxinfa/Unity-Minimap
在这里插入图片描述

四、具体步骤

1、创建一个顶视角摄像机

在这里插入图片描述

2、创建Render Texture

创建Render Texture
在这里插入图片描述
Render Texture赋值给顶视角摄像机的Target Texture
在这里插入图片描述

3、使用Raw Image显示

创建一个RawImage
在这里插入图片描述
RenderTexture赋值给RawImageTexture属性
在这里插入图片描述

四、运行效果

运行Unity效果如下
在这里插入图片描述

五、优化

1、Mask蒙版边缘锯齿问题

上面小地图通过了Mask组件实现了圆形蒙版的效果。有一个小小的缺点,就是圆形边缘有锯齿。放大看一看就可以看到
在这里插入图片描述

2、解决方案

首先我们的蒙版图片如下,是一个白色的圆,周围是透明的。
我们知道颜色值的表示是:(r, g, b, a),白色就是(1, 1, 1, 1),透明部分的颜色为(0, 0, 0, 0)
在这里插入图片描述

摄像机渲染出来的小地图RenderTexture如下。
如果我们取某个像素点的颜色看,它可能是这样子的(0.31, 0.15, 0.7, 1)
在这里插入图片描述
想要让小地图以蒙版图的形状显示,我们只需取蒙版图的alpha通道作为小地图的alpha通道即可。
对应成shader代码就是

fixed4 frag(v2f IN) : SV_Target
{
	//原图
    half4 color = tex2D(_MainTex, IN.texcoord) * IN.color;
    //蒙版图
    half4 color2 = tex2D(_Mask, IN.texcoord);
    //以蒙版图的alpha通道作为原图的alpha通道输出
    return fixed4(color.r, color.g, color.b, color2.a);
}

现在我们只自定义一个shader(完整shader代码见下文),赋值给一个材质球,然后把材质球赋值给小地图RawImageMaterial即可
在这里插入图片描述
我们放大效果对比看看,左边是UGUIMask效果,右边是我们通过shader实现的Mask效果,可以看到锯齿基本没有了,平滑了很多。
在这里插入图片描述 在这里插入图片描述

六、加个主角和摄像机跟随逻辑

主角,我就用一个正方体Cube替代了,加上WASD控制移动逻辑,然后主摄像机和顶视角摄像机跟随逻辑,再对比展示两种方式实现的小地图效果,最终效果如下,详细代码见下文或直接下载我的Demo工程学习
在这里插入图片描述

七、代码部分

1、shader脚本:Default-Mask.shader
Shader " UIEx/Default-Mask"
{
    Properties
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
         _Mask ("Mask Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)

        _StencilComp ("Stencil Comparison", Float) = 8
        _Stencil ("Stencil ID", Float) = 0
        _StencilOp ("Stencil Operation", Float) = 0
        _StencilWriteMask ("Stencil Write Mask", Float) = 255
        _StencilReadMask ("Stencil Read Mask", Float) = 255

        _ColorMask ("Color Mask", Float) = 15
    }

    SubShader
    {
        Tags
        {
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

        Stencil
        {
            Ref [_Stencil]
            Comp [_StencilComp]
            Pass [_StencilOp]
            ReadMask [_StencilReadMask]
            WriteMask [_StencilWriteMask]
        }

        Cull Off
        Lighting Off
        ZWrite Off
        ZTest [unity_GUIZTestMode]
        Blend SrcAlpha OneMinusSrcAlpha
        ColorMask [_ColorMask]

        Pass
        {
        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata_t
            {
                float4 vertex   : POSITION;
                float4 color    : COLOR;
                float2 texcoord : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex   : SV_POSITION;
                fixed4 color    : COLOR;
                half2 texcoord  : TEXCOORD0;
            };

            fixed4 _Color;

            v2f vert(appdata_t IN)
            {
                v2f OUT;
                OUT.vertex = UnityObjectToClipPos(IN.vertex);
                OUT.texcoord = IN.texcoord;
#ifdef UNITY_HALF_TEXEL_OFFSET
                OUT.vertex.xy += (_ScreenParams.zw-1.0)*float2(-1,1);
#endif
                OUT.color = IN.color * _Color;
                return OUT;
            }

            sampler2D _MainTex;
            sampler2D _Mask;

            fixed4 frag(v2f IN) : SV_Target
            {
				//原图
			    half4 color = tex2D(_MainTex, IN.texcoord) * IN.color;
			    //蒙版图
			    half4 color2 = tex2D(_Mask, IN.texcoord);
			    //以蒙版图的alpha通道作为原图的alpha通道输出
			    return fixed4(color.r, color.g, color.b, color2.a);
            }
        ENDCG
        }
    }
}
2、主角移动控制脚本:PlayerCtrler.cs
//PlayerCtrler.cs
using UnityEngine;

/// <summary>
/// 角色移动控制
/// Author: linxinfa
/// </summary>
public class PlayerCtrler : MonoBehaviour
{
    /// <summary>
    /// 移动速度
    /// </summary>
    public float speed;

    private Transform m_transform;
    private Vector3 m_curPos;

    void Start()
    {
        m_transform = transform;
    }

    void Update()
    {
        var delta = Time.deltaTime* speed;
        m_curPos = m_transform.position;

        if (Input.GetKey(KeyCode.W))
        {
            m_curPos += new Vector3(0, 0, 1) * delta;
        }
        else if(Input.GetKey(KeyCode.S))
        {
            m_curPos -= new Vector3(0, 0, 1) * delta;
        }

        if (Input.GetKey(KeyCode.A))
        {
            m_curPos -= new Vector3(1, 0, 0) * delta;
        }
        else if (Input.GetKey(KeyCode.D))
        {
            m_curPos += new Vector3(1, 0, 0) * delta;
        }
        m_transform.position = m_curPos;
    }
}
3、主摄像机跟随脚本:PlayerCameraFollow.cs
//PlayerCameraFollow.cs
using UnityEngine;

/// <summary>
/// 第三人称视角摄像机跟随
/// Author: linxinfa
/// </summary>
public class PlayerCameraFollow : MonoBehaviour
{
    public Transform targetPlayer;

    /// <summary>
    /// 距离
    /// </summary>
    public float distanceAway = 1.7f;
    /// <summary>
    /// 高度
    /// </summary>
    public float distanceUp = 1.3f;
    /// <summary>
    /// 平滑度
    /// </summary>
    public float smooth = 2f;               

    private Vector3 m_targetPos;
    private Transform m_cameraTransform;
    
    void Awake()
    {
        m_cameraTransform = transform;
    }

    void LateUpdate()
    {
        if (null == targetPlayer) return;
        // 计算摄像机的目标坐标
        m_targetPos = targetPlayer.position + Vector3.up * distanceUp - targetPlayer.forward * distanceAway;

        // 插值设置坐标,有个平滑跟随效果
        m_cameraTransform.position = Vector3.Lerp(m_cameraTransform.position, m_targetPos, Time.deltaTime * smooth);

        // 设置摄像机角度,对准跟随目标
        m_cameraTransform.LookAt(targetPlayer);
    }
}
4、顶视角摄像机跟随脚本:MiniMapCameraFollow.cs
//MiniMapCameraFollow.cs
using UnityEngine;

/// <summary>
/// 小地图摄像机跟随
/// Auhor: linxinfa
/// </summary>
public class MiniMapCameraFollow : MonoBehaviour
{
    public Transform targetPlayer;

    /// <summary>
    /// 高度
    /// </summary>
    public float distanceUp = 1.3f;

    private Transform m_cameraTransform;

    void Awake()
    {
        m_cameraTransform = transform;
    }

    void LateUpdate()
    {
        if (null == targetPlayer) return;

        m_cameraTransform.position = targetPlayer.position + Vector3.up * distanceUp; 
    }
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林新发

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值