I am trying to make a falling sand simulation by myself.
In my SandSimulation script I have slider values determining the length and height of the grid. When I save my code and run it changing the slider values in the inspecter does not cause an error. But when I run the game everytime after and change the slider values it comes up with the following error:
The gameobject that the SandSimulation script is attached to is never destroyed or disabled and my two other scripts have no interaction with this one. Other times I have got errors like this unity has told me where the problem might be but here I don’t even know what’s going wrong.
I did suspect that it might have to do with the Invoke() I used in OnValidate() to a warning about not being able to SendCall on methods like OnValidate(). But even after deleting that section the error still persists so I’m out of ideas.
Another strange thing is that even with this MissingReferenceExpection, everything still functions perfectly fine (although I haven’t tried building the game yet).
This is the contents of the SandSimulation script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SandSimulation : MonoBehaviour
{
public ChunkRenderer[] ChunkRenderers;
public GameObject ChunkPrefab;
[Space] [Header("Simulation Settings")]
[Range(1, 14)] public int GridLength = 1;
[Range(1, 8)] public int GridHeight = 1;
private Vector2Int _gridDimentions; // used to check if the above values were changed in the inspector
private Camera _cam;
private void Awake()
{
_cam = Camera.main;
UpdateGridPositionAndReferences();
}
void Update()
{
if (Input.GetMouseButton(0))
{
Vector2 mouseWorldPosition = _cam.ScreenToWorldPoint(Input.mousePosition);
Vector2Int chunkPosition = new Vector2Int(
(int)mouseWorldPosition.x / 16 + 1,
(int)mouseWorldPosition.y / 16 + 1);
int chunkChildNumber = chunkPosition.x + (chunkPosition.y - 1) * GridLength - 1;
if (IsVaildChunk(chunkChildNumber, chunkPosition))
ChunkRenderers[chunkChildNumber].Draw((int)mouseWorldPosition.x, (int)mouseWorldPosition.y);
}
}
private bool IsVaildChunk(int chunkChildNumber, Vector2Int chunkPosition)
{
return chunkChildNumber >= 0 && chunkChildNumber < GridLength * GridHeight
&& chunkPosition.x > 0 && chunkPosition.x <= GridLength
&& chunkPosition.y > 0 && chunkPosition.y <= GridHeight;
}
// Gets called when a field is changed in the inspector
private void OnValidate()
{
// If Grid Dimentions are changed
if (_gridDimentions.x != GridLength || _gridDimentions.y != GridHeight)
Invoke(nameof(UpdateGridPositionAndReferences), 0.01f);
}
private void UpdateGridPositionAndReferences()
{
// Updates comparisont variables
_gridDimentions = new Vector2Int(GridLength, GridHeight);
// Creates new chunks if nessersary
int amountToInstantiate = GridLength * GridHeight - transform.childCount;
for (int i = 0; i < amountToInstantiate; i++)
Instantiate(ChunkPrefab, transform);
// Destroys chunks if nessersary
if (transform.childCount != GridLength * GridHeight)
{
for (int i = transform.childCount - 1; i > GridLength * GridHeight - 1; i--)
{
ChunkRenderers[i].gameObject.SetActive(false);
Destroy(ChunkRenderers[i].gameObject);
}
}
ChunkRenderers = GetComponentsInChildren<ChunkRenderer>();
// Sets positions of chunks
for (int i = 0; i < ChunkRenderers.Length; i++)
{
int x = i % GridLength * 16;
int y = i / GridLength * 16;
ChunkRenderers[i].transform.position = new Vector2(x + 8, y + 8); // + 8 to move the bottom left corner to the origin
}
}
}
And the ChunkPrefab is a just a 2d object with a Quad and MeshRenderer component and a ChunkRenderer script.
This is the contents of the ChunkRenderer script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ChunkRenderer : MonoBehaviour
{
private MeshRenderer _mr;
private Texture2D _t2d;
private void Awake()
{
_mr = GetComponent<MeshRenderer>();
// Create Chunk
_t2d = new Texture2D(16, 16);
_t2d.filterMode = FilterMode.Point;
_mr.material.shader = Shader.Find("Unlit/Texture");
_mr.material.SetTexture("_MainTex", _t2d);
}
public void Draw(int localX, int localY)
{
_t2d.SetPixel(localX, localY, Color.red);
_t2d.Apply();
}
}