Search Unity

Calling a specific instance of an attached script.

Discussion in '2D' started by LankyJon, Mar 24, 2018.

  1. LankyJon

    LankyJon

    Joined:
    Mar 18, 2018
    Posts:
    13
    Hello all. I am using a noise generating script, and have attached multiple instances to my game object so that I can tailor different noise patterns. These components allow me to give them a name. I am wondering how I can access these particular components from within another script attached to the same game object. My best guess so far has been:


    My best guess, which doesn't work, is:
    caveNoise= GetComponent("caveNoise") as FastNoise;


    But that obviously doesn't work. Any help greatly appreciated.
     
  2. orb

    orb

    Joined:
    Nov 24, 2010
    Posts:
    3,037
    Add a public string with a variant name to the script. Get all instances of the script during Start() in whichever script is collecting them, and store references to each by name in a dictionary.

    Rough pseudo-code:
    Code (CSharp):
    1. list = GetComponents<NoiseMaker>()
    2. for each item in list dictionary[item.variant]=item
     
  3. LankyJon

    LankyJon

    Joined:
    Mar 18, 2018
    Posts:
    13
    Thanks. This has been helpful, but I'm still trying to work it out fully. I'm not sure what language you are working in, firstly. Anyways, what I have so far is:

    string mapName;
    noises = GetComponents<FastNoiseSIMDUnity>();
    foreach (FastNoiseSIMDUnity noise in noises)
    mapName = noise.noiseName + "Map";


    I'm not sure how to translate this into generating actual objects though.

    Earlier on, I have declared:


    float[,] landscapeNoiseMap;
    float[,] caveNoiseMap;


    I then have FastNoiseSIMDUnity components attached with the noiseName strings "landscapeNoise" and "caveNoise". What I would like to do would be:

    landscapeNoiseMap = landscapeNoise.GenerateNoiseMap();


    GenerateNoiseMap(); returns a float[,], so that part of the code is fine. The problem is figuring out how to grab the landscapeNoise component, call GenerateNoiseMap from is, and then, crucially, assign this to the landscapeNoiseMap variable. I'm sure one way I could do it would be to do a separate loop through the FastNoiseSIMDUnity objects for each NoiseMap I want to generate, but that seems inefficient. Is there a way to use the mapName string I have created in order to assign these NoiseMaps from within one loop?

    Thanks so much?
     
  4. orb

    orb

    Joined:
    Nov 24, 2010
    Posts:
    3,037
    Pseudocode, no specific language as it was late :)

    Although the one, true language is of course C#. Anything else is irrelevant in a Unity context.

    Also, use CODE tags for longer text, not ICODE tags. That button you clicked for quoting code should not actually exist, as one of UT's people mentioned in another thread. It's meant for quoting
    functions("like this")
    in the middle of sentences (the I is for inline) :)

    The actual code quote function is under the dropdown after movie insertion.

    Let's assume you have an object which will hold the generated noise. It doesn't matter how, just that one script has the data and calls the correct noise generator, depending on whatever determines that sort of thing (dropdown with the names of other generators, function call with argument, configurable string), and multiple instances of a FastNoiseSIMD script exist as the other components on it.

    Example of some dummy scripts:


    The FastNoiseSIMS script has a public string "name", which is set to something different for each instance added to the game object. Part of the Generator script could look like this:

    Code (CSharp):
    1. public class Generator : MonoBehaviour
    2. {
    3.     private Dictionary<string, FastNoiseSIMD> noise;
    4.  
    5.     void Start()
    6.     {
    7.         noise = new Dictionary<string, FastNoiseSIMD>();
    8.         var list = GetComponents<FastNoiseSIMD>();
    9.         foreach (FastNoiseSIMD item in list)
    10.         {
    11.             noise.Add(item.name, item);
    12.         }
    13.     }
    14. }
    Now the generator script just needs some settings in variables to determine the sort of noise it generates and a Generate() method. All you need to do to call the generator is something like
    noise["cave"].Generate()
    . Use lists or dictionaries to cut down on the number of variables, and to make extension later easier. If you prefer integer lookup instead of strings, you can create an enum and make the dictionary keys that instead.
     
  5. LankyJon

    LankyJon

    Joined:
    Mar 18, 2018
    Posts:
    13
    Thanks so much! This is all super helpful. I think I'm getting it close to working (almost certainly with some spaghettification), but I am having one issue now. Relevant code looks like this:


    Code (CSharp):
    1. public class NoiseGenerator : MonoBehaviour {
    2.  
    3.     public Dictionary<string, FastNoiseSIMD> noise;
    4.  
    5.     public NoiseGenerator() { }
    6.  
    7.     void Start() {
    8.         noise = new Dictionary<string, FastNoiseSIMD>();
    9.         var list = GetComponents<FastNoiseSIMDUnity>();
    10.         foreach (FastNoiseSIMDUnity item in list) {
    11.             Debug.Log(item.noiseName);
    12.             noise.Add(item.noiseName, item.ReturnNoise());
    13.  
    14.         }
    15.     }
    16.  
    17.     public float[,] GenerateMap(string noiseName, int x, int y) {
    18.         return noise[noiseName].GenerateNoiseMap(x, y);
    19.     }
    20. }
    21.  
    Code (CSharp):
    1.     void GenerateNoise() {
    2.         noiseMaps = new Dictionary<string, float[,]>();
    3.         noiseGenerator = GetComponent<NoiseGenerator>();
    4.         foreach (var item in noiseGenerator.noise)
    5.             noiseMaps.Add(item.Key, item.Value.GenerateNoiseMap(width, height));
    I am getting the error:
    NullReferenceException: Object reference not set to an instance of an object
    WorldGeneration.GenerateNoise () (at Assets/Scripts/WorldGeneration.cs:47)

    Which is
        foreach (var item in noiseGenerator.noise)


    I think what is happening is that noiseGenerator.noise is not actually populated yet. I am not sure why this is. I have my Noise Generator placed above World Generation in the Scene order, which I think means that it should populate first, no?
     
  6. orb

    orb

    Joined:
    Nov 24, 2010
    Posts:
    3,037
    You're mixing names in the first script. Pick a name and stick with it - I recommend just naming the class FastNoise, or FastNoiseSIMD if you for some reason will be making two variants.

    The order of components have nothing to do with execution order. Use Awake() methods to ensure something exists, then initialise things which depend on them in Start(). Awake() always happens before Start() in a MonoBehaviour, and both happen before the first Update().

    The different generators shouldn't really be doing anything which relies on the other components either. I suggest initialising anything special each of them need to do before they can be used in Awake() (reading the variables set in the inspector and creating any structures required). Then ensure you don't call individual generators earlier than in some script's Start() method.

    Ensure knowledge of the existence of other components only goes in one direction. One controller should know about generators it relies on, generators shouldn't know about anything but their data.

    There's also a section in the editor to set execution order of scripts, but it's most experienced people's opinion that once you have to go there you probably designed something the wrong way :)

    If that doesn't help, I might need to see the entirety of the program and any screenshots of the inspector to show how it's all pieced together :)
     
  7. LankyJon

    LankyJon

    Joined:
    Mar 18, 2018
    Posts:
    13
    I assume by the mixing of the names you're referring to FastNoiseSIMD and FastNoiseSIMDUnity. This is due to the asset I got from the asset store. FastNoiseSIMD is where the actual code is, whereas FastNoiseSIMDUnity is a wrapper the author made in order to make it usable as a component. Unfortunately, the actual functions themselves exist within FastNoiseSIMD, and I cannot call them from FastNoiseSIMDUnity. It's actually been a pain in the ass to figure out how I am supposed to properly use this script, and so I've created a workaround.

    On awake, FastNoiseSIMDUnity saves it's settings to a FastNoiseSIMD variable called fastNoiseSIMD. I have added a method which will return fastNoiseSIMD, and then have to call that method to assign the FastNoiseSIMD objects that I can actually use. It's a pain, and I wish I could understand how I was actually supposed to use this asset.

    As for why I'm getting the NullReferenceException, I figured out one issue I had, which was that I forgot to attach the script to the object. Oops. Aaaand I just realised that I had my noise generator script attached to a noise generator object, and not to the world generation object. So
    noiseGenerator = GetComponent<NoiseGenerator>();
    wasn't doing anything. I attached the noisegenerator and all fastnoise scripts to the world generation object now. I don't know if that's good practice, but it works for now... So I can actually generate maps of some sort! Thanks so much!

    The only issue I'm having now is with processing the map data, because the creator has been somewhat unhelpful with figuring out how to actually process it. Currently, my code looks like this.

    Code (CSharp):
    1.     public float[,] GenerateNoiseMap(int xSize, int ySize) {
    2.         float[,] noiseMap = new float[xSize, ySize];
    3.         float[] temp = GetNoiseSet(0, 0, 0, xSize, ySize, 1, 1.0f);
    4.         foreach (float item in temp)
    5.             Debug.Log(item);
    6.         for (int x = 0; x < xSize; x++) {
    7.             for (int y = 0; y < ySize; y++) {
    8.                 noiseMap[x, y] = temp[((y * xSize) + x)];
    9.             }
    10.         }
    11.         return noiseMap;
    12.     }
    13.  
    14.     public float[] GetNoiseSet(int xStart, int yStart, int zStart, int xSize, int ySize, int zSize, float scaleModifier = 1.0f)
    15.     {
    16.         float[] noiseSet = GetEmptyNoiseSet(xSize, ySize, zSize);
    17.         NativeFillNoiseSet(nativePointer, noiseSet, xStart, yStart, zStart, xSize, ySize, zSize, scaleModifier);
    18.         return noiseSet;
    19.     }
    GetNoiseSet is the code that came with the asset, GenerateNoiseMap is the code I wrote to try and make sense of it.

    I have messed around a bit with the order of the variables, seeing if they are stored as x's and then y's, or vice versa. This doesn't seem to be the case. In fact, I have no idea what the case seems to be. Sigh.

    Also, this code as it currently stand seems to be returning twice the size of the float array I want, so it's probably going through two z-values. For example, a 4x4 grid is returning 32 values, not 16. But if I set zSize to 0, the program freezes, and if I set zStart to 1, I still get 32 values.
     
    Last edited: Mar 24, 2018
  8. orb

    orb

    Joined:
    Nov 24, 2010
    Posts:
    3,037
    OK, 3rd party assets can be a pain sometimes, especially if the functions are only minimally documented and the source is all over the place. Many assets seem to have a limited lifetime too :/

    I'm waiting for a plugin for Substance Designer in the Unity beta personally, as I've found substances can be a great source of noise generators. More visual, and possible to just generate the darn height map you often want straight from the material. Plus you can make amazing materials too.

    If you've got it making maps that's at least something. This wiki is helpful when it comes to learning more and trying to make your own procedural generators:
    http://pcg.wikidot.com
     
  9. LankyJon

    LankyJon

    Joined:
    Mar 18, 2018
    Posts:
    13
    Thanks for all the help on everything! Messing around with it some, I seem to have gotten it sorted out, and now it's making the maps I want more or less. Current issue is that it's just so damn sloooooow. This, I think, is not an issue with the noise generator, but some other part of the program. Perhaps instantiating the tiles.
     
  10. orb

    orb

    Joined:
    Nov 24, 2010
    Posts:
    3,037
    Yep, time to dig into the profiler :)