Search Unity

[RELEASED] IoC+, Inversion of Control for Unity

Discussion in 'Assets and Asset Store' started by softlion, Oct 2, 2016.

  1. softlion

    softlion

    Joined:
    Sep 6, 2013
    Posts:
    100
    ioc_logo_forum.jpg
    From the creator of Code Control,

    IoC+ is designed to be the most complete C# Inversion of Control framework for Unity. Coming with nested contexts, context as state machines and an interactive live feed of all contexts in game; IoC+ takes IoC to a whole 'nother level!


    IoC+, Inversion of Control for Unity

    Inversion of Control is a design principle that can be used as a foundation for an entire game or application. It focuses on keeping classes decoupled so they can easily be managed, extended and unit tested.

    Code Monitoring
    Coming with the insightful IoC+ Monitor, a Unity Editor extension, providing a live feed of what's happening in game. This includes context hierarchies, bindings, visualized command executions and signal dispatches. All interactive to open associated code files, making it much easier for new programmers to hop in on your project and understand the code.

    Highly Unit Testable
    Unit tests are small tests written in code that validates whether a component is still functioning as it should. IoC+ enforces your code to be loosely coupled so that components can be tested without dependencies.

    Nested Contexts
    IoC+ is indeed a big addition to the standard IoC pattern. Contexts can be nested to have full control over isolated injections. It's easy to add, remove and switch contexts to change behavior. IoC+ even integrates the Finite State Machine pattern for contexts, a big benefit for games.

    Includes Full Source Code
    Just like the creator's MVC framework, this package contains the full beautiful source code of IoC+ including its unit tests, allowing you to instantly tweak it when necessary!

    IoC+ Monitor Demo Video


    Additional Links:
    Product Website: http://iocplus.com/
    Tutorials: http://iocplus.com/tutorials/
    Documentation: http://iocplus.com/documentation/
     
    Last edited: Dec 21, 2016
  2. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    Hey. I just found this. And you are the guy who developed Code Control which I already have. That is a great asset. And if the quality and usefulness of Code Control is any indication of what IoC+ is like then I got to get this too!
     
  3. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    Is IoC+ based on any of the other IoC Assets. e.g. Zenject?
     
  4. softlion

    softlion

    Joined:
    Sep 6, 2013
    Posts:
    100
    We meet again, TokyoDan, hello! Thank you for the nice response :D

    As said in the introduction page on the official website, IoC+ takes inspiration from both the Code Control monitor workflow as the StrangeIoC inversion of control framework. The usage of IoC+ however is quite different, as it enforces context hierarchies and state machines. It really is optimized for both apps as games.

    I hope you'll like the asset! It has just now been accepted by the admins of the store. Please let me know what you think!
     
  5. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    Hello softlion. Great. I mean does it use or include any other DI / IoC framework? I know that some of the other DI / IoC assets in the Asset Store include a well known framework like ninject...so you have to study two frameworks to really understand it: the one you get from the asset store and the one upon which it is based, usually from github.
     
  6. softlion

    softlion

    Joined:
    Sep 6, 2013
    Posts:
    100
    No it does not! IoC+ is completely standalone and the tutorial section of the official website contains all information to get started.
     
  7. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    I just bought it. I wonder if I am the first.
     
  8. softlion

    softlion

    Joined:
    Sep 6, 2013
    Posts:
    100
    Thank you for the purchase!

    Yes you are, after two days being released :D Please leave a review when you have build up an opinion!
     
  9. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    Will do, and I'm sure it will be a good review.
    Was IoC+ part of your plan when you made Code Control and did you start working on it as soon as you released Code Control?

    You know, I've tried all the IoC assets in the Asset Store, about 5 or 6 of them. I thought StrangeIoC was too complicated and convoluted. I ended up with Zenject as that was the only one I could halfway figure out and it has a big user base and it is easy to get support. But the documentation, tutorials, and examples are not user friendly at all. So I was not completely happy with it. I was on the lookout for something better. I bought IoC+ because it seems to be the most complete and most Unity-like and the documentation seems good with a good tutorial.
     
  10. softlion

    softlion

    Joined:
    Sep 6, 2013
    Posts:
    100
    I've started developing IoC+ roughly 6 months ago.. I've worked on some other personal projects in between Code Control and IoC+, IoC+ was not on my mind during the development of Code Control.

    I do believe IoC+ is more scale-able for larger projects than Code Control, as signals can be isolated in contexts easily, whereas in Code Control all messages are global. Just as with Code Control, I've tried to make everything as easy to understand as possibe. I've thoroughly tested the tutorials with co-workers, they should now explain all that is needed to get started.

    Please let me know if you get stuck!
     
    Last edited: Dec 14, 2016
  11. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    Hi. I'm not stuck. I'm just finished "Injecting a Custom Signal" on page 18 in the Documentation tutorial. I got a question about Signals. So far I came across two Signals. The first defined in class PlayerInputView
    public readonly Signal JumpInputSignal = new Signal();

    and the 2nd which is it's own class in it's own source file:
    public Class JumpInputSignal : Signal {}

    I guess the first is a standard basic IoCPlus Signal, and the second (JumpInputSignal) is a subclass of Signal... When and where is JumpInputSignal instantiated?


    NOTE: Just a little thing but...In PlayerInputView.cs, shouldn't the field name 'JumpInputSignal' start with a lower class 'j' to keep it from getting confused with the class JumpInputSignal?
    (This would have to be changed in Add/RemoveListener in PlayerInputMediator.cs also)
     
    Last edited: Dec 19, 2016
  12. softlion

    softlion

    Joined:
    Sep 6, 2013
    Posts:
    100
    Hi TokyoDan!

    Correct, the first Signal is the standard basic signal. The class "JumpInputSignal" subclasses that signal.

    There are indeed two instances that signal the jump input.

    View Signal (standard basic IoCPlus Signal)
    The view's signal is instantiated and dispatched in the View to let the Mediator know about the input to jump. The view's signal does not have to be of the JumpInputSignal type as it does not need to be injected. This line instantiates the signal in the view:

    public readonly Signal JumpInputSignal = new Signal();

    Injected Signal (JumpInputSignal)
    The mediator's signal is injected by the context and has the actual JumpInputSignal type. It needs the JumpInputSignal type to let the context know what signal is to be injected. This signal is instantiated by the context when it is bound to an injection, on this line in the GameContext:

    Bind<JumpInputSignal>();

    Calling the Bind<T>(); method in the context will instantiate a new instance of type T internally. All mediators and commands in the context that have [Inject] private T myInjection; will have their field set to the instance that was instantiated by the context. (I probably want to add these lines of text to the tutorial).

    Signal Flow
    When the mediator receives the dispatch of the view's basic signal, it "mediates" by dispatching its injected JumpInputSignal to let the context know about the input. Take a look at the following image.



    The View Signal is the signal from view to mediator, this does not have to be of any custom type. The Injected Signal is the signal from mediator to context, which has to be of a custom type to be properly injected. The signal from command to mediator will be covered in later tutorials.

    My personal convention states all public fields start with an upper case and all private fields start with a lower case. The compiler won't mind having a public field with the same name as the type unless there are statics or constants defined in that class, which is not the case here. You can of course follow your own conventions here!

    Please let me know if this makes sense or if I need to explain it in any other way!
     
    Last edited: Dec 19, 2016
  13. sonicviz

    sonicviz

    Joined:
    May 19, 2009
    Posts:
    1,051
    Hi,
    Looks interesting, and I think I'll try it out as I've had the same framework issues as TokyoDan.

    How well will it integrate with other assets like AI BT's such as NodeCanvas etc?

    And does it come with a full working game example as demonstrated in the screen shots?

    The big problem with all these frameworks is they show small simple bits of functionality that make it seem simple to implement, but when you actually try to make a decent app with them they quickly become unmanageable.

    Would be great if there there is a semi-complex example that really demonstrates it working in a production setting.
     
    Last edited: Dec 20, 2016
  14. softlion

    softlion

    Joined:
    Sep 6, 2013
    Posts:
    100
    Hi and welcome sonicviz!

    It depends whether the other asset is able to be controlled and listened to by separate monobehaviours. I did not work with NodeCanvas yet, but it looks like you can use methods of custom scripts as actions in the visual editor. This could very well be an IoC+ view, which will be a nice integration with IoC+.

    Yes, the full source of the working "Space Adventures" game example is available in the IoC+ package, along with all of its 130 unit tests. The source code includes services, models, views, mediators and context state machines.

    We've used IoC+ in three big projects now at the company where I work, and it has proven to really pick up speed during production. IoC+ is all about keeping code loosely coupled. Once you have properly set up the building blocks, it's really easy to switch functionality around on a high level. Further more, the IoC+ Monitor really helps with debugging, testing parts of functionality and getting everyone up to speed that joins your project in a later stage of development.

    I am unfortunately not permitted to share the code bases I've made in a professional setting, which would demonstrate what you are looking for. Although the example project might not be a full blown game, it should be large enough to give you a taste of how IoC+ can look on a bigger scale.
     
    Last edited: Dec 20, 2016
  15. sonicviz

    sonicviz

    Joined:
    May 19, 2009
    Posts:
    1,051
    kk, ty for the info
     
  16. softlion

    softlion

    Joined:
    Sep 6, 2013
    Posts:
    100
    Hey guys,

    Just to let you know, I've just uploaded a short video to demo the IoC+ Monitor:

     
  17. jk15

    jk15

    Joined:
    Jun 23, 2011
    Posts:
    49
    Hi softlion,

    I am interested in IoC+ but could not see anywhere what platforms it supports or has been tested on. Can you please advise?

    Thanks.
     
  18. softlion

    softlion

    Joined:
    Sep 6, 2013
    Posts:
    100
    Hi and welcome jk314!

    We've finished projects for Windows, Vive, iOS and Android. I'm sure it will be no problem on any other platform as the framework is written in pure C#. If Unity can do it, IoC+ should be able to as well.
     
  19. jk15

    jk15

    Joined:
    Jun 23, 2011
    Posts:
    49
    Thanks for the reply, good to know it hits all those as they are the ones I primarily cover, any idea or can you test if it will work for consoles? From what I have read, StrangeIoc and Zenject (the 2 I have experience with) don't.
     
  20. softlion

    softlion

    Joined:
    Sep 6, 2013
    Posts:
    100
    I'm afraid I am unable test on the consoles but like I said, I do believe it will work just fine. IoC+ is all build out of basic C# and a pinch of reflection magic, no plugins whatsoever!
     
    jk15 likes this.
  21. jk15

    jk15

    Joined:
    Jun 23, 2011
    Posts:
    49
    Another question, do you have any examples of using multiple scenes with cross context abilities?
     
    Last edited: Dec 23, 2016
  22. softlion

    softlion

    Joined:
    Sep 6, 2013
    Posts:
    100
    There is no example on our website, but you can set this up yourself quite easily using a service and command execution parameters. First you create the service:

    Code (CSharp):
    1. public interface ISceneService {
    2.     void GotoToScene(string sceneName);
    3. }
    4.  
    5. public class SceneService : ISceneService {
    6.  
    7.     public void GotoToScene(string sceneName) {
    8.         SceneManager.LoadScene(sceneName);
    9.     }
    10.  
    11. }
    By setting the SceneService injection binding via an interface you keep the option to unit test your commands.

    You then create the command:

    Code (CSharp):
    1. public class GotoSceneCommand : Command<string> {
    2.  
    3.     [Inject] private ISceneService sceneService;
    4.  
    5.     protected override void Execute(string sceneName) {
    6.         sceneService.GotoToScene(sceneName);
    7.     }
    8.  
    9. }
    The command injects the SceneService and uses it to go to the scene defined in the command's execution parameter, which we will set in the Context. Now we can create our context:

    Code (CSharp):
    1. public class GameContext : Context {
    2.  
    3.     protected override void SetBindings() {
    4.         base.SetBindings();
    5.  
    6.         Bind<ISceneService, SceneService>();
    7.  
    8.         On<EnterContextSignal>().Do<GotoSceneCommand>("YourSceneName");
    9.     }
    10.  
    11. }
    In the GameContext, you first set the SceneService injection binding. You then bind the GotoSceneCommand to the EnterContextSignal and add the scene name you want to load as execution parameter.

    Now that you have this GotoSceneCommand you can basically use it as a response to any signal. Contexts are no monobehaviour and therefor will not be affected when loading/unloading scenes. Views will automatically be destroyed by Unity when their scene is unloaded, which will trigger mediators to be cleaned up as well.

    You could also make the SceneService load the scene in additive mode, the GameContext would not be affected. It might be wise to start with one empty scene that instantiates to root context of your application/game and let that context in turn add child contexts which load and manage their own scene. We want our contexts to control our scenes, not the other way around.

    Let me know if this answers your question!
     
    Last edited: Dec 23, 2016
    jk15 likes this.
  23. T2RKUS

    T2RKUS

    Joined:
    Jul 13, 2015
    Posts:
    4
    Hello, I really like the look of this new framework. I already have Code Control and I think that is it brilliant but I have two questions regarding this product.

    1) How is the performance with Ioc+ with all the injecting - how much overhead does this add vs wiithout using it and compared to other ioc frameworks like A.D.I.C and zenject.
    2) Can Ioc+ itself be used without Unity (I'm aware that the view/mediator code cannot be used without unity due to the view being coupled to MonoBehaviour).

    Thanks.
     
  24. softlion

    softlion

    Joined:
    Sep 6, 2013
    Posts:
    100
    Hi and welcome T2RKUS!

    I've spent quite a bit of time to make IoC+ cache as much as possible. When IoC+ uses reflection to inject values into an instance of a certain type, it stores the [inject] field-info's of that type so the reflection queries won't have to be performed again. I've done performance tests and have not encountered any performance drops or issue with garbage collection on account of the IoC+ framework.

    A very big part of the IoC+ code base does not depend on the Unity framework, but classes like the Context and indeed the View and Mediator do use the UnityEngine namespace. The IoC+ package does come with the full source code of the framework. With some custom refactoring it is definitely possible to get IoC+ running in another environment.

    Let me know if this answers your questions!
     
    T2RKUS likes this.
  25. softlion

    softlion

    Joined:
    Sep 6, 2013
    Posts:
    100
    Hi TokyoDan,

    I've just got an email with a question you've posted here, but can't seem to find it on the thread. Just in case, I will answer it anyway.

    What's probably the case is that your PlayerMediator is targeting the PlayerInputView where it should target the PlayerView.
    Code (CSharp):
    1. // This is incorrect:
    2. public class PlayerMediator : Mediator<PlayerInputView> {
    3.     ...
    4. }
    5.  
    6. // This is correct:
    7. public class PlayerMediator : Mediator<PlayerView> {
    8.     ...
    9. }
    The first mediator's view will be of type PlayerInputView which does not have the Jump() method. The second mediator's view will be of type PlayerView, which does have the Jump() method.

    The mediator class requires the view it mediates as a generic type. This provides IDE code-hinting and autocompletion when accessing the view from the mediator.

    Let me know if this answers your question, or if you've already found out and deleted your question because of it!
     
    Last edited: Feb 5, 2017
  26. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    Yeah I figured that out on my own and deleted the post so as not to trouble you with my stupid mistake. Thanks
     
  27. softlion

    softlion

    Joined:
    Sep 6, 2013
    Posts:
    100
    I see, no problem! Feel free to ask if you get stuck ;)
     
  28. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    I am confused at to why Listeners have to be combined with signals in PlayerInputView and PlayerInputMediator.

    In PlayerInputView and PlayerInputMediator the JumpInputSignal seems to be just a delegate to hold a listener.

    It seems to me that there could be a more simple way of doing this using just one or the other: delegate-listeners or signals.
     
  29. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    As you can see I am desperately trying to get a handle on the tutorial. Either I am stupid or it is not easy. The names of signals and listeners being so similar is also a source of confusion to me. So I renamed a few things so it'd make more sense to me.

    File 2-6-29 H, 13 00 56.png

    I renamed
    Class JumpInputSignal to Class JumpCommandSignal

    and In PlayerInputMediator I renamed
    [Inject] JumpInputSignal jumpInputSignal
    to
    [Inject] JumpCommandSignal jumpCommandSignal;
     
  30. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    I have a question about the tutorial.

    In the section Command to Mediator, under the heading The Jump Signal is:
    "Remember the JumpInputSignal? That signal was named after an event..."

    Does that use of JumpInputSignal refer to the JumpInputSignal Signal defined in PlayerInputView.cs, or to the JumpInputSignal class defined in JumpInputSignal.cs?
     
  31. softlion

    softlion

    Joined:
    Sep 6, 2013
    Posts:
    100
    At this point it might seems senseless to have a mediator at all to delegate the signals of the view. But in time the mediator gets more logic that should not reside in the view, as the view can never be unit tested properly. In this case the mediator is indeed simply delegating the view's signals.

    That seems like a fair rename. At work we're also experimenting with signal naming conventions:
    Signal dispatched in a view: "JumpInputSignal" (when player presses the jump key)
    Signal dispatched in a mediator: "JumpInputEvent" (when view's JumpInputSignal is dispatched)
    Signal dispatched in a command: "JumpAction" (listened to by another mediator to make its view jump)

    We're still trying it out. If it works well for us I will update the tutorials or add an additional tutorial to the "Conventions and Patterns" section.

    The image you've drawn seems correct by the way.

    I can see how that part can be confusing. It refers to the JumpInputSignal class definition, thus the signal injected in the mediator.

    I've added "Update (tutorial) signal naming conventions" to the Trello to-do list. The Trello board is public, if you want to take a look: https://trello.com/b/W8PjPVzh.
     
  32. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    Thanks a lot. That clears things up some. Since you are thinking about renaming things I've included my C# source files (with naming that makes more sense to up to this point) up to but not including the Signal Parameters section. Although my current naming helps to make things more clear to me I know that the conventions you are considering are probably better for overall standardization and because you know what you are doing and where your are going with IoCPlus.

    One thing I do recommend is to start all variables, whether public or private, with a lowercase character because (I may be wrong though) that is what Unity uses in all their official tutorials, and I think that is what most beginning game developers that started with Unity are used to.
     

    Attached Files:

  33. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    I have more questions about what's written in the section Command to Mediator, under the heading The Jump Signal:

    "Remember the JumpInputSignal? That signal was named after an event, because it was triggered in the player’s jump input."

    Can this be more specific? e.g.
    "because it was triggered by JumpInputSignal.Dispatch() in Input.GetKeyDown() in Update() method of the PlayerInputView class."
    or
    "because it was triggered by jumpInputSignal.Dispatch() in the OnViewJumpInputSignal() method of the PlayerInputMediator class"


    AND

    "Naming signals as event or action accordingly is a great convention to give insight as to whether we’re (1) using the signal to trigger a set of commands or we’re (2) using the signal in a command to make a mediator act accordingly."

    Is (1) the 'event', and (2) the 'action'?

    And if events are distinguished from actions what would JumpInputSignal defined in PlayerInputView.cs be classified as?
     
    Last edited: Feb 7, 2017
  34. softlion

    softlion

    Joined:
    Sep 6, 2013
    Posts:
    100
    Hi TokyoDan!
    I've decided to rewrite it to: "... because it was triggered by the player's input". This way I can keep the paragraph about naming conventions without diving too much in detail.

    I've changed this part to: "Naming a signal as 'event' when using it to trigger a set of commands or naming it as 'action' when using the signal in a command to make a mediator act accordingly is a great convention to keep a clear flow of signals."

    Not as important to take note of, but all signals in views should be considered events. Action-signals are not needed in views as mediators will just use public methods to make the view act accordingly.
     
  35. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    Please let me know when you get things updated.
     
  36. softlion

    softlion

    Joined:
    Sep 6, 2013
    Posts:
    100
    The tutorial changes are online!

    By the way, at work we are currently prototyping IoC+ with mediators set as optional. This means views are able to use the [Inject] attribute as well, and are instantiated without a mediator when there is no BindMediator<Mediator, View>() called for that view.

    The primary reason for mediators to exist is so we can unit test them easily, as they aren't mono-behaviours. For our current project at work, development goes too rapid to do any unit testing. Therefor the mediators seems unnecessary. If this works out for us, I'll exclude mediators from the starting tutorials, as they will only become interesting when unit testing. If you are interested in trying this version out, then feel free to share your email with me so I can send you the beta package.
     
  37. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    I never do unit tests as I almost test every line of code I write as I write it. I like the KISS principal. Please send me the beta package. I DMed you my email.
     
  38. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    @softlion I see your DM that you sent it but I never received it.
     
  39. softlion

    softlion

    Joined:
    Sep 6, 2013
    Posts:
    100
    Weird! Maybe it's in the spam folder. I've sent the beta package in our closed conversation.
     
  40. Zigrivers

    Zigrivers

    Joined:
    Jan 4, 2017
    Posts:
    2
    I hope this isn't a silly question, but if it is forgive me as I'm very new to Unity and C# and still getting my bearings. I've been wrapping my arms around IoC+ and feel like I've got a basic understanding as I'm building my first scene with it.

    I've recently learned about Unity's sciptableobject and I'm wondering how this fits into things, if at all?

    One of the suggestions I read is to to use the scriptableobject when you wanted to create a singleton. I'm facing that scenario now, but thought I would ask about this before I get a chance to actually try it in code later tonight or tomorrow morning.

    Can I Bind my scriptableobject in each context that needs it and inject it into the appropriate commands and mediators and all of them will reference the same instance?

    If I can do this is this the right approach? Or should I be approaching this differently?

    Thanks for your help!
     
    Last edited: Feb 18, 2017
  41. softlion

    softlion

    Joined:
    Sep 6, 2013
    Posts:
    100
    Hi and welcome Zigrivers!

    They can perfectly fit in the IoC landscape of IoC+. Take a look at the following example.
    Code (CSharp):
    1. // The scriptable object, with an instance stored in the Resources folder.
    2. public class Balancing : ScriptableObject {
    3.     public float MaxHealth;
    4.     public float JumpForce;
    5.     public float RunSpeed;
    6. }
    7.  
    8. public class BalancingReference {
    9.     public Balancing Instance;
    10. }
    11.  
    12. public class LoadBalancingCommand : Command {
    13.  
    14.     [Inject] BalancingReference balancingReference;
    15.    
    16.     public void Execute() {
    17.         balancingReference.Instance = Resources.Load<Balancing>("Balancing");
    18.     }
    19.  
    20. }
    21.  
    22. public class PlayerMediator : Mediator<PlayerView> {
    23.  
    24.     [Inject] BalancingReference balancingReference;
    25.    
    26.     public void Initialize() {
    27.         // Use the balancingReference.Instance here.
    28.     }
    29.  
    30. }
    31.  
    32. public class GameContext {
    33.  
    34.     Bind<BalancingReference>();
    35.    
    36.     BindMediator<PlayerMediator, PlayerView>();
    37.    
    38.     On<EnterContextSignal>()
    39.         .Do<LoadBalancingCommand>()
    40.         .Do<InstantiatePlayerCommand>();
    41.  
    42. }
    The Balancing class is a scriptable object. The LoadBalancingCommand loads an instance of this object out of the resources folder, and stores it in an injected model. The basic use of models is further described here.

    After executing the LoadBalancingCommand, other commands and mediators like PlayerMediator can inject the BalancingReference. Very much like you said.

    Let me know if you have any other question. This was by no means a silly one!
     
  42. Zigrivers

    Zigrivers

    Joined:
    Jan 4, 2017
    Posts:
    2
    Thank you! That helps a ton.

    I'm running into another situation that I could use some help with. I have a prefab that I'm instantiating via a command. The prefab consists of a panel and three children (Text label, Dropdown control, and another Text control).

    I instantiate this panel w/ its three children. And then in the View, I set the Dropdown options and setup a Mediator to listen to the View. Everything works fine except that I can only click on the Dropdown control one time. After the first click, the Dropdown control doesn't respond to any additional mouse clicks.

    If I drag the Prefab into the Scene and don't instantiate it, the Dropdown control behaves as expected (I can change values multiple times).

    I've been banging on this problem for a few hours now and it has me stumped. Here's my simplified code:

    Code (csharp):
    1. public class HeritageContext : Context {
    2.     protected override void SetBindings() {
    3.         base.SetBindings();
    4.  
    5.         this.Bind<MajorHeritageValueChangeSignal>();
    6.  
    7.         this.Bind<UIHeritageSelectCanvas>();
    8.  
    9.         this.BindMediator<HeritageSelectPanelMediator, HeritageSelectPanelView>();
    10.  
    11.         this.On<EnterContextSignal>()
    12.             .Do<InstantiateHeritageSelectCanvasCommand>()
    13.             .Do<InstantiateHeritageSelectViewCommand>();
    14.     }
    15. }
    16.  
    17. public class InstantiateHeritageSelectViewCommand : Command {
    18.     [Inject] private UIHeritageSelectCanvas canvas;
    19.     [Inject] private IContext context;
    20.  
    21.     protected override void Execute() {
    22.         var prefab = Resources.Load<HeritageSelectPanelView>("HeritagePanel");
    23.         if (prefab != null) {
    24.             var heritagePanel = this.context.InstantiateView(prefab);
    25.             this.canvas.HeritageCanvas.AddChild(heritagePanel.GetGameObject());
    26.         }
    27.     }
    28. }
    29.  
    30. public class HeritageSelectPanelView : View, IHeritageSelectPanelView {
    31.     public Dropdown HeritageDropdown;
    32.     public Text HeritageLabel;
    33.     public Text HeritageBonus;
    34.     public Signal<int> HeritageSelectedSignal = new Signal<int>();
    35.  
    36.     private const string BonusText = "Attribute Bonus: ";
    37.     private List<string> majorHeritageList;
    38.  
    39.     public void HeritageSelected(int selectedIndex) {
    40.         this.HeritageDropdown.value = selectedIndex;
    41.     }
    42.  
    43.     private void HeritageSelectedValueChanged(int selectedIndex) {
    44.         this.HeritageSelectedSignal.Dispatch(selectedIndex);
    45.     }
    46.  
    47.     private void Awake() {
    48.         this.majorHeritageList = new List<string>();
    49.         var heritageNames = Enum.GetNames(typeof(MajorHeritage));
    50.  
    51.         foreach (var heritage in heritageNames) { this.majorHeritageList.Add(heritage); }
    52.         this.HeritageDropdown.AddOptions(this.majorHeritageList);
    53.     }
    54.    
    55.     private void Start() {
    56.         UnityAction<int> valueChanged = this.HeritageSelectedValueChanged;
    57.         this.transform.GetChild(1).GetComponent<Dropdown>().onValueChanged.AddListener(valueChanged);
    58.     }
    59. }
    60.  
    61. public class HeritageSelectPanelMediator : Mediator<HeritageSelectPanelView> {
    62.  
    63.     public override void Initialize() {
    64.         this.view.HeritageSelectedSignal.AddListener(this.OnHeritageSelectedSignal);
    65.     }
    66.  
    67.     public override void Dispose() {
    68.         this.view.HeritageSelectedSignal.RemoveListener(this.OnHeritageSelectedSignal);
    69.     }
    70.  
    71.     private void OnHeritageSelectedSignal(int selectedIndex) {
    72.         this.view.HeritageSelected(selectedIndex);
    73.     }
    74. }
    I'm not sure if I'm misunderstanding something with the framework, or if it's something more basic that I'm missing.

    Thanks for any help you can provide!
     
  43. softlion

    softlion

    Joined:
    Sep 6, 2013
    Posts:
    100
    Hi again, Zigrivers!

    Everything in your code seems fine on the IoC+ end of things.

    Does the dropdown spawn correctly in the canvas?
    Is there an EventSystem in your scene? (Like the SpaceAdventures example scene)

    I can help you best if you'd send me the unity project in a .zip file or export and share your assets as a package.

    Let me know!
     
  44. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    I received the 1.3.3 beta package in which Mediators are not needed.

    So to try to simplify my code by not using Mediators I basically moved everything that was in the PlayerViewMediator into the PlayerView, got rid of the PlayerViewMediator, and made the corresponding changes to the GameContext. Am I doing things right? One thing that surprised me was that I didn't need to do Bind<JumpCommandSignal>(); because it seems that On<JumpCommandSignal>().Do<JumpCommand>(); is enough. (Does that also cause a Bind/Inject ?)

    (I changed some names of signals and routines so they'd make more sense to me in my simplified version.)

    Code (CSharp):
    1. using IoCPlus;
    2.  
    3. public class GameContext : Context
    4. {
    5.  
    6.     protected override void SetBindings()
    7.     {
    8.         base.SetBindings();
    9.  
    10.         Bind<string>("Welcome to the world");
    11.  
    12.         //Bind<JumpCommandSignal>();   // DON'T NEED THESE NOW
    13.         //Bind<MoveCommandSignal>();
    14.  
    15.         Bind<JumpSignal>();
    16.         Bind<MoveSignal>();
    17.  
    18.         On<EnterContextSignal>().Do<WelcomeCommand>()
    19.                                 .Do<InstantiatePlayerInputCommand>()
    20.                                 .Do<InstantiatePlayerCommand>();
    21.  
    22.         // EVENTS
    23.         On<JumpCommandSignal>().Do<JumpCommand>();
    24.         On<MoveCommandSignal>().Do<MoveCommand>();
    25.     }
    26. }
    27.  
    Code (CSharp):
    1. using IoCPlus;
    2. using UnityEngine;
    3.  
    4. public class PlayerView : View {
    5.     [Inject] private JumpSignal jumpSignal; // ACTION. Same Signal as in JumpCommand
    6.     [Inject] private MoveSignal moveSignal;
    7.  
    8.     Rigidbody myRigidBody;
    9.  
    10.     private void Awake()
    11.     {
    12.         myRigidBody = GetComponent<Rigidbody>();
    13.     }
    14.  
    15.  
    16.     public override void Initialize()
    17.     {
    18.         Debug.Log( "PlayerView Initalize called\n" );
    19.  
    20.         jumpSignal.AddListener( OnJumpSignal );
    21.         moveSignal.AddListener( OnMoveSignal );
    22.     }
    23.  
    24.     public override void Dispose()
    25.     {
    26.         Debug.Log( "PlayerView Dispose called\n" );
    27.  
    28.         jumpSignal.RemoveListener( OnJumpSignal );
    29.         moveSignal.RemoveListener( OnMoveSignal );
    30.     }
    31.  
    32.     // Called by the ACTION jumpSignal.Dispatch() in JumpCommand()
    33.     private void OnJumpSignal()
    34.     {
    35.         //Jump();
    36.         myRigidBody.AddForce( Vector3.up * 300.0f );
    37.     }
    38.  
    39.     private void OnMoveSignal( Vector3 direction )
    40.     {
    41.         //Move( direction );
    42.         myRigidBody.AddForce( direction * 100.0f );
    43.     }
    44. }
    45.  
    Code (CSharp):
    1. using UnityEngine;
    2. using IoCPlus;
    3.  
    4. public class PlayerInputView : View {
    5.  
    6.     [Inject] private JumpCommandSignal jumpCommandSignal;  // EVENT
    7.     [Inject] private MoveCommandSignal moveCommandSignal;  // EVENT
    8.  
    9.     void Awake()
    10.     {
    11.         //Debug.Log( "PlayerInputView is instantiated!\n" );
    12.     }
    13.  
    14.     void Update()
    15.     {
    16.         if( Input.GetKeyDown( KeyCode.UpArrow ) )
    17.         {
    18.             // JUMP INPUT EVENT (part 1 of 2)
    19.             jumpCommandSignal.Dispatch();
    20.         }
    21.  
    22.         if( Input.GetKeyDown( KeyCode.LeftArrow ) )
    23.         {
    24.             // MOVE INPUT EVENT (part 1 of 2);
    25.             moveCommandSignal.Dispatch( Vector3.left );
    26.         }
    27.  
    28.         if( Input.GetKeyDown( KeyCode.RightArrow ) )
    29.         {
    30.             // MOVE INPUT EVENT (part 1 of 2)
    31.             moveCommandSignal.Dispatch( Vector3.right );
    32.         }
    33.     }
    34.  
    35. }
    36.  
     
  45. TokyoDan

    TokyoDan

    Joined:
    Jun 16, 2012
    Posts:
    1,080
    Am I doing things right so far?

    In your private message you mentioned that my PlayerInputView still had public readonly signals that weren't needed anymore as there is no mediator to listen to those. Well I still got two signals (jumpCommandSignal and moveCommandSignal) but they are not readonly and are private. I think these are necessary. Am I correct in this?
     
  46. softlion

    softlion

    Joined:
    Sep 6, 2013
    Posts:
    100
    Hi TokyoDan!

    Yes, this is the right approach!

    Yes you are correct. In IoC+ 1.3.3 views are able to inject just like mediators do.
     
  47. mrcoffee666

    mrcoffee666

    Joined:
    Jun 21, 2013
    Posts:
    6
    Hi i am currently evaluating your IOC framework. Unitl now i could not figure out if it is possible to use constructor injection. Is there a way to do this? My second probelm is the i could not manage to bind e.g. an GameService as singleton. In my case this GameService extends an IGameService interface and the constructor would also have some injected parameters.
     
  48. softlion

    softlion

    Joined:
    Sep 6, 2013
    Posts:
    100
    Hi mrcoffee666!

    I'm not sure if this answers your question, but you can inject a constructed instance like so:
    Code (CSharp):
    1. public class MyContext : Context {
    2.     protected override void SetBindings() {
    3.         base.SetBindings();
    4.         Bind<MyClass>(new MyClass());
    5.     }
    6. }
    Injected values are not passed as parameters via the constructer, but are rather "set" by the context after construction. Review this tutorial to get a better understanding of injection.

    Let me know if this helps you out!
     
  49. mrcoffee666

    mrcoffee666

    Joined:
    Jun 21, 2013
    Posts:
    6
    Thanks for the quick answer.
    I think my problem is the missing constructor injection or even method injection. I am very much used to constructor injections like:


    Code (CSharp):
    1.  public interface IGameService
    2.     {
    3.         void Start();
    4.     }
    5.  
    6.     public class GameService : IGameService
    7.     {
    8.         private readonly IUserService _userSettings;
    9.  
    10.         public GameService(IUserService userSettings)
    11.         {
    12.             _userSettings = userSettings;
    13.         }
    14.  
    15.         public void Start()
    16.         {
    17.             Console.WriteLine(_userSettings.GetName());
    18.         }
    19.     }
    20.  
    21.     public interface IUserService
    22.     {
    23.         string GetName();
    24.     }
    25.  
    26.     class LocalUserService : IUserService
    27.     {
    28.         public string GetName()
    29.         {
    30.             return "xzy";
    31.         }
    32.     }
    33.  
    34.     class DatabaseUserService : IUserService
    35.     {
    36.         public string GetName()
    37.         {
    38.             return "sql";
    39.         }
    40.     }
    41.  
    42.    // then i would like to setup the container like similar to this:
    43.     Bind<IUserService>().To<LocalUserService>().AsSingle();
    44.     Bind<IGameService>().To<GameService>().AsSingle();
    45.  
     
  50. mrcoffee666

    mrcoffee666

    Joined:
    Jun 21, 2013
    Posts:
    6
    I really like your framework it is very useful and easy to use. Is there any chance that we can get constructor and method injection?