OnDrawGizmos with foreach loop

So I got a list of roads, in game user can add more roads to this list. It starts as null.
So I create one road and OnDrawGismos going through all edges of my road and displays debug rays. All great.
As soon as I add second road to the list gizmos form the first road disappears and only second road gizmos are displayed !?

I think I am not fully understand how foreach loop and OnDrawGizmos work.
Appreciate some help.

Thank you

// Debug road gizmos
    private void OnDrawGizmos()
    {
        foreach (var road in roads)
        {
            foreach (var edge in road.Edges)
            {
                // Road edge debug
                Gizmos.color = Color.red;
                Gizmos.DrawRay(edge.StartPosition, edge.GetDirection());

                // Road offset debug vector
                Gizmos.color = Color.blue;
                Gizmos.DrawRay(edge.StartPosition, edge.GetOffsetVector() * roadWidth);
                Gizmos.DrawRay(edge.StartPosition, edge.GetOffsetVector() * -roadWidth);
                Gizmos.DrawRay(edge.EndPosition, edge.GetOffsetVector() * roadWidth);
                Gizmos.DrawRay(edge.EndPosition, edge.GetOffsetVector() * -roadWidth);
            }
        }
    }

foreach() is just standard C#.

OnDrawGizmos() is just a normal callback.

What is often happening in these cases is one of the following:

  • the code you think is executing is not actually executing at all
  • the code is executing far EARLIER or LATER than you think
  • the code is executing far LESS OFTEN than you think
  • the code is executing far MORE OFTEN than you think
  • the code is executing on another GameObject than you think it is

To help gain more insight into your problem, I recommend liberally sprinkling Debug.Log() statements through your code to display information in realtime.

Doing this should help you answer these types of questions:

  • is this code even running? which parts are running? how often does it run? what order does it run in?
  • what are the values of the variables involved? Are they initialized? Are the values reasonable?
  • are you meeting ALL the requirements to receive callbacks such as triggers / colliders (review the documentation)

Knowing this information will help you reason about the behavior you are seeing.

You can also put in Debug.Break() to pause the Editor when certain interesting pieces of code run, and then study the scene

You could also just display various important quantities in UI Text elements to watch them change as you play the game.

If you are running a mobile device you can also view the console output. Google for how on your particular mobile target.

Here’s an example of putting in a laser-focused Debug.Log() and how that can save you a TON of time wallowing around speculating what might be going wrong:

https://discussions.unity.com/t/839300/3

Is it because I assign the same nodes and edges list to a different road object, but because the same one was assigned to previous road object. So basically both road objects will have the same lists?

public class RoadDraw : MonoBehaviour
{
    private List<Road> roads = new List<Road>();
    private List<RoadNode> nodes = new List<RoadNode>();
    private List<RoadEdge> edges = new List<RoadEdge>();

    public GameObject nodeIconPrefab;
    public float roadWidth = 5f;

    private int roadIndexCount = 0;

    // Start is called before the first frame update
    void Start()
    {
        InputController.onRoadDraw += CreateRoadData;
    }

    // Unsubscribe from event
    private void OnDestroy()
    {
        InputController.onRoadDraw -= CreateRoadData;
    }

    // Debug road gizmos
    private void OnDrawGizmos()
    {
        foreach (var road in roads)
        {
            foreach (var edge in road.Edges)
            {
                // Road edge debug
                Gizmos.color = Color.red;
                Gizmos.DrawRay(edge.StartPosition, edge.GetDirection());

                // Road offset debug vector
                Gizmos.color = Color.blue;
                Gizmos.DrawRay(edge.StartPosition, edge.GetOffsetVector() * roadWidth);
                Gizmos.DrawRay(edge.StartPosition, edge.GetOffsetVector() * -roadWidth);
                Gizmos.DrawRay(edge.EndPosition, edge.GetOffsetVector() * roadWidth);
                Gizmos.DrawRay(edge.EndPosition, edge.GetOffsetVector() * -roadWidth);
            }
        }
    }

    // Add new road to the list
    public void AddRoad()
    {
        roads.Add(new Road());
        nodes.Clear();
        edges.Clear();
        roadIndexCount++;
    }

    // Draw road by placing nodes
    private void CreateRoadData()
    {
        PlaceNode();
        CreateEdges();
        SetRoadData();
    }

    // Place nodes
    private void PlaceNode()
    {
        Vector3 nodePosition = GeneralHelper.GetMouseWorldPosition();
        GameObject nodeIcon = Instantiate(nodeIconPrefab);
        nodeIcon.transform.SetParent(this.transform);
        nodes.Add(new RoadNode(nodePosition, nodeIcon));
    }

    // Create road edges
    public void CreateEdges()
    {
        edges.Clear();

        for (int i = 0; i < nodes.Count - 1; i++)
        {
            edges.Add(new RoadEdge(nodes[i].Position, nodes[i + 1].Position));
        }
    }

    // Set Road data to current road
    public void SetRoadData()
    {
        int currentRoadIndex = roadIndexCount - 1;

        roads[currentRoadIndex].Nodes = nodes;
        roads[currentRoadIndex].Edges = edges;
        roads[currentRoadIndex].Width = roadWidth;
    }

Sorry @Kurt-Dekker posted my reply before seeing yours.
So the question is. If I have two object and I assign a lest to them. If I would change the list these changes will apply to both objects?

You have a list of roads and a list of edges. Those are separate lists.

If you want edges to “belong” to a road, then perhaps put the list of edges inside the Road or RoadNode class.

Otherwise they’re just a list of edges.

Again, to find out what’s happening, strip it down to two tiny 1-segment roads, print out the values involved. It can’t be that complicated to see where the data storage logic is not matching your data model.

1 Like

I think I got it, when I assign a list to my roads objects. I just passing the reference. So each time I create a new road object I changing a list for all road objects I had previously.

https://stackoverflow.com/questions/4311226/list-passed-by-ref-help-me-explain-this-behaviour

How would I assign a new list to each road, so each road has its own independent list of information?

// Set Road data to current road
    public void SetRoadData()
    {
        int currentRoadIndex = roadIndexCount - 1;
        roads[currentRoadIndex].Nodes = nodes;
        roads[currentRoadIndex].Edges = edges;
        roads[currentRoadIndex].Width = roadWidth;
    }

Should I use ToList()

You would make multiple road objects, not just this one.

Yes I am creating multiple road objects within roads list. I have nodes and edges lists within road class.
I am creating separate lists of edges and nodes in RoadDraw script and passing them to roads.

But I think because my nodes and edges are basically the same list to each road object. I have to make a clone of before passing them to the road, otherwise they still pointing to the same list

Yep, I was right. Thank you @Kurt-Dekker . Speaking with someone always helps.
This fixed the problem:

roads[currentRoadIndex].Nodes.Clear();
        roads[currentRoadIndex].Nodes.AddRange(nodes);
1 Like