C# – Interactive 2D map in Unity

cinteractiveshapeunity3d

I am pretty new to Unity but I have been able to find my way around. However, I have encountered a problem and no matter how much I google it I can't seem to find a solution that works. I am probably just doing something silly. Any help would be appreciated. I posted to the Unity forum but no one is responding so I figured I would give Stack Overflow a chance too.

So what I am trying to do is to create a 2D map in Unity. This map will be used to navigate through a 3D world. The map must be clickable because I will start with a world map and then based off the user's click, the map needs to change to that country's map. I have talked to different people and the way I want to go is with shapefiles since they already contain the information as to where the country and state boundaries are. In addition, I know I will have to work with shapefiles in the future so I figured I would just get some practice with them now. I wanted to start small so I downloaded a zipfile that contained the shapefile for the united states. I downloaded QGIS per a forums instructions and opened the shapefile in that software. In QGIS I was able to highlight individual states as shown in the photo below

What I get from QGIS. Still want the highlighting and selecting in Unity

Now I want this same functionality in Unity, except I don't just want to highlight it, I also want it to be clickable and to be able to identify that state as Montana.

So from here I was able to converted this file to a DXF and then converted that to an FBX. But after that I am lost. I am not sure I even had to do those conversions.

If anyone could help me in this endeavor I would great appreciate it! Again I am new to Unity so if you could keep that in mind I would greatly appreciate it.

Best Answer

enter image description here

The answer below describes how do accomplish that. This was done with just 8 States for demonstration purposes.

There are 4 steps to do this but each step may have another step inside it. Create the model, export it and import it into Unity, add Mesh Collider to each one, detect click with OnPointerDown then change the color of the clicked GameObject,.

You need Maya 2016 and Unity 5.5 for this.

First thing to note is that Maya has a simple menu and option menu.

If I say "(option menu)", I am talking about the box that is next to the menu on the right side. Take a look at the image below for the difference between menu and option menu:

enter image description here

Make sure to check if any of instruction below has "(option menu)" in it.

1.Creating Image Reference for modeling the states.

A.Get reference map image.

Find a black and white image reference of the U.S. states. I will be using this one so download it.

B.Make Image Plane with the reference file.

Open Maya and create new Project and Import the the state image you downloaded.

To do this, go to Create --> Free Image Plane.

Under Image Plane Attribute, Select the icon in the Image Name then put the path of the image map reference you downloaded there.

enter image description here

C.Switch to Front view.

Select the Image Plane, position your mouse in the middle of the view then press the SPACE key on the keyboard. You will see four camera views. Move your mouse to the left-bottom view (Front View) then press the SPACE key again. Maya will switch to Front View. You will be working in Front View.

Now, Press F to zoom into the Image Plane.

enter image description here

D.Put the Image Plane in a layer then lock it.

Once the camera is zoomed in Image Plane, select the Image Plane again if not selected then click on Channel Box/Layer Editor.

Now, click on Layers --> Create Layer from Selected. There are three checkboxes on the created layer.

Continue to click on the third(last) checkboxes until it says "R". The "R" will lock the Image Plane so that it can't be selected again or interfere when modeling.

enter image description here

2.Creating each state Model.

A.Create curve Go to Create --> Curve Tools --> Pencil Curve Tool

Draw any state one by one by holding down the left mouse button and tracing it around the chosen state. Leave a little space between the beginning and end of the line.

I will draw Nebraska as an Example.

enter image description here B.Close the curve.

Select the curve then go to Curves --> Open/Close

enter image description here

C.Create a Nurbs Plane to cover the whole shape of curve you drew.

Select the curve then go to Modify --> Center Pivot

You will use this curve to cut out its shape from the Nurbs Plane.

Go to Create --> NURBS Primitives then make sure that Interactive Creation and Exit On Completion are both checked/enabled.

Go to Create --> NURBS Primitives --> Plane.

Hold down the Left mouse button then drag and move the mouse to create a Plane. Make sure that the size of the Plane covers the shape of the state drawn in step 2A.

enter image description here

D.Project the curve to the surface

Select both the Plane created in step 2C and the curve created in step 2A.

You do that by holding down the left mouse button and drag the mouse cursor over both of them then release.

Both Objects should now be selected.

Go to Surfaces --> Project Curve on Surface

enter image description here

E.Trim the selected object.

Select the Plane from Step D.

Go to Surfaces --> Trim Tool

Click inside(middle) of the curve then press Enter.

The shape of the curve should now be cut out of the Plane,

enter image description here

F.Delete History then Center the pivot.

Select everything created in this process(Left click and hold then move mouse around everything then release).

Go to Edit --> Delete by Type --> History.

Go to Modify --> Center Pivot

enter image description here

G.Convert the NubSurface Plane to Polygon because Unity and other Game Engines uses Polygon.

Select the Plane from Step E.

Go to Modify --> Convert -->NURBS To Polygons (option menu)

Use the Settings Below:

Type: Quads

Tessellation method: Standard fit

Chord height ratio: 0.54

Fractional tolerance: 0.01

Minimal Edge length: 0.001

3D delta: 0.0461

Click Apply.

Now, you have a polygon, rename it to the appropriate state name(Nebraska).

enter image description here

H.Delete Objects behind

With the polygon still selected.

Go to Modify --> Freeze Transformations

Go to Modify --> Center Pivot

Move the polygon away. After this, delete every other objects(NubSurface and curve) behind it.

Select the polygon again then go to Modify --> Reset Transformations. This will bring the polygon back to its original position.

Go to Modify --> Center Pivot

enter image description here

I.Add Material the state created.

Go to the Rendering tab.

Click on the Round Icon which is a Blinn material. It will create a Blinn Material. Name it "State_Material". If you have already created "State_Material", you don't have to create a new one.

Go to Rendering Mode.

Select the polygon.

Go to Lighting/Shading --> Assign Existing Material--> State_Material.

The goal is to assign one material(State_Material) to all State/polygon. This will prevent having to export 50 materials to Unity.

enter image description here

J.Automatically Map the UV.

Go to UV --> Automatic

enter image description here

K.Delete History then Center the pivot of the converted Object.

Go to Edit --> Delete by Type --> History.

Go to Modify --> Center Pivot

enter image description here

Done! Jump back to 2.Creating each state Model. and do the-same for the rest of the state.

3.Export as FBX.

A.Export as Fbx.

Select all the States. I have 8 in my scene.

Go to File --> Export Selection...(option menu).

Change File type to FBX export.

Click the Export Selection Button

Choose the Directory and file name then click Export Selection again.

enter image description here

4.Using Maps in Unity.

A.Import the Fbx into Unity.

Open Unity, go to Assets --> Import New Asset...

Choose the directory and FBX file saved through Maya.

enter image description here

B.Attach Mesh collider to each State/Plane.

Drag the Map to the Hierarchy.

Select all the states/models then go to Components --> Physics --> Mesh Collider.

enter image description here

C.Position the model to camera view.

Had to rotate the Object to 180 deg in y-axis then scale the x and y axis to 100. If you followed this tutorial exactly like like this with the-same image reference, then you will have to rotate and scale it the-same way.

enter image description here

D.Setup Event System

Go to GameObject --> Create Empty then name it "EventSystem".

Go to Component --> Event --> Event System.

Go to Component --> Event --> Standalone Input Module.

Select the "Main Camera" and go to Component --> Event --> Physics Raycaster. I said Physics Raycaster not Physics 2D Raycaster

enter image description here

E.Attach test Scripts

Go to GameObject --> Create Empty then name it "MapManager".

Select the MapManager GameObject and attach the MapManager script to it.

Select all the states/Model except for the parent Object then attach MapClickDetector script to all of them. Doing it once is easier.

enter image description here

Description of the Script:

The MapClickDetector script will detect mouse click, mouse over and other mouse actions then send it to the MapManager script. You can then do whatever you want in the MapManager script. It also sends the Map GameObject that is clicked so that you can get the name of the State.

To detect a click, IPointerDownHandler is implemented then OnPointerDown is used to detect which state in the map is clicked. You can also use Raycast but IPointerDownHandler is is better since it prevents problems with the UI.

MapManager script:

using UnityEngine;

public class MapManager : MonoBehaviour
{
    Color normalColor = Color.red;

    Color mouseDownColor = Color.green;
    Color mouseEnterColor = Color.yellow;

    // Use this for initialization
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {

    }

    public void mapclick(GameObject objClicked)
    {
        Debug.Log("Clicked: " + objClicked.name);
    }

    public void mapMouseDown(GameObject objClicked)
    {
        Debug.Log("Pointer Down: " + objClicked.name);

        MeshRenderer mr = objClicked.GetComponent<MeshRenderer>();
        mr.material.color = mouseDownColor;
    }

    public void mapMouseUp(GameObject objClicked)
    {
        Debug.Log("Pointer Up: " + objClicked.name);

        MeshRenderer mr = objClicked.GetComponent<MeshRenderer>();
        mr.material.color = normalColor; ;
    }

    public void mapMouseEnter(GameObject objClicked)
    {
        Debug.Log("Pointer Enter: " + objClicked.name);

        MeshRenderer mr = objClicked.GetComponent<MeshRenderer>();
        mr.material.color = mouseEnterColor;
    }

    public void mapMouseExit(GameObject objClicked)
    {
        Debug.Log("Pointer Exit: " + objClicked.name);

        MeshRenderer mr = objClicked.GetComponent<MeshRenderer>();
        mr.material.color = normalColor;
    }
}

MapClickDetector script:

using UnityEngine;
using UnityEngine.EventSystems;

public class MapClickDetector : MonoBehaviour, IPointerClickHandler, IPointerDownHandler,
    IPointerUpHandler, IPointerEnterHandler, IPointerExitHandler
{
    MapManager mapManager;

    void Start()
    {
        addPhysicsRaycaster();

        mapManager = GameObject.Find("MapManager").GetComponent<MapManager>();
    }

    void addPhysicsRaycaster()
    {
        PhysicsRaycaster physicsRaycaster = GameObject.FindObjectOfType<PhysicsRaycaster>();
        if (physicsRaycaster == null)
        {
            Camera.main.gameObject.AddComponent<PhysicsRaycaster>();
        }
    }

    public void OnPointerClick(PointerEventData eventData)
    {
        mapManager.mapclick(gameObject);
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        mapManager.mapMouseDown(gameObject);
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        mapManager.mapMouseUp(gameObject);
    }

    public void OnPointerEnter(PointerEventData eventData)
    {
        mapManager.mapMouseEnter(gameObject);
    }

    public void OnPointerExit(PointerEventData eventData)
    {
        mapManager.mapMouseExit(gameObject);
    }
}
Related Topic