Search Unity

Component oriented programming and Unity

Discussion in 'General Discussion' started by corax, Jul 31, 2013.

  1. corax

    corax

    Joined:
    Jun 4, 2012
    Posts:
    34
    Hi guys,

    Anyone knows a good course/tutorial/book on how to properly structure a game following component pattern design? I know that's complex and broad topic but I need something to get my feet wet. I've tried book like Head Frist Design pattern and some other book on good component oriented C# design but I still have a lot of worries about proper solution with Unity. I don't want to spent month to re-invent the wheel and then find out that gazillions of guys already discovered a proper way to get start you project with a good pattern design. Tips, Do's/Don'ts would be even appreciate :D thanks!
     
  2. BlackMantis

    BlackMantis

    Joined:
    Feb 7, 2010
    Posts:
    1,475
    You may have better luck in Gossip section. What did you find in the forum search or web search ?
     
  3. corax

    corax

    Joined:
    Jun 4, 2012
    Posts:
    34
    Oh thank, maybe I will notify an admin to move the 3d. On google I found quite a lot of stuff but c++ mostly. My problem are specific stuff dealing with Unity. I undestood the basic concept underneath the componet programming but I don't know how to apply them on Unity. An example:

    Let's assume I have an interface weapon and a class player. What I will do in XNA would be create the class with a construct
    Code (csharp):
    1.  
    2. public class Player
    3. {
    4.  
    5. private Weapon _w;
    6.  
    7. public player(Weapon w)
    8. {
    9. _w = w;
    10. }
    11.  
    12. and then in Main create the player
    13.  
    14. public class Main
    15. {
    16. Player p = new Player(new Weapon());
    17. }
    18. }
    19.  
    The problem is I cannot use new keyword with monobehavior. Shoudl I get rid of monobehavior inheritance? Or maybe create an Init() method? There are a lot of question like this, that I cannot solve with my present knowledge of unity whitout screwing up :p
     
    Last edited: Aug 1, 2013
  4. Jallen182

    Jallen182

    Joined:
    Aug 21, 2012
    Posts:
    97
  5. corax

    corax

    Joined:
    Jun 4, 2012
    Posts:
    34
    Thanks!
     
  6. Deleted User

    Deleted User

    Guest

    Think in terms of scenes and gameobjects with components. Look at the Angry Bots demo - there's a Player GameObject with one or more script attached that provide the Player behavior. The scripts are components so they have no useful existence except when attached to a GameObject. All that is added to the scene manually, but if you needed to create that GameObject and its attached components at runtime, you'd make it a prefab and call Instantiate on it.
     
  7. corax

    corax

    Joined:
    Jun 4, 2012
    Posts:
    34
    Thanks I will try that :D
     
  8. SpaceMammoth

    SpaceMammoth

    Joined:
    Jan 2, 2013
    Posts:
    220
    I struggled with this aswell, coming from XNA and similar. I had a massive object hierarchy that represented all aspects of my game - player, ships, bullets, planets, explosions. None of that worked very well with Unity. The links posted above are the ones I found and helped me considerably. I ended up with a hybrid approach where all of my game logic uses pure c# object hierarchy, which calls into a Unity layer for visualisation and user interaction, which has worked well for me, but I'm not sure its the right approach if you are starting from scratch.
     
  9. corax

    corax

    Joined:
    Jun 4, 2012
    Posts:
    34
    Uhm I guess I have to evaluate the right "way" on each project. Maybe I can use singleton for managers and then create only interface I'm sure I will reuse. I can solve the absence of a construct in monobehavior with an init() method called after the the actual creation of the prefab (instantiate).
     
  10. Deleted User

    Deleted User

    Guest

    Use the MonoBehaviour Awake or Start callbacks for initialization. In the Scripting Reference, look at the MonoBehaviour.Start doc (the Awake doc is somewhat outdated)
     
  11. cdevl

    cdevl

    Joined:
    Apr 10, 2013
    Posts:
    180
  12. corax

    corax

    Joined:
    Jun 4, 2012
    Posts:
    34
    After a lot of reading and a lot of tought I ended up structuring the code this way:

    Singleton managers: game manager, enemy wave manager.

    Then I have an abstract class Ship deriving from Monobehavior. This will be parent class for the player Ship and enemy Ships. This class sets only the skeleton of the ship:

    Code (csharp):
    1.  
    2. public abstract class Ship : MonoBehavior
    3. {
    4. protected Weapon w;
    5. protected Motor m;
    6.  
    7. //Set/get methods omitted
    8.  
    9. void Move()
    10. {
    11. m.move;
    12. };
    13. void UseWeapon()
    14. {
    15. w.UseWeapon();
    16. }
    17.  
    18.  
    19. }
    20.  
    21. //Then I create two interface Iweapon and Imotor:
    22.  
    23. public interface Iweapon
    24. {
    25. void Useweapon();
    26. }
    27.  
    28. public interface Imotor
    29. {
    30. void Move();
    31. }
    32.  
    33. //Then create our first weapon
    34.  
    35. public class MachineGun: Monobehavior , Weapon
    36. {
    37.  
    38. maxAmmo;
    39. current ammo;
    40.  
    41. void Start()
    42. {
    43. Init();
    44. }
    45.  
    46. void Init()
    47. {
    48. maxammo = 10;
    49. currentammo = 10;
    50. }
    51.  
    52. public void UseWeapon()
    53. {
    54. if(currentammo < 0)
    55. Shoot();
    56. }
    57. Shoot(){ Debug.Log("Bang Bang");}
    58. }
    59.  
    60. //Now our ship class will looks like
    61.  
    62. public class PlayerShip : Ship
    63. {
    64. void Start()
    65. {
    66. Init();
    67. }
    68.  
    69. Init()
    70. {
    71.  w = gameobject.AddComponent<MachineGun>();
    72. //add motor omitted
    73. }
    74. }
    75.  
    76.  
    Now my dilemma is: should I use Monobehavior for motor and weapon? Because a neat solution could be add an Update in the interfeces and let the player class to update them every frame:

    Code (csharp):
    1.  
    2.  
    3. public interface Iweapon
    4. {
    5. void Update();
    6. void Useweapon();
    7. }
    8.  
    9.  
    10. public class PlayerShip : Ship
    11. {
    12. void Start()
    13. {
    14. Init();
    15. }
    16.  
    17. void Update()
    18. {
    19. w.Update();
    20. m.Update();
    21. }
    22.  
    23. Init()
    24. {
    25.  w = gameobject.AddComponent<MachineGun>();
    26. //add motor omitted
    27. }
    28. }
    29.  
    This last solution can be tight but doesn't let me to tweak the value in the inspector nor can I create new prefabs dragging the components on the game objects .Any advice? I think that solved this I have to move towards a neat way to handle state without screw up this design patterns. I've seen this article but doesn't look like a good solution to me :|
     
    Last edited: Aug 1, 2013
  13. trooper

    trooper

    Joined:
    Aug 21, 2009
    Posts:
    748
    I'm constantly thinking about how this should work and this is my short summary of how to decide what a class should be, if you answer yes too all the questions, it should inherit from monobehaviour.

    1) Does the class require Update, FixedUpdate or LateUpdate?
    2) Is the update the main part of this piece of code / game mechanic?

    Basically, I have a vehicle class which inherit's from MonoBehaviour and it has static Create functions that can spawn itself with different models, stats etc.

    The separate parts of the vehicle are not MonoBehaviours, such as the wheels, drivetrain, motor etc, they are just normal classes that have certain update functions called from the Vehicle class.

    The vehicle class handles input, visual updating (such as moving the wheels) but receive's data from it's other classes when they update.

    PROS:
    Keeps the gameobjects simple so that there is usually only 1 script added to an entity
    Tiny tiny tiny performance boost as there aren't multiple updates being polled

    CONS:
    Profiling is a bit harder (solved with adding profile tags)
    Not really fit for instantiating as a prefab (we don't instantiate prefabs with scripts, prefabs are only used for graphics, everything is spawn via a .Create static function within the parent class such as Vehicle, Level, Checkpoint etc).
     
  14. corax

    corax

    Joined:
    Jun 4, 2012
    Posts:
    34
    Man Thanks for the advice, I think that step by step I'm undestanding how to reshape my whole thinking process. How do you handle graphics spawn? I mean If I have a dirty wheels class and a racetrack wheel class, how do you spawn the right mesh and place it under the car?
     
  15. BlackMantis

    BlackMantis

    Joined:
    Feb 7, 2010
    Posts:
    1,475
    Destroy - Instantiate - make child of car - localPositon / localScale if needed. Tutorials are the best way to get up to speed on the basics. If you have any scripts your working on, post them here with errors you may have.

    The Gossip sector is better suited for general discussion about game design. ;)
     
  16. corax

    corax

    Joined:
    Jun 4, 2012
    Posts:
    34
    Yep I know this stuff xD I meant did you spawn graphic from the wheel class or from the vehicle class? Anyway I must ask moderator to move this discussion in gossip section. Thanks!
     
  17. Aurore

    Aurore

    Director of Real-Time Learning

    Joined:
    Aug 1, 2012
    Posts:
    3,106
    Hey corax, please use the
    Code (csharp):
    1.  tags when writing code :)
     
  18. tonyd

    tonyd

    Joined:
    Jun 2, 2009
    Posts:
    1,224
    Yes, get rid of the monobehaviour inheritance. You don't need it unless your script gets attached to an object, or unless you need to call Monobehaviour functions.

    Then in your main script, create an array of weapons and populate that in the inspector.
     
  19. corax

    corax

    Joined:
    Jun 4, 2012
    Posts:
    34
    At the end of the day I kept the monobehavior inheritance because I want to build from editor the prefabs with the specific components. Now I'm stuck with the interfaces design. Slowly, step by step, I think I'm grasping the overall logic of designing components. They must be generic and use interface as bluprints to access and communicate (I guess?) So

    If we wants that something is a Motor component we design an interface like this:

    Code (csharp):
    1.  
    2. public interface iMotor
    3. {
    4.     void Move(Vector3 Position);
    5.     void Rotate(Vector3 Rotation);
    6. }
    7.  
    This means that any other componet that want to access a iMotor component must use that methods to "communicate". It would be easier to have speficic references but in this way we can be sure that any iMotor component will work with other dependent componets.

    A carMotor that implements iMotor works in a different way than a aircraftMotor but both of them will implement Move and Rotate. This means that as long as a wheel and a helm use Rotate to steer, we will not have any problem.

    In theory, this seems pretty much ok but:

    First I'm not sure I'm using in the right way the interfaces. If I define a couple of methods in an interface, the methods must be used by all the classes that implements that particular interface? I know that I could leave blank the method but I guess that this will broke the "contract" defined by the interface.

    Second passing messages and values between componet is giving me very hard time. I'm trying to handle this thing creating a food "flow" and keeping the component as indipented as they could be. But still sometime you need to pass values. I was thinking of creating a component designed only to "wire up" components: something that can take a value or an event from component A and feed component B. But still Is this a good solution to handle things like physics or reactive player controls?
     
  20. JohnnyA

    JohnnyA

    Joined:
    Apr 9, 2010
    Posts:
    5,041
    Just a note that the hierarchy approach you have decided upon is somewhat different to the component based approach your title suggest. Not that I think there is anything wrong with your approach but I just want to point out that its a somewhat hybrid approach.

    In a component based system you tend to avoid high level entities like Ship and even more so avoid specific entities like PlayerShip. You don't need to know that something is a ship. If it is a Shooter it can shoot, if it is a Flyer it can fly, if it is Damageable it can take damage. The benefit of this is the same code can control a shooter regardless of if that shooter is a ship or a space station, hell you can even shoot a banana as long as it has a Shooter component.

    Personally I'm of the opinion that there are many good ways to model any problem; although some approaches may be more suited to some problems, in general any well structured approach will enable you to solve your problem.

    That said the introduction of a component to pass values between other components sounds like you are starting to over-architect your problem. If anything that component may be making your system more tightly coupled rather than less.
     
  21. corax

    corax

    Joined:
    Jun 4, 2012
    Posts:
    34
    Yep totally right, but for now the ship class is just a convenience that I will delete later. As you can see my Player ship class just attach a couple of component because I'm lazy :p

    Code (csharp):
    1.  
    2.  
    3. iCamera cameraCockpit;
    4.     iController controller;
    5.     Rigidbody rigidbody;
    6.    
    7.    
    8.     // Use this for initialization
    9.     void Start ()
    10.     {
    11.         Init();
    12.     }
    13.    
    14.    
    15.     void Init()
    16.     {
    17.         weapon = Helper.GetGenericComponent<MachineGun>(this.gameObject);
    18.         cameraCockpit = Helper.GetGenericComponent<CameraCockpit>(this.gameObject);
    19.         controller = Helper.GetGenericComponent<PlayerController>(this.gameObject);
    20.        
    21.     }
    22.  
    23.  
    My real problem here is that If I have a Motor component and a Lever (actual 3D mesh with a Leverscript) component on my ship how do I increase the speed of the ship without passing a value? There must be something that connects the lever with the player controls and moreover with the motor component to speed up the ship. Am I right? Maybe a step could be avoided adding to the Lever componet the ability to know when is interact with and fire off to the motor the right amount of throttle... (just a silly example to understand better what I'm talking about :D)
     
    Last edited: Aug 2, 2013
  22. JohnnyA

    JohnnyA

    Joined:
    Apr 9, 2010
    Posts:
    5,041
    There's nothing that says you can't pass values between components (in fact you would typically expect that components have well defined interfaces that tell other components how to interact with them ... note I mean interfaces in the abstract... you may or may not choose to implement them as interfaces in c#).

    Lots of ways to model your scenario, here's one that is component based:

    Code (csharp):
    1.  
    2. Motor: MonoBehaviour {
    3.   IOneAxisControl throttle;
    4. }
    5.  
    6. Lever: MonoBehaviour, IOneAxisControl {
    7. ...
    8. }
    9.  
    10. interface IOneAxisControl {
    11.   float Value {get;}
    12. }
    13.  
    This allows you to have different controls for your lever and also for your lever to control different things.

    I'm sure someone could argue that its a bad thing to check the value of the lever each update, but it actually simplifies your motor code as you don't need events or functions for throttle up/down, you just set your thrust (or whatever) each frame based on the value, its also probably closer to how the motor would likely work in real life (immaterial but comforting nonetheless).

    EDIT: A little off track but in case someone worries about the performance of doing something every frame ... in many if not most cases its better to have a consistent but small drain on CPU then an occasional large one. Of course checking a single property is such a basic operation we can almost certainly discount it.

    EDIT: If your lever is an on/off type lever you can make it a bool and call the interface ITwoPhaseControl or some such.
     
    Last edited: Aug 2, 2013
  23. corax

    corax

    Joined:
    Jun 4, 2012
    Posts:
    34
    Yep, man, I was thinking at a scenario like that, but I have no experince with COP programming and I cannot forseen where the code will end up. Is there a risk that at some point you will have tons of interfaces with only or few specific uses? Anyway I will use another day to go deeper in this stuff and eventually post an update. . .
     
  24. Gigiwoo

    Gigiwoo

    Joined:
    Mar 16, 2011
    Posts:
    2,981
    Here's 3 stages of engineering I've witnessed:

    1) The novice: knows too little to appreciate architecture. With just the basics in an area, much of their time is spent learning and growing. They are constantly experimenting with new ideas and spend a lot of time 'refactoring' stuff.

    2) The architect astronaut (by Joel Spolsky): has learned a lot, and that knowledge has morphed into 'knows-best'. Learning is replaced by a blind, dogmatic adherence to 1,000 rules. Whole categories of design are forbidden, whether it's inheritance, or singletons, or the use of Linux or Mac. Every nuance becomes a religious war.

    3) The veteran: people like Jonathan Blow, who are magically able to release masterpieces, with small teams. Having grown beyond the zealous nature of the Astronaut, they realize that simplicity is often the best architecture. Dogma is replaced by a dogged focus on the end-goal - working software. The veteran knows that massive refactoring is often a waste of their most precious resource: an engineer's time.

    Spend your time in #1. Check yourself when you stray into #2, and strive to achieve #3 by building simple architectures that though imperfect, are more likely to result in finished software that is used by real customers.

    Gigi.
     
    Last edited: Aug 2, 2013
  25. corax

    corax

    Joined:
    Jun 4, 2012
    Posts:
    34
    Man I've smiled all the time, reading your post :D I must say that I'm at stage 1 and I will spend a very long time there. I'm a freelance game designer but I've learned to love code and system design is something that really intrigue me. Maybe I'm at stage 1 when comes to code and stage 2 game design-wise. I tend to think about game like interesting device with a lot of rules, maybe it's time to move to stage 3 :D the real problem is that my day job is design for hire and in my spare time I design for pleasure. Most of time, in my spare time, I create game like a sculpture, throwing clay and keeping what it sticks. A solid component structure gave me the idea that I could get infinite felixibility and maybe add/delete/modify features whitout breaking the game. That's why, I'm seeking perfection :D but maybe I'm asking too much to myself and to COP paradigm.

    P.s. I want to apologize to the readers for the big Off topic. Sorry guys got carried away :D
     
  26. Gigiwoo

    Gigiwoo

    Joined:
    Mar 16, 2011
    Posts:
    2,981
    The bolded text highlights your transition between Phase 1 to 2. You've learned enough to believe infinite flexibility is worth seeking, and not enough to realize it's a fool's errand. My mistakes taught me that flexibility is easiest to achieve through simple, functioning code. Get it working fast, with minimal architecture, and refactor it ONLY when necesary. Though, to be honest, I'd heard Albert Einstein express similar ideas, before I'd written a single line of code.

    A smart man learns from his mistakes; a wise man learns from others.

    Gigi.
     
  27. corax

    corax

    Joined:
    Jun 4, 2012
    Posts:
    34
    Thank you guys :D I will try to wrap my mind around the massive amount of stuff you throw me and maybe start to be "humble" in thinking :D
     
  28. vexe

    vexe

    Joined:
    May 18, 2013
    Posts:
    644
    Hey corax, I'm also trying to wrap my head around component-oriented design, your thread helped me. You mentioned game controllers and singletons? check this out :)
     
  29. Lypheus

    Lypheus

    Joined:
    Apr 16, 2010
    Posts:
    664
    Yep, same experience here - it was probably the toughest part about coming over to development over a game engine for me.