Unity Roguelike Project: Argument Out of Range Exception - unity3d

I'm getting an Argument Out of Range Exception for this script I'm following in a Unity tutorial.
Here's the exception:
ArgumentOutOfRangeException: Argument is out of range.
Parameter name: index
System.Collections.Generic.List`1[UnityEngine.Vector3].get_Item (Int32 index) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:225)
BoardManager.RandomPosition () (at Assets/Scripts/BoardManager.cs:75)
And here's the actual script for the project:
using UnityEngine;
using System; //Allows serializable attribute, which modifies how variables appear
using System.Collections.Generic; //Generic allows use of lists
using Random = UnityEngine.Random; //Used because Random class is in both System and Unity engine namespaces
public class BoardManager : MonoBehaviour
{
[Serializable]
public class Count
{
public int maximum;
public int minimum;
public Count (int min, int max) //Assignment constructor for Count to set values of min, max when we declare a new Count
{
minimum = min;
maximum = max;
}
}
public int columns = 8; //8x8 gameboard
public int rows = 8;
public Count wallCount = new Count(5, 9); //Specifies random range for walls to spawn in each level
public Count foodCount = new Count(1, 5); //Random range for food spawns
public GameObject exit; //Variable that holds exit prefab
public GameObject[] floorTiles; //Game Object Arrays hold our different prefabs to choose between
public GameObject[] wallTiles;
public GameObject[] foodTiles;
public GameObject[] enemyTiles;
public GameObject[] outerWallTiles;
private Transform boardHolder; //Manages Hierarchy
private List<Vector3> gridPositions = new List<Vector3>(); //Declares a private list of Vector3s
//Tracks all possible positions on gameboard, and keeps track of whether object has been spawned in that position or not
void InitialiseList()
{
gridPositions.Clear();
for (int x = 1; x < columns - 1; x++) //Nested FOR loops to fill list with each of the positions on our gameboard as a Vector3
{
for (int y = 1; y < rows - 1; y++)
{
gridPositions.Add(new Vector3(x, y, 0f)); //Adds x and y positions to the list
}
}
}
void BoardSetup() //Sets up outer wall and floor of gameboard
{
boardHolder = new GameObject("Board").transform;
for (int x = -1; x < columns + 1; x++)
{
for (int y = -1; y < rows + 1; y++)
{
GameObject toInstantiate = floorTiles[Random.Range(0, floorTiles.Length)]; //Defines a new GameObject
if (x == -1 || x == columns || y == -1 || y == rows)
{
toInstantiate = outerWallTiles[Random.Range(0, outerWallTiles.Length)];
}
GameObject instance = Instantiate(toInstantiate, new Vector3(x, y, 0f), Quaternion.identity) as GameObject;
//Instantiates a new GameObject. Rotation is always the same. Used as GameObject.
instance.transform.SetParent(boardHolder);
}
}
}
Vector3 RandomPosition()
{
try
{
int randomIndex = Random.Range(0, gridPositions.Count);
Vector3 randomPosition = gridPositions[randomIndex]; //THROWING AN EXCEPTION
gridPositions.RemoveAt(randomIndex); //Prevents duplicate position spawns
return randomPosition;
}
catch (Exception ex1)
{
Debug.Log(ex1);
throw;
}
}
void LayoutObjectAtRandom(GameObject[] tileArray, int minimum, int maximum)
{
int objectCount = Random.Range(minimum, maximum + 1);
for (int i = 0; i < objectCount; objectCount++)
{
Vector3 randomPosition = RandomPosition();
GameObject tileChoice = tileArray[Random.Range(0, tileArray.Length)];
Instantiate(tileChoice, randomPosition, Quaternion.identity);
}
}
public void SetupScene(int level) //Called by the GameManager
{
BoardSetup();
InitialiseList();
LayoutObjectAtRandom(wallTiles, wallCount.minimum, wallCount.maximum);
LayoutObjectAtRandom(foodTiles, foodCount.minimum, foodCount.maximum);
int enemyCount = (int)Mathf.Log(level, 2f); //Scales enemy count based on level
LayoutObjectAtRandom(enemyTiles, enemyCount, enemyCount);
Instantiate(exit, new Vector3(columns - 1, rows - 1, 0f), Quaternion.identity);
}
}
I'm assuming that the RandomPositions() function is throwing the exception, though I am unable to find a problem with the code. Does anyone see the problem?

Related

How Can I Not Make Points Spawn In The Same Place Twice? (Unity)

guys so I'm trying to make an endless Map Generator for my 2D grappling Hook Game Kinda like Danis square Game and I was trying to generate a set number of swingable points in certain range to test.
Everything works fine except that some Points overlap each other when spawned So I was wondering how could I prevent this from happening?
Should I change my code or is there any method of spawning things I should try so they don't overlap?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MapGenerator : MonoBehaviour
{
public Transform Endpositin;
public int NumOfSwwingablePoints;
public GameObject SwinggablePoint;
public int MinX, MaxX;
public int MinY, MaxY;
public int Xpos;
public int Ypos;
Vector2 PointPos;
void Start()
{
for (int i = 0; i <= NumOfSwwingablePoints; i++)
{
Xpos = Random.Range(MinX, MaxX);
Ypos = Random.Range(MinY, MaxY);
PointPos = new Vector2(Xpos, Ypos);
Instantiate(SwinggablePoint, PointPos, Quaternion.identity);
}
}
// Update is called once per frame
void Update()
{
}
}
I have Tried this Method and I have also tried to increase values and giving the points colliders to see if they overlap but none of that works. I have also tried update function, but they just keep spawning, and it never stops
My suggestion is everytime you create a new block then save the position of the block in a list.
when next time you create a new block, check the new position with the list.
Here is the code.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MapGenerator : MonoBehaviour
{
public Transform Endpositin;
public int NumOfSwwingablePoints;
public GameObject SwinggablePoint;
public int MinX, MaxX;
public int MinY, MaxY;
private int Xpos;
private int Ypos; // this can be private since we use only privatly
Vector2 PointPos;
List<Vector2> PointPosList = new List<Vector2>();
void Start()
{
for (int i = 0; i < NumOfSwwingablePoints; i++) // I change <= to < since we start from 0
{
Generating();
}
}
void Generating()
{
bool foundSamePosition;
do
{
foundSamePosition = false;
Xpos = Random.Range(MinX, MaxX);
Ypos = Random.Range(MinY, MaxY);
PointPos = new Vector2(Xpos, Ypos);
// finding the generated position is already exist
for (int i = 0; i < PointPosList.Count; i++)
{
if (SamePosition(PointPos, PointPosList[i]))
{
foundSamePosition = true;
}
}
} while (foundSamePosition);// if we found the position try again
// if not found, add the new position to a list for next time check
PointPosList.Add(PointPos);
Instantiate(SwinggablePoint, PointPos, Quaternion.identity);
}
private bool SamePosition(Vector2 v1, Vector2 v2)
{
return (v1.x == v2.x && v1.y == v2.y);
}
}

How can I scale a unity sprite programmatically to a fractional number of units?

I've spawned a tile as follows:
private GameObject SpawnTile(int col, int row, Color color, GameObject parent, string label)
{
GameObject g = new GameObject("C: " + col + " R: " + row);
g.transform.position = world_grid.GetWorldPosition(col, row);
g.transform.localScale = new Vector3(world_grid.cell_size, world_grid.cell_size);
g.transform.parent = parent.transform;
return g;
}
I then add a spriterenderer, and display a sprite of a given color.
tile.game_object = SpawnTile(col, row, color, parent, label);
tile.sprite_renderer = tile.game_object.AddComponent<SpriteRenderer>();
tile.sprite_renderer.sprite = tile_sprite;
//Bounds bounds = tile.sprite_renderer.bounds;
tile.sprite_renderer.color = color;
I have the ability to display grids using this mechanism. I can scale the size of a tile up by an integer amount. However when I try to scale the object down, the sprite does not scale down with it.
In the following, I have 3 1x1 grids. The grid on the right has a cell_size of 2. The grid in the middle has a cell_size of 1. The grid on the left has a cell_size of 0.5.
The "tile" object is of an appropriate size. As we see this object is shown as half size in Unity's Scene View. However in the game view on the right, it shows as the same size as the pink grid in the middle.
Further details, I have spent quite a bit of time trying to figure this out, and though I see a variety of posts that seem to relate to sprite sizing, none of them are clear for a relative unity beginner.
Some of the posts I've found seem to suggest that I need to use the sprite renderer bounds to ensure that the sprite is proeprly sized. But its not clear to me how.
This is my grid class. Notice that it is constructed with a cell_size (in units). Notice that when I create the object of a given size, I size the object, but not the sprite.
public class WorldGrid<TGridObject> : Grid<TGridObject>
{
public float cell_size { get; private set; }
// Defines the center point in world coordinates
public Vector3 center_point { get; private set; }
public WorldGrid(Vector3 _center_point, int _columns, int _rows, float _cell_size) : base(_columns, _rows)
{
cell_size = _cell_size;
center_point = _center_point;
}
public Vector3 GetWorldPosition(int col, int row)
{
float x_pos = col * cell_size + cell_size / 2f;
float y_pos = row * cell_size + cell_size / 2f;
// wp = cp + gp
return center_point + new Vector3(x_pos, y_pos);
}
public void GetGridPosition(Vector3 world_position, out int col, out int row)
{
// gp = wp - cp
// position in grid-centered reference frame
Vector3 gp = world_position - center_point;
row = Mathf.FloorToInt(gp.y / cell_size);
col = Mathf.FloorToInt(gp.x / cell_size);
}
public void SetValue(Vector3 world_position, TGridObject value)
{
int row, col;
GetGridPosition(world_position, out row, out col);
SetValue(row, col, value);
}
}
This references a lower level grid class (that is not in world units):
public class Grid<TGridObject>
{
public int columns { get; private set; }
public int rows { get; private set; }
public TGridObject[,] grid { get; private set; }
private int cnt = 0;
public event EventHandler<OnGridCellValueChangedEventArgs> OnGridCellValueChanged;
public class OnGridCellValueChangedEventArgs : EventArgs
{
public int cnt;
public int row;
public int column;
}
public Grid(int _columns, int _rows)
{
columns = _columns;
rows = _rows;
grid = new TGridObject[columns, rows];
}
public void SetValue(int col, int row, TGridObject value)
{
if((row >= 0 && row < rows) &&
(col >= 0 && col < columns))
{
grid[col, row] = value;
if(OnGridCellValueChanged != null)
{
cnt += 1;
Debug.LogError("Triggering [" + col + "," + row + "] of [" + columns + "," + rows + "]");
OnGridCellValueChanged(this,
new OnGridCellValueChangedEventArgs { row = row, column = col, cnt = cnt }
);
}
}
}
public TGridObject GetValue(int col, int row)
{
if ((row >= 0 && row < rows) &&
(col >= 0 && col < columns))
{
return grid[col, row];
} else
{
return default(TGridObject);
}
}
}
I instantiate grids using a GridManager class:
public class GridManager : MonoBehaviour
{
[System.Serializable]
private struct WorldGridDescriptor
{
public int rows;
public int columns;
public float cell_size;
public Vector3 center_point;
public bool randomize_start;
}
public Sprite tile_sprite;
public int world_size_factor = 1;
private int columns, rows;
private float unit_step_size = 1.0f;
private List<WorldGrid<float>> world_grids = new List<WorldGrid<float>>();
private List<GridView> world_grid_views = new List<GridView>();
[SerializeField] private List<WorldGridDescriptor> world_grid_descriptors;
// Start is called before the first frame update
void Start()
{
// The orthographic size specifies the camera units from
// the horizontal centerline to the top of screen
// The vertical_units are then the number of tile rows
// from the centerline to the top of screen
rows = (int)Camera.main.orthographicSize * world_size_factor * 2;
float aspect_ratio = ((float)Screen.width / (float)Screen.height);
columns = (int)(rows * aspect_ratio);
// Will parent the tiles in the empty Grid.
// Perhaps this container should be public.
// For now it is a hard-coded assumption, that it exists.
GameObject grid_container = this.gameObject.transform.Find("Grid").gameObject;
if (world_grid_descriptors != null)
{
foreach (WorldGridDescriptor wgd in world_grid_descriptors)
{
WorldGrid<float> wg = new WorldGrid<float>(wgd.center_point, wgd.columns, wgd.rows, wgd.cell_size);
if(wgd.randomize_start)
{
RandomizeState(wg);
}
GridView grid_view = new GridView(wg, tile_sprite, grid_container);
world_grids.Add(wg);
world_grid_views.Add(grid_view);
}
}
}
private void RandomizeState(WorldGrid<float> g)
{
for (int col = 0; col < g.columns; col++)
{
for (int row = 0; row < g.rows; row++)
{
g.SetValue(col, row, Random.Range(0.0f, 1.0f));
}
}
}
private void Update()
{
if (Input.GetMouseButtonDown(0)) {
Vector3 worldPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
foreach (WorldGrid<float> wg in world_grids)
{
int c, r;
wg.GetGridPosition(worldPosition, out c, out r);
wg.SetValue(c, r, Random.Range(0.0f, 1.0f));
}
}
}
}
Lastly, the view into the grids are created as follows (this shows you exactly how I create the grid representation that uses a sprite renderer component to render sprites for each cell in the grid):
public class GridView
{
public struct Tile
{
public GameObject game_object;
public SpriteRenderer sprite_renderer;
public TextMesh text_mesh;
}
private Tile[,] tiles;
private WorldGrid<float> world_grid;
protected Sprite tile_sprite { get; private set; }
public GridView(WorldGrid<float> wg, Sprite ts, GameObject parent)
{
world_grid = wg;
tile_sprite = ts;
tiles = new Tile[wg.columns, wg.rows];
wg.OnGridCellValueChanged += OnGridCellValueChanged;
Create(parent);
}
private void Create(GameObject parent)
{
for (int col = 0; col < world_grid.columns; col++)
{
for (int row = 0; row < world_grid.rows; row++)
{
Tile tile = new Tile();
float r, g, b;
r = g = b = world_grid.grid[col, row];
float a = 1f;
Color color = new Color(r, g, b, a);
string label = (string)((int)(255 * world_grid.grid[col, row])).ToString("x");
tile.game_object = SpawnTile(col, row, color, parent, label);
tile.sprite_renderer = tile.game_object.AddComponent<SpriteRenderer>();
tile.sprite_renderer.sprite = tile_sprite;
//Bounds bounds = tile.sprite_renderer.bounds;
tile.sprite_renderer.color = color;
tile.text_mesh = UtilsClass.CreateWorldText(label, parent.transform,
tile.game_object.transform.position, 7, Color.red, TextAnchor.MiddleCenter);
tiles[col, row] = tile;
}
}
}
// Update is called once per frame
private GameObject SpawnTile(int col, int row, Color color, GameObject parent, string label)
{
GameObject g = new GameObject("C: " + col + " R: " + row);
g.transform.position = world_grid.GetWorldPosition(col, row);
g.transform.localScale = new Vector3(world_grid.cell_size, world_grid.cell_size);
g.transform.parent = parent.transform;
return g;
}
private void OnGridCellValueChanged(object sender, Grid<float>.OnGridCellValueChangedEventArgs e)
{
Debug.LogError(e.cnt + " - Updating [" + e.column + "," + e.row + "]");
Update(e.row, e.column);
}
private void Update(int row, int col)
{
if (row >= 0 && row < world_grid.rows && col >= 0 && col < world_grid.columns)
{
Tile tile = tiles[col, row];
float r, g, b;
r = g = b = world_grid.grid[col, row];
float a = 1f;
Color color = new Color(r, g, b, a);
string label = (string)((int)(255 * world_grid.grid[col, row])).ToString("x");
tile.text_mesh.text = label;
tile.sprite_renderer.color = color;
}
}
}
My sprites are a simple blank image that is 256 x 256 pixels in size. I then set the pixels per unit (in the unity editor) to 256.
How can I scale the sprite so that it aligns properly with the spawned tile?
Looks like my only issue was in the Display setting.
I had been using a 10x10 display.
When I changed it to something more reasonable, I realized that my gameobjects were being displayed and selected as expected.

Problem in Unity: ArgumentOutOfRangeException: Index was out of range

I always have the following problem when I execute the code in Unity:
ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
The property Grid I set directly in the Unity editor with a value of 10 for x and y.
public class World : MonoBehaviour
{
public Room[,] Dungeon { get; set; }
public Vector2 Grid;
private void Awake()
{
Dungeon = new Room[(int)Grid.x, (int)Grid.y];
StartCoroutine(GenerateFloor());
}
public IEnumerator GenerateFloor()
{
for (int x = 0; x < Grid.x; x++)
{
for (int y = 0; y < Grid.y; y++)
{
Dungeon[x, y] = new Room
{
RoomIndex = new Vector2(x, y)
};
}
}
yield return new WaitForSeconds(3);
Vector2 exitLocation = new Vector2((int)Random.Range(0, Grid.x), (int)Random.Range(0, Grid.y));
Dungeon[(int)exitLocation.x, (int)exitLocation.y].Exit = true;
Dungeon[(int)exitLocation.x, (int)exitLocation.y].Empty = false;
Debug.Log("Exit is at: " + exitLocation); }}
I hope you can help me in this case
It's pretty hard to see an error like this, without debugging the code. But in General,try to break public Vector2 Grid; 2 int variables and to use them,so do not need to be explicitly cast to an int, and your code will become significantly readable if you want x==y then we can make the test in Awake and fail to accidentally ask the wrong value. And Vector2 exitLocation can be replaced with Vector2Int or 2 variables.

RayCast2D ignor hitting own Collider

Board.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Board : MonoBehaviour
{
public Transform m_emptySprite;
private int m_height = 14;
private int m_width = 6;
// number of rows where we won't have grid lines at the top
public int m_header = 8;
// Start is called before the first frame update
void Start()
{
DrawEmptyCells();
}
void DrawEmptyCells()
{
for (int y = 0; y < m_height - m_header; y++)
{
for (int x = 0; x < m_width; x++)
{
Transform tile;
tile = Instantiate(m_emptySprite, new Vector3(x, y, 0), Quaternion.identity) as Transform;
tile.name = "Tile ( x = " + x.ToString() + " ,y = " + y.ToString() + ")";
tile.transform.parent = transform;
}
}
}
}
Tile.cs
public class Tile : MonoBehaviour
{
private Vector2[] adjacentDirections = new Vector2[] { Vector2.up, Vector2.down, Vector2.left, Vector2.right };
void OnMouseDown()
{
GetAllAdjacentTiles();
}
private GameObject GetAdjacent(Vector2 castDir)
{
RaycastHit2D hit = Physics2D.Raycast(transform.position, castDir);
if (hit.collider != null)
{
print(hit.collider.gameObject.name);
return hit.collider.gameObject;
}
return null;
}
private List<GameObject> GetAllAdjacentTiles()
{
List<GameObject> adjacentTiles = new List<GameObject>();
for (int i = 0; i < adjacentDirections.Length; i++)
{
adjacentTiles.Add(GetAdjacent(adjacentDirections[i]));
}
return adjacentTiles;
}
}
I have tried to use the code above to detect tiles in all 4 directions but when I click on a tile I just get the name of the tile that was clicked.
Each tile has the Tile Script and a BoxCollider2D attached, why is it not printing all 4 tiles surrounding the current tile?
By default for Physics2D you are hitting your own collider the Raycast starts in.
To solve this go to the Physics2D Settings via Edit &rightarrow; Project Settings &rightarrow; Physics2D and disable the option
Queries Start In Colliders
Enable this option if you want physics queries that start inside a Collider 2D to detect the collider they start in.

Switching active states of two GameObjects in Unity3D

With Unity3D I am trying to create a scene with an alpha texture as a silhouette, which upon looking up is added, then looking down removes.
Currently I have the exposure of an equirectangular image changing on look up, but my silhouette object says I have not assigned it to an instance:
As you can see from the console, it is eventualy recognised, but I cannot set the active state. This is the current state of my code being applied to the scene:
using UnityEngine;
using System.Collections;
public class switchScript : MonoBehaviour {
public Cardboard cb;
public Renderer leftEyeDay;
public Renderer rightEyeDay;
private GameObject[] dayObjects;
public GameObject nightObject;
void Start () {
MeshFilter filter = GetComponent(typeof (MeshFilter)) as MeshFilter;
if (filter != null) {
Mesh mesh = filter.mesh;
Vector3[] normals = mesh.normals;
for (int i=0;i<normals.Length;i++)
normals[i] = -normals[i];
mesh.normals = normals;
for (int m=0;m<mesh.subMeshCount;m++)
{
int[] triangles = mesh.GetTriangles(m);
for (int i=0;i<triangles.Length;i+=3)
{
int temp = triangles[i + 0];
triangles[i + 0] = triangles[i + 1];
triangles[i + 1] = temp;
}
mesh.SetTriangles(triangles, m);
}
}
}
// Update is called once per frame
void Update () {
float xAngle = cb.HeadPose.Orientation.eulerAngles.x;
if (isLookingUp (xAngle)) {
var exposureValue = getExposureValue (xAngle);
leftEyeDay.material.SetFloat ("_Exposure", exposureValue);
rightEyeDay.material.SetFloat ("_Exposure", exposureValue);
toggleNight ();
} else {
leftEyeDay.material.SetFloat ("_Exposure", 1F);
rightEyeDay.material.SetFloat ("_Exposure", 1F);
toggleNight ();
}
}
public bool isLookingUp (float xAngle) {
return xAngle > 270 && xAngle < 340;
}
public float getExposureValue (float xAngle) {
var _xAngle = Mathf.Clamp (xAngle, 320, 340);
return ScaleValue (320.0F, 340.0F, 0.3F, 1.0F, _xAngle);
}
public float ScaleValue (float from1, float to1, float from2, float to2, float v) {
return from2 + (v - from1) * (to2 - from2) / (to1 - from1);
}
void toggleDay() {
print (nightObject);
nightObject = GameObject.FindGameObjectWithTag ("Night");
nightObject.SetActive (false);
}
void toggleNight() {
print (nightObject);
nightObject = GameObject.FindGameObjectWithTag ("Night");
nightObject.SetActive (true);
}
}
GameObject.FindGameObjectWithTag ("Night") returns null when the whole object is set to !active. Just toggle the scripts on that object instead of the whole GO.
Ok, if anyone needs this, this is an easy way to create scene switch:
void Awake() {
silhouetteObject = GameObject.FindGameObjectWithTag ("Night");
silhouetteObject.GetComponent<Renderer>().enabled = false;
}