Unity Enemy Radar Tutorial

Create a UI Canvas.
Create an enemy radar display on the UI canvas.
Display enemies and NPC’s on the radar as blips.
Use a UI mask to hide the edges of the radar blips.

0. Introduction

In this Unity enemy radar tutorial we will create a basic UI enemy radar display on a UI canvas that will show different types of entities like enemies and NPC’s with different colored radar blips.

This tutorial was made with Unity 2019.3.6f1

The end result of this
tutorial

1. Create a new Unity project

For this tutorial I’ve used Unity 2019.3.6f1 with a standard 3D project template but this tutorial should work with any recent Unity version and with every available rendering pipeline:

Example image showing the new project creation window of Unity3D.

2. Import Standard Assets and radar textures

Open the Asset Store window with Window > Asset Store, search for ‘Unity Standard Assets’ and import the package into your project.
Alternatively you can use the link below to download the Standard Assets from the Asset Store in your web browser:
https://assetstore.unity.com/packages/essentials/asset-packs/standard-assets-for-unity-2017-3-32351
The only asset from the Standard Assets that we really need for this tutorial is the FPSController prefab but the script that it uses has some other dependencies.
(If you get errors about GUITexture being obsolete after importing the Standard Assets you can delete the ForcedReset.cs and SimpleActivatorMenu.cs files, since we don’t need those anyway.)

Download the three textures below by right-clicking on them and import them into your Unity project. After importing them select the radar and blip textures in the Project view and make sure ‘Alpha is Transparency’ is checked in the Inspector:

Circular texture for use as an enemy radar background.
The enemy radar background image
Circular radar blip texture.
The enemy radar blip texture
10x10 checkerboard pattern grid texture for use as a placeholder floor texture.
The floor grid texture

3. Create a testing environment

3.1 Creating a floor

Create a floor plane for the player to walk on with GameObject > 3D Object > Plane.
Select the Plane and mark is as static in the Inspector so it will be baked into the NavMesh later on.
Scale the plane up to 5 on the x and z axes in the Transform component to make it larger.
Download the texture from below into your project and drag it from the Project view onto the plane in the Scene view. A new material with the texture applied will be created inside of a folder named Materials.
Change the texture tiling from the grid material to 5 on the x and y axes in the Inspector, to match the scale of the plane:

Example image showing the floor plain with a grid texture applied.

3.2 The First Person Shooter Controller

From the project view, drag the FPSController prefab from the Standard Assets/Characters/FirstPersonCharacter/Prefabs folder onto the floor plane in the Scene view and make sure it sits above the floor by setting the Transform position on the y-axis to 1 or higher in the Inspector.
Delete the default Main Camera from the Hierarchy because the FPSController has its own camera attached:

Image showing the first person shooter controller object placed on the floor.

3.3 Random Walking Enemies and NPC’s

Create two capsules with GameObject > 3D Object > Capsule, these will serve as placeholders for enemies and NPC’s so name one of them enemy and the other NPC. Color the enemy capsule red by making a new red material in the Project view with right click and Create > Material.
Drag the red material on the enemy in the Scene view to apply it.
Do the same with a new green material for the NPC capsule.
Select the red capsule and tag it with a new tag ‘Enemy’ in the top left of the Inspector. Tag the green capsule ‘NPC’:

Image showing the enemy capsule with the red material and enemy tag applied.
Image showing the NPC capsule with the green material and NPC tag applied.

Select both capsules in the Hierarchy and add a Nav Mesh Agent component to them with the Add Component button in the Inspector.

Image showing inspector with the Nav Mesh agent component added to the capsules.

With both still selected press Add Component again and type RandomWalk into the search field. After typing it in press on New script and then Create and Add:

Image showing the random walk script name typed into the add component menu.

Double click on the newly created script in the Project view to open it, make sure it contains the following code and then save the script:

//using System.Collections;
//using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

// Walk to a random position and repeat
[RequireComponent(typeof(NavMeshAgent))]
public class RandomWalk : MonoBehaviour
{
    public float m_Range = 25.0f;
    NavMeshAgent m_agent;

    void Start()
    {
        m_agent = GetComponent<NavMeshAgent>();
    }

    void Update()
    {
        if (m_agent.pathPending || m_agent.remainingDistance > 0.1f)
            return;

        Vector2 randomPos = Random.insideUnitCircle;
        m_agent.destination = m_Range * new Vector3(randomPos.x,0,randomPos.y);
    }
}

Open the Navigation window with Window > AI > Navigation. Select the Bake tab and click on the Bake button to generate a NavMesh for our Enemy and NPC NavMeshAgents to walk on:

Image showing the NavMesh bake tab of the Navigation window.

If you press play now you can walk around using the WASD keys and your mouse. You will also see that the enemy and NPC placeholders walk around randomly across the floor plane,.. Purrfect for testing radars!:

Animated GIF showing the enemy and npc placeholder cylinders walking around.
Advertisements

4. Create a UI Canvas and Radar

4.1 The Radar Background Image

Right click in the Hierarchy and create a UI > RawImage. A new Canvas GameObject will be created with the RawImage as a child. Change the name from the RawImage to EnemyRadar:

Image showing a canvas with a raw image as a child.

Tip: When working with UI Canvasses it is usually best to enable 2D view mode in the Scene view with the 2D button. Then if you zoom out quite a bit in the scene view, you get a good view on the canvas..

In the Rect Transform, position and anchor the radar image by clicking on the top-left anchor preset while holding both Shift and Alt:

Image showing the EnemyRadar RawImage anchor preset settings.


Set the width and height in the Rect Transform to 200.
Select the EnemyRadar GameObject in the Hierarchy panel and drag the EnemyRadar-Background-Tex image from the Project view into the Texture property of the Raw Image component:

Image showing the radar background texture applied to the raw image component texture property.

4.2 Radar -Blips-

Create another RawImage in the Hierarchy, name it EnemyRadar-Blip-Red and assign the RadarBlip texture to the RawImage’s Texture property.
Set the RawImage color to red:
Tag the EnemyRadar-Blip-Red GameObject with a new tag ‘RadarBlip’.
Duplicate EnemyRadar-Blip-Red in the Hierarchy and rename it to EnemyRadar-Blip-Green. Also Change the color to green:

Image showing the radar blip image with the texture, color and tag applied.


Drag EnemyRadar-Blip-Red from the Hierarchy into the Project view to create a new Prefab and do the same with the green blip:

Image showing the Project view with the created enemy radar blip prefabs.

Not really necessary, but we can delete the red and green blips from the Hierarchy because they will be spawned in (and destroyed) by the radar script during play mode anyway.

4.3 The Radar Script

Create a new C# script called EnemyRadar, make sure it contains the following code and save the script:

//using System.Collections;
//using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class EnemyRadar : MonoBehaviour
{
    public float radarDistance = 20, blipSizePercentage = 15;
    public bool usePlayerDirection = true;
    public Transform player;
    public GameObject blipRedPrefab, blipGreenPrefab;
    public string redBlipTag = "Enemy",greenBlipTag = "NPC";

    private float radarWidth, radarHeight, blipWidth, blipHeight;

    void Start() {
        radarWidth  = GetComponent<RectTransform>().rect.width;
        radarHeight = GetComponent<RectTransform>().rect.height;
        blipHeight  = radarHeight * blipSizePercentage/100;
        blipWidth   = radarWidth * blipSizePercentage/100;
    }

    void Update() {
        RemoveAllBlips();
        DisplayBlips(redBlipTag, blipRedPrefab);
        DisplayBlips(greenBlipTag, blipGreenPrefab);        
    }

    private void DisplayBlips(string tag, GameObject prefabBlip) {
        Vector3 playerPos = player.position;
        GameObject[] targets = GameObject.FindGameObjectsWithTag(tag);

        foreach (GameObject target in targets) {
            Vector3 targetPos = target.transform.position;
            float distanceToTarget = Vector3.Distance(targetPos, playerPos);

            if(distanceToTarget <= radarDistance) {
                Vector3 normalisedTargetPosition = NormalisedPosition(playerPos, targetPos);
                Vector2 blipPosition = CalculateBlipPosition(normalisedTargetPosition);
                DrawBlip(blipPosition, prefabBlip);
            }
        }
    }
    
    private void RemoveAllBlips() {
        GameObject[] blips = GameObject.FindGameObjectsWithTag("RadarBlip");
        foreach (GameObject blip in blips)
            Destroy(blip);
    }

    private Vector3 NormalisedPosition(Vector3 playerPos, Vector3 targetPos) {
        float normalisedTargetX = (targetPos.x - playerPos.x)/radarDistance;
        float normalisedTargetZ = (targetPos.z - playerPos.z)/radarDistance;
        
        return new Vector3(normalisedTargetX, 0, normalisedTargetZ);
    }

    private Vector2 CalculateBlipPosition(Vector3 targetPos) {
        // find the angle from the player to the target.
        float angleToTarget = Mathf.Atan2(targetPos.x,targetPos.z) * Mathf.Rad2Deg;

        // The direction the player is facing.
        float anglePlayer = usePlayerDirection? player.eulerAngles.y : 0;

        // Subtract player angle, to get relative angle to object subtract 90
        // so 0 degrees (same direction as player) is Up.
        float angleRadarDegrees = angleToTarget - anglePlayer - 90;

        // Calculate the xy position given the angle and the distance.
        float normalisedDistanceToTarget = targetPos.magnitude;
        float angleRadians = angleRadarDegrees * Mathf.Deg2Rad;
        float blipX = normalisedDistanceToTarget * Mathf.Cos(angleRadians);
        float blipY = normalisedDistanceToTarget * Mathf.Sin(angleRadians);

        // Scale the blip position according to the radar size.
        blipX *= radarWidth*.5f;
        blipY *= radarHeight*.5f;

        // Offset the blip position relative to the radar center
        blipX += (radarWidth*.5f) - blipWidth*.5f;
        blipY += (radarHeight*.5f) - blipHeight*.5f;

        return new Vector2(blipX, blipY);
    }

    private void DrawBlip(Vector2 pos, GameObject blipPrefab) {
        GameObject blip = (GameObject) Instantiate(blipPrefab);
        blip.transform.SetParent(transform);
        RectTransform rt = blip.GetComponent<RectTransform>();
        rt.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left,pos.x, blipWidth);
        rt.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Top,pos.y, blipHeight);
    }
}

Add the script to the EnemyRadar GameObject in the Hierarchy by dragging it onto it or with the Add Component button.
Select EnemyRadar in the Hierarchy and drag the FPSController from the Hierarchy into the Player property of the Enemy Radar script in the Inspector.
Drag the EnemyRadar-Blip-Red and EnemyRadar-Blip-Green prefabs from the Project view into the Blip Red and Blip Green properties of the Enemy Radar script in the Inspector:

Image showing the enemy radar script in the inspector, populated with values.

Finally, add a Mask component to the EnemyRadar GameObject with Add Component > UI > Mask in the Inspector.
The mask component uses the RawImage of the radar as the mask and will hide the areas of the blips that are outside of the radar circle when the blips are near the edge.:

Image showing how the areas of the blips that are outside of the radar circle are masked out.

Tip: We don’t have to use the radar background texture as the mask. We could also use a differently shaped mask texture if we used a separate raw image for the mask graphic. In that case we can hide the mask graphic with the ‘Show Mask Graphic’ property of the Mask component. The only rule with masks is that the mask always needs to be a parent of the masked objects.

5. Play Testing

If you press play now and walk around with the WASD keys you will see the blips on the radar being displayed at the relative positions of the enemies and the NPC’s, and if you move far enough away the blips will disappear!:

Animated GIF showing the end result of the Unity Enemy Radar Tutorial.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Powered by WordPress.com.

Up ↑

%d bloggers like this: