Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Code Management: Best Practice?

Discussion in 'Scripting' started by Olleus, Mar 8, 2022.

  1. Olleus

    Olleus

    Joined:
    Mar 1, 2022
    Posts:
    26
    I'm a newbie at Unity, more playing around than writing games, but I do have reasonable experience in programming more generally (Python, C++, Matlab, Mathematica, ...). My issue is that there are a few things with Unity that feel odd to me, I'd like to know how people design their code architecture around that, and if my gut solution is any good.

    Unity seems very much visual orientated. By that I mean that everything created via the interface is a GameObject, and they can all (even if they don't have to be) rendered on screen. This seems weird to me, as I prefer to separate game logic (the underlying rules of the game) as much as possible from the visual representation of the game. The latter needs to be able to read-from the former, but not the other way around.

    For example, if I'm making a game where the player is a ship that moves around on a tilemap, I want to have a Ship class in c# that keeps track of orientation, position, hull hit points, sail setting, etc... etc... without being a game object. Then, I'd have a separate GameObject that takes an instance of this Ship class as a constructor argument. This deals with actually drawing the ship on the map depending on the position and state of the underlying object. That way, I can write and test the code the sailing mechanics completely independently of graphics, rendering and animations.


    Is this at all a reasonable way of thinking? Or am I too locked in an OOP mode of thinking than a Unity Developer should be? If I'm posting things in the wrong place or using Unity terminology incorrectly, or these questions have already been answered before, don't be shy about correcting me.
     
  2. Olleus

    Olleus

    Joined:
    Mar 1, 2022
    Posts:
    26
    [For some reason, I couldn't include this paragraph in the post above without getting a "Permission Denied" error??]

    A secondary aspect is that I don't seem able to be able to pass arguments into GameObject constructors. Or with the Start method that seems to act as an ad-hoc constructor. My work around for this is to create a script (not linked to any GameObject) with a static class called GameState or similar full of attributes about the underlying game logic such that the GameObjects can access them easily. This feels kind of unsafe to me (could lead to bugs that propagate far and wide), but I can't think of any other way of doing it.
     
  3. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    Yeah, it'll take some time to adjust to the "Unity way" of doing things. Your first inclination will be to try to do everything in code, creating absurd systems of logic to try to make it work, until you eventually come to terms and realize it's significantly faster to work with Unity rather than against it. You can also leverage editor scripting to make your life significantly easier and streamline the Unity processes for your own game's requirements.

    Constructors can either be replaced with an Init() function you invoke manually after Instantiate, or in most cases, prefabs with preconfigured values ready to go. You don't really need to do
    new Goblin("Boss", health: 100)
    when you can do
    Instantiate<Goblin>(goblinBossPrefab)
    . It's just a different way of thinking.

    As for separating logic from visuals, that's definitely you should strive for. There's nothing stopping you from using POCOs as well. Not everything needs to be a GameObject. My game's headquarters, campaign, hangars, sectors, fleets, inventory, etc. are all just regular objects.
     
  4. _met44

    _met44

    Joined:
    Jun 1, 2013
    Posts:
    633
    Hey there, welcome here ! ^^

    All good interrogations you got there. Experience will come fast dont worry.

    Regarding separating things, if your game requires a deep simulation in which not everything is visible on screen all the time it might make sens in which case sure go ahead (and eventually check out DOTS which might be better if that simulation includes a great many entities to process).

    but more often than not, what you need most is speed of creation and be able to evaluate how your gameplay is, it will be a more impactful criteria to the succes of your projects and using gameobjects directly is a much simpler and better way to go.


    Here's the solution to this:

    1/ Wrap your prefab reference behind a ShipSpawner class (can be either MB or POCO).
    2/ Make the ship spawner require your ship POCO reference as parameter for the instantiate/spawn method.
    Code (CSharp):
    1. var shipMB = _shipSpawner.Spawn(shipPOCO);
    => This way as long as you dont instiantiate the ship prefab elsewhere, the only way from your code to spawn it is through that API thus making the ship POCO ref mandatory.

    Then all thats missing is an Init() method to pass the ship POCO ref to the ship Monobehaviour script that your spawner calls when spawning the prefab.
    Code (CSharp):
    1. public ShipMB Spawn(ShipPOCO shipRef)
    2. {
    3.     var shipMB = Instantiate(_shipMbPrefab);
    4.     shipMB.Init(shipRef);
    5.     return (shipMB);
    6. }
    Also chances are you'll use pooling at some point, so you wont want to use the constructor for initialization anyways.
     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,711
    Yeah, forum is misbehaving the past month or so... it's not you.

    I hear you... so when doing traditional programming, you usually either have a command line app (everything runs from main), or you have some kind of UI-driven messaging thing that you respond to in a main loop, firing actions off.

    Unity is closer to the latter, but FAR less structured. Basically Unity IS the app, not you... and by tradition, Unity loads the first scene in your list of scenes (Editor Build Settings), instantiates ALL the stuff in the scene, and immediately starts calling code that you have connected in there.

    It's like hiring an enormous group of people to staff your factory and they all know their parts and at 8am the lights come on and everybody just starts working. What's weird and what totally BLOWS the object oriented computer science major minds is that anybody can get at just about any other object at will and manipulate its public properties... this makes OO people twitchy, but it's just how it is, and requires you to bring the discipline to what you are doing.

    For instance, any piece of code anywhere anytime can say "Give me all the doors," and then iterate all those doors and tell them to open... that doesn't care if there is a door manager supposed to be in charge of them. Unity just lets you find everything and go nuts, and as such it is INCREDIBLY flexible and powerful, letting you trivially make massively-complicated connected stuff... but you can also spagetti yourself into a knot, so be careful.
     
  6. Olleus

    Olleus

    Joined:
    Mar 1, 2022
    Posts:
    26
    Thanks for all the advice and code examples, I'll have to think about them carefully to really understand them. Can someone help me off by telling me what POCO, DOTS and MB stand for as acronyms? As with learning anything technical new, the jargon wall is hitting me pretty hard (I won't tell you how long I spent confused between mouse position, screen position, cell position, world position until I figured it out).
     
  7. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,711
    POCO "Plain old C# Object" is a generic object that contains data; this has nothing to do with Unity.

    MB is likely MonoBehaviour, the base class for any script that interoperates with a GameObject in Unity

    DOTS is the "data oriented tech stack," about which you can read more here:

    https://forum.unity.com/forums/data-oriented-technology-stack.147/

    EDIT: also, don't forget about TLA - Three Letter Acronym. Those are everywhere. :)
     
    _met44 and Olleus like this.
  8. Olleus

    Olleus

    Joined:
    Mar 1, 2022
    Posts:
    26
    A similar question as before as a follow up. What's the best way of handling user input? Centralised or decentralised?

    Say there are two ships on the map. I can select one by left-clicking on it (at which point it becomes highlighted) and I can then tell it to move somewhere else by right-clicking on open water. I can think of a couple of ways of implementing this: either by having the code in each ShipMB class, or in a centralised Event MB class that keeps track of what is currently selected. What's the most "Unity" way of doing this that is both robust and flexible? Or are there built-in tools that already do all this for me?
     
  9. WallaceT_MFM

    WallaceT_MFM

    Joined:
    Sep 25, 2017
    Posts:
    394
    There's a built-in input system (two of them, actually but the new one requires importing a package) for listening to input. Funny enough, this is more of an OOP question than strictly a Unity one. Personally, I'd have a central input manager class and then create some interfaces like ISelectable and IMovable. Then the input manger (or even another Selection manager, depending on how complicated the input manager gets) would keep track of the current selection. When a move command comes from the user, the input manager passes the message to the current selection. The Selectable then decides what to do with the input.
     
    Olleus likes this.