Merge textures at Runtime - unity3d

Is there any way to "bake" one texture to another, except for using SetPixels()?
Now i'm trying to use something like that, but it too slow:
public static Texture2D CombineTextures(Texture2D aBaseTexture, Texture2D aToCopyTexture, int x, int y)
{
int aWidth = aBaseTexture.width;
int aHeight = aBaseTexture.height;
int bWidth = aToCopyTexture.width;
int bHeight = aToCopyTexture.height;
Texture2D aReturnTexture = new Texture2D(aWidth, aHeight, TextureFormat.RGBA32, false);
Color[] aBaseTexturePixels = aBaseTexture.GetPixels();
Color[] aCopyTexturePixels = aToCopyTexture.GetPixels();
int aPixelLength = aBaseTexturePixels.Length;
for(int y1 = y, y2 = 0; y1 < aHeight && y2 < bHeight ; y1++, y2++)
{
for(int x1 = x, x2 = 0 ; x1 < aWidth && x2 < bWidth; x1++, x2++)
{
aBaseTexturePixels[x1 + y1*aWidth] = Color.Lerp(aBaseTexturePixels[x1 + y1*aWidth], aCopyTexturePixels[x2 + y2*bWidth], aCopyTexturePixels[x2 + y2*bWidth].a);
}
}
aReturnTexture.SetPixels(aBaseTexturePixels);
aReturnTexture.Apply(false);
return aReturnTexture;
}
The problem is, that i need to display a lot of sprites on 2d surface (blood, enemy corpses, etc.), and just instantiating every sprite will greatly reduce fps.

