- Home /
Generating a Random Linear Pathway
I am stuck on this and can't quite wrap my head around what is wrong. I am trying to create a single pathway with random turns, simply starting at 0,0,0 and randomly branching out from there until it hits it's limit. The path also can't intersect itself at any time.
Right now, the code works great unless it's a large path, Unity gets hung up making it. I can't quite figure out where it's getting hung up, I can't get anything print in the console because Unity just freezes. My guess is that it closes in on itself and has nowhere to go, but isn't destroying the path and trying again, which is what I'm trying to get it to do if that happens.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class PathCreator : MonoBehaviour
{
// to do:
// create path
// start coroutine
// create first block
// create loop for creating other blocks
// pick random directon
// after direction selected, find the coordinates of where new step would be
// check that there isn't already a block there
// if there isn't, check all coordinates around it.
// if there are 3 empty coordinates create block
public List<Vector2> steps = new List<Vector2> ();
public List<Vector2> randomDirections = new List<Vector2> ();
public int pathCountMin;
public int pathCountMax;
int pathCount;
GameObject pathContainer;
public Material matHidden;
public Material matShown;
public Material matCheckPoint;
public Material matEnd;
// Use this for initialization
void Start ()
{
//add 4 random directions to the list
randomDirections.Add (new Vector2 (0, 1));
randomDirections.Add (new Vector2 (0, -1));
randomDirections.Add (new Vector2 (1, 0));
randomDirections.Add (new Vector2 (-1, 0));
pathContainer = GameObject.FindWithTag ("PathContainer");
}
// Update is called once per frame
void Update ()
{
}
public void CreatePath ()
{
steps.Clear ();
//get a final count to know how long to make the path
pathCount = Random.Range (pathCountMin, pathCountMax);
//create first step to start the path
GameObject firstStep = GameObject.CreatePrimitive (PrimitiveType.Cube);
firstStep.transform.position = new Vector3 (0, -0.5f, 0);
firstStep.transform.localScale = new Vector3 (1, 0.5f, 1);
firstStep.name = "Cube " + steps.Count;
firstStep.transform.parent = pathContainer.transform;
steps.Add (new Vector2 (firstStep.transform.position.x, firstStep.transform.position.z));
StartCoroutine (CreateSteps ());
//HidePath ();
//ShowFirstStep ();
}
IEnumerator CreateSteps ()
{
//this will run the code for each step until pathCount is reached
for (int i = 0; i <= pathCount - 1; i++) {
bool testGood = false;
bool blockCreated = false;
List<Vector2> usedDirections = new List<Vector2> ();
do {
//find coordinates of the new step and mark openSpace as true if there is no step already there
Vector2 direction = randomDirections [Random.Range (0, randomDirections.Count)];
Vector2 lastStep = steps [steps.Count - 1];
Vector2 newStep = lastStep + direction;
if (!usedDirections.Contains (direction)) {
usedDirections.Add (direction);
}
while (steps.Contains(newStep)) {
//destroys path if all directions have been attempted
if (usedDirections.Count > 3) {
DestroyPath ();
} else {
//picks a new direction until it lands on one that hasn't been used yet.
while (usedDirections.Contains(direction)) {
direction = randomDirections [Random.Range (0, randomDirections.Count)];
}
if (!usedDirections.Contains (direction)) {
usedDirections.Add (direction);
}
newStep = lastStep + direction;
}
}
print (usedDirections.Count);
testGood = false;
int testCount = 0;
// //Check each direction around the new step. For each empty spot add 1 to testCount. If 3
// //successful testCounts exist, mark it as tested.
foreach (Vector2 dir in randomDirections) {
Vector2 testStep = dir + newStep;
if (!steps.Contains (testStep)) {
testCount += 1;
}
}
if (testCount <= 2) {
print ("tests failed");
if (!usedDirections.Contains (direction)) {
usedDirections.Add (direction);
}
testGood = false;
} else if (testCount == 0) {
DestroyPath ();
} else {
testGood = true;
}
//if tests pan out, create the next step and move on
if (testGood == true) {
GameObject step = GameObject.CreatePrimitive (PrimitiveType.Cube);
step.transform.position = new Vector3 (newStep.x, -0.5f, newStep.y);
step.transform.localScale = new Vector3 (1, 0.5f, 1);
step.name = "Cube " + steps.Count;
step.transform.parent = pathContainer.transform;
steps.Add (new Vector2 (step.transform.position.x, step.transform.position.z));
blockCreated = true;
usedDirections.Clear ();
}
} while (blockCreated == false);
}
yield return null;
}
void DestroyPath ()
{
StopAllCoroutines ();
foreach (Transform step in pathContainer.GetComponentsInChildren<Transform>()) {
if (step.tag == "PathContainer") {
} else {
Destroy (step.gameObject);
}
}
steps.Clear ();
print ("Had to Destroy Path");
CreatePath ();
}
void HidePath ()
{
foreach (Transform step in pathContainer.GetComponentsInChildren<Transform>()) {
if (step.tag == "PathContainer") {
} else {
step.gameObject.renderer.material = matHidden;
}
}
}
void ShowFirstStep ()
{
Collider[] colliders;
if ((colliders = Physics.OverlapSphere (new Vector3 (0, -0.5f, 0), 0.2f)).Length > 0) { //Presuming the object you are testing also has a collider 0 otherwise
foreach (Collider col in colliders) {
GameObject go = col.gameObject;
Debug.Log ("hello");
Debug.Log (go);
go.renderer.material = matShown;
}
}
}
}
Answer by Bunny83 · Dec 09, 2014 at 01:18 AM
Well you're doing some really risky stuff here. Your main problem is your DestroyPath calls doesn't do what you expect them to do. You call StopAllCoroutines but that only marks all currently running coroutines to stop at the next yield. Since the DestroyPath call is called from the current coroutine it will continue to execute until the next yield.
For large paths it's very likely that you go into a dead end (every closed spiral will end there). If your algorithm reaches a dead end you just call DestroyPath and don't care about the current iteration. blockCreated will never be true since your usedDirections will contain all 4 directions and your stuck inside that while loop without ever reaching the next yield.
Furthermore each time you call DestroyPath you create a new coroutine which will run up until the first yield and then return from the StartCoroutine call and continue with the very first coroutine which is stuck inside the while loop and just spawns more coroutines without ever actually yield.
Thanks for the quick reply. All of that makes a lot of sense to me, thanks. I have found some maze tutorials that more or less have the same ideas I am looking for. I think I'll try some new ways and keep what you said in $$anonymous$$d. Thanks!