I am currently programming a game in which an infinite procedural city is generated. so far everything works but because it leads to laggs if there are too many objects in the scene I wanted to make a script in which objects only appear near the player. I watched this video for help:https://www.youtube.com/watch?v=xlSkYjiE-Ck. When I tried to link this to my script (GenerateBuilding script) this error came:ArgumentException:
An item with the same key has already been added. Key: (0.0, 1.0)
System.Collections.Generic.Dictionary...
I need help to make the script work in which the houses are generated as well as the planes do, they should only be showed when the player is nearby
---Relevant Lines---
(Endless City)
calling updateChunk function in update()(updateChunk/building function is in GenerateBuilding script)
public void UpdateBuildings()
{
for (int i = 0; i < buildingObjects.Count; i++)
{
buildingObjects[i].SetVisible(false);
}
buildingObjects.Clear();
}
adding to dictionary line 68-80(UpdateVisibleChunks function)
if (building.cityChunkDictionary.ContainsKey(viewedChunkCoord))
{
building.cityChunkDictionary[viewedChunkCoord].UpdateCityChunk(viewerPosition, viewedChunkCoord, chunkSize, maxViewDst);
if (building.cityChunkDictionary[viewedChunkCoord].IsVisible())
{
building.buildingObjects.Add(building.cityChunkDictionary[viewedChunkCoord]);
}
}
else
{
building.AddTest(viewedChunkCoord, chunkSize);
}
EndlessCity, CityChunk class
CityChunk function, sending position to GenerateBuilding script to instantiate buildings in right position.
building.requestBuildingSquad(positionV3);
GenerateBuilding relevant lines
builderH function, instantiates the buildings
public float builderH(GameObject[] obj, float Height, Vector3 position)
{
Transform objTrans = obj[Random.Range(0, obj.Length)].transform;
//Instantiate house Object
GameObject objekt = Instantiate(objTrans.gameObject, position + new Vector3(xOfsset * spaceBetween, Height, zOfsset * spaceBetween), transform.rotation);
float height = Test.transform.localScale.y;
objectsss.Add(objekt);
return height;
}
AddTest function, adds instantiates objects from builderH to a dictionary
public void AddTest(Vector2 viewedChunkCoord, float chunkSize)
{
for (int i = 0; i < objectsss.Count; i++)
{
cityChunkDictionary.Add(viewedChunkCoord, new Testing(objectsss[i]));
}
}
Testing class, testing function, adds objects to class
public Testing(GameObject obj)
{
MeshObject = obj;
}
that should be all relevant lines
full scripts(really similar)
EndlessCity Script(this scripts generates the planes and gives position for GenerateBuilding script)
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
public class EndlessCity : MonoBehaviour
{
public const float maxViewDst = 10;
public Transform viewer;
private GenerateBuilding building;
public static Vector2 viewerPosition;
int chunkSize;
int chunksVisibleInViewDst;
Dictionary<Vector2, CityChunk> terrainChunkDictionary = new Dictionary<Vector2, CityChunk>();
List<CityChunk> terrainChunksVisibleLastUpdate = new List<CityChunk>();
void Start()
{
chunkSize = 8 - 1;
chunksVisibleInViewDst = Mathf.RoundToInt(maxViewDst / chunkSize);
}
void Update()
{
viewerPosition = new Vector2(viewer.position.x, viewer.position.z);
UpdateVisibleChunks();
}
void UpdateVisibleChunks()
{
building = FindObjectOfType<GenerateBuilding>();
building.UpdateBuildings();
for (int i = 0; i < terrainChunksVisibleLastUpdate.Count; i++)
{
terrainChunksVisibleLastUpdate[i].SetVisible(false);
}
terrainChunksVisibleLastUpdate.Clear();
int currentChunkCoordX = Mathf.RoundToInt(viewerPosition.x / chunkSize);
int currentChunkCoordY = Mathf.RoundToInt(viewerPosition.y / chunkSize);
for (int yOffset = -chunksVisibleInViewDst; yOffset <= chunksVisibleInViewDst; yOffset++)
{
for (int xOffset = -chunksVisibleInViewDst; xOffset <= chunksVisibleInViewDst; xOffset++)
{
Vector2 viewedChunkCoord = new Vector2(currentChunkCoordX + xOffset, currentChunkCoordY + yOffset);
if (terrainChunkDictionary.ContainsKey(viewedChunkCoord))
{
terrainChunkDictionary[viewedChunkCoord].UpdateTerrainChunk();
if (terrainChunkDictionary[viewedChunkCoord].IsVisible())
{
terrainChunksVisibleLastUpdate.Add(terrainChunkDictionary[viewedChunkCoord]);
}
}
else
{
terrainChunkDictionary.Add(viewedChunkCoord, new CityChunk(viewedChunkCoord, chunkSize, transform));
}
if (building.cityChunkDictionary.ContainsKey(viewedChunkCoord))
{
building.cityChunkDictionary[viewedChunkCoord].UpdateCityChunk(viewerPosition, viewedChunkCoord, chunkSize, maxViewDst);
if (building.cityChunkDictionary[viewedChunkCoord].IsVisible())
{
building.buildingObjects.Add(building.cityChunkDictionary[viewedChunkCoord]);
}
}
else
{
building.AddTest(viewedChunkCoord, chunkSize);
}
}
}
}
public class CityChunk
{
private GenerateBuilding building;
public GameObject meshObject;
public Vector3 positionV3;
Vector2 position;
Bounds bounds;
public CityChunk(Vector2 coord, int size, Transform parent)
{
building = FindObjectOfType<GenerateBuilding>();
position = coord * size;
bounds = new Bounds(position, Vector2.one * size);
positionV3 = new Vector3(position.x, 0, position.y);
int xPosition = building.xLength / 2;
int zPosition = building.zLength / 2;
float xOfsset = building.xOfsset;
float zOfsset = building.zOfsset;
float spaceBetween = building.spaceBetween;
//Instantiate plane
meshObject = Instantiate(building.groundObject, positionV3 + new Vector3((xPosition + xOfsset) * spaceBetween, -.5f, (xPosition + 1 + zOfsset) * spaceBetween), Quaternion.identity);
SetVisible(false);
building.requestBuildingSquad(positionV3);
}
public void UpdateTerrainChunk()
{
float viewerDstFromNearestEdge = Mathf.Sqrt(bounds.SqrDistance(viewerPosition));
bool visible = viewerDstFromNearestEdge <= maxViewDst;
SetVisible(visible);
}
public void SetVisible(bool visible)
{
meshObject.SetActive(visible);
}
public bool IsVisible()
{
return meshObject.activeSelf;
}
}
}
GenerateBuilding(this script generates Buildings on the planes)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GenerateBuilding : MonoBehaviour
{
public int minHeight = 2;
public int maxHeight = 8;
public int cubeTileX;
public int cubeTileZ;
public int xLength;
public int zLength;
public float spaceBetween;
public float xOfsset;
public float zOfsset;
public GameObject TesObject;
public GameObject[] Base;
public GameObject[] secondB;
public GameObject[] roof;
public GameObject groundObject;
public List<GameObject> objectsss;
public Dictionary<Vector2, Testing> cityChunkDictionary = new Dictionary<Vector2, Testing>();
public List<Testing> buildingObjects = new List<Testing>();
public GameObject Test;
void Start()
{
//requestBuildingSquad(this.transform.position);
}
void Update()
{
if (Input.GetKeyDown(KeyCode.I))
{
//
}
}
public void requestBuildingSquad(Vector3 position)
{
//*getting the middle of the city squad
int xPosition = xLength / 2;
int zPosition = zLength / 2;
//*
for (int z = 0; z < zLength; z++)
{
zOfsset++;
for (int x = 0; x < xLength; x++)
{
GenerateBuildings(position);
}
xOfsset = 0;
}
zOfsset = 0;
}
public void GenerateBuildings(Vector3 position)
{
int bHeight = Random.Range(minHeight, maxHeight);
float bOfsset = 0;
bOfsset += builderH(Base, bOfsset, position);
for (int i = 0; i < bHeight; i++)
{
bOfsset += builderH(secondB, bOfsset, position);
}
bOfsset += builderH(roof, bOfsset, position);
xOfsset++;
}
public float builderH(GameObject[] obj, float Height, Vector3 position)
{
Transform objTrans = obj[Random.Range(0, obj.Length)].transform;
//Instantiate house Object
GameObject objekt = Instantiate(objTrans.gameObject, position + new Vector3(xOfsset * spaceBetween, Height, zOfsset * spaceBetween), transform.rotation);
float height = Test.transform.localScale.y;
objectsss.Add(objekt);
return height;
}
public void AddTest(Vector2 viewedChunkCoord, float chunkSize)
{
for (int i = 0; i < objectsss.Count; i++)
{
cityChunkDictionary.Add(viewedChunkCoord, new Testing(objectsss[i]));
}
}
public void UpdateBuildings()
{
for (int i = 0; i < buildingObjects.Count; i++)
{
buildingObjects[i].SetVisible(false);
}
buildingObjects.Clear();
}
public class Testing
{
public GameObject MeshObject;
Vector2 position;
Bounds bounds;
public Testing(GameObject obj)
{
MeshObject = obj;
}
public void SetVisible(bool visiblee)
{
MeshObject.SetActive(visiblee);
}
public bool IsVisible()
{
return MeshObject.activeSelf;
}
public void UpdateCityChunk(Vector3 viewerPosition, Vector2 coord, int size, float maxViewDst)
{
position = coord * size;
bounds = new Bounds(position, Vector2.one * size);
float viewerDstFromNearestEdge = Mathf.Sqrt(bounds.SqrDistance(viewerPosition));
bool visible = viewerDstFromNearestEdge <= maxViewDst;
SetVisible(visible);
}
}
}
The problem is that you are trying to add twice elements with the same key.
here is the documentation of the Add method for dictionaries, and as it states, trying to add an existing key throws an error.
You can either use the TryAdd method, which adds an item only if the key doesn't exist already in the dictionary, or update the value with the existing key as you can see here.
Related
I'm working on Unity3d project where the floor has to unfold gradually. I created a script FloorModule.cs where using coroutine the floor tiles are laying out gradually. Each next module has to unfold right after previous is completed. There for I created Spawner.cs to loop a new FloorModule.cs right after previous one is completed.
I can't seem to get my head around how to use coroutine to synchronize the mainloop (Spawner.cs) with subloop on prefab (FloorModule.cs).
Here is the link to the example
https://1drv.ms/u/s!AkVZpIE6f1GV4M5Ju7G5zPOrQcCe8w?e=QrghRT
P.S.
In given example, as loop goes forward I'm using "Reference.cs" class to change some variable values .
FloorModule.cs
public class FloorModule : MonoBehaviour
{
public float zSpacer = 0f;
public int instPrefabCount;
public Transform spawnPoint;
public int lenght = 15;
public int width = 5;
public GameObject floorTiles;
void Start()
{
spawnPoint = GetComponent<Transform>();
StartCoroutine(FwFloorDelay(spawnPoint));
}
public IEnumerator FwFloorDelay(Transform origin)
{
for (int l = 0; l < lenght; l++)
{
float xAngle = 90;
float yPos = 0;
float zPos = 0 + l;
for (int w = 0; w < width; w++)
{
int xSelection = Random.Range(0, 6);
GameObject xFloor = Instantiate(floorTiles, origin);
TileStatusNames(xFloor, l, w);
// defining positiona and angles
float xPos = w + (zSpacer * w);
xFloor.transform.localEulerAngles = new Vector3(xAngle, 0, 0);
xFloor.transform.localPosition = new Vector3(xPos, yPos, zPos);
yield return new WaitForSeconds(.05f);
}
}
Spawner.cs
public class Spawner : MonoBehaviour
{
public GameObject FloorModPrefab;
public References[] referenceScript;
void Start()
{
StartCoroutine(SpawnModules());
}
IEnumerator SpawnModules()
{
for (int i = 0; i < referenceScript.Length; i++)
{
referenceScript[i].instance =
Instantiate(FloorModPrefab, referenceScript[i].ref_spawnPoint.position, referenceScript[i].ref_spawnPoint.rotation);
referenceScript[i].ref_instFloorModCount = i + 1;
referenceScript[i].Setup();
yield return new WaitForSeconds(5f);
}
}
}
References.cs
[Serializable]
public class References
{
FloorModule prefabObjScript;
public GameObject instance;
public int ref_instFloorModCount;
public Transform ref_spawnPoint;
public int ref_Width = 5;
public int ref_Lenght = 15;
public void Setup()
{
// Get references to the components.
prefabObjScript = instance.GetComponent<FloorModule>();
// Set the player numbers to be consistent across the scripts.
prefabObjScript.instPrefabCount = ref_instFloorModCount;
prefabObjScript.spawnPoint = ref_spawnPoint;
prefabObjScript.width = ref_Width;
prefabObjScript.lenght = ref_Lenght;
}
}
I tried to use coroutines unfortunately in given context I realize it's impossible for me to resolve this task.
You can yield a coroutine from within another coroutine.
Changes to your Code
In References change public GameObject instance; to public FloorModule instance;
In Spawner change public GameObject FloorModPrefab; to public FloorModule FloorModPrefab;
Remove the code from Start of FloorModule.
Modify FwFloorDelay to
public IEnumerator FwFloorDelay(Transform origin = null)
{
if (origin == null)
{
origin = transform;
}
...
}
In SpawnModules, chain the floor delay coroutine
IEnumerator SpawnModules()
{
for (int i = 0; i < referenceScript.Length; i++)
{
...
yield return referenceScript[i].instance.FwFloorDelay();
}
}
Your goal should not be to synchronize separate coroutines, but rather to get the code running sequentially in one coroutine.
For example, you could make References.Setup() asynchronous, and then have it invoke FloorModel.FwFloorDelay directly instead of the FloorModel starting its own separate coroutine.
I have been working with some script which is supposed to cause the childmeshes of an object move away from their center by a certain distance on wake, Unfortunately while I am not getting any errors, the script also doesnt seem to work no matter what values I adjust. I tried to debug but am still struggling a bit, Any advice would be appreciated.
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Interactions
{
public class ExplodedViewPart
{
public Vector3 StartPosition { get; }
public Vector3 Destination;
public float MoveDistance
{
set => Destination = m_meshCenter * value;
}
private readonly Vector3 m_meshCenter;
public ExplodedViewPart(Vector3 startPosition, Vector3 meshCenter, float moveDistance)
{
m_meshCenter = meshCenter;
StartPosition = startPosition;
Destination = meshCenter * moveDistance;
}
}
public class ExplodedView : MonoBehaviour
{
[Range(0.0f, 1.0f)]
[SerializeField] private float m_percentage;
[SerializeField] private float m_moveDistance = 3f;
private float m_oldMoveDistance = 3f;
private readonly List<ExplodedViewPart> m_parts = new List<ExplodedViewPart>();
private readonly List<Transform> m_partTransforms = new List<Transform>();
private void Awake()
{
foreach (var component in GetComponentsInChildren<MeshRenderer>())
{
Transform partTransform = component.transform;
m_partTransforms.Add(partTransform);
m_parts.Add(new ExplodedViewPart(partTransform.position, component.bounds.center, m_moveDistance));
}
}
private void ChangeMaxDistance(float value)
{
for (int i = 0; i < m_partTransforms.Count; i++)
{
m_parts[i].MoveDistance = value;
}
}
private void OnValidate()
{
if (Math.Abs(m_oldMoveDistance - m_moveDistance) > float.Epsilon)
{
ChangeMaxDistance(m_moveDistance);
m_oldMoveDistance = m_moveDistance;
}
Explode(m_percentage);
}
public void Explode(float percentage)
{
for (int i = 0; i < m_partTransforms.Count; i++)
{
m_partTransforms[i].position = (1 - percentage) * m_parts[i].StartPosition +
percentage * m_parts[i].Destination;
}
}
}
}
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
In Unity (C#), why am I getting a NullReferenceException and how do I fix it? [duplicate]
(1 answer)
Closed 1 year ago.
I follow a Unity Tutorial to program a version of flappy bird but I get the error NullReferenceException: Object reference not set to an instance of an object
Parallaxer.Shift () (at Assets/scripts/Parallaxer.cs:130)
Parallaxer.Update () (at Assets/scripts/Parallaxer.cs:75)
uijk,hfgmjdcfmhfc,ghvkmbgµfhj,ddgxngsdfaf
This is the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Parallaxer : MonoBehaviour
{
class PoolObject
{
public Transform transform;
public bool inUse;
public PoolObject(Transform t) { transform = t; }
public void Use() { inUse = true; }
public void Dispose() { inUse = false; }
}
[System.Serializable]
public struct YSpawnRange
{
public float min;
public float max;
}
public GameObject Prefab;
public int poolSize;
public float shiftSpeed;
public float spawnRate;
public YSpawnRange ySpawnRange;
public Vector3 defaultSpawnPos;
public bool spawnImmediate;
public Vector3 immediateSpawnPos;
public Vector2 targetAspectRatio;
float spawnTimer;
float targetASpect;
PoolObject[] poolObjects;
GameManager game;
private void Awake()
{
}
private void Start()
{
game = GameManager.Instance;
}
private void OnEnable()
{
GameManager.OnGameOverConfirmed += OnGameOverConfirmed;
}
private void OnDisable()
{
GameManager.OnGameOverConfirmed -= OnGameOverConfirmed;
}
void OnGameOverConfirmed()
{
for (int i = 0; i < poolObjects.Length; i++)
{
poolObjects[i].Dispose();
poolObjects[i].transform.position = Vector3.one * 1000;
}
if (spawnImmediate)
{
SpawnImmediate();
}
}
void Update()
{
if (game.GameOver) return;
Shift();
spawnTimer += Time.deltaTime;
if (spawnTimer > spawnRate)
{
Spawn();
spawnTimer = 0;
}
}
void Configure()
{
targetASpect = targetAspectRatio.x / targetAspectRatio.y;
poolObjects = new PoolObject[poolSize];
for (int i = 0; i < poolObjects.Length; i++)
{
GameObject go = Instantiate(Prefab) as GameObject;
Transform t = go.transform;
t.SetParent(transform);
t.position = Vector3.one * 1000;
poolObjects[i] = new PoolObject(t);
}
if (spawnImmediate)
{
SpawnImmediate();
}
}
void Spawn()
{
Transform t = GetPoolObject();
if (t == null) return;
Vector3 pos = Vector3.zero;
pos.x = defaultSpawnPos.x;
pos.y = Random.Range(ySpawnRange.min, ySpawnRange.max);
t.position = pos;
}
void SpawnImmediate()
{
Transform t = GetPoolObject();
if (t == null) return;
Vector3 pos = Vector3.zero;
pos.x = immediateSpawnPos.x;
pos.y = Random.Range(ySpawnRange.min, ySpawnRange.max);
t.position = pos;
Spawn();
}
void Shift()
{
for (int i = 0; i < poolObjects.Length; i++)
{
poolObjects[i].transform.position += -Vector3.right * shiftSpeed * Time.deltaTime;
CheckDisposeObject(poolObjects[i]);
}
}
void CheckDisposeObject(PoolObject poolObject)
{
if (poolObject.transform.position.x < -defaultSpawnPos.x)
{
poolObject.Dispose();
poolObject.transform.position = Vector3.one * 1000;
}
}
Transform GetPoolObject()
{
for (int i = 0; i < poolObjects.Length; i++)
{
if (!poolObjects[i].inUse)
{
poolObjects[i].Use();
return poolObjects[i].transform;
}
}
return null;
}
}
Thank you for your help.
I think you need to call Configure in Start or Awake. Otherwise the poolObjects array doesn't get initialized and trying to access poolObjects[i] in Shift will cause the NullRefrence error.
I want to instantiate GameObjects(specifically hexagonal tiles) at the hexagonalCoodinates(hexcoordinates).
For this I wrote a custom coordinate system.
But I found out that unity doesn't accept anything other than Vector3 or transform.
How do I make it do that?
Or is there a easier way to do this?
This is the method to generate the gameObjects
private void TouchCell(Vector3 point)//This method instantiates cubes
{
point = transform.InverseTransformPoint(point);
HexCoordinates coordinates = HexCoordinates.FromPosition(point);
Instantiate(cubes, coordinates, Quaternion.identity);//<-The coordinate variable here is a hex coordinate.
Debug.Log("Touched at:" + coordinates);
}
And This is the Hex coordinate generator:
public struct HexCoordinates
{
public int X { get; private set; }
public int Z { get; private set; }
public int Y { get
{
return -X - Z;
} }
public HexCoordinates(int x,int z)
{
X = x;
Z = z;
}
public static HexCoordinates FromOffsetCoordinates(int x,int z)
{
return new HexCoordinates(x-z/2, z);
}
public override string ToString()
{
return "("+X.ToString()+","+Y.ToString()+","+Z.ToString()+")";
}
public string ToStringOnSeperateLines()
{
return X.ToString() + "\n" +Y.ToString()+ "\n" + Z.ToString();
}
public static HexCoordinates FromPosition(Vector3 point)//This converts the Vector3 to Hex coords
{
float x = point.x / (HexMetrics.InnerRadius * 2f);
float y = -x;
float offset = point.z / (HexMetrics.OuterRadius * 3f);
x -= offset;
y -= offset;
int iX = Mathf.RoundToInt(x);
int iY = Mathf.RoundToInt(y);
int iZ = Mathf.RoundToInt(-x - y);
if (iX + iY + iZ != 0)
{
float dX = Mathf.Abs(x-iX);
float dY = Mathf.Abs(y - iY);
float dZ = Mathf.Abs(-x-y-iZ);
if(dX>dY&&dX>dZ)
{
iX = -iY - iZ;
}
else if(dZ>dY)
{
iZ = -iX - iY;
}
}
return new HexCoordinates(iX,iZ);
}
}
Just convert from your HexCoordinates to Vector3 using any way:
create method for HexCoordinates, something like public Vector3 ToVector3() {...}
create implicit operator for implicit cast to Vector3
public struct HexCoordinates
{
public int X { get; private set; }
public int Z { get; private set; }
public int Y => -X - Z;
public HexCoordinates(int x,int z)
{
X = x;
Z = z;
}
...
public static implicit operator Vector3(HexCoordinates coords)
{
Vector3 result = // convert to Vector3
// Vector3 result = ToVector3() -- or like this for code reuse
return result;
}
public Vector3 ToVector3()
{
Vector3 result = //convert to Vector3
return result;
}
}
And then you can extend Unity's Object class and add overloading for Instantiate() method that will accept HexCoordinates, convert to Vector3 and call Instantiate()
public static class ObjectExtension
{
public static void Instantiate(this Object obj, Object origin, HexCoordinates coords, Quaternion q)
{
Vector3 position = coords.ToVector3();
obj.Instantiate(origin, position, q);
}
}
Also if you create implicit cast for your HexCoordinates to Vector3 you don't need to create overloading for Instantiate() method because converting will be implicitly
You are using using catlike coding's code. (This would have been helpful to mention in the question ;) ). In part 3 of the tutorial featuring this hex coordinate system, you can see how they would do something like below, accessing a hex inside of an array by calculating an index:
public HexCell GetCell (Vector3 position) {
position = transform.InverseTransformPoint(position);
HexCoordinates coordinates = HexCoordinates.FromPosition(position);
int index = coordinates.X + coordinates.Z * width + coordinates.Z / 2;
return cells[index];
}
So, since a HexCell has a transform.position, you can use that to get its center (making sure you don't access out of bounds):
private void TouchCell(Vector3 point)
{
point = transform.InverseTransformPoint(point);
HexCoordinates coordinates = HexCoordinates.FromPosition(point);
int index = coordinates.X + coordinates.Z * width + coordinates.Z / 2;
if (index >=0 && index < cells.Length)
{
Vector3 worldPos = cells[index].transform.position;
Instantiate(cubes, worldPos, Quaternion.identity);
Debug.Log("Touched at:" + coordinates);
}
}
Better yet, it may be worthwhile to make a method to retrieve this index, for the sake of code reuse:
private bool IsValidCellIndex(Vector3 point, out int index)
{
point = transform.InverseTransformPoint(point);
HexCoordinates coordinates = HexCoordinates.FromPosition(point);
index = coordinates.X + coordinates.Z * width + coordinates.Z / 2;
return index >=0 && index < cells.Length;
}
private void TouchCell(Vector3 point)
{
if (IsValidCellIndex(point, out int index))
{
Vector3 worldPos = cells[index].transform.position;
Instantiate(cubes, worldPos, Quaternion.identity);
Debug.Log("Touched at:" + worldPos);
}
}
Or, just use GetCell as the tutorial does :)
Both scripts are attached to the same empty GameObject in the hierarchy.
First the SpawnObjects script is attached then the MoveObjects.
This is the script with the exception.
The exception is on the line:
mover.minXPos = minXPos;
The exception message:
NullReferenceException: Object reference not set to an instance of an object
SpawnObjects.RandomSpawn () (at Assets/MyScripts/SpawnObjects.cs:28)
SpawnObjects.Start () (at Assets/MyScripts/SpawnObjects.cs:18)
My code:
using UnityEngine;
using System.Collections;
public class SpawnObjects : MonoBehaviour {
public GameObject PrefabToSpawn;
public int MaximumObjects = 100;
public int minXPos = -1000;
public int maxXPos = 1000;
public int minYPos = 50;
public int maxYPos = 150;
public int minZPos = -1000;
public int maxZPos = 1000;
// Use this for initialization
void Start () {
RandomSpawn();
}
private void RandomSpawn()
{
for (int i = 0; i < MaximumObjects; i++)
{
Vector3 spawnLocation = new Vector3(Random.Range(minXPos, maxXPos), Random.Range(minYPos, maxYPos), Random.Range(minZPos, maxZPos));
GameObject spawned = (GameObject)Instantiate(PrefabToSpawn, spawnLocation, Quaternion.identity);
MoveObjects mover = spawned.GetComponent<MoveObjects>();
mover.minXPos = minXPos;
mover.maxXPos = maxXPos;
mover.minYPos = minYPos;
mover.maxYPos = maxYPos;
mover.minZPos = minZPos;
mover.maxZPos = maxZPos;
}
}
}
And this is the script of the MoveObjects
using UnityEngine;
using System.Collections;
public class MoveObjects : MonoBehaviour {
public int minXPos = -1000;
public int maxXPos = 1000;
public int minYPos = 50;
public int maxYPos = 150;
public int minZPos = -1000;
public int maxZPos = 1000;
public float speed = 30;
private Vector3 destinationLocation;
private float midX;
private float midY;
private float midZ;
void Start()
{
midX = (minXPos + maxXPos) / 2;
midY = (minYPos + maxYPos) / 2;
midZ = (minYPos + maxYPos) / 2;
GenerateNewDestinationPoint();
}
void Update()
{
Move();
if (ArrivedAtLocation())
GenerateNewDestinationPoint();
}
private void Move()
{
transform.LookAt(destinationLocation);
transform.Translate(transform.forward * speed * Time.deltaTime);
}
private bool ArrivedAtLocation()
{
return (Vector3.Distance(transform.position, destinationLocation) < 1);
}
private void GenerateNewDestinationPoint()
{
float newX = (transform.position.x < midX) ? Random.Range(midX, maxXPos) : Random.Range(minXPos, midX);
float newY = (transform.position.y < midY) ? Random.Range(midY, maxYPos) : Random.Range(minYPos, midY);
float newZ = (transform.position.z < midZ) ? Random.Range(midZ, maxZPos) : Random.Range(minZPos, midZ);
destinationLocation = new Vector3(newX, newY, newZ);
}
}
It is a possibility that your spawned object doesn't have the component attached that you are trying to access. So its always safe to check for null before using it.
MoveObjects mover = spawned.GetComponent<MoveObjects>();
if(mover == null)
{
// your prefab doesn't have the component attached. maybe add it.
mover = spawned.AddComponent<MoveObject>();
}
mover.minXPos = minXPos;
mover.maxXPos = maxXPos;
mover.minYPos = minYPos;
mover.maxYPos = maxYPos;
mover.minZPos = minZPos;
mover.maxZPos = maxZPos;