Go modular for multiple games?

JeffW.'s Avatar

JeffW.

20 Jun, 2013 08:47 AM

hi,

I would appreciate some advice about how to set up my project. Let me describe what I want.

It is an AIR desktop app that shows two games, first game A, then game B, then game A again - but with other content - et cetera.
Game B needs to know a little about Game A, for instance the 'content theme' or the speed.
Both games need to listen to the same sort of data, for instance keyboard input.
Both games share the same views, but only partly, for instance the game over view.
Both games could share the same commands but only partly. For instance, when the keyboard is pressed
a service should send certain data to an external device, but the data are different.

I want to use RL2 because it has a ViewManager which makes it easier for me to work with multiple windows/monitors.

One thing that comes to mind is working with 'modules' which also seems to be possible with RL2, using the ModuleExtension,
but the threads I read about it and the examples I saw look kinda daunting so far.
I also feel I should use a common library that is shared by the two games. And if needed, extended, because another thing that comes to mind is working with inheritance, extending commands and views.
Another thought is create the games in separate RL2 projects - using code from a common lib, compile them as SWFs and load them into a main AIR app.

So I wonder how I can 'add' two games, with their own contexts to a main 'shell' and allow each game to listen to events (signals) dispatched by the main app. Is this possible without going modular?
And do the games need their own contexts? If not, would that not end in a lot of 'if game A ...else if game B...' statements, something I don't prefer?

Sorry if I'm a bit vague and my questions - and the title of this post - may not be very specific, but I hope you can share some thoughts,

