1
\$\begingroup\$

After a couple tries, I still haven't found any solution to this problem of mine.

Currently, I have a script inheriting from MonoBehavior which creates a map with Perlin Noise. Let's call it mapGenerator. It instantiates a HexTile object from another script, which does not inherit from MonoBehaviour, that contains a refere to my terrain prefab and an enum value which says its type (so water, grass, wood, etc.). There is an array declared at the beginning of the script that I manually change as I'm adding terrains.

Everything works fine except when tweaking the map generation, If I want to change some values for perlin intervals, I might have to add another value to my HexTile[] and then drag my terrain prefab and set the enum value.

What I want is to know if there is a way to make the script select a prefab depending on the value the enum was set to.

Here is the code (simplified):

public class ProceduralTerrainGenerator : MonoBehaviour
{
public bool regenerateMap = true;

public Hextile[] hexTiles;

void Update()
{     
    CreateMap();
}

private void CreateMap()
{
    // Some code for Perlin Noise generation inside perlinMap[,] 
    for (int i = 0; i < mapLength; i++)
    {
        for (int j = 0; j < mapHeight; j++)
        {
            // Setting up position from i and j index
            foreach (HexTile hexTile in hexTiles)
            {
                if (ValueInRange(perlinMap[i,j], hexTile.LowLimit, hexTile.HighLimit)
                {
                    Instantiate<HexTile>(hexTile.tile, position, Quaternion.Euler(0f, 0f, 0f), this.transform);
                }
            }
        }
    }
}
}

and my other script

[System.Serializable]
public class HexTile 
{
    public GameObject tile;
    public HexMetrics.HexType hexType;
    public float perlinNoiseLowLimit;
    public float perlinNoiseHighLimit;
}

=========== EDIT 1 ===========

I tried simplifing my problem, because I'm more interested in the concept, but here are more details.

I have a class named HexSettings, which contains the hex prefab (HexTile) named tile, its HexType, and the perlin noise bundaries.

[System.Serializable]
public class HexSettings
{
    public HexTile tile;
    public HexMetrics.HexType hexType;
    public float perlinNoiseLowLimit;
    public float perlinNoiseHighLimit;
}

Here is the HexType enum contained inside HexMetrics

public class HexMetrics
{
    // Hex metrics data for placement in grid data

    public enum HexType
    {
        GRASS,
        FOREST,
        WATER,
        SNOW,
        DESERT,
        LAKE
    }
}

What I would like is to have some soprt of dictionnary that contains the correct prefab according to the selected HexType.

As you can see in the image below, I can set my HexType to my terrain types, but I also have to set the HexTile object manually, where it could be set automatically according to the HexType value set. That is what I'm looking for.

I tried adding it in a dictionnary (something like Dictionnary<Hextype, HexTile>), looking for prefabs in my folder prefab with Resources.LoadAll<HexTile>("prefab");, no success, prefabs couldn't be loaded in HexSettings.

current setup

\$\endgroup\$
3
  • 1
    \$\begingroup\$ You should add the code of HexType, too. \$\endgroup\$
    – aybe
    Commented Mar 19, 2019 at 22:42
  • \$\begingroup\$ It wold also help to include some pseudo code showing what you want to do, because I’m not exactly sure what you are asking for \$\endgroup\$
    – Ed Marty
    Commented Mar 20, 2019 at 0:04
  • \$\begingroup\$ HexType is a simple enumeration, it shows the terrain types. I'll add the snippet. I will update to be more clear \$\endgroup\$ Commented Mar 20, 2019 at 3:32

2 Answers 2

3
\$\begingroup\$

Try a ScriptableObject containing everything you need.

using UnityEngine;
using System;

[CreateAssetMenu]
public class HexTileCollection : ScriptableObject {
    [Serializable]
    public class HexPrefab {
        public GameObject prefab;
        public HexMetrics.HexType hexType;
    }
    private Dictionary<HexMetrics.HexType, GameObject> dict;
    public HexPrefab[] AllPrefabs;
    public GameObject this[HexMetrics.HexType type] {
        get {
            Init();
            return dict[type];
        }
    }

    private void Init() {
        if (dict != null)
            return;
        dict = new Dictionary<HexMetrics.HexType, GameObject>();
        foreach (var tile in AllPrefabs) {
            dict[tile.hexType] = tile.prefab;
        }
    }
}

Create an instance of this class in the project view by right clicking and choosing create Hex Tile Collection. Select this object, then set up your associations between hex types and prefabs once. It will look similar to your existing generator's hexTiles, except without Perlin noise ranges.

Your generator will change to use this newly created object:

class ProceduralTerrainGenerator : MonoBehaviour {
    [System.Serializable]
    public class HexTile 
    {
        public HexMetrics.HexType hexType;
        public float perlinNoiseLowLimit;
        public float perlinNoiseHighLimit;
    }

    public HexTileCollection prefabs;
    public HexTile[] hexTiles;

    private void CreateMap()
    {
        // Some code for Perlin Noise generation inside perlinMap[,] 
        for (int i = 0; i < mapLength; i++)
        {
            for (int j = 0; j < mapHeight; j++)
            {
                // Setting up position from i and j index
                foreach (HexTile hexTile in hexTiles)
                {
                    if (ValueInRange(perlinMap[i,j], hexTile.LowLimit, hexTile.HighLimit)
                    {
                        Instantiate(prefabs[hexTile.hexType], position, Quaternion.Identity, this.transform);
                        break;
                    }
                }
            }
        }
    }
}

Assign the HexTileCollection you created earlier into this component's prefabs field in the inspector. The hexTiles field now lists hex type and Perlin noise ranges but no prefab. That instead is fetched from the HexTileCollection. I'm not sure if this is what you're looking for, but it's what came to mind for me.

Note that at least one reason your attempt (Resources.LoadAll<HexTile>("prefab");) did not work is because HexTile would need to be a MonoBehavior or ScriptableObject to be able to be loaded. More generally, Resources.Load* works only on Unity Object assets, not on simple serialized objects.

\$\endgroup\$
3
  • 2
    \$\begingroup\$ This is probably the way to go. Some form of scriptable object that holds a collection. This is super useful if you later want to add skins to your game. Just load another collection with different prefabs. \$\endgroup\$
    – gjh33
    Commented Mar 20, 2019 at 13:38
  • \$\begingroup\$ It seems really cool. I dont know scriptable objects yet (very new), but I will try to after work and will give feedback if everything goes well but I'm sure it will. Really helpful ! \$\endgroup\$ Commented Mar 20, 2019 at 15:27
  • \$\begingroup\$ Wow, it works perfectly, thanks a lot !! \$\endgroup\$ Commented Mar 21, 2019 at 2:10
1
\$\begingroup\$

This question boils down to "how can I access prefabs dynamically without inspector references".

You can absolutely can .Load() prefabs dynamically. Just make sure they're in the /Resources folder. As long as the string values of each enum entry match the names of those prefabs (or can be converted to them) , you should have no problems doing what you intend.

Call .Load in Awake() or in your own custom Initialise() after Instantiate() / Awake().

It's also possible to key them in the dictionary using the Type (from typeof) or type name (string) rather than an enum value, if you so prefer.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .