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

Having trouble not using circular reference

Discussion in 'Scripting' started by Sendatsu_Yoshimitsu, Oct 5, 2016.

  1. Sendatsu_Yoshimitsu

    Sendatsu_Yoshimitsu

    Joined:
    May 19, 2014
    Posts:
    691
    I'm working on a vehicle-based game like FTL, where each vehicle is represented by some class Ship, and each Ship contains n Ship_Components (engines, cannons, medical bays). My initial plan was to put all of the functionality in ship_components, and to make the ship itself a dumb container that only tracked its contents (components and crew members), and its maneuvering details (position in the world, speed/heading).

    However, I'm running into some trouble figuring out how to structure the hierarchy- a lot of ship's components have functionality which requires them to be aware of the ship ('heal all crewmembers within x squares of me'/'extinguish all fires in my compartment'), and many need to be aware of each other ('fire a cannonball at the Sensor component's target'). This points to a circular and really poorly scoped relationship, which tells me that there's probably something fundamentally wrong with how I'm structuring this.

    However, I don't really see an obvious flaw: putting functionality into components instead of the core ship class feels like good OOP, but I don't think there's any way for said components to work unless they can see and interact with the ship's layout and each other. Am I missing something obvious, or is my use case worth putting up with the tight coupling of ship to ship_component?
     
  2. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,840
    Most likely the tight coupling is not a problem here. The ship and the ship components form a system; while it would be possible to decouple them, it's almost certainly not worth it.

    Instead, I'd look at it in terms of interfaces. The ship has an interface that provides for ship stuff: finding/extinguishing fires in a given compartment, finding neighboring compartments, finding components within a compartment, etc. And then the components — all of them — should have a single interface; ShipComponent or some such. I'm talking about an actual C# interface here, that defines anything components should be able to do (I dunno, power up, power down, activate, whatever).

    Now you can define a whole bunch of specific components, but the Ship doesn't really know about those specifics — it just keeps a list (or other collection) of ShipComponents. This frees you to add/remove/modify ship component classes with a fair amount of impunity, as the Ship class can't know anything about it except what's in the ShipComponent interface.
     
    Dave-Carlile and Kiwasi like this.
  3. absolute_disgrace

    absolute_disgrace

    Joined:
    Aug 28, 2016
    Posts:
    253
    The answer might depend on how much variability and types of effects your components will be performing. One option could be to have each component store a bool for each status effect/benefit that is possible, a position, and a radius of effect. When you create the object prefabs you simply give it the stats you want the component to have. The game can then get those stats and work out how it applies those stats into the game world.

    The downside of this is that if you have 50 components each with a unique effect then you won't see as much benefit. But if you have 50 components that are a combination of 5 unique effects, then you will have a huge benefit.
     
  4. Sendatsu_Yoshimitsu

    Sendatsu_Yoshimitsu

    Joined:
    May 19, 2014
    Posts:
    691
    So this is more basic programming than it is unity-specific scripting, but help me understand something: I kinda understand interfaces, but it seems to me that the same behavior you're describing could also be accomplished with inheritance, where the base component class has all the functions the ship would ever access (ToggleOn/Off, Activate, Explode, etc), and to implement actual behavior you would make a child class for each broad category of component (engine/weapon/sextant/whatever). Is there a particular reason to favor doing this with interfaces over inheritance?
     
  5. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Two reasons
    • In C# you can only inherit from a single class. So you couldn't use one script for two purposes.
    • Inheritance forces you to share implementation. Interfaces only force you to share the interface.
     
    JoeStrout likes this.
  6. Kalladystine

    Kalladystine

    Joined:
    Jan 12, 2015
    Posts:
    227
    I wouldn't treat it as an either/or question - why not use both?

    An abstract ShipComponentBase class that would have a virtual (overridable) implementation of common functionalities/properties (Toggle, Activate, Explode, Destroy, "Health" etc.). It then serves as both the common interface for other objects to rely on (f.e. a "You've been robbed" event can operate directly on this representation, removing random component), but also as the default behaviour implementation, to eliminate code duplication.

    Concrete ShipComponents would inherit from ShipComponentBase, and implement needed Interfaces like IWeapon, IExtinguisher etc. Since methods in Base are virtual, if a component need to Explode differently (f.e. damaging everything close), it can override the Explode. But if it doesn't, it will still happily use the base implementation.
     
    JoeStrout likes this.
  7. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Totally sensible.

    My post was only answering half the question: "Why would you use an interface over abstract inheritance?". I didn't address the other half of the question "Why would you use abstract inheritance over an interface?".

    Just about every programming tool has a time and a place. Except maybe UnityScript. :p
     
    Dave-Carlile and LiterallyJeff like this.
  8. Sendatsu_Yoshimitsu

    Sendatsu_Yoshimitsu

    Joined:
    May 19, 2014
    Posts:
    691
    Ooh okay, that makes a lot more sense... thank you for patiently parsing it out! Interfaces vs. Inheritance is one of those things that I know makes sense in practice, but reading about it in the abstract always tends to make my brain hurt. :)
     
  9. ruudvangaal

    ruudvangaal

    Joined:
    May 15, 2017
    Posts:
    27
    I know it's an old thread but I see this all the time in all kinds of programming. Hierarchies tend to be bad for gameplay code. Aggregation (so putting ship components in a ship container class) is much more to the point and less maintenance/debugging headache. A one-level derived class covers 99% of the things you probably want to do.

    Realize that you're thinking in multiple levels of abstraction; a cannon, a collection of cannons, a ship containing a collection of cannons. These are your exact classes. You can create a common 'collection' top class from which other collections are derived, so you can ask things to collections (like 'give me all objects within a radius/position').

    A real-life cannon doesn't known and doesn't need to know about any other cannons around itself. Only something which looks at multiple cannons needs to. It's a level of abstraction, so a different class. That class DOES know about cannon relationships.

    The ship then stays a collection of components, mostly collections of lower-level components.