cheap and easy minimap (No 2nd Cam)

Hey, most tutorials i find on making a mini map for unity are base on rendering a second camera to a render texture. which in my opinion is way to expensive for something like a mini map.
So here’s a cheap and easy way of doing it.

1 First we need to take a screenshot of the scene in top view.

  • Place a Camera in your scene and rotate the x axis with 90 degrees.
  • Set it to orthographic and set the size so that you see the whole area that is your map. Remember the size because we need this later.

to take a screenshot put this on your Camera. btw: No need to be in play mode.

using UnityEditor;
using UnityEngine;

public class EditorScreenShots : MonoBehaviour
{

}

[CustomEditor(typeof(EditorScreenShots))]
public class EditorScreenShotsEditor : Editor
{
    public string textureName = "Minimap_";
    public string path = "Assets/Textures/Minimap/";
    static int counter;

    public override void OnInspectorGUI()
    {
        textureName = EditorGUILayout.TextField("Name:", textureName);
        path = EditorGUILayout.TextField("Path:", path);
        if(GUILayout.Button("Capture"))
        {
            Screenshot();
        }
    }

    //[MenuItem("Screenshot/Take screenshot")]
    void Screenshot()
    {
        ScreenCapture.CaptureScreenshot(path + textureName + counter+ ".png");
        counter++;
    }
}

In photoshop or any photo editing software, make the screenshot a square and save it as a jpg or png.

Next we do a small shader:

  • Create a new Unlit shader
  • copy paste below code in it :wink:

I explained in the comments how it works…

Shader "Unlit/Minimap"
{
    Properties
    {
        _MainTex("Minimap", 2D) = "white" {}
        _Zoom("Zoom", Float) = 1
        _Radius("Radius", Float) = 1
    }
    SubShader
    {
        Tags {"Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout"}
        LOD 100
        ZWrite Off
        Blend SrcAlpha OneMinusSrcAlpha
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            half _Zoom;
            half _Radius;
            half3 _PlayerPos;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            //return 0 to 1 value based on the uv coordinate to the center of the uv
            half circle(half2 _uv, half _r)
            {
                half2 dist = _uv - half2(.5, .5);
                return 1.0 - smoothstep(_r - (_r*0.01), _r + (_r*0.01), dot(dist, dist)*4.0);
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                //offset the texture to the player position and add the offset made by the _Zoom relative to the center of the uv
                fixed4 col = tex2D(_MainTex, (_Zoom * i.uv) + half2(_PlayerPos.z + (.5-(_Zoom / 2)), _PlayerPos.x*-1 + (.5-(_Zoom / 2))));
              
//lerp over the alpha with the value we get from the circle function, so value 0 to _r * 0.01 will render a gradient fade to all above that will be our texture
                col.a = circle(i.uv, _Radius);
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}

next make a canvas and put an image inside. Make a material out of the above shader and drag it into to material slot on the image component.
You will see the material properties appear in the inspector. Drag your texture into the texture slot.

Last thing we need to do is make a small script you can use to past the player position.

using UnityEngine.UI;
using UnityEngine;

public class MiniMap : MonoBehaviour
{
    public Image playerImage;
    public float size;
    public Material material;
    private Transform player;
    private void Start()
    {
        player = FindObjectOfType<UnityStandardAssets.Characters.FirstPerson.FirstPersonController>().transform;
    }
    private void Update()
    {
        //align the player icon to the player rotation; i do - and +90 because my game north is not on 0 degrees
        playerImage.rectTransform.rotation = Quaternion.Euler(new Vector3(0,0,-player.eulerAngles.y+90f));
        //size is the value of the orthographic size of the camera you toke the screenshot with. *2 because the orthograpic size represents half the height of the screen.
        material.SetVector("_PlayerPos", player.position / (size*2f));
    }
}

put this on the MiniMap Image in the canvas or anywhere actually. you can make a player icon, which is just a small triangle in the middle of the Image. Drag that to playerImage and the material from the MiniMap Image into material and you should be good to go.

good luck and enjoy.

2 Likes

I used this as a starting point and it worked great for me! One thing I would add is you probably want to account for your camera’s aspect ratio (if the image is not square) - i ended up using these formulas:

MapObjectMaterial.SetTextureScale(“_MainTex”, new Vector2(1/_aspectRatio, 1f));
MapObjectMaterial.SetTextureOffset(“_MainTex”, new Vector2(0.5f * (1 - (1/_aspectRatio)), 0f));

1 Like

Hi, you can use this method to make a mini map without a second camera. Take a screenshot of the map, place it inside a canvas. Then create 4 transform positions and place it in the game world edges. Then use this script and assign the values.

         public RectTransform marker; //player pointer image
         public RectTransform mapImage;//Map screenshot used in canvas
         public Transform playerReference;//player
         public Transform[] mapEdges;//4 edges, make sure its in rectangle.
         public Vector2 offset;//Adjust the value to match you map
    
         private Vector2 mapDimentions;
         private Vector2 areaDimentions;
    
         private void Start()
         {
             for (int i = 0; i < mapEdges.Length; i++)
                 for (int j = i + 1; j < mapEdges.Length; j++)
                 {
                     if (mapEdges[j].position.x < mapEdges[i].position.x || mapEdges[j].position.z < mapEdges[i].position.z)
                     {
                         Transform temp = mapEdges[j];
                         mapEdges[j] = mapEdges[i];
                         mapEdges[i] = temp;
                     }
                 }
             mapDimentions = new Vector2(mapImage.sizeDelta.x, mapImage.sizeDelta.y);
             areaDimentions.x = mapEdges[1].position.x - mapEdges[0].position.x;
             areaDimentions.y = mapEdges[2].position.z - mapEdges[0].position.z;
         }
        
         private void Update()
         {
             SetMarketPosition();
         }
        
         private void SetMarketPosition()
         {
             Vector3 distance = playerReference.position - mapEdges[0].position;
             Vector2 coordinates = new Vector2(distance.x / areaDimentions.x, distance.z / areaDimentions.y);
             marker.anchoredPosition = new Vector2(coordinates.x * mapDimentions.x, coordinates.y * mapDimentions.y) + offset;
             marker.rotation = Quaternion.Euler(new Vector3(0, 0, -playerReference.eulerAngles.y));
         }

Hope this helps.

1 Like