Minimaps are very common in video games these days. You can find them in almost every video game genre, like RPGs, MOBAs and FPSes. Minimaps are actually a mini representation of the entire game map, which helps players to get a quick snapshot of the in-game map. Games like Valorant and MMORPG games allow players to interact with the minimap itself in order to move or use some specific powerups.

A few weeks back I was trying to replicate the base mechanics of an FPS game character attribute. In this game, the character places some marks on the map. Then, after he presses the right mouse button, the game drops smokes in the same exact area as the minimap marking. The challenge was to get the exact position of the player's click in the minimap then convert it into the real world position and finally place the smokes ready for spawn. The result is below.

Valorant agent brimstone's smoke ability prototype

Creating the Minimap

Before getting started, make sure that you have created a camera that casts as your minimap.

If you don't know how to create a minimap in Unity, you can do it by creating a topdown camera that captures your map, then setting your culling masks to the object layers that you want to minimap to show, and finally creating a render texture then assign it to the minimap camera's texture.

You can now create a raw image in your UI canvas and assign it with the render texture that we just made. This will make the UI image render everything your minimap camera sees.  

Getting World Position Through the Minimap Camera

First of all, we create a class called MiniMapController which implements the IPointerClickHandler which helps us get pointer clicks. So let's split the point conversion into two parts where first we take the local cursor from the click in the minimap and in the second part we cast the minimap's click ray into the world position.

public class MiniMapController : MonoBehaviour, IPointerClickHandler
{
    public Camera miniMapCam;
    

    public void OnPointerClick(PointerEventData eventData)
    {
        Vector2 curosr = new Vector2(0, 0);

        if (RectTransformUtility.ScreenPointToLocalPointInRectangle(GetComponent<RawImage>().rectTransform,
            eventData.pressPosition, eventData.pressEventCamera, out curosr))
        {

            Texture texture = GetComponent<RawImage>().texture;
            Rect rect = GetComponent<RawImage>().rectTransform.rect;

            float coordX = Mathf.Clamp(0, (((curosr.x - rect.x) * texture.width) / rect.width), texture.width);
            float coordY = Mathf.Clamp(0, (((curosr.y - rect.y) * texture.height) / rect.height), texture.height);

            float calX = coordX / texture.width;
            float calY = coordY / texture.height;

       
            curosr = new Vector2(calX, calY);
            
            CastRayToWorld(curosr);
        }
        
        
    }
    
    private void CastRayToWorld(Vector2 vec)
    {
        Ray MapRay = miniMapCam.ScreenPointToRay(new Vector2(vec.x * miniMapCam.pixelWidth,
            vec.y * miniMapCam.pixelHeight));

        RaycastHit miniMapHit;

        if (Physics.Raycast(MapRay, out miniMapHit, Mathf.Infinity))
        {
            Debug.Log("miniMapHit: " + miniMapHit.collider.gameObject);
            hitTransforms.Add(miniMapHit.point);
        }
        
    }
Minimap Controller Script

How It Works

As you can see in the script above, on pointer click we use the recttransform utility to convert our screen point to a local position in recttransform.

We create a reference to our map's texture and a rect that takes rect transform's rect as a value. Then, we clamp our local cursor X and Y values respective to the value 0, the subtraction of the cursor and rect, which we later multiply by the texture's width and rect's width. You can do this similarly by using Y values, but in that case, we use the Y value instead of X and height instead of width.

Then we create two floats calX and calY which is converted to a value between 0 and 1 and then assign our vector2 cursor with the values of calX and calY. After doing this we have received the ray that we cast into the minimap as a vector2 value which now needs to be converted into the real-world position of the player.

In order to do that I have created a function called CastRayToWorld which takes a vector as a parameter. So in this function, we create a Ray MapRay that converts our minimap camera's screen point to ray with reference to the vector2 we got from our pointer click. Then we create a RaycastHit called miniMapHit which is later used with Physics. RayCast to check the RayCast hit points which give us our exact converted hit in the miniMapHit variable. So finally, the miniMapHit point is the exact point where our player pressed in the minimap.