thanks,
Jeff.

  1. Support Staff 1 Posted by Ondina D.F. on 20 Jun, 2013 08:56 AM

    Ondina D.F.'s Avatar

    Hey Jeff;

    Just a quick answer, before digesting your entire post;)

    Take a look at the Module Connector, which ist he new way of communicating between multiple contexts.
    https://github.com/robotlegs/robotlegs-framework/tree/master/src/ro...

    Ondina

  2. Support Staff 2 Posted by Ondina D.F. on 20 Jun, 2013 09:15 AM

    Ondina D.F.'s Avatar

    Next iteration:

    Loading the games as Modules or as separate applications is quite similar.

    I also feel I should use a common library that is shared by the two games.

    That’s right, the shared classes should reside inside a library.

    Take a look at my answer here:
    http://knowledge.robotlegs.org/discussions/robotlegs-2/3285-help-wi...

    to see how you can make use of different configuration classes.

  3. Support Staff 3 Posted by Ondina D.F. on 20 Jun, 2013 09:20 AM

    Ondina D.F.'s Avatar

    And do the games need their own contexts?

    Yep, I’d definitely let each game have its own context!

  4. 4 Posted by JeffW. on 20 Jun, 2013 09:44 AM

    JeffW.'s Avatar

    Thanks, Ondina.
    What I just don't see yet is - when you work with modules - how you 'load' and 'start' and also 'reset' or 'unload' a module. Usually, when I have one project with one context there is smt like a startup command. I guess that if I add the two contexts and a shell to one project, I should move startup code to the shell, dispatch an event from the shell and if a module listens for it, then that module does its thing?

    When I want to switch to the other module, how would I stop/cleanup/reset/unload/whatever the first module?

  5. Support Staff 5 Posted by Ondina D.F. on 20 Jun, 2013 09:59 AM

    Ondina D.F.'s Avatar

    What I just don't see yet is - when you work with modules - how you 'load' and 'start' and also 'reset' or 'unload' a module.

    In Flex I use a ModuleLoader. Should I paste a code snippet? Or, are you rather asking which shell class should load/unload the module? Please clarify this.

    When I want to switch to the other module, how would I stop/cleanup/reset/unload/whatever the first module

    OK, I’ll try to explain this. Just need a little bit more time.
    I actually have an example where I’m trying different things out, like the new rl2 ModuleConnector. I can’t show you the entire code yet, because it’s still a mess :) But I’ll paste some relevant code (note: it’s Flex though)

  6. 6 Posted by JeffW. on 20 Jun, 2013 10:17 AM

    JeffW.'s Avatar

    In Flex I use a ModuleLoader
    Are you referring to Flex modules or to RL modules now? I'm working in a pure as3 project as you remember correctly ( I ported the already existing game A from rl1 to rl2 the other day, quite easy to do btw).

    I was asking about how the (one and only) shell loads a RL module. Can I use RL modules in an as3 project? Then I would love to see a very basic example albeit in flex :)

    I still wonder if I need modules at all, why not just each game its own context. If I do that could I still have communication between my main app and the two game contexts, without modules?

    Btw, how do you do that quote format in a post? Don't see it in the formatting help...

  7. Support Staff 7 Posted by Ondina D.F. on 20 Jun, 2013 10:42 AM

    Ondina D.F.'s Avatar

    There are no “rl modules”. When you (everyone) say “rl modules”, you actually mean multiple contexts. You could (but you shouldn’t) let each and every component in your app have its own context. All you need to do is to let rl know what to use as a contextView.

    So, if you add GameA to the stage, you instantiate the context with GameA (or a sub-component) as the contextView.

    I still wonder if I need modules at all, why not just each game its own context. If I do that could I still have communication between my main app and the two game contexts, without modules?

    I’m currently writing down an explanation for you about how to configure the 3 contexts and how to communicate between them. I’ll be back… sooner or later;)

  8. Support Staff 8 Posted by Ondina D.F. on 20 Jun, 2013 11:26 AM

    Ondina D.F.'s Avatar

    GamesCenter is the main application, and it will load GameA and GameB alternatively.
    We are talking about 3 contexts: ShellContext, GameAContext, and GameBContext.

    In order to work with multiple contexts, we need the ModularityExtension, which is part of the MVCSBundle. With the ModularityExtension installed we’ll have access to the IModuleConnector.

    Now, let’s configure the 3 contexts.

    ShellContext

    public function ShellContext(view:DisplayObjectContainer)
    {
        context = new Context()
            .install(MVCSBundle)
            .configure(ControllersConfig)
            .configure(ModelsConfig)
            .configure(ServicesConfig)
            .configure(MediatorsConfig)
            .configure(ShellConnectorConfig);
            .configure(new ContextView(view));
    }
    

    GameAContext

    public function GameAContext(view:DisplayObjectContainer)
    {
        context = new Context()
            .install(MVCSBundle)
            .configure(ControllersConfig)
            .configure(ModelsConfig)
            .configure(ServicesConfig)
            .configure(MediatorsConfig)
            .configure(GameAConnectorConfig);
            .configure(new ContextView(view));
    }
    

    GameBContext

    public function GameBContext(view:DisplayObjectContainer)
    {
        context = new Context()
            .install(MVCSBundle)
            .configure(ControllersConfig)
            .configure(ModelsConfig)
            .configure(ServicesConfig)
            .configure(MediatorsConfig)
            .configure(GameBConnectorConfig);
            .configure(new ContextView(view));
    }
    
    public class ShellConnectorConfig implements IConfig
    {
        [Inject]
        public var moduleConnector: IModuleConnector;
        
        public function configure():void
        {
            moduleConnector.onDefaultChannel()
            .relayEvent(ModularConnectorEvent.SHELL_TO_MODULES);
                        
            moduleConnector.onChannel('A-and-B')
            .receiveEvent(ModularConnectorEvent.A_TO_B_MESSAGE);
                
            moduleConnector.onChannel('A-and-B')
            .receiveEvent(ModularConnectorEvent.B_TO_A_MESSAGE);
            }
    }
    
    public class GameAConnectorConfig implements IConfig
    {
        [Inject]
        public var moduleConnector: IModuleConnector;
        
        public function configure():void
        {
            moduleConnector.onDefaultChannel()
            .receiveEvent(ModularConnectorEvent.SHELL_TO_MODULES);
                        
            moduleConnector.onChannel('A-and-B')
            .relayEvent(ModularConnectorEvent.A_TO_B_MESSAGE);
                
            moduleConnector.onChannel('A-and-B')
            .receiveEvent(ModularConnectorEvent.B_TO_A_MESSAGE);
        }
    }
    
    public class GameBConnectorConfig implements IConfig
    {
        [Inject]
        public var moduleConnector: IModuleConnector;
        
        public function configure():void
        {
            moduleConnector.onDefaultChannel()
            .receiveEvent(ModularConnectorEvent.SHELL_TO_MODULES);
                        
            moduleConnector.onChannel('A-and-B')
            .receiveEvent(ModularConnectorEvent.A_TO_B_MESSAGE);
                
            moduleConnector.onChannel('A-and-B')
            .relayEvent(ModularConnectorEvent.B_TO_A_MESSAGE);
        }
    }
    

    Now, the ShellContext can dispatch ModularConnectorEvent.SHELL_TO_MODULES, and any class inside GameAContext that registered a listener for it, can hear that event. ShellContext listens to the events dispatched by GameA and GameB on channel 'A-and-B'. GameA listens for ModularConnectorEvent.B_TO_A_MESSAGE, and GameB to ModularConnectorEvent.A_TO_B_MESSAGE. Yes, I know, it makes you dizzy :)

    Now let’s add the 2 “modules”. If you’re not using Flex, you’ll probably add your GameA or GameB using addChild(), right? Of, course you could load them as swfs, too.

    Anyway, onAddedToStage you do this in GameA:

    context = new GameAContext (this);

    and, you guessed it, context = new GameBContext (this); in GameB.

    When you remove a game from the shell, its context will be automatically destroyed, and re-created if you add it again to the stage.

    Try it out in a simple project, and let me know how it goes.
    I don’t think I’ll have the time today to make a pure as3 example, but I’ll attach my Flex example as soon as I get it cleaned up. If you want, you can attach your as3 app built as shown above, and I’ll take a look at it.

  9. 9 Posted by JeffW. on 20 Jun, 2013 11:42 AM

    JeffW.'s Avatar

    omg, thanks for this code, I could never have figured this out :)
    I'll let you know how it goes!

  10. Support Staff 10 Posted by Ondina D.F. on 20 Jun, 2013 02:14 PM

    Ondina D.F.'s Avatar

    Hehe, no problem.

    I'm attaching the Flex (actually AIR) example I've talked about. It's still work in progress, but perhaps you can understand better what I meant in my previous messages after taking a look at the classes. If you want, I can attach the released AIR app, too, to see it in action.

  11. Support Staff 11 Posted by creynders on 20 Jun, 2013 02:25 PM

    creynders's Avatar

    Very cool, Ondina. Maybe it's a good idea to make this an official RL
    bare-bones example on modular communication?
    It's a question which comes back a lot.
    We already added you to the RL contributors team on Github. If you push
    this to a GH repo I can fork it to the RL contributors team.

  12. Support Staff 12 Posted by Ondina D.F. on 20 Jun, 2013 02:31 PM

    Ondina D.F.'s Avatar

    Ooh, thanks, creynders !! :)

    Maybe it's a good idea to make this an official RL bare-bones example on modular communication?

    But it’s kind of a doodle, really. I should at least add comments and make it look nicer, no?

  13. 13 Posted by JeffW. on 20 Jun, 2013 02:47 PM

    JeffW.'s Avatar

    Great, Ondina, I got your example running and will try to convert it to an as3 project. Or create one of my own based on your example.

  14. Support Staff 14 Posted by Ondina D.F. on 20 Jun, 2013 03:01 PM

    Ondina D.F.'s Avatar

    Please try to convert my example to an as3 project. If my example is meant to become an official demo, as creyenders suggested, it would be really cool to have it in 2 flavors: Flex and pure as3 ;) I’ll have to refine my example a bit, so, maybe you should wait a few days?

  15. 15 Posted by JeffW. on 20 Jun, 2013 03:30 PM

    JeffW.'s Avatar

    Btw, what was a bit confusing in your project to me initially is that I needed to compile the modules into SWFs - by making them my default application, and that they ended up in my bin folder so that I had to move them to the associative component folder or simply change the path to the module SWFs.

  16. Support Staff 16 Posted by Ondina D.F. on 20 Jun, 2013 03:42 PM

    Ondina D.F.'s Avatar

    I don’t know what IDE you use, but in FlashBuilder all you’d have to do would be to add the FlexModules to the project. (Project->Properties->Flex Modules->add – where you choose the views in question) In Flex they land in the bin-debug folder, but the compiler knows about them…

    simply change the path to the module SWFs

    Yes, that’s all that’s required, actually.

  17. 17 Posted by JeffW. on 20 Jun, 2013 03:43 PM

    JeffW.'s Avatar

    Oh, I wasn't familiar with that, never worked with FlexModules :)

  18. Support Staff 18 Posted by Ondina D.F. on 20 Jun, 2013 03:50 PM

    Ondina D.F.'s Avatar

    The Modules are: SomeModule, SomeOtherModule and AnotherModule. I’ll have to change the comment in ShellContextView accordingly and also to mention how to add them in FlashBuilder. Maybe I'll come up with better names for the modules, as well.

  19. 19 Posted by JeffW. on 20 Jun, 2013 03:53 PM

    JeffW.'s Avatar

    Yes, I compiled all three of them the unorthodox way and they are being loaded. Will check out the FB feature of adding modules. The names sound fine to me, I would just add some more comments.

  20. Support Staff 20 Posted by Ondina D.F. on 20 Jun, 2013 03:56 PM

    Ondina D.F.'s Avatar

    Ok, Jeff.
    Any other questions will be answered tomorrow :)

    Untill then, you can join our "party" at:
    http://knowledge.robotlegs.org/discussions/problems/2516-let-the-ro...

  21. Support Staff 21 Posted by creynders on 20 Jun, 2013 05:04 PM

    creynders's Avatar

    @ondina yeah, obviously there's some refactoring and commenting to do, but
    I really like the idea of having a very clear, basic example w/o any
    distracting extra stuff. I myself always struggle with filtering out the
    cruft from the examples, when I'm learning something new. And it shows
    people there's really no reason not to be using modules. They scare people
    unnecessarily.
    >
    >

  22. Support Staff 22 Posted by Ondina D.F. on 21 Jun, 2013 09:53 AM

    Ondina D.F.'s Avatar

    @creynders

    I really like the idea of having a very clear, basic example w/o any distracting extra stuff.

    Yes, good point. Less work for me as well.

    There is a little problem with setting up the ModuleConnector, and I want to discuss it with you.
    I understand that is difficult to find a way to prevent circular relays, which happen if you have a setting like this:

    moduleConnector.onChannel('A-and-B')
    .receiveEvent(ModularConnectorEvent.SOME_TYPE);
    
    moduleConnector.onChannel('A-and-B')
    .relayEvent (ModularConnectorEvent.SOME_TYPE);
    

    The question is: If I load several instances of a Module, how can I communicate between them?
    I tried lots of things to make it work, even using guards, but nothing helped, and even if it did, it would be a pretty convoluted logic.
    I’m sure I’m missing something obvious. Help me see it:)

    With the ScopedEventDispatcherExtension it was possible to communicate between instances of the same module. I’ll have to make a lot of changes in my projects where I used the ScopedEventDispatcherExtension.

    Don’t get me wrong, relaying events is very cool!

  23. Support Staff 23 Posted by creynders on 21 Jun, 2013 10:37 AM

    creynders's Avatar

    With the ScopedEventDispatcherExtension it was possible to communicate between instances of the same module.

    How exactly did that work? The ModuleConnector basically works the same way as the ScopedEventDispatcher behind the scenes. I mean, the limitations of both should be the same.

  24. 24 Posted by JeffW. on 21 Jun, 2013 10:39 AM

    JeffW.'s Avatar

    hi Ondina, creynders,

    here is a pure as3 version of your demo, Ondina, I'm sorry I couldn't wait for your cleaned up version.
    This one is an even more stripped down. It only has a shell and two modules, no popups or extra windows. It seems to work well for me but I hope you can take a look at the code to see if it looks okay?

    Jeff.

  25. Support Staff 25 Posted by Ondina D.F. on 21 Jun, 2013 10:43 AM

    Ondina D.F.'s Avatar

    Hi Jeff, I'll take a look at it!

  26. Support Staff 26 Posted by Ondina D.F. on 21 Jun, 2013 11:26 AM

    Ondina D.F.'s Avatar

    How exactly did that work?

    Easy:
    If you wanted a channel other than the default one, you did this in the parent:

    context.extend(new ScopedEventDispatcherExtension("channelOne"));
    

    Then injected the dispatcher into the Modules, which inherited from the parent:

    [Inject(name="channelOne")]
    public var someSharedDispatcher:IEventDispatcher;
    

    then added listeners to and dispatched events on the someSharedDispatcher.

    I’m not saying that I like named injections, but it worked well.

    A pretty long explanation is here:
    http://knowledge.robotlegs.org/discussions/robotlegs-2/68-modular-a...

    I mean, the limitations of both should be the same.

    No, they are not the same, but it would take me too long to list all the differences, or limitations…

  27. Support Staff 27 Posted by Ondina D.F. on 21 Jun, 2013 11:37 AM

    Ondina D.F.'s Avatar

    @Jeff At first sight, it looks perfect :) I’ll go through all the classes and let you know if there is anything that should be changed. We can synchronize our projects after I’m done with refactoring and commenting mine .

  28. Support Staff 28 Posted by creynders on 21 Jun, 2013 12:59 PM

    creynders's Avatar

    @ondina I don't get it. The ModuleConnector really just wraps an API on top of the same mechanism as the ScopeEventDispatcherExtension: it too uses a named mapping for an event dispatcher. It literally does the same. That's why I don't quite get how something you did with the SEDE wouldn't be possible with the module connector.

    This

    moduleConnector.onChannel('A-and-B').receiveEvent(ModularConnectorEvent.SOME_TYPE);
    eventDispatcher.addEventListener(ModularConnectorEvent.SOME_TYPE);
    moduleConnector.onChannel('A-and-B').relayEvent (ModularConnectorEvent.SOME_TYPE);
    eventDispatcher.dispatchEvent(ModularConnectorEvent.SOME_TYPE);
    

    is the equivalent of

    [Injector(name='A-and-B')]
    public var eventDispatcher:IEventDispatcher;
    eventDispatcher.addEventListener(ModularConnectorEvent.SOME_TYPE);
    eventDispatcher.dispatchEvent(ModularConnectorEvent.SOME_TYPE);
    
    Surely that caused an infinite loop too?
  29. Support Staff 29 Posted by Ondina D.F. on 21 Jun, 2013 01:17 PM

    Ondina D.F.'s Avatar

    Surely that caused an infinite loop too?

    I tried this already, and, yes, it caused an infinite loop. But I’ll test it again. Maybe I did something wrong after desperately trying other stuff…
    I’ll be right back..

  30. Support Staff 30 Posted by Ondina D.F. on 21 Jun, 2013 01:21 PM

    Ondina D.F.'s Avatar

    hmm, no luck.. should we move this to github, open an issue?

Comments are currently closed for this discussion. You can start a new one.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac