0
\$\begingroup\$

For example i have a grid that it's size is 10x10 But the script should be working for any size of grid but to explain let's say the size is 10x10. So i have a grid of 100 GameObject.

12345678910
11121314151617181920
212223242526........


                 100

So if i take the left side of the grid it will be: 0,11,21,31,41,51,61,71,81,91 The right side: 10,20,30,40,50,60,70,80,90,100 The top 1,2,3,4,5,6,7,8,9,10 The bottom 91,92,93,94,95,96,97,98,99,100

Now i want in the code in the script that each time i will running the game it will pick a random number from each side of the grid. For example:

21,30,1,98

Then i want to destroy this 4 numbers to destroy this 4 GameObjects from the grid.

What i tried first to do considering the grid size is 10x10:

private void GenerateRandom()
    {        
        var arr = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        int mynum = arr[Random.Range(0, arr.Length)];
        GameObject Top = objects[mynum];

        int[] arr1 = new int[] { 1, 11, 21, 31, 41, 51, 61, 71, 81, 91 };
        int mynum1 = arr1[Random.Range(0, arr1.Length)];
        GameObject Left = objects[mynum1];

        int[] arr2 = new int[] { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
        int mynum2 = arr2[Random.Range(0, arr2.Length)];
        GameObject Right = objects[mynum2];

        int[] arr3 = new int[] { 91, 92, 93, 94, 95, 96, 97, 98, 99, 100 };
        int mynum3 = arr3[Random.Range(0, arr3.Length)];
        GameObject Bottom = objects[mynum3];
    }

And this is how i create the Grid it self:

for (int x = 0; x < mapWidth; x++)
        {
            float positionWidth = Spacing * (float)x;

            for (int z = 0; z < mapHeight; z++)
            {
                float positionWidth1 = Spacing * (float)z;
                GameObject block = Instantiate(Node, Vector3.zero, Node.transform.rotation) as GameObject;
                block.transform.parent = transform;
                block.transform.localPosition = new Vector3(positionWidth, 0, positionWidth1);
                block.transform.localScale = new Vector3(nodeScale.x, nodeScale.y, nodeScale.z);
                block.tag = "NodeDestroyable";
                objects.Add(block);
            }
        }

But in this way i have some problems.

First in the two loops i start them from 0:

int x = 0; x < mapWidth; x++
int z = 0; z < mapHeight; z++

So when i make arrays to get random numbers from should i start them from 0 or maybe from 1 ?

var arr = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

Should the array start from 0 or 1 ? 0 to 9 or 1 to 10 ?

The second problem it didn't work fine didn't destroyed objects like i wanted and sometimes gave me exception/s out of range index.

This is the completed code:

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;

public class GeneratorTesting : MonoBehaviour
{
    public GameObject Node;
    public Vector3 nodeScale = new Vector3(4, 1, 4);
    public int mapWidth = 10;
    public int mapHeight = 10;
    public float Spacing = 2.0f;
    public float spawnSpeed = 5;
    private List<GameObject> objects = new List<GameObject>();

    private void Start()
    {
        Generate();

        GenerateRandom();
    }

    private void Generate()
    {
        if (objects.Count > 0)
            objects = new List<GameObject>();

        if (Node == null)
            Node = GameObject.FindGameObjectWithTag("Main Node");

        for (int x = 0; x < mapWidth; x++)
        {
            float positionWidth = Spacing * (float)x;

            for (int z = 0; z < mapHeight; z++)
            {
                float positionWidth1 = Spacing * (float)z;
                GameObject block = Instantiate(Node, Vector3.zero, Node.transform.rotation) as GameObject;
                block.transform.parent = transform;
                block.transform.localPosition = new Vector3(positionWidth, 0, positionWidth1);
                block.transform.localScale = new Vector3(nodeScale.x, nodeScale.y, nodeScale.z);
                block.tag = "NodeDestroyable";
                objects.Add(block);
            }
        }
    }

    private void GenerateRandom()
    {
        var arr = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        int mynum = arr[Random.Range(0, arr.Length)];
        GameObject Top = objects[mynum];

        int[] arr1 = new int[] { 1, 11, 21, 31, 41, 51, 61, 71, 81, 91 };
        int mynum1 = arr1[Random.Range(0, arr1.Length)];
        GameObject Left = objects[mynum1];

        int[] arr2 = new int[] { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
        int mynum2 = arr2[Random.Range(0, arr2.Length)];
        GameObject Right = objects[mynum2];

        int[] arr3 = new int[] { 91, 92, 93, 94, 95, 96, 97, 98, 99, 100 };
        int mynum3 = arr3[Random.Range(0, arr3.Length)];
        GameObject Bottom = objects[mynum3];

        DestroyImmediate(Bottom);
        DestroyImmediate(Left);
    }

    public void GenerateNew()
    {
        for (int i = 0; i < objects.Count; i++)
        {
            DestroyImmediate(objects[i]);
        }
        Node = GameObject.FindGameObjectWithTag("Main Node");
        Generate();
        GenerateRoute();
    }

    public void DestroyNodes()
    {
        GameObject[] nodes = GameObject.FindGameObjectsWithTag("NodeDestroyable");

        if (nodes.Length == 0)
        {
            Debug.Log("No nodes to destroy found");
        }
        else
        {
            for (int i = 0; i < nodes.Length; i++)
            {
                DestroyImmediate(nodes[i]);
            }
        }
    }

    private void Update()
    {
        Erase();
    }

    private void Erase()
    {
        if (Input.GetMouseButton(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit))
            {
                if (hit.collider.tag == "NodeDestroyable")
                {
                    Destroy(hit.collider.gameObject);
                }
            }
        }
    }
}

So the first thing is to fix the arrays and getting random number from each side of the grid.

Second to make it for any size of grid not only 10x10 the grid might be in size 3x4 or 40x2 or 9x9

Since this is not working fine i thought maybe adding a helper class but not sure if it's right idea and how to use it:

public static class ArrayExtensions
{
    public static T SelectRandomElement<T> ( this T[] source )
    {
        if (source.Length == 0)
            return default(T);

        var index = s_random.Next(0, source.Length - 1);

        return source[index];
    }

    private static readonly Random s_random = new Random();
}

And inside the ArrayExtensions i should also use this two methods:

//Extension methods in the class given earlier
public static T SelectRandomElementFromLeft<T> ( this IEnumerable<T[]> source )
{            
    //First select a random array
    var totalArrays = source.Count();
    var arrayIndex = s_random.Next(0, totalArrays - 1);

    //Now select the first element from that row
    var arr = source.Skip(arrayIndex).Take(1).FirstOrDefault();
    return arr.FirstOrDefault();
}

public static T SelectRandomElementFromRight<T> ( this IEnumerable<T[]> source )
{
    //First select a random array
    var totalArrays = source.Count();
    var arrayIndex = s_random.Next(0, totalArrays - 1);

    //Now select the last element from that row
    var arr = source.Skip(arrayIndex).Take(1).FirstOrDefault();
    return arr.LastOrDefault();
}

But not sure how to use it with the arrays in GenerateRandom()

The goal the main idea is each time running the game to get 4 random numbers each from each side of the grid. And i think there might be two cases that there can be same numbers so if there is such a case to automatic generate new random numbers or to check and make sure there will be no same numbers like 1 and 1 or 91 and 91 or 100 and 100.

\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

The first problem as you stated, when you initialise 10x10 grid (array), its elements are indexed from 0-99. You are getting unexpected behaviour and also indexOutOfBounds error because you give an exceeding index for your purpose for example:

var arr = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int mynum = arr[Random.Range(0, arr.Length)];
GameObject Top = objects[mynum];

Here you are trying to pick an element from the top side, however the top side of the grid contains only 10 elements, but you give the random function a range consists of 11 elements (0 and 10 are inclusive for that function). Hence you need to set the arr[] to include 10 numbers.

For the second problem (picking the same elements via selecting corners), I suggest you to keep the selected items in a List<int> selectedNumbers; and each time you pick random element for the sides, check if the currently selected element is already been selected before. And also modify the GenerateRandom() function as follows, in order to decouple the selecting procedure from the size of the grid:

private void GenerateRandom(int gridWidth, int gridHeight)
{        
    List<int> selectedIndexes = new List<int>();

    //Create an array for indexes of the TOP side
    var arr = new int[gridWidth]; 
    for(int i = 0; i < gridWidth; i++){
       arr[i] = i;
    }
    int mynum = arr[Random.Range(0, arr.Length)];
    GameObject Top = objects[mynum];
    selectedIndexes.add(mynum);

    //Create an array for indexes of the LEFT side
    var arr = new int[gridHeight]; 
    for(int i = 0; i < gridHeight; i++){
       arr[i] = i * gridWidth;
    }
    do {
       mynum = arr[Random.Range(0, arr.Length)];
    }
    while(selectedIndexes.contains(mynum));
    GameObject Left = objects[mynum];
    selectedIndexes.add(mynum);


    //Create an array for indexes of the BOTTOM side
    var arr = new int[gridWidth]; 
    for(int i = 0; i < gridWidth; i++){
       arr[i] = (gridWidth * (gridHeight - 1)) + i;   //Reach the last row and iterate with i.
    }
    do {
       mynum = arr[Random.Range(0, arr.Length)];
    }
    while(selectedIndexes.contains(mynum));
    GameObject Bottom = objects[mynum];
    selectedIndexes.add(mynum);

    //Create an array for indexes of the RIGHT side
    var arr = new int[gridHeight]; 
    for(int i = 0; i < gridHeight; i++){
       arr[i] = (i * gridWith) + gridHeight - 1;   
    }
    do {
       mynum = arr[Random.Range(0, arr.Length)];
    }
    while(selectedIndexes.contains(mynum));
    GameObject Right = objects[mynum];

    selectedIndexes.clear();
}

Note: Please beware you need to check the size of the grid before calling this function since it cannot iterate on grids which have height or width as 1.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .