Search Unity

[RELEASED] StrangeIoC - Inversion-of-Control Framework for Unity C#

Discussion in 'Assets and Asset Store' started by srimarc, Jun 13, 2013.

  1. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    Thanks! We've validated it in-house on web, desktop and iOS. We've used it a little on Android and so far it seems to work just fine there too (I'd be astonished if it didn't). We don't develop for Blackberry and no one in our user base has given me any feedback yet to tell me whether they've actually seen it work there. There's also Flash, which we also don't actively develop for. I have a notion it won't work on Flash builds given how we do reflection...but again I can't yet say for sure.
     
  2. EmeralLotus

    EmeralLotus

    Joined:
    Aug 10, 2012
    Posts:
    1,462
    Last edited: Sep 22, 2013
  3. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    Win8 is honestly not something I've given any thought to. We don't use Hashtable anywhere, and I don't believe we use ArrayList either, so we should at least be ok there. I'm inclined to be optimistic, but I can't make any promises. My suggestion would be to try one of the example projects and see how that goes first. I'd definitely love to find out what you discover! :)

    [EDIT]I should have said that I don't use ArrayList in the Strange library. But I *do* use it a few times in the example projects. Looks like you might need a handful of edits to try the example on Win8.[/EDIT]
     
    Last edited: Sep 24, 2013
  4. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    #StrangeIoC v0.6.0, including #StrangeSignals is now available from the Unity Asset Store:

    http://u3d.as/4Xj

    Update powers...ACTIVATE!!!
     
  5. EmeralLotus

    EmeralLotus

    Joined:
    Aug 10, 2012
    Posts:
    1,462
    I can make the edits but moving forward, if StrangeIOC has new versions, then things will break.
    Would you be open to making StrangeIOC work on Win8 officially.

    The reason I am asking is that as a fundamental framework for Unity projects, if one decides to use in a project and should it not work on a certain platform that one needs to target, then it's quite tragic.

    P.S. since StrangeIOC was inspired from RobotLegs, it would have been nice for StrangeIOC to work on Unity Flash. Do you know if there is a work around to making StrangeIOC to work on Flash?
     
  6. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    As I say, there's no particular reason to think it wouldn't work on Win8. But our firm doesn't develop for that platform. We have no development devices nor QA resources to devote to it. If you test it and tells me it works, that's great and we can let others know. If you discover "it would work if we made the following changes" you could send us a pull request and we could see about integrating those changes into the release. That's what open source projects are all about! :)

    As to Flash, I obviously come from an AS3 background, and would love for it to run there, but it's been reported to me that C# reflection doesn't survive Unity's export process. Add to this that Unity have announced an end (http://blogs.unity3d.com/2013/04/23/sunsetting-flash/) to Flash support, and the argument for putting energy into Flash becomes fairly tenuous. As before, if someone else wants to figure out how to make it happen, I'm not opposed, but my time and resources are limited, so I feel my effort is best spent making the most of the core framework.
     
  7. EmeralLotus

    EmeralLotus

    Joined:
    Aug 10, 2012
    Posts:
    1,462
    Definitely appreciate the effort that you are putting into this project and I am very supportive of it being successfully adopted by Unity Developers. I know that you are investing time in the core and I wanted to mention that those who are new and would like to use StrangeIOC as a core framework would probably be concerned about platform coverage. Pull request is fine but if too many changes are needed then it might be a problem. It would be easier if while working on the core, you have in mind the limitations of the various platforms then there will be less work needed. Please don't think of this comment as asking you to do more work, it's just honest feedback from someone who really wants to use this awesome framework.
     
  8. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    Thanks rocki! It's a fair point which I'll certainly keep in mind. :)

    Keep me posted if you do any experiments around Win8 or Flash (or anything else, really).
     
  9. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    New patch release on github. http://bit.ly/185ZhB6

    Fixes: double-firing of C-Context events; double-firing of value-bound PostConstructs.
     
  10. PrimeAlly

    PrimeAlly

    Joined:
    Jan 2, 2013
    Posts:
    35
    This project looks great! Good documentation for newcomers such as myself, I'm looking forward to implementing this in my game!

    Since this is aimed at being a C# framework, I was thinking that maybe it should follow the C# naming guidelines rather than Unity's naming conventions. What I mean is that i.a. public properties should be named using PascalCase instead of camelCase.
    Any thoughts on naming?
     
  11. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    First, thanks for your kind words!

    In regards to naming conventions, I've had many comments on this and probably have to admit that the route I took initially was wrong. I personally dislike C#'s use of PascalCase as being uninformative, so I attempted to hedge a bit between Pascal for public methods and camel for properties and private/protected methods. The fact that Unity, C# and some 3rd-party libraries actually disagree on some conventions made it easier to hedge, but in light of the goal I'm trying to achieve (ie, encourage clean coding practices) I probably should have gone full-on C#.

    The real question is what to do about it now? Strange has been "in the wild" for several months now and appears to have a respectably-sized user base (it's hard to be sure how big). I of course don't want to arbitrarily alter the API and break everyone. The best solution might be a duplicate API which applies the standards, though I'm not entirely sure when I'll make time for this, as opposed to other features of more direct practical benefit.

    Thanks for listening to my mea culpa. :)
     
  12. cjow

    cjow

    Joined:
    Feb 29, 2012
    Posts:
    132
    Is there a particular reason the asset store says 4.2.1 only or am I ok to download it from github and get on with it?
     
  13. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    Hi cjow!

    Strange works with all 4.x versions of Unity (I know at least one production house using it with 3.5, but some have reported an issue with that because we use namespaces).

    A bizarre quirk of the Asset Store is that it uses the author's current Unity version to determine the "minimum required". In order to fix the problem I'd have to have a second install of 4.0, purely for making submissions. Maybe I should do that, but it rankles.

    Pulling from GitHub is the best way to know you've got the most up-to-date version. Note that there's also a strange-core repo for the advanced user who just wants the code, minus docs, examples, etc.
     
  14. cjow

    cjow

    Joined:
    Feb 29, 2012
    Posts:
    132
    Cool, thanks for the info!
     
  15. Butabah

    Butabah

    Joined:
    Oct 14, 2013
    Posts:
    2
    Hey there, thanks so much for the IoC container, it works amazingly. I've been using this pattern for a while now at my job as a web developer, and it is fantastic

    Quick question regarding IoC design and networking.

    In an MVC sense, I group game rules to be a part of the Model, as it seems like the most logical way to group application logic, keeping the View specific to user inputs, GUI, and rendering.

    The problem I face from a design perspective is the fact that it's hard to make the game rules authoritative, seeing how I need a way to send RPCs to the game rules to validate user inputs. This requires the Game Rules to have a NetworkView, however, which I believe can only be attached to GameObjects.

    What are some thoughts on this? Is my idea of the game rules as being a Model wrong? Should I just have the Game Rules extend MonoBehaviour but behave it as if it were a model? Maybe I'm overthinking my design.
     
  16. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    I'm not one to tell anyone not working for me that their approach is wrong. There are an infinite number of ways to approach any problem. My personal interpretation of MVC is that a model is just a maintainer of state, which is to say that the game rules are not maintained in the Model, but in the Controller. In Strange, Commands — which form the bulk of the Controller — are the bits that do things, thus (IMO) the correct place for rules.

    Strange actually thinks in terms of MVCS, the 'S' standing for Service, which is what you're talking about when you get into RPCs or anything else that travels outside the app.

    Now, I've never worked with Unity's NetworkView (it's basically the Service, whereas we create Service classes to handle our network communication), but like a lot of things in Unity it appears to force you to make some dubious choices. The very name "NetworkView" tells you that a View is doing work that has nothing to do with display. Similarly, Unity requires a MonoBehaviour to listen to Javascript on web or the native layer on mobile. To deal with these MBs, we tend to "hide them in plain sight" by attaching them to the ContextView and then mapping them if necessary for injection. The advantage with this approach is that we satisfy Unity's requirements without necessarily tying a service to what we view as poor practice. If we later re-write the service to do away with the MB, the service itself remains largely unaffected.

    So bottom line: if it were me, I'd map the NetworkView to a Mediator and limit the activity of those two classes to just the network communication. I'd put the rules inside a set of Commands and store any state in Models.

    HTH!
     
  17. Butabah

    Butabah

    Joined:
    Oct 14, 2013
    Posts:
    2
    Srimarc,

    Thanks for the advice! I took your design into consideration and it works really well.
     
  18. infine

    infine

    Joined:
    Sep 30, 2013
    Posts:
    7
    Hi Srimarc!
    It seems I have found a mistake. When my EventView object with mediator comes to inactive mediator doesn't go away. And my game object continues receiving signals. Is it mistake? if you're not going to fix it, please tell me how I can remove mediator correctly on inactivating object. It's very important for me while I work with pool (see Pool Manager)
     
  19. infine

    infine

    Joined:
    Sep 30, 2013
    Posts:
    7
    I think it would be better to make an opportunity not to send Signals to inactive objects as it works in SendMessage function
     
  20. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    Strange puts you in control of your Event/Signal reception. Turning off/removing the Mediator when the View is inactive would be a very bad idea, since it would mean that an invisible GO wouldn't be able to receive instructions to turn it back on. But Your View and Mediator are both MonoBehaviours, so it's in your hands to implement OnDisable in either to do anything you might normally do in a MonoBehaviour, such as stop listening to certain Signals, or even to destroy the Mediator if you think that's a good idea.

    HTH!
     
  21. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    I’ll be presenting StrangeIoC to the Bay Area Unity Users Meetup on November 20th. The talk will be entitled The Good, The Bad and The Strange and will offer a quick intro on what Strange is and how it can help you, primarily focused on the benefits of Dependency Injection and Inversion-of-Control.

    http://www.meetup.com/Bay-Area-Unity-Users/events/147140342/

    If you're in the San Francisco Bay Area I'll hope to see you there!
     
  22. OrekaIngenierie

    OrekaIngenierie

    Joined:
    Jul 6, 2012
    Posts:
    67
    hi i start a new project with strange and i have a little weird bug i setup the base of my multiple context app and when i hit play from my main scene "use for load additive the over scenes like my gui scene" my gui context view is not binded with my gui context mediator. But If i hit play in my gui scene it work perfecly i can see my view binded with my mediator.

    I missed some thing ?

    I give you my project demo to see the purpose.

    https://drive.google.com/file/d/0B7tPUWigHdTGaFY1SVRad25WWlU/edit?usp=sharing

    Thanks
     
  23. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    The error is in GUI_ROOT

    this line:
    GUI_Context gui_context = new GUI_Context(this,true);

    should read:
    context = new GUI_Context(this,true);

    Setting the value to a local variable means that context is null. The View was unable to identify the Context as it bubbled.
     
  24. OrekaIngenierie

    OrekaIngenierie

    Joined:
    Jul 6, 2012
    Posts:
    67
    Sorry Thanks i found the error yesterday that change since the v0,6 i right ?

    I try to play with signals it works but when my third context "just a cube in a third scene load a the same time of my gui" is removed i still have a debug that tell me it respond to the signal send from my gui.

    i map my signal in my main context
    Code (csharp):
    1. injectionBinder.Bind<RemoveContextSignal>().ToSingleton().CrossContext();
    I know that i should do something wrong so i continue to search !

    Ps i didn't play with strange since 3 months and it take me little time to understand !

    Thanks

    Edit :

    I found my error if i map a signal in my main context
    Code (csharp):
    1. injectionBinder.Bind<RemoveContextSignal>().ToSingleton().CrossContext();
    and in my third context that is remove
    commandBinder.Bind<RemoveContextSignal>().To<Maquette_RemoveCommand>();

    my signal still exists and i have to remove the listener when my view and mediator are removed
    in my third context.

     
    Last edited: Nov 28, 2013
  25. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    This was not a change. Bubbling has always looked for a ContextView and its Context in order to register.

    It appears you've solved your other problem? Remember that a Context must clean up after itself, just like any other bit of allocated memory. Hanging Cross-Context dependencies will definitely result in Contexts not being properly removed.
     
  26. OrekaIngenierie

    OrekaIngenierie

    Joined:
    Jul 6, 2012
    Posts:
    67
    Ohhh Sh!!!!!

    I check my older project with strange and it is ok, i spent an hour 3 days ago to check what i made wrong, and i never be able to see my mistake !
    I'm really an idiot !!!

    I have just a little question, in my last project, i used crosscontext so when i needed to dispatch a event they was always injected locally. If i hit play in my gui context for exemple i had no error when i inject a crosscontext! this way i could test it locally just for map gui command. But now with signal i inject my signal in my main context with crosscontext not locally in my gui context !
    so if i hit play in my gui context i have an error because my signal is not injected locally in my gui context ! I understand why i have this error !!!

    It war very handy to have this ability "Just for me perhaps !!!

    Is there a way to avoid this error with signal, i know that i could inject in my gui context but if i do that i will not inject it in my third context ?

    I hope you can understand !

    Thanks
     
  27. TheSheyper

    TheSheyper

    Joined:
    Jul 3, 2013
    Posts:
    22
    Hi srimarc !

    Great job and Thanks for this framework !!

    I was trying the framework with a little example and I got a bit lost... Where should I put the game logic ??

    For instance if I want to play a sound when the user click on a button :

    - View : shows the button

    - Mediator : dispatches the button click event

    - Command ?? Plays the sound ?? After all that's the controller, but that's not a monobehaviour, I mean if I need components or coroutines.

    I must admit that I'm missing something...

    Thanks in advance
     
  28. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    We typically have a conditional in our Context like so:

    Code (csharp):
    1.  
    2. If (this == Context.firstContext)
    3. {
    4.     //bootstrap cross-context dependencies
    5. }
    6.  
    This ensures that any such dependencies...such as CC signals...are satisfied locally.
     
  29. OrekaIngenierie

    OrekaIngenierie

    Joined:
    Jul 6, 2012
    Posts:
    67

    Great i see, i inject signal in each context but only the first created will create the binding !

    Thanks
     
  30. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    First, I'd submit to you that sounds are Views. The fact that you hear it rather than see it doesn't really alter the fact that you're dealing with the presentation layer rather than true Controller logic. Just food for thought! :)

    To your actual question, though, it isn't uncommon in Unity to hit a place where you require MB capabilities (Coroutines, e.g.) in Controller/Model/Service locations. We recommend a hybrid model for these occasions. It's not perfect, in that it introduces MB-dependency in a place you don't expect, but it allows you to limit dependencies, provides relative isolation and permits refactoring/abstraction if you ever find a better solution.

    I actually get variations of this question a lot, so I'm going to write an example of the hybrid scenario and add it to the FAQ today. I'll re-post when I do.

    [EDIT]
    I've added this to the FAQ...but I pretty much lifted the text from here, so you can use this explanation:
    https://github.com/thirdmotion/strangeioc/issues/51
    [/EDIT]
     
    Last edited: Dec 1, 2013
  31. TheSheyper

    TheSheyper

    Joined:
    Jul 3, 2013
    Posts:
    22
    Thanks for your answer, I got the point... ;)

    I need to practice but anyway thanks for this framework !
     
  32. dlwalker

    dlwalker

    Joined:
    Oct 15, 2013
    Posts:
    2
    Hi srimarc,

    Thanks for this framework, looks pretty awesome! We are looking at possibly using Strange as we've heard a lot of positive things about it, but after going through your howto and examples I'm left with a few questions... which are possibly me misunderstanding IOC, but hopefully you can help there as well.

    The gist of my question is, is there a way to dynamically map multiple concrete classes to a single member variable in multiple instances of a class?

    Let me phrase this in terms of your spaceship and weapon example from the how-to. Say you wanted your spaceship to be able to use a large number of weapons, and didn't want to have to know in advance which weapon you were working with (as it will change during gameplay), you just need the weapon to be an IWeapon. Using named binding won't solve this issue as that would imply I have a different weapon member variable in my spaceship class for each type of weapon. Mapping via context at game startup will only map one type of weapon, so that isn't an option either. So, it seems that we would need to have a context that remaps dynamically during gameplay, but it's not clear to me in your examples if this is good practice, or even possible. To take it a step further, what if I had multiple instances of spaceship, all needing a different concrete class mapped at the same time?

    I tend to think this is a problem that is outside the realm of what IOC was built to solve and the best method here is still some sort of management or factory class that provides the concrete implementation of IWeapon as needed. However, I wanted to be sure I was understanding this correctly since you seemed to advocate using IOC to avoid needing the factory pattern.

    Thanks again! I think we will likely use StrangeIOC regardless of your answer on this particular problem, but getting an answer to this will help solidify my understanding of the framework.

    Cheers!
     
  33. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    Hi dlwalker,

    Thanks first for your kind words. It's always good to hear about people interested in what we're doing.

    You do have a rather complex case, but it's entirely handleable within the realm of Strange IoC. There's not a definitive perfect solution, but I can certainly suggest some ways of thinking about the problem.

    You mentioned named bindings and that is probably part of the solution, but let's try turning it on its head. Instead of a name for every weapon, how about a name for every ship? Here are a few things to consider:

    - Binding on-the-fly is perfectly acceptable practice.
    - Names can be dynamic, not just static.
    - A Command can work as a Factory.

    So now let's consider some pseudo code that allows us to make and arm a new ship:

    Here's the ship...
    Code (csharp):
    1.  
    2. //A View that needs a weapon. Note we're not tagging with [Inject], just setting.
    3. //This is still IoC, we're just not using the magic of reflection here.
    4.  
    5. public IWeapon weapon{get;set;}
    6.  
    Here's how we create and arm the ship on the fly...
    Code (csharp):
    1.  
    2. //A CreateShipCommand in response to an Event/Signal
    3.  
    4. override public void Execute()
    5. {
    6.     GameObject go = new GameObject(shipType + someIncrementingValue); //you need to ensure a UNIQUE name for this to work
    7.     go.AddComponent<ShipView>();
    8.     //this is the key line. Note that the name is defined dynamically
    9.     injectionBinder.Bind<IWeapon>().To<DefaultWeapon>().ToName(go.name);
    10.     ship.weapon = injectionBinder.Get<IWeapon>().Named(go.name);
    11. }
    12.  
    Now let's say we want to re-arm this ship. We fire a re-arm ship Event or Signal. The payload of the signal should include the View and the desired new Weapon type (or, possibly better, a constant).
    Code (csharp):
    1.  
    2. //A RearmShipCommand in response to an Event/Signal
    3.  
    4. override public void Execute()
    5. {
    6.     GameObject go = evt.data.gameObject;
    7.     Type WeaponType = typeof(evt.data.weaponType);
    8.     //unbind the currently bound weapon
    9.     injectionBinder.Unbind<IWeapon>().Named(go.name);
    10.     //rebind to the new weapon
    11.     injectionBinder.Bind<IWeapon>().To(WeaponType).ToName(go.name);
    12.     ship.weapon = injectionBinder.Get<IWeapon>().Named(go.name);
    13. }
    14.  
    So from the Ship's standpoint, it only ever has an IWeapon. The Commands do the swapping work, but they don't really know about the specific weapons either. Someone somewhere knows the details...probably a special Command which just maintains the list.

    Bear in mind that this is untested pseudocode. Hopefully, though, it'll give you an idea as to how injection/IoC can be done in a way consistent with what you're trying to achieve.

    All the best!
     
  34. dlwalker

    dlwalker

    Joined:
    Oct 15, 2013
    Posts:
    2
    Ah, fantastic! That does seem like a workable solution, I'll have to dig in a bit and see how we would tailor that to our particular case.

    I appreciate the quick reply. I'm looking forward to working with the framework, and I suspect we may talk in the future.

    Thanks again!
     
  35. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
  36. WilliamBNewton

    WilliamBNewton

    Joined:
    Nov 18, 2013
    Posts:
    54
    I'm very interested this, but I'm coming from that wonderful place of being someone who's taught myself all I know about programming from various C# Unity tutorials all throughout the internet, trying to implement the concepts, hitting walls, searching through questions and trying to work the answers into my own project. Basically, I'm a baby when it comes to programming concepts, and constantly find out about new C# (and Unity) features every time I try to push forward.

    Recently I came upon a problem in my prototype... I've used static classes as managers in my "similar to tetris" type puzzle game. The way the game is built up, everything is so dependent on one another that I have no idea how I'll make the game be two player when moving on from the prototype to the finalized code. So I search for help, find tons of stuff about the Observer pattern, the Mediator pattern, ext. And read about the concept of IoC and Dependency Injection. Stumble here, read through all the help on the webpage, the few tutorials that are out there, read through the code in the examples, but I'm still drawing a big blank when it comes to how to use this for an actual game.

    I'm requesting some sort of moderate to advanced "from the ground up" tutorial be made by somebody. Not a tutorial that teaches you C# or Unity, but something that would just show how to use this in the making of a simple game. Because I think Strange would really help my project, but as of now I'm just stumbling around in the dark, and I wouldn't be surprised if there's a lot of people very much like me. Even something that would just take something as simple as the 2D platformer level and build it up using StrangeIoC would be immensely helpful.


    Everything I've seen about this framework is amazing, besides the support for beginner programmers at grasping the concepts of IoC in Unity. If IoC is really the way to go, I'd think it'd be fantastic for StrangeIoC to grab hold of the ignorant "building a game in their mom's basement" developers and hand them a roadmap to coding in a reusable and quick and safe fashion. Thanks for the framework! Even if I can't figure it out I'll keep banging my head against the wall until I do. At least focusing on using this will allow me to know which wall I'm banging my head against.
     
  37. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    William,

    It's certainly on my TODO list to create a simple game...an Asteroids or Breakout clone, probably...to explain the concepts. Quite simply, I haven't had the time!

    But there is this beauty happening over on Kickstarter.
    http://www.kickstarter.com/projects/cwssoftware/colony-capture-teaching-games-through-doing
    They're a long way from funded with only 4 days to go, so not sure it'll see the light of day, but the vision is to build a Unity game with StrangeIoC and show the whole process via tutorials.

    Please feel free to ask any questions here or on our Google Group or GitHub. We'll all do our best to keep you from too much head banging!
    https://groups.google.com/forum/#!forum/strangeioc <-- Best place for questions
    https://github.com/thirdmotion/strangeioc/issues <-- Best place to report bugs/request features
     
  38. Zynx87

    Zynx87

    Joined:
    Oct 31, 2013
    Posts:
    9
    I'm having difficulty letting my commands listen upon the first signal dispatch, since the AddListener hasn't
    been called yet. (I apologize, I'm very new to all this and it is likely a simple problem).

    I've tried a few ideas, but I feel like I'm missing something much simpler.

    In my setup, I have an ItemHandler command that listens for an itemInteractionSignal from the GameplayViewMediator

    One idea is to create an ItemHandlerStartupSignal that is dispatched when the mediator registers, which will add the listener.
    Unfortunately I realized that once the callback is completed and I remove the listener, this leaves me with the same problem of having to dispatch ItemHandlerStartupSignal again.

    So as it stands my solution is to send two signals, but I wouldn't be surprised if I've failed to grasp something.

    I'm guessing I could also call ItemHandler.Interaction(Gameobject, InputClick) within the mediator, but unless I'm mistaken this goes against the framework by creating a concrete dependency.

    Any advice is appreciated.
     
  39. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    Zynx,

    Startup is often the trickiest time, since Unity scripts don't always fire in the order you'd like them to. One solution is to use the Script Execution Order feature of Unity to ensure that startup Views execute before or after your ContextView. That can help the correct bits of bootstrapping happen in the proper order. For example, in your case it sounds as it the GameplayView is triggering before the Context maps bindings, with the result that OnRegister fires too soon, and your signal gets dispatched to thin air. By getting the GameplayView to execute after the ContextView, the mappings will be in place before the signal gets fired.

    Let me know if that helps!
     
  40. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    One of the most requested "features" for StrangeIoC has been a game showing how to build things Strangely. Well, over the holiday break, the elves got to work and wrote StrangeRocks.

    StrangeRocks is a familiar, simple game with highly commented, open-source code. It demonstrates how to build a StrangeIoC project using as much of the Strange toolset as we could cram in! Some features demo'd in StrangeRocks include: Dependency Injection, Mediation, Signals, PostConstructs, Implicit Binding, Pools, Multiple/Cross-Contexts, and Cross-Platform Mapping.

    Note that a few of the demonstrated features are so new they're not even in the Unity Store or on the master branch yet!

    https://github.com/thirdmotion/strangerocks

    Some additional notes:
    - We're working on demonstrating unit testing, but the tests aren't ready yet.
    - Although the game is 2D, we opted not to use the new 2D features as these mightn't be familiar yet to some devs.
    - Should work with all 4.0 versions of Unity.
    - StrangeRocks is brand new and very much a work in progress. If you find a bug or want to request a feature, the link above includes an 'issues' button. Please make any such inquiries there. :)

    Happy New Year!
     
    Last edited: Jan 4, 2014
  41. FREEZX

    FREEZX

    Joined:
    Apr 2, 2013
    Posts:
    64
    Hi and thanks for making StrangeRocks. I've tried to grasp this framework multiple times, and things seem to clear up a little bit.

    However, i'm having issues trying to understand how everything works, so i would recommend doing a few tutorials with full code examples, each tutorial explaining a single feature of Strange.
    Currently i'm only looking to replace all singletons in my code by using Strange, and while exploring the code i'm having thoughts such as:
    -Oh ok i need a Context that will store all bindings.
    -What's with these models?
    -What are these views used for?
    -How do i bind from MonoBehaviour classes?
    etc.
    Until i eventually quit trying and go back to lame but effective and simple singletons.
     
  42. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    FREEZX,

    You've anticipated my intent. StrangeRocks is the beginning of an idea not the end. My plan is to break it down and create video tutorials explaining it feature by feature. So I intend that you should get your wish, but possibly not for a little while yet. I'll do my best!

    In the meantime, we have a Google group, a GitHub page, and this thread to answer all your questions. HTH! :)
     
  43. Zynx87

    Zynx87

    Joined:
    Oct 31, 2013
    Posts:
    9
    I appreciate the help, although I believe there may have been some misunderstanding. I think the issue was more related to first dispatches not executing the callback, as the AddListener hadn't been placed yet (dispatch -> goes to the command's execute method, which adds the listener -> from there on subsequent dispatches do the callback).

    I mistakenly believed I needed to add a listener in my command, to receive the gameobject and the type of click. I couldn't understand how the command would receive the necessary params (gameobject, click) otherwise.

    I saw in the CreateEnemyCommand of StrangeRocks and how it injected the level as an int which seems to be the solution. Now I'm just injecting a GameObject and a byte for click. To be honest I'm still surprised the injection knows that it is referencing the params dispatched by the signal - I would think it wouldn't know what type of byte/int/gameobject I'm referencing. I'm assuming its something that goes on within the Strange framework.

    In any case, thank you for the examples, it has been very helpful. Seeing pools in use is interesting - I am surprised though that both signals and events are used as I thought one replaced the other, but I honestly haven't sat and thought about what sort of benefits might be had of using both.
     
  44. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    Er...do I do that? If so, it's a mistake. Can you point me to the line? StrangeRocks should be completely using Signals. You're correct when you suppose that they largely don't mix-and-match.

    Part of the internal magic of the SignalCommandBinder is to inject the parameters of a Signal into a bound Command. This is really useful for type safety. Just remember that you can't inject two params of the same Type. So a Signal<int,int> (Which is a perfectly legal Signal) can't be bound to a Command, since there's no way at present to differentiate the two ints when injecting.
     
  45. Zynx87

    Zynx87

    Joined:
    Oct 31, 2013
    Posts:
    9
    Aha, thanks for the explanation, I believe I read that in the big how-to but I guess it didn't click.

    As for the events, my mistake! I saw parts in Keyboard Input and OnScreenControlsView using GameInputEvent (not events in that sense) and I've been had.

    Thanks again.
     
  46. WilliamBNewton

    WilliamBNewton

    Joined:
    Nov 18, 2013
    Posts:
    54
    Edit: As Srimarc pointed out in the post following this, this method does not work as intended.

    I've continued to mess around with strange, working with one of the more recent branches on dev, and I just thought I'd share something.

    In the "Pools at last..." entry of the blog, the example of setting up your own instance provider is given. I tried using this implementation with a minor change that would add a unique instance ID to the model I'm pooling so that I could use that as a key in dictionaries. When doing this, however, I realized that this doesn't allow for Injecting into the model. It took me a while to figure out why, realizing that since it wasn't getting an instance from the injectionBinder it was bypassing all the fancy injection magic. So I made a minor change to the instance provider for the model:

    Code (csharp):
    1.  
    2. class GridPanelModelIP : IInstanceProvider
    3. {
    4.         private int identifier = 0;
    5.  
    6.         [Inject]
    7.         public GridPanelModel getModel { get; set; }
    8.  
    9.         public T GetInstance<T>()
    10.         {
    11.                 object instance = GetInstance(typeof(T));
    12.                 T retv = (T)instance;
    13.                 return retv;
    14.         }
    15.  
    16.         public object GetInstance(Type key)
    17.         {
    18.                 getModel.id = identifier++;
    19.                 return getModel;
    20.         }
    21. }
    So this injects the instance instead of creating a new one. Some minor changes to how the pool and model are set up in the context:

    Code (csharp):
    1.  
    2.         protected override void mapBindings()
    3.         {
    4.  
    5.                 ...
    6.  
    7.                 injectionBinder.Bind<IPool<GridPanelModel>>().To<Pool<GridPanelModel>>().ToSingleton();
    8.                 injectionBinder.Bind<GridPanelModelIP>().ToSingleton();
    9.                 injectionBinder.Bind<GridPanelModel>().To<GridPanelModel>();
    10.  
    11.                 ...
    12.  
    13.         }
    14.  
    15.         ...
    16.  
    17.         protected override void postBindings()
    18.         {
    19.  
    20.                 ...
    21.  
    22.                 IPool<GridPanelModel> gridPanelModelPool = injectionBinder.GetInstance<IPool<GridPanelModel>>();
    23.                 gridPanelModelPool.instanceProvider = injectionBinder.GetInstance<GridPanelModelIP>();
    24.                 gridPanelModelPool.inflationType = PoolInflationType.INCREMENT;
    25.  
    26.                 ...
    27.  
    28.         }
    29.  
    And it works!

    So I'm wondering, is this the best way to achieve this, or is this something I should even be doing? If so, whenever pools are pushed to master, I'd recommend having some form of documentation to achieve this effect. It was driving me crazy at around 3 AM last night.
     
    Last edited: Jan 24, 2014
  47. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    WilliamBNewton,

    What you're doing is kind of a hybrid between using the default Injector as InstanceProvider and your own custom InstanceProvider, which is totally fine. Based on your mappings/implementation, though, I *think* you're always using the same instance of GridPanelModel. For this specific use, I'd probably do things a little differently:

    Code (csharp):
    1.  
    2. class IncrementingInstanceProvider : IInstanceProvider
    3.  
    4. {
    5.         private int identifier = 0;
    6.  
    7.         [Inject]
    8.         public IInjectionBinder injectionBinder { get; set; }
    9.  
    10.         public T GetInstance<T>()
    11.         {
    12.                 object instance = GetInstance(typeof(T));
    13.                 T retv = (T)instance;
    14.                 return retv;
    15.         }
    16.  
    17.         public object GetInstance(Type key)
    18.         {
    19.                 object retv = injectionBinder.GetInstance(key);
    20.                 retv.id = identifier++;
    21.                 return retv;
    22.         }
    23. }
    24.  
    The key difference is that I'm injecting the injectionBinder, so we're just kind of overriding the basic functionality. What's nice about this version is that it's totally agnostic about the specific instance type, so it's reusable.
     
  48. WilliamBNewton

    WilliamBNewton

    Joined:
    Nov 18, 2013
    Posts:
    54
    Thanks for point out the flaw in my approach (I don't know how long it would have taken me to realize it was always the same instance). Unfortunately, the code you posted doesn't quite work as is. Seeing how object doesn't have an id, it doesn't work. But going off of what you posted I made up an interface and instance provider that does work as you suggested it should, reusable (with the one downside that it'll only work for objects implementing the IIdBase interface).

    So the instance provider:

    Code (csharp):
    1.  
    2. class IdObjectInstanceProvider : IInstanceProvider
    3. {
    4.     private int identifier = 0;
    5.  
    6.     [Inject]
    7.     public IInjectionBinder injectionBinder { get; set; }
    8.  
    9.     public T GetInstance<T>()
    10.     {
    11.         object instance = GetInstance(typeof(T));
    12.         T retv = (T)instance;
    13.         return retv;
    14.     }
    15.  
    16.     public object GetInstance(Type key)
    17.     {
    18.         IIdBase retv = (IIdBase)injectionBinder.GetInstance(key);
    19.         retv.id = identifier++;
    20.         return retv;
    21.     }
    22. }
    23.  
    And the interface for IIdBase:

    Code (csharp):
    1. public interface IIdBase
    2. {        
    3.     int id { get; set; }
    4. }
    And finally the pooled model that would use it:

    Code (csharp):
    1.  
    2.     public class GridPanelModel : IIdBase, IPoolable
    3.     {
    4.         public int id { get; set; }
    5.  
    6.         #region IPoolable Implimentation
    7.         public void Restore()
    8.         {
    9.         }
    10.  
    11.         public void Retain()
    12.         {
    13.             retain = true;
    14.         }
    15.  
    16.         public void Release()
    17.         {
    18.             retain = false;
    19.         }
    20.  
    21.         public bool retain { get; set; }
    22.         #endregion
    23.     }
    24.  
    I'm just posting this so that if anyone needs to do anything similar they can get some (hopefully?) sample code on how to do this, but again if I'm doing anything that really raises some red flags I'd really appreciate it being pointed out.

    Also, thank you so much for Strange. It's completely changed the way I think about C# and programming in general. While getting use to it is painful, I can tell that it's going to make things a lot easier going forward.
     
    Last edited: Jan 25, 2014
  49. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    Good catch! Sorry...I rushed my answer a bit since we're in a crunch at the office. :)

    I'm delighted you're finding Strange useful. I went through much the same revelatory experience when I started using Robotlegs. The learning curve *is* a bit steep, but once you get used to the workflow it's amazing how much better everything becomes.
     
  50. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    v0.7.0 of Strange has been released at GitHub: https://github.com/thirdmotion/strangeioc

    Will be available soon in the Asset Store.

    Here's the skinny:

    v0.7.0
    - New feature: Pooling
    - New feature: Implicit and Weak Bindings
    - Fix: bug on removal of first Context
    - Fix: Fix to multiple context removal regarding false circular dependencies
    - Fix: Fixed a race condition that allowed some Views to register before the Context was ready
    - Fix: Fixed an EventDispatcher bug where a listener removed during Dispatch still receives callback.
    - Change: Many methods that previously returned ‘object’ now return T.
    - Change: MediationBinder now Mediates Views ‘bottom-up’ so that more deeply nested Views receive injection first.
    - Change: Added a new exception to warn if accidentally mapped a View to something not a MonoBehaviour
    - [Issue #56] Improved Context startup syntax
    - [Issue #53] Exposing a checkbox that allows a View to not register with the Context
    - [Issue #45] Fix for issue that could cause double-instantiation of a mapped Singleton
    - [Issue #44] Prevent empty Constructors from improperly firing when a longer Constructor has been tagged as default
    - [Issue #40] Fixes a null pointer when using CrossContext without MVCSContext
    - [Issue #39] Add some porcelain methods to clarify View/Mediation binding behaviour
    - [Issue #34] Fixed a bug where constructor injection resulted in NPE when mapped ToSingleton
    - [Issue #13] Reflector now throws an Exception when attempting to inject into non-public setter
    - [Issue #1] PostConstructs now support priority ordering