-
Notifications
You must be signed in to change notification settings - Fork 0
Distance calculation
Monobehaviour functionality:
public void CalculateDistance(List<GameObject> planets)
{
float overallMin = float.infinity;
float overallMax = 0f;
//Go through each planet and find the shortest and longest distance between all other planets
foreach(GameObject planet in planets)
{
float planetMin = float.infinity;
float planetMax = 0f;
Vector3 planetPos = planet.transform.position;
//Find the shortest and longest distance between the current planet and all other planets
foreach(GameObject otherPlanet in planets)
{
if(otherPlanet == planet)
continue;
Vector3 otherPos = otherPlanet.transform.position;
float distance = Vector3.distance(otherPos, planetPos);
if(distance < planetMin)
planetMin = distance;
if(distance > planetMax)
planetMax = distance;
}
if(planetMin < overallMin)
overallMin = planetMin;
if(planetMax > overallMax)
overallMax = planetMax;
}
}Breaking out the unity-based and data-based functionality.
//Create a data structure
struct PlanetData
{
//reference to GameObject (We can pass a reference to it, but we can't actually run its methods in another thread)
public GameObject gameObject;
public Vector3 position;
public PlanetData(GameObject _gameObject, Vector3 _position)
{
gameObject = _gameObject;
position = _position;
}
}
//Original monobehaviour
public void MonoCalculateDistance()
{
List<PlanetData> planets = new List<PlanetData>();
foreach(GameObject planet in planets)
{
PlanetData planetData = new PlanetData(planet, planet.transform.position);
planetsPos.Add(planetData);
}
//Create new thread with planets
thread.Run();
}
public struct PlanetPair
{
public PlanetData planetA;
public PlanetData planetB;
public PlanetPair(PlanetData _planetA, PlanetData _planetB)
{
planetA = _planetA;
planetB = _planetB;
}
}
//Thread
//These values should sit on the thread itself.
public PlanetPair closestPlanets {get;}
List<PlanetData> planets;
public void CalculateClosest()
{
float overallMin = float.infinity;
PlanetData planetA, planetB;
//Go through each planet and find the shortest and longest distance between all other planets
foreach(PlanetData planet in planets)
{
float planetMin = float.infinity;
Vector3 planetPos = planet.position;
PlanetData closestPlanet;
//Find the shortest and longest distance between the current planet and all other planets
foreach(PlanetData otherPlanet in planets)
{
//Compare struct references.
if(otherPlanet == planet)
continue;
Vector3 otherPos = otherPlanet.position;
float distance = Vector3.distance(otherPos, planetPos);
if(distance < planetMin)
{
closestPlanet = otherPlanet;
planetMin = distance;
}
}
if(planetMin < overallMin)
{
planetA = planet;
planetB = closestPlanet;
}
closestPlanets = new PlanetPair(planetA, planetB);
}
}We can't read from the thread while it's running, or else there is risk of reading partially updated data. The main thread still has to wait for this logic to be completed. However, since it's no longer on the main thread, the ongoing process is not freezing up the main thread. In our monobehaviour, let's add a coroutine that waits for the thread to finish, and then draw a line between the 2 closest planets.
How can I make this cooler while also showing some other cool capabilities of multithreading?
How about instead of waiting for the end of my thread before reading the closest pair, the game shows the progress as it identifies closer and closer planets along the way. It's time to talk about THREADSAFE COLLECTIONS!
In our MonoBehaviour that is managing this thread, a threadsafe collection called a ConcurrentQueue will be created for PlanetPair data
ConcurrentQueue<PlanetPair> pairQueue;
public void MonoCalculateDistance()
{
//Need to add a reference to all of the planets
List<PlanetData> planets = new List<PlanetData>();
foreach(GameObject planet in planets)
{
PlanetData planetData = new PlanetData(planet, planet.transform.position);
planetsPos.Add(planetData);
}
pairQueue = new ConcurrentQueue<PlanetPair>();
//Create new thread with planets and queue
thread.Run();
}
In our thread class, let's push to the queue instead
ConcurrentQueue<PlanetPair> closestPlanets {get;}
List<PlanetData> planets;
public void CalculateClosest()
{
float overallMin = float.infinity;
PlanetData planetA, planetB;
//Go through each planet and find the shortest and longest distance between all other planets
foreach(PlanetData planet in planets)
{
float planetMin = float.infinity;
Vector3 planetPos = planet.position;
PlanetData closestPlanet;
//Find the shortest and longest distance between the current planet and all other planets
foreach(PlanetData otherPlanet in planets)
{
//Compare struct references.
if(otherPlanet == planet)
continue;
Vector3 otherPos = otherPlanet.position;
float distance = Vector3.distance(otherPos, planetPos);
if(distance < planetMin)
{
closestPlanet = otherPlanet;
planetMin = distance;
}
}
if(planetMin < overallMin)
{
closestPlanets.Enqueue(new PlanetPair(planet, closestPlanet));
}
}
}
Now let's update the code in our monobehaviour to update a line whenever a new pair comes across the queue.
LineRenderer line;
public IEnumerator RenderLines()
{
//...
while(thread.running || pairQueue.length > 0)
{
if(pairQueue.TryDequeue(out PlanetPair pair))
{
line.SetPositions(new Vector3[]{pair.planetA.transform.position, pair.planetB.transform.position});
}
yield return null;
}
}