Search Unity

Writing Better Code with Programmatically Created Objects

Discussion in 'Scripting' started by unity_8revgage, Sep 27, 2021.

  1. unity_8revgage

    unity_8revgage

    Joined:
    Sep 27, 2021
    Posts:
    2
    Hi,

    I come from a background of C++ and C# in the R&D sector, so writing "Good" code has always been second to writing functional code. I'm trying to make a tactical RPG in 3D, and I'm not sure how to create a better implementation of my grid based map.

    Currently I have a custom class called 3DGrid that holds a two-dimensional array of Tile objects (another custom class). When loading a 3D grid, I then iterate over the array and run each tile's setup function, which creates a primitive cube and then applies all the necessary changes (Material, location, height, etc...). My tile class also holds a reference to whatever unit may be standing there.

    This all feels very wrong, as it means the Unit, 3DGrid, and Tile classes are all intrinsically coupled. It also means that none of these game objects exist in the scene until they are generated by my scripts, which feels like it defeats the purpose of using unity.

    If anyone has any suggestions on how to improve this specific design, or any resources you've found particularly helpful for writing better code specifically for working with unity I would really appreciate it.

    Thank you!
     
  2. exiguous

    exiguous

    Joined:
    Nov 21, 2010
    Posts:
    1,749
    The Editor is powerfull but when you don't need it you don't have to use it. How is your world "Designed"? By hand or procedurally? The former would benefit from the editor the latter not so much.
    Note you could also execute your scripts in editmode to see the map in the editor.

    The process you describe is very inefficient. It depends on how large your wolrd/levels will be and how much of it you can see at a given time but I suggest some optimizations. The grid you store should only be a data lookup with no visual representation. When the camera moves or scrolles you calculate which region of the map it can see and create the map of it "on the fly". You could even put all this in a single mesh so create the quads and the tile (grass, desert, swamp) are applied by the UV's of the quads. CatlikeCoding has a good tutorial on this mesh generation for a hex map.
    Additionally you could split the world into "chunks" (for example a 32x32 grid) and create a mesh for each of those separately. This could be done in another thread preemptively to avoid hickups. And you could keep some of them still invisible around the player/camera so they are ready when needed.
    If you insist on using cubes I hope you are pooling them.

    "Better" is subjective to some extend. Some people "advertise" dependency injection but I've read an article with quite some cons. And personally I just found it makes the code uncessarily hard to read. So my suggestion is to read alot (forums, blogs) so you get an impression of what other people think and can weigh the pros and cons for yourself. I always smile when (new) people in the forum are asking for "the best way" of doing something. Because there is no "best" in development. Optimized code is often harder to read. And you best method may let your teammate rip his hair out. Here are two quotes I found matching this topic.
    But it's hard to advise on so little information. So you should give more details for a "narrow" advise.
     
  3. SunnyValleyStudio

    SunnyValleyStudio

    Joined:
    Mar 24, 2017
    Posts:
    67
    Hey!

    I think that you might be interested in methods such as https://docs.unity3d.com/ScriptReference/Object.FindObjectsOfType.html.

    I usually try to apply Model-View-Controller architecture to my games. Your game object would have something like Tile.cs script that would know its position (through Transform component) but otherwise it would only know how to do things related to how it is rendered.

    Next you would have a Grid object that would store the references to all the Tile classes in an array or a dictionary - using the FindObjetsOfType<>() method - so that Tile class has no idea that there is a Grid (Data) object.
    Lastly you would have classes that would control the flow of the game, asking the Grid (Data) for the references to the tiles and making the Tiles do some actions (get highlighted) etc.

    As for the Units you could also save the reference to it in the Grid (Data) class.

    Maybe you will find my hex grid movement tutorial useful


    I hope it helps!
     
  4. Lekret

    Lekret

    Joined:
    Sep 10, 2020
    Posts:
    356
    In your case I don't see the reason to add Renderer with Material at runtime, there is no reason for that.
    You can create Prefab for all kinds of tile, Ground, Water, Mountain etc. then you instantiate required tile and apply differing parameters such as height.
    It's completely ok to have an empty scene which is generated at runtime.
    But it's usually not ok to use gameObject.AddComponent, or create primitives if it's not debugging.
    Nobody really do that, it's slow and to change something you need to change your code which isn't convenient.

    Regarding code design, coupling is real problem with object-oriented paradigm. OO is simply not a good fit for game development.

    Only thing I can recommend is considering using ECS approach. It's architectural pattern (not OO pattern) made specifically for game development, which works faster than OO, and will make your code much more flexible and simpler. Still supported and powerful, yet simple ECS library: https://github.com/Leopotam/ecs

    In world of ECS you would have GenerateMapSystem which would create Entity with Tile component and Position component.
    Then there would be GenerateEnemiesSystem which would take entities with Tile component, then randomly add EnemyOnTile component to some of them.
    Third SpawnEnemiesSystem would took entities with Tile, Position and EnemyOnTile components and spawn real prefabs.
    In that case all three systems would care about very specific components and communicate via data, which will result in low coupling, flexibility and extensebility.
     
  5. unity_8revgage

    unity_8revgage

    Joined:
    Sep 27, 2021
    Posts:
    2
    Thanks to everyone who gave an answer to help!

    This is a great thing for me to keep in mind, though my maps are relatively small, maybe 50x50 at the largest, so I don't know how much optimizations like that will help my performance. Thanks you for taking the time to write out an answer for me exiguous.

    Tekrel, I'll be spending some time familiarizing myself with the various pros and cons of ECS over OOP, this was a good direction for me to look into, regardless of it's application to my specific project.

    SunnyValleyStudio, Your tutorials were amazing, and super helpful. The unity essentials pathway doesn't touch on the ability to make prefabs, which is what I was missing in my mental list of tools. I'm excited to watch more and more content come out of your channel, and I've definitely subscribed
     
    SunnyValleyStudio likes this.