If you are concerned about fps drop when instantiating prefabs you should definitely build a Object pooling system. So you will have a system that:
Instantiating all objects in the pool and keep it far away from the main camera
Once you need the object you will "borrow" it from the pool
Once object is not needed anymore you will return it back to the object pool (for example when sprite is out the camera view
Baking it all to one texture isn't the best practice. You will need huge amounts of RAM for this. Consider steps above, its very common practice
Good example here:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Linq;
public class BackgroundPool : MonoBehaviour
{
public static BackgroundPool instance;
public List<BackgroundSection> sectionsLibrary = new List<BackgroundSection>();
public int poolSize = 4;
public List<BackgroundSection> pool = new List<BackgroundSection>();
void Awake()
{
instance = this;
DateTime startGenTime = DateTime.Now;
//generateSectionsPool
for (int i=0; i<sectionsLibrary.Count; i++)
{
for (int j=0; j<poolSize; j++)
{
if (j == 0)
{
sectionsLibrary[i].positionInPool = sectionsLibrary[i].transform.position;
pool.Add(sectionsLibrary[i]);
}
else
{
BackgroundSection section = (BackgroundSection)Instantiate(sectionsLibrary[i]);
section.transform.parent = this.transform;
section.transform.position = new Vector3((-(ExtensionMethods.GetBounds(sectionsLibrary[i].gameObject).extents.x * 2) * j) + sectionsLibrary[i].transform.position.x,
sectionsLibrary[i].transform.position.y);
section.transform.localEulerAngles = Vector3.zero;
section.gameObject.name = sectionsLibrary[i].gameObject.name + ":" + j.ToString();
section.positionInPool = section.transform.position;
pool.Add(section);
}
}
}
Debug.Log("Background Pool generated in: " + (DateTime.Now - startGenTime).TotalSeconds.ToString() + " s");
}
public BackgroundSection GetPiece(Scenery scenery, SceneryLayer _layer)
{
List<BackgroundSection> allScenery = new List<BackgroundSection>();
foreach (BackgroundSection section in pool) { if (section.scenery == scenery) allScenery.Add(section); }
List<BackgroundSection> matchingPieces = new List<BackgroundSection>();
foreach (BackgroundSection section in allScenery) { if (section.sceneryLayer == _layer) matchingPieces.Add(section); }
if (matchingPieces.Count > 0)
{
BackgroundSection pickedSection = matchingPieces[UnityEngine.Random.Range(0,matchingPieces.Count-1)];
pool.Remove(pickedSection);
return pickedSection;
}
else
{
Debug.LogError("Cann't get background piece matching criteria, scenery: " + scenery + ", layer" + _layer);
return null;
}
}
public void ReturnPiece(BackgroundSection section)
{
pool.Add(section);
section.transform.parent = this.transform;
section.transform.position = section.positionInPool;
}
}

Related

Unity Object Detection: Barracuda, MobileNET and Webcam

I am trying to run the following ONNX model in Unity, using Barracuda and the following pre-trained model:
https://github.com/onnx/models/tree/master/vision/classification/mobilenet
with my webcam as the camera, and using the following script to test the system:
using UnityEngine;
using UnityEngine.UI;
using Unity.Barracuda;
using System.IO;
using System.Linq;
public class Webcam : MonoBehaviour
{
public NNModel mob_net;
public Model model;
private IWorker worker;
int current_cam_index = 0;
WebCamTexture tex;
public RawImage display;
private bool brain_on = false;
private const int SIZE = 224;
public void start_cam()
{
model = ModelLoader.Load(mob_net);
worker = WorkerFactory.CreateWorker(WorkerFactory.Type.ComputePrecompiled, model);
if (tex != null)
{
stop_cam();
}
else
{
WebCamDevice device = WebCamTexture.devices[current_cam_index];
tex = new WebCamTexture(device.name);
display.texture = tex;
tex.Play();
}
}
public void stop_cam()
{
display.texture = null;
tex.Stop();
tex = null;
}
void crop__normalize_inference(WebCamTexture src)
{
int x = Mathf.FloorToInt(display.transform.position.x);
int y = Mathf.FloorToInt(display.transform.position.y);
Color[] pix = src.GetPixels(x, y, SIZE, SIZE);
Texture2D dest = new Texture2D(SIZE, SIZE);
dest.SetPixels(pix);
dest.Apply();
float[] floats = new float[224 * 224 * 3];
for (int i = 0; i < pix.Length; ++i)
{
var color = pix[i];
floats[i * 3 + 0] = (color.r - 127) / 127.5f;
floats[i * 3 + 1] = (color.g - 127) / 127.5f;
floats[i * 3 + 2] = (color.b - 127) / 127.5f;
}
Tensor in_tensor = new Tensor(1, 224, 224, 3, floats);
worker.Execute(in_tensor);
Tensor out_tensor = worker.PeekOutput("MobilenetV2/Predictions/Reshape_1");
var max = Mathf.Max(out_tensor.ToReadOnlyArray());
var arr = out_tensor.ToReadOnlyArray();
var index = System.Array.IndexOf(arr, max);
string line = File.ReadLines(#"D:\Unity\WebCam\Cam\Assets\Scenes\mobile_net.txt").Skip(index).Take(1).First();
Debug.Log(line);
in_tensor.Dispose();
out_tensor.Dispose();
worker.Dispose();
}
public void brain()
{
brain_on = !brain_on;
}
private void Update()
{
if (brain_on)
{
crop_and_normalize(tex);
brain_on = false;
}
}
}
But when I run it I get an error which says:
ArgumentException: Can only specify one unknown dimension
My guess is that either Unity doesn't support the model, or that for some reason my input tensor form is incorrect . . .
Any help would be massively appreciated,
K
Solution: Set to barracuda V1.3.0

Floating origin and visual effect graph in Unity

I am sure that everybody knows about this script, http://wiki.unity3d.com/index.php/Floating_Origin, that fixes problems with floating origin easily.
The problem is that the script is outdated and does not move the particle effects created by visual effect graph.
I was trying to rewrite it but I cant seem to make an array to store all the particles, like with the previous one, thus I can't continue from there.
Here is my code:
// Based on the Unity Wiki FloatingOrigin script by Peter Stirling
// URL: http://wiki.unity3d.com/index.php/Floating_Origin
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.VFX;
using UnityEngine.Experimental.VFX;
public class FloatingOrigin : MonoBehaviour
{
[Tooltip("Point of reference from which to check the distance to origin.")]
public Transform ReferenceObject = null;
[Tooltip("Distance from the origin the reference object must be in order to trigger an origin shift.")]
public float Threshold = 5000f;
[Header("Options")]
[Tooltip("When true, origin shifts are considered only from the horizontal distance to orign.")]
public bool Use2DDistance = false;
[Tooltip("When true, updates ALL open scenes. When false, updates only the active scene.")]
public bool UpdateAllScenes = true;
[Tooltip("Should ParticleSystems be moved with an origin shift.")]
public bool UpdateParticles = true;
[Tooltip("Should TrailRenderers be moved with an origin shift.")]
public bool UpdateTrailRenderers = true;
[Tooltip("Should LineRenderers be moved with an origin shift.")]
public bool UpdateLineRenderers = true;
private ParticleSystem.Particle[] parts = null;
VisualEffect[] visualEffect = null;
void LateUpdate()
{
if (ReferenceObject == null)
return;
Vector3 referencePosition = ReferenceObject.position;
if (Use2DDistance)
referencePosition.y = 0f;
if (referencePosition.magnitude > Threshold)
{
MoveRootTransforms(referencePosition);
if (UpdateParticles)
MoveParticles(referencePosition);
if (UpdateTrailRenderers)
MoveTrailRenderers(referencePosition);
if (UpdateLineRenderers)
MoveLineRenderers(referencePosition);
}
}
private void MoveRootTransforms(Vector3 offset)
{
if (UpdateAllScenes)
{
for (int z = 0; z < SceneManager.sceneCount; z++)
{
foreach (GameObject g in SceneManager.GetSceneAt(z).GetRootGameObjects())
g.transform.position -= offset;
}
}
else
{
foreach (GameObject g in SceneManager.GetActiveScene().GetRootGameObjects())
g.transform.position -= offset;
}
}
private void MoveTrailRenderers(Vector3 offset)
{
var trails = FindObjectsOfType<TrailRenderer>() as TrailRenderer[];
foreach (var trail in trails)
{
Vector3[] positions = new Vector3[trail.positionCount];
int positionCount = trail.GetPositions(positions);
for (int i = 0; i < positionCount; ++i)
positions[i] -= offset;
trail.SetPositions(positions);
}
}
private void MoveLineRenderers(Vector3 offset)
{
var lines = FindObjectsOfType<LineRenderer>() as LineRenderer[];
foreach (var line in lines)
{
Vector3[] positions = new Vector3[line.positionCount];
int positionCount = line.GetPositions(positions);
for (int i = 0; i < positionCount; ++i)
positions[i] -= offset;
line.SetPositions(positions);
}
}
private void MoveParticles(Vector3 offset)
{
var particles = FindObjectsOfType<ParticleSystem>() as ParticleSystem[];
foreach (ParticleSystem system in particles)
{
if (system.main.simulationSpace != ParticleSystemSimulationSpace.World)
continue;
int particlesNeeded = system.main.maxParticles;
if (particlesNeeded <= 0)
continue;
bool wasPaused = system.isPaused;
bool wasPlaying = system.isPlaying;
if (!wasPaused)
system.Pause();
// ensure a sufficiently large array in which to store the particles
if (parts == null || parts.Length < particlesNeeded)
{
parts = new ParticleSystem.Particle[particlesNeeded];
}
// now get the particles
int num = system.GetParticles(parts);
for (int i = 0; i < num; i++)
{
parts[i].position -= offset;
}
system.SetParticles(parts, num);
if (wasPlaying)
system.Play();
}
var particles2 = FindObjectsOfType<VisualEffect>() as VisualEffect[];
foreach (VisualEffect system in particles2)
{
int particlesNeeded = system.aliveParticleCount;
if (particlesNeeded <= 0)
continue;
bool wasPaused = !system.isActiveAndEnabled;
bool wasPlaying = system.isActiveAndEnabled;
if (!wasPaused)
system.Stop();
// ensure a sufficiently large array in which to store the particles
if (visualEffect == null || visualEffect.Length < particlesNeeded)
{
visualEffect = new VisualEffect().visualEffectAsset[particlesNeeded];
}
// now get the particles
int num = system.GetParticles(parts);
for (int i = 0; i < num; i++)
{
parts[i].position -= offset;
}
system.SetParticles(parts, num);
if (wasPlaying)
system.Play();
}
}
}
On the line(this is a wrong line and everything below it too)
visualEffect = new VisualEffect().visualEffectAsset[particlesNeeded];
, I need to create a similar array to the line (correct one, but for the old particle system)
parts = new ParticleSystem.Particle[particlesNeeded];
that creates array full of particles (but with VisualEffect class).
If I can fix this one, there should not be any problem with the rest.
I think that solving this problem will help literally thousands of people now and in the future, since limitation for floating origin in unity are horrible and majority of people working in unity will need floating origin for their game worlds, with VFX graph particles.
Thanks for the help.
My question has been answered here:
https://forum.unity.com/threads/floating-origin-and-visual-effect-graph.962646/#post-6270837

Microphone, gain value and spectrum values do not sync using Unity

I am making a simple voice visualization program. My goals are:
Playback microphone input
Visualize voice spectrum and gain in real time
Here is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class VisualizeVoice : MonoBehaviour
{
private const int NUM_SPECTRUM_SAMPLES = 256;
private const int NUM_SPECTRUM_BARS = 32;
private const int NUM_PCM_SAMPLES = 16000;
private const float BAR_DROP_SPEED = 1e-3f;
private const int NUM_SAMPLES_TO_AVERAGE = 8;
private string _deviceName;
private float[] _spectrumData = new float[NUM_SPECTRUM_SAMPLES];
private float[] _fPCMData = new float[NUM_PCM_SAMPLES];
private float _gain = 0;
private AudioClip _audio; // Audio from microphone
private AudioSource _playback; // To play the audio from microphone
// For visualization
private GameObject[] _spectrumBars = new GameObject[NUM_SPECTRUM_BARS];
private GameObject _gainBar;
// Start is called before the first frame update
void Start()
{
if (Microphone.devices.Length == 0) {
Debug.LogError("No Microphone");
return;
}
_deviceName = Microphone.devices[0];
Debug.Log("Current microphone is " + _deviceName);
if ((_playback = this.GetComponent<AudioSource>()) == null) {
_playback = this.gameObject.AddComponent<AudioSource>();
}
_playback.loop = true;
_playback.bypassEffects = true;
_playback.bypassListenerEffects = true;
_playback.bypassReverbZones = true;
_playback.priority = 0;
_playback.pitch = 1;
_playback.clip = _audio = Microphone.Start(_deviceName, true, 1, AudioSettings.outputSampleRate);
// Sync microphone and playback, but it always fails
float waitTime = 0;
while (!(Microphone.GetPosition(_deviceName) > 0) && waitTime <= 2)
waitTime += Time.deltaTime;
if (waitTime > 2) {
Debug.LogError("time out waiting for microphone");
}
_playback.Play();
InitVisualization();
}
// Update is called once per frame
void Update()
{
// Get PCM data and calculate gain
var audioPosition = Microphone.GetPosition(_deviceName);
_audio.GetData(_fPCMData, audioPosition);
UpdateGain();
// Get spectrum data
_playback.GetSpectrumData(_spectrumData, 0, FFTWindow.BlackmanHarris);
// Update visualization
UpdateVisualization();
}
private void InitVisualization()
{
// Initialize spectrum bars
for (int ibar = 0; ibar < NUM_SPECTRUM_BARS; ibar++) {
_spectrumBars[ibar] = GameObject.CreatePrimitive(PrimitiveType.Cube);
_spectrumBars[ibar].transform.parent = this.transform;
_spectrumBars[ibar].transform.localPosition = new Vector3(ibar, 0, 0);
_spectrumBars[ibar].transform.localScale = new Vector3(1, 0, 1);
}
// Initialize gain bar
_gainBar = GameObject.CreatePrimitive(PrimitiveType.Cube);
_gainBar.transform.parent = this.transform;
_gainBar.transform.localPosition = new Vector3(-5, 0, 0);
_gainBar.transform.localScale = new Vector3(4, 0, 1);
// Overall dimension
this.transform.localScale = new Vector3(0.2f, 10.0f, 0.2f);
}
private void UpdateVisualization()
{
// Update spectrum bars
int nSamplesPerBar = NUM_SPECTRUM_SAMPLES / NUM_SPECTRUM_BARS;
for (int ibar = 0; ibar < NUM_SPECTRUM_BARS; ibar++) {
// Calculate value of each bar
float value = 0;
for (int isample = 0; isample < nSamplesPerBar; isample++) {
value += _spectrumData[ibar * nSamplesPerBar + isample];
}
value /= nSamplesPerBar;
// Use current value if increasing, or slowly drop previous value if decreasing
float prevValue = _spectrumBars[ibar].transform.localScale.y;
if (value < prevValue)
value = prevValue - BAR_DROP_SPEED;
// Y scale is set to value
_spectrumBars[ibar].transform.localScale = new Vector3(1, value, 1);
}
// Update gain bar
_gainBar.transform.localScale = new Vector3(4, _gain, 1);
}
private void UpdateGain()
{
_gain = 0;
for(int i = 0; i < NUM_SAMPLES_TO_AVERAGE; i++) {
_gain += Mathf.Abs(_fPCMData[NUM_PCM_SAMPLES - i - 1]);
}
_gain /= NUM_SAMPLES_TO_AVERAGE;
}
}
Here are my questions:
I can't use while (!Microphone.GetPosition(_deviceName) > 0)); to avoid latency from microphone to speaker. If I use it, my application just freezes. If I add code to allow time-out, it has time-out every time.
The gain bar seems irrelevant with my voice. I don't know if my calculation is right.
I'm not sure if I need to average over multiple samples calculating gains, and how many samples I need to average over. I need this gain value later to detect silent moments and cut audio data.
To 1.
You can. Unity allows to define Start as a Coroutine
private IEnumerator Start()
{
...
}
On this way you can use a non blocking
while (!Microphone.GetPosition(_deviceName) > 0))
{
yield return null;
}

Dynamically adding tiles to a grid based map

I want to have an infinitely explorable map. The plan is to create categories of game tiles (roads, obstacles, buildings), and randomly choose a category of game tile to be added when the player approaches the edge of the existing set of tiles. Tiles will also be destroyed once the player is 2 grid squares away from that tile. Currently I am using a multidimensional array that requires a size initializer.
What I have so far:
public class GameManager : MonoBehaviour
{
private GameObject[,] tileArray;
public GameObject groundTile;
public GameObject player;
private int tileSize = 80;
private int nextFarX = 1;
private int nextFarZ = 1;
private int nextNearX = -1;
private int nextNearZ = -1;
private float padding = .1f;
private int arrayOffset;
private int arrayDimension;
// Use this for initialization
void Start ()
{
arrayDimension = 200;
arrayOffset = arrayDimension / 2;
tileArray = new GameObject[,];
this.AddCubeAt(0, 0);
}
// Update is called once per frame
void Update () {
var x = Convert.ToInt32(player.transform.position.x / tileSize);
var z = Convert.ToInt32(player.transform.position.z / tileSize);
for (int i = -1; i < 2; i++)
{
for (int j = -1; j < 2; j++)
{
var checkX = x + i;
var checkZ = z + j;
if (tileArray[checkX + arrayOffset, checkZ + arrayOffset] == null)
{
//player is less than 2 tiles away from this grid, add a tile
this.AddCubeAt(checkX, checkZ);
}
}
}
// feels like a hack, but it will remove tiles that are not touching the tile that the player occupies
for (int i = 0; i < 6; i++)
{
for (int j = 0; j < 6; j++)
{
if (i == 0 | i == 5 | j == 0 | j == 5)
{
if (tileArray[x + (i-2) + arrayOffset, z + (j-2) + arrayOffset] != null)
{
Destroy(tileArray[x + (i - 2) + arrayOffset, z + (j - 2) + arrayOffset]);
tileArray[x + (i - 2) + arrayOffset, z + (j - 2) + arrayOffset] = null;
}
}
}
}
}
private void AddCubeAt(int x, int z)
{
var pos = new Vector3(x * tileSize, 0, z * tileSize);
var rot = Quaternion.identity;
GameObject newCube = (GameObject)Instantiate(groundTile, pos, rot);
tileArray[x + arrayOffset, z + arrayOffset] = newCube;
}
}
What is a better way to approach this?
You should familiarize yourself with Graph Data Structure (not adjacency matrix implementation). It's much more appropriate for this task. And, I would solve this
Tiles will also be destroyed once the player is 2 grid squares away from that tile
in another way: Every time player changed his position I would start DFS on target depth (in your case it's 2) and remove found tiles.
Decided to go with a simple Dictionary and methods to query/update it:
private GameObject RetrieveTileAt(int x, int z)
{
string key = string.Format("{0}.{1}", x, z);
if (tileDictionary.ContainsKey(key))
{
return tileDictionary[key];
}
else
{
return null;
}
}
private void InsertTileAt(int x, int z, GameObject tile)
{
string key = string.Format("{0}.{1}", x, z);
tileDictionary[key] = tile;
}
It is not an infinitely sized grid, (int min + int max)squared, but it should be far more than I need.

Unity Roguelike Project: Argument Out of Range Exception

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?