Overview
When using Unity for AEC-type project visualisations / interactive models it is quite useful to have a distance measuring tool.
I tried finding one from the Unity Asset store or some online samples on how to achieve this - with no luck.
I then used some of my previous scripts that utilised the RaycastHit class in Unity together with some samples on how to draw simple lines and created a solution for my 3D projects to measure distances.
Solution(s)
I used an FBX generated from Infraworks for my model (that's another article). The next step is to create the following components:
- A model
- A new script called "RayCoords"
- An empty Game Object to attach some scripts to. You can add these to any object however I like to keep them separate
- A material for the line object and some values for the line width and the model scale. I had to scale my Infraworks model down by a factor of 0.01 so my distance measure scale = 100
You'll also need a few labels to display the results, this is what I used:
- 2 x labels for X,Y,Z start- and end coordinates
- Another label for the distance result value (The slider has to do with other functions that I'll talk about in another article - so ignore that for now)
Finally you need the script, this is what it looks like:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using UnityEngine;
using UnityEngine.UI;
public class RayCoords : MonoBehaviour
{
int cli;
Vector3 p1, p2;
public Material lineMat;
public float lineWidth;
public float xscale = 100;
private Vector3? lStart = null;
private GameObject gObject;
// Update is called once per frame
void Update()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Input.GetMouseButtonDown(0))
{
if (Physics.Raycast(ray, out hit))
{
if (cli == 0)
{
cli = 1;
GameObject infcnt = GameObject.Find("lblDist1");
p1 = new Vector3(hit.point.x * xscale, hit.point.y * xscale, hit.point.z * xscale);
infcnt.GetComponent().text = p1.x + "," + p1.y + "," + p1.z;
lStart = new Vector3(hit.point.x, hit.point.y, hit.point.z);
return;
}
if (cli == 1)
{
cli = 2;
GameObject infcnt = GameObject.Find("lblDist2");
p2 = new Vector3(hit.point.x * xscale, hit.point.y * xscale, hit.point.z * xscale);
infcnt.GetComponent().text = p2.x + "," + p2.y + "," + p2.z;
float dist = Vector3.Distance(p2, p1);
GameObject infcnt2 = GameObject.Find("lblDist3");
infcnt2.GetComponent().text = "Distance = " + dist;
Vector3 lEnd = new Vector3(hit.point.x, hit.point.y, hit.point.z);
gObject = new GameObject();
var lineRender = gObject.AddComponent();
lineRender.material = lineMat;
lineRender.SetPositions(new Vector3[] { lStart.Value, lEnd });
lineRender.startWidth = lineWidth;
lineRender.endWidth = lineWidth;
return;
}
if (cli == 2)
{
cli = 0;
GameObject infcnt = GameObject.Find("lblDist1");
infcnt.GetComponent().text = "...";
GameObject infcnt1 = GameObject.Find("lblDist2");
infcnt1.GetComponent().text = "...";
GameObject infcnt2 = GameObject.Find("lblDist3");
infcnt2.GetComponent().text = "...";
lStart = null;
Destroy(gObject);
return;
}
}
else
{
cli = 0;
GameObject infcnt = GameObject.Find("lblDist1");
infcnt.GetComponent().text = "...";
GameObject infcnt1 = GameObject.Find("lblDist2");
infcnt1.GetComponent().text = "...";
GameObject infcnt2 = GameObject.Find("lblDist3");
infcnt2.GetComponent().text = "...";
lStart = null;
Destroy(gObject);
return;
}
}
if (Input.GetMouseButtonDown(1))
{
cli = 0;
GameObject infcnt = GameObject.Find("lblDist1");
infcnt.GetComponent().text = "...";
GameObject infcnt1 = GameObject.Find("lblDist2");
infcnt1.GetComponent().text = "...";
GameObject infcnt2 = GameObject.Find("lblDist3");
infcnt2.GetComponent().text = "...";
lStart = null;
Destroy(gObject);
return;
}
}
private static double GetDistance(double x1, double y1, double x2, double y2)
{
return Math.Sqrt(Math.Pow((x2 - x1), 2) + Math.Pow((y2 - y1), 2));
}
}
The user should then be able to click anywhere to indicate a start point, click again for the endpoint after which the resulting distance will be displayed in the label and also a temporary line to indicate the distance measured between P1 and P2:
After a third click, or a right-click, the temporary line is removed and the labels are reset.
Note(s)
Adjust the code if you are using other names for your labels. It is assumed that you already have a navigation script in your model (I'll add another article on that in future).
Conclusion
Simple but effective way to measure and display distances between 2 points in a Unity model.