- Home /
How to access property of a prefab before Instantiating.
I am randomly generating instances of a group of prefabs. Currently this means there is an equal chance of generating each one. I would like to place a 'weight' on each prefab for its chance on being picked. I have setup a simple script to hold this property and placed it on each prefab. How can I access this property in my code? The prefabs count as Object datatypes and not GameObject so it isn't as easy as GetComponent. I want to do this before creating GameObjects with them.
Answer by HappyMoo · Jan 09, 2014 at 05:38 PM
Putting the weights on the prefabs is so global.. why don't you put it on your prefab list? Seems to make more sense that way... Then one prefab could be instantiated with different weights in different situations.
public List<WeightedPrefab> prefabPickList;
...
...
[System.Serializable]
public class WeightedPrefab
{
public Transform prefab;
public float weight;
}
EDIT: Some Example code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[System.Serializable]
public class WeightedTile {
public Transform tile;
public float weight;
}
public class MapGenerator : MonoBehaviour {
public List<WeightedTile> floorTiles;
public List<WeightedTile> wallTiles;
public float tileWidth = 1f;
public float tileLength = 1f;
void Start () {
createARoom();
}
void createARoom()
{
int width = Random.Range(4,8);
int length = Random.Range(4,8);
for(int x = 0; x<width; x++) for(int z = 0; z<length; z++)
{
Transform tile;
if (x==0 || x==width-1 || z==0 || z==length-1)
{
tile = pickWeightedOne(wallTiles);
}else{
tile = pickWeightedOne(floorTiles);
}
Instantiate(tile, new Vector3(x*tileWidth, 0, z*tileLength), Quaternion.identity);
}
}
Transform pickWeightedOne(List<WeightedTile> tiles)
{
float weightSum = 0f;
foreach(WeightedTile tile in tiles)
{
if (tile != null && tile.tile != null)
{
weightSum += tile.weight;
} else {
Debug.LogWarning("Warning. Found empty tile");
}
}
float runningWeight = 0f;
float pick = Random.Range(0f, weightSum);
foreach(WeightedTile tile in tiles)
{
if (tile != null && tile.tile != null)
{
runningWeight += tile.weight;
if (runningWeight>=pick) return tile.tile;
}
}
Debug.LogError("OMG. We shouldn't be here");
return null; // this should never be reached!
}
}
Yes, you place this on your Generator...
Should you then later have another Generator, you can have a completely different list and other weights on that one.
doesn't this assume that he already has a list of prefabs and isn't doing something more data driven, like loading all prefabs under a path, or a path defined in data somewhere? what is a 'Generator' in this context?
also, for the record, nothing actually wrong with global state - just everything wrong with lazy common abuse of it - is C# where I can't produce a free standing function any better? It encourages a different set of bad habits... like using singletons which are horrible at compile/run-time for things which genuinely are global state and should be treated as such. (just my 2 cents on that)
Yes, you can define classes in your Generator class, but don't have to. Depending on what makes sense in your Design.
You can also move it to the front of your file and define it in the same File as your Generator or move it in its own file.
The class is public. Not out of scope.
you don't need to define object, you can drag a prefab on a Transform... which would also allow you to call GetComponent, but that's still bad design.
Just replace your current list of objects with a list of WeightedPrefabs