Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by darryldonohoe · Apr 26, 2015 at 12:07 AM · arraylistclassnullgeneric

Level generation using a "tree" type structure, why when i add a cell i get an error argument out of range?

Hey there, i have been struggling for a while trying to get a generator working. I already have a working level generator using a 2D array. But that one just places nodes on random slots adjacent to already placed nodes.

(i call them nodes, but the nodes resembles eventual rooms)

I wanted to go for a different structure which would help me out in the long run.(when placing puzzles like: keys, boss rooms etc...) And a tree structure would do the trick perfectly. (since closing 1 node, would close all the nodes beyond it)

Anyway, i have a sort of working method. A generator script(which basically just places a simple 2D array, and generates the first node) Once the first node has been placed, i have another script/class. This resembles all nodes/rooms. It checks for adjacent open slots, and how many children this node is going to have. Once decided, it will try to add (a) node(s) in a random direction on one of the previously checked open slots. And here is it, where it goes wrong. Or at least sometimes it works but most of the times it leaves you with half of the level which it was originally going to generate.

Here is the generator script:

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 
 public class Map : MonoBehaviour
 {
     public static Map _instance;
 
     public static Map instance
     {
         get
         {
             if(_instance == null)
                 _instance = GameObject.FindObjectOfType<Map>();
             return _instance;
         }
     }
 
     public int width;
     public int height;
 
     public int iterations;
     [HideInInspector]
     public int nodes;
 
     public List<Node> node = new List<Node>();
     public Node[,] grid;
 
 
     public bool insideGrid(int x, int y)
     {
         if(x >= 0 && x < width && y >= 0 && y < height)
         {
             return true;
         }
         else return false;
     }
     
     public bool validPosition(int x, int y)
     {
         if(grid[x,y].type == 0) return true;
         else return false;
     }
 
     public Node addNode(int x, int y, int id, int type, int children)
     {
         Node addedNode = new Node(x, y, id, type, children);
 
         grid[x,y] = addedNode;
         node.Add(new Node());
         node[grid[x,y].id] = addedNode;
 
         return addedNode;
     }
 
 
     void Start()
     {
         grid = new Node[width,height];
         nodes = 0;
 
         for(int x = 0; x < width; x++)
         {
             for(int y = 0; y < height; y++)
             { 
                 grid[x,y] = new Node();
                 grid[x,y].type = 0;
                 grid[x,y].x = x;
                 grid[x,y].y = y;
             }
         }
 
         Generate();
     }
 
 
     void Generate()
     {
         Node firstNode = addNode(width/2,height/2,nodes,2,Random.Range(2,4));
 
         firstNode.GenerateChildren();
     }
 
     void OnGUI()
     {
         float screenX = Screen.width;
         float screenY = Screen.height;
 
         float iconX = screenX/width;
         float iconY = screenY/height;
 
         //float nodeList = screenY/iterations;
         
         int i = 0;
 
 
         for(int x = 0; x < width; x++)
         {
             for(int y = 0; y < height; y++)
             {
                 if(grid[x,y].type != 0)
                 {
                     GUI.Box(new Rect(x*iconX,y*iconY,iconX,iconY), "" + grid[x,y].id);
                 }
             }
         }
 
         while(i < node.Count)
         {
             //GUI.Box(new Rect(0,i*nodeList,250,nodeList), "Node: " + node[i].id + " | " + node[i].x + "." + node[i].y);
             i++;
         }
     }
 }
 

And here is the class Node script:

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 
 [System.Serializable]
 public class Node
 {
     public int id;
     public int type;
 
     public int x;
     public int y;
 
     public int children;
     public int connections;
 
     public bool top;
     public bool bottom;
     public bool left;
     public bool right;
 
     public Texture2D icon;
 
     public List<Node> childs = new List<Node>();
 
     public Node(int _x, int _y, int _id, int _type, int _children)
     {
         x = _x;
         y = _y;
         id = _id;
         type = _type;
         children = _children;
     }
 
     public List<int[]> setNeighbors()
     {
         List<int[]> neighbors = new List<int[]>();
 
         if(Map.instance.insideGrid(x+1,y))
         {
             if(Map.instance.validPosition(x+1,y)) neighbors.Add(new int[]{x+1,y});
         }
         if(Map.instance.insideGrid(x-1,y))
         {
             if(Map.instance.validPosition(x-1,y)) neighbors.Add(new int[]{x-1,y});
         }
         if(Map.instance.insideGrid(x,y+1))
         {
             if(Map.instance.validPosition(x,y+1)) neighbors.Add(new int[]{x,y+1});
         }
         if(Map.instance.insideGrid(x,y-1))
         {
             if(Map.instance.validPosition(x,y-1)) neighbors.Add(new int[]{x,y-1});
         }
         return neighbors;
     }
 
     public void GenerateChildren()
     {
         List<int[]> neighbors = setNeighbors();
         int i = 0;
         int selected = 0;
         int j = 0;
 
         if(neighbors.Count <= 0)
         {
             neighbors = setNeighbors();
         }
     
         while(i < children)
         {
             selected = Random.Range(0,neighbors.Count);
             Debug.Log("neighbors: " + neighbors.Count + " / childs: " + children + " / selected: " + selected);
             childs.Add(new Node());
 
             if(Map.instance.nodes < Map.instance.iterations)
             {
                 Map.instance.nodes++;
 
                 childs[i] = addChildren(neighbors[selected][0],neighbors[selected][1],Map.instance.nodes,1,Random.Range(1,3));
                 neighbors.RemoveAt(selected);
             }
             i++;
         }
 
         while(j < children)
         {
             if(Map.instance.nodes < Map.instance.iterations)
             {
                 childs[j].GenerateChildren();
             }
 
             j++;
         }
     }
 
     public Node addChildren(int _x, int _y, int _id, int _type, int _children)
     {
         return Map.instance.addNode(_x,_y,_id,_type,_children);
     }
 
     public Node()
     {
 
     }
 }
 

It gives me an error on the line where i try to add a child to the child's List. I added a Debug.Log above it, trying to see how it could select a child which is out of range. (but i don't see the problem in the debug log, since it should select within child.Count ' s reach)

I hope someone could help me! The class code is probably messy, i would have to clean this up once i got the problem sorted out.

Any help is appreciated!

Cheers, Darryl

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

1 Reply

· Add your reply
  • Sort: 
avatar image
0

Answer by darryldonohoe · Apr 26, 2015 at 05:16 PM

Okay i seemed to fix the problem. If anyone is wondering how i fixed it. Here are the codes:

The generator:

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 
 public class Map : MonoBehaviour
 {
     public static Map _instance;
 
     public static Map instance
     {
         get
         {
             if(_instance == null)
                 _instance = GameObject.FindObjectOfType<Map>();
             return _instance;
         }
     }
 
     public int width;
     public int height;
 
     public int iterations;
     [HideInInspector]
     public int nodes;
 
     public List<Node> node = new List<Node>();
     public Node[,] grid;
 
 
     public bool insideGrid(int x, int y)
     {
         if(x >= 0 && x < width && y >= 0 && y < height)
         {
             return true;
         }
         else return false;
     }
     
     public bool validPosition(int x, int y)
     {
         if(grid[x,y].type == 0) return true;
         else return false;
     }
 
     public Node addNode(int x, int y, int id, int type, int children)
     {
         Node addedNode = new Node(x, y, id, type, children);
         grid[x,y] =  new Node(x, y, id, type, children);
         node.Add(new Node(x,y,id,type,children));
         return addedNode;
     }
 
 
     void Start()
     {
         grid = new Node[width,height];
         nodes = 0;
 
         for(int x = 0; x < width; x++)
         {
             for(int y = 0; y < height; y++)
             { 
                 grid[x,y] = new Node();
                 grid[x,y].type = 0;
                 grid[x,y].x = x;
                 grid[x,y].y = y;
             }
         }
 
         Generate();
     }
 
 
     void Generate()
     {
         Node firstNode = addNode(width/2,height/2,nodes,2,Random.Range(2,4));
         nodes++;
 
         firstNode.GenerateChildren();
     }
 
     void OnGUI()
     {
         float screenX = Screen.width;
         float screenY = Screen.height;
 
         float iconX = screenX/width;
         float iconY = screenY/height;
 
         //float nodeList = screenY/iterations;
         
         int i = 0;
 
 
         for(int x = 0; x < width; x++)
         {
             for(int y = 0; y < height; y++)
             {
                 if(grid[x,y].type != 0)
                 {
                     GUI.Box(new Rect(x*iconX,y*iconY,iconX,iconY), "" + grid[x,y].id);
                 }
             }
         }
 
         while(i < node.Count)
         {
             //GUI.Box(new Rect(0,i*nodeList,250,nodeList), "Node: " + node[i].id + " | " + node[i].x + "." + node[i].y);
             i++;
         }
     }
 }
 


And the Node/class:

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 
 [System.Serializable]
 public class Node
 {
     public int id;
     public int type;
 
     public int x;
     public int y;
 
     public int children;
     public int connections;
 
     public bool top;
     public bool bottom;
     public bool left;
     public bool right;
 
     public Texture2D icon;
 
     public List<Node> child = new List<Node>();
 
     public Node(int _x, int _y, int _id, int _type, int _children)
     {
         x = _x;
         y = _y;
         id = _id;
         type = _type;
         children = _children;
     }
 
     public List<int[]> setNeighbors()
     {
         List<int[]> neighbors = new List<int[]>();
 
         if(Map.instance.insideGrid(x+1,y))
         {
             if(Map.instance.validPosition(x+1,y)) neighbors.Add(new int[]{x+1,y});
         }
         if(Map.instance.insideGrid(x-1,y))
         {
             if(Map.instance.validPosition(x-1,y)) neighbors.Add(new int[]{x-1,y});
         }
         if(Map.instance.insideGrid(x,y+1))
         {
             if(Map.instance.validPosition(x,y+1)) neighbors.Add(new int[]{x,y+1});
         }
         if(Map.instance.insideGrid(x,y-1))
         {
             if(Map.instance.validPosition(x,y-1)) neighbors.Add(new int[]{x,y-1});
         }
         return neighbors;
     }
 
     public void GenerateChildren()
     {
         List<int[]> neighbors = setNeighbors();
         int selected = 0;
         int i = 0;
 
         while(i < children)
         {
             if(neighbors.Count == 0)
             {
                 i++;
             }
             else
             {
                 selected = Random.Range(0,neighbors.Count);
 
                 if(Map.instance.nodes < Map.instance.iterations)
                 {
                     child.Add(new Node());
                     child[i] = addChildren(neighbors[selected][0],neighbors[selected][1],Map.instance.nodes,1,Random.Range(1,3));
                 }
                 neighbors.RemoveAt(selected);
 
 
                 i++;
             }
         }
 
         foreach(Node childs in child)
         {
             if(childs == null)
             {
                 Debug.Log ("null");
             }
             else childs.GenerateChildren();
         }
     }
 
     public Node addChildren(int _x, int _y, int _id, int _type, int _children)
     {
         Map.instance.nodes++;
         return Map.instance.addNode(_x,_y,_id,_type,_children);
     }
 
     public Node()
     {
 
     }
 }
 


It can generate a level with 5000 nodes in under 2 seconds(on my computer). However since its not optimized for a level with that amount of rooms, i get a 3 - 4 fps once the level has been generated.

I still want to ask if someone can give me tips on this script. Like is this readable for you guys, how would you write certain methods and such.

Anyway thanks!

Cheers, Darryl

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

19 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

foreach a array inside a generic list 1 Answer

Remove and Add to List By Name aad 1 Answer

Checking array fails each time. But change anything and resave and it works until unity closes...... 1 Answer

Generic List No Sense Problem 1 Answer

Removing null object from array in unity 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges