[PloobsEngine] Tutorial 1 – Components and Input System

This second tutorial will teach you about the PloobsEngine Components and introduce the  Input handler System. (tutorials series here)

The idea is to make a little introduction describing the Components design, after show how the Input handler works and finish with a code sample.

What the hell are the PloobsEngine Components ?

Components are an abstraction to funcionalities that should be global to all screens everywhere. Its was designed to be completely independent and pluggable. To accomplish this task we used a slightly modified version of the Design Pattern Command Processor.

Every component extends the abstract class IComponent. You only communicate with components sending Commands objects through the CommandProcessor to them. The components contact our objects using specifics callbacks. Every Component used must be registered before, the PloobsEngine automatically register the Input and the DebugRender Components others are user responsability to register.

Remember, this is very important: Components are global, they are not binded to screen.

The CommandProcessor is a static class that is responsible to deliver Commands to the right component. It can send a command syncronous (the command is send exactely when the function is called) or assyncronous (the engine will send  the command as soon as possible, ALWAYS PREFFER THIS METHOD OF SENDING COMMANDS, IF YOU CAN — this method does not use threads)

As said, before using a component you need to add them, when you dont need it anymore you can remove

To add a Component just use this (we normally add components in the Initialize method of the IScreen, it has a parameter called EngineStuff that you use to add/remove and check if a component exists):


Where COMPONENT is the actual component. After this we can send commands.

To send a Commmand, just use:





The input advanced Component

This component is reponsible for handling Keyboard and Mouse. You send a command to this component describing an event (like Key B being pressed or Key O released) and the component will call a callback when the event happens (the process of associating a key event to a callback is called binding in the engine).

The input advanced component recieves the following commands:

  • ClearInputsCommand -> CLEAR ALL THE BINDINGS
  • BindMouseCommand -> BIND A MOUSE HANDLER
  • TurnOffInputMaskCommand -> TURN OF AN INPUT MASK
  • TurnOnInputMaskCommand -> TURN OF AN INPUT MASK

The BindKeyCommand for example recieve two parameters, the first is an object called SimpleConcreteKeyboardInputPlayable that describes teh key event and the callback to call when the event is fired. The second is the action (add or remove the event). BindMouseCommand works in a similar way.

We can separate the inputs events (key and mouse handlers) in groups and enable and disable them when needed. A simple example is when we want to disable all character inputs when we enter in a in game menu (just put the inputs in diferrent groups and enable/disable them when needed).

To define the groups we use an InputMask (whe can pass it as a parameter in the SimpleConcreteKeyboardInputPlayable for example). It is a bit field. The engine has a active InputMask, if it input mask matchs with your event InputMask.

One example make everything clear:

Suppose that the engine has the 101 inputmask active.

And suppose that we bind 2 key events, the first has the input mask 100 and the second has 010.

Every frame The engine will check its Inputmask againt the key handlers input mask.

This is done by bit. The 101 in the engine InputMask means that the group 100 and the goup 001 are on and the 010 is off.

So, only the first handler will have the chance to be activated (the engine first check the InputMask of the registered event handler, if successful, it checks if the event really needs to be fired)

Internaly (if you are curious to know =P) the engine does this calculation: 101 ^ 100 = 100 Activate (100 = 100) for the first event handler and 101 ^ 010 != 101 (010 != 101) Not Activate for the second.

You can change the engine InputMask using TurnOffInputMaskCommand and TurnOnInputMaskCommand (you can use bit field operation to set the mask)

There are some special InputMasks like GALL that is 11111111111 (default in engine — means that all groups are enabled)

GSYSTEM is a very special inputmaks, cause it CANT be turned off (even if you try), if you bind a key handler to it, it will have the chance to be fired always.

The ClearInputsCommand, as the name said, clears ALL the event handlers =P.

An example of registering a event handler is:

SimpleConcreteKeyboardInputPlayable ik4 = new SimpleConcreteKeyboardInputPlayable(StateKey.DOWN, new Keys[] { Keys.LeftControl, Keys.U }, Multiple);
BindKeyCommand bk = new BindKeyCommand(ik4, BindAction.ADD);

We created the SimpleConcretekeyboardInputPlayable passing the state of the key, the Keys (for Combo, you can use a single key if you want) and the Callback. After we create the command and send it using the Command Processor (we used the default InputMask GSYSTEM).

The callback is a function with the following signature:

public void Multiple(InputPlayableKeyBoard ipk)
/// Handle the event =P

There lots of ways to create the SimpleConcretekeyboardInputPlayable (dont set the event on the construtore, use the property to do this, it is easier =P)

REMEMBER, COMPONENTS ARE GLOBAL, the binding will remains until you delete it.

BUT, sometimes, we want to bind the inputs for just one screen, when the screen goes away, we want to remove the bindings (it is boring to override the clean up method of the screen to done this …). So the engine provides an alternate solution, instead of sending the commands directely to the input component, you can use the method BindInput of the IScreen to does the job.

SimpleConcreteKeyboardInputPlayable ik2 = new SimpleConcreteKeyboardInputPlayable(StateKey.PRESS, Keys.Y, g2,InputMask.G2);

In this case we “atached” the SimpleConcretekeyboardInputPlayable to the screen, that will take care of makind the binding and removing it when the screen goes away. (look, in this case we used the G2 InputMask — this is not a special mask, can be used for anything). The binding made this way can be removed before the screen goes away, just call the method RemoveInputBinding()


Code Example

The following code sample show how to use the InputAvanced. The event key T Pressed is binded to the InputMask G1, the event key Y Pressed is binded to the InputMaks G2 and the Key Space is binded to the GSystem (always active).

When you press Space you change the engine active InputMask to G1 and G2 (and you see that Key T Pressed does not work when G1 is active)

There is also a binding showing how to use the Combos.

The code is:

using PloobsEngine.SceneControl;
using PloobsEngine.Physics;
using PloobsEngine;
using PloobsEngine.Cameras;
using PloobsEngine.Input;
using Microsoft.Xna.Framework;
using PloobsEngine.Physics.Bepu;
using PloobsEngine.Modelo;
using PloobsEngine.Material;
using PloobsEngine.Commands;
using PloobsEngine.Light;
using Microsoft.Xna.Framework.Input;

namespace IntroductionDemo4._0
    /// InputScreen
    public class KeyboardInputScreen: IScene
        ICamera cam;        

        protected override void SetWorldAndRenderTechnich(out IRenderTechnic renderTech, out IWorld world)
            world = new IWorld(new BepuPhysicWorld(-0.097f,true), new SimpleCuller());
            DeferredRenderTechnicInitDescription desc = DeferredRenderTechnicInitDescription.Default();
            desc.DefferedDebug = false;
            desc.UseFloatingBufferForLightMap = false;
            renderTech = new DeferredRenderTechnic(desc) ;
        protected override void  LoadContent(PloobsEngine.Engine.GraphicInfo GraphicInfo, PloobsEngine.Engine.GraphicFactory factory, IContentManager contentManager)
            base.LoadContent(GraphicInfo, factory, contentManager);

            #region Models

                SimpleModel sm = new SimpleModel(factory,"..\\Content\\Model\\cenario");
                IPhysicObject pi = new TriangleMeshObject(sm,Vector3.Zero, Matrix.Identity,Vector3.One,MaterialDescription.DefaultBepuMaterial());
                DeferredNormalShader shader = new DeferredNormalShader();
                shader.SpecularIntensity = 0;
                shader.SpecularPower = 0;
                IMaterial mat = new DeferredMaterial(shader);
                IObject obj3 = new IObject(mat, sm, pi);


            cam = new CameraFirstPerson(GraphicInfo.Viewport);
            cam.FarPlane = 3000;

            #region NormalLight
            DirectionalLightPE ld1 = new DirectionalLightPE(Vector3.Left, Color.White);
            DirectionalLightPE ld2 = new DirectionalLightPE(Vector3.Right, Color.White);
            DirectionalLightPE ld3 = new DirectionalLightPE(Vector3.Backward, Color.White);
            DirectionalLightPE ld4 = new DirectionalLightPE(Vector3.Forward, Color.White);
            DirectionalLightPE ld5 = new DirectionalLightPE(Vector3.Down, Color.White);
            float li = 0.4f;
            ld1.LightIntensity = li;
            ld2.LightIntensity = li;
            ld3.LightIntensity = li;
            ld4.LightIntensity = li;
            ld5.LightIntensity = li;


            ///Bind a Key event (combination of Key + state(pressed, Released ...) + inputMask ) to a function
            SimpleConcreteKeyboardInputPlayable ik1 = new SimpleConcreteKeyboardInputPlayable(StateKey.PRESS, Keys.T, g1, InputMask.G1);
            ///When you use the method Bind of a IScreen, The key event will be sent by the engine while this screen remains added in the ScreenManager.
            ///TO create a Gloal Input (Keep working even if the screen goes away), see the DemosHomeScreen.cs

            SimpleConcreteKeyboardInputPlayable ik2 = new SimpleConcreteKeyboardInputPlayable(StateKey.PRESS, Keys.Y, g2,InputMask.G2);

            ///The SYSTEM Mask is Always On (cant be turned off)
            SimpleConcreteKeyboardInputPlayable ik3 = new SimpleConcreteKeyboardInputPlayable(StateKey.PRESS, Keys.Space, ChangeGroup,InputMask.GSYSTEM);

            ///StateKey.DOWN mean when the key is down the event will be fired --looooots of times(samae as UP)
            ///StateKey.PRESS is fired ONCE when the key is pressed (same as RELEASE)
            ///WHEN USING COMBOS, use DOWN AND UP (better for precision)
            ///The parameter EntityType is not used internaly
            SimpleConcreteKeyboardInputPlayable ik4 = new SimpleConcreteKeyboardInputPlayable(StateKey.DOWN, new Keys[] { Keys.LeftControl, Keys.U }, Multiple);

            ///Send a command (design pattern) to the InputSystem to change the InputMask
            TurnOnInputMaskCommand tom = new TurnOnInputMaskCommand(InputMask.GALL);

            isAllActive = true;

            this.RenderTechnic.AddPostEffect(new AntiAliasingPostEffect());

        bool isAllActive;
        bool isGroup1;
        bool isGroup2;
        bool isChangeGroup;
        bool isComboPressed;

        protected override void  Draw(GameTime gameTime, RenderHelper render)
 	        base.Draw(gameTime, render);

            render.RenderText("Demo: Keyboard Input", new Vector2(GraphicInfo.Viewport.Width - 515, 15),Vector2.One ,Color.White);
            render.RenderText("Press Space to change the Active Input Mask (G1 or G2)", new Vector2(GraphicInfo.Viewport.Width - 515, 40), Vector2.One, Color.White);
            render.RenderText("Press T to use a G1 InputMask Input", new Vector2(GraphicInfo.Viewport.Width - 515, 60), Vector2.One, Color.White);
            render.RenderText("Press Y to use a G2 InputMask Input", new Vector2(GraphicInfo.Viewport.Width - 515, 80), Vector2.One, Color.White);
            render.RenderText("Press Ctrl + U TO use a Combo (Registered in all Masks)", new Vector2(GraphicInfo.Viewport.Width - 515, 100), Vector2.One, Color.White);
                render.RenderText("Group ALL Active", new Vector2(20, 40),Vector2.One, Color.White);
            if (isGroup1)
                render.RenderText("Group1", new Vector2(20, 20), Vector2.One, Color.White);
                render.RenderText("Group2", new Vector2(100, 20),Vector2.One, Color.White);
                render.RenderText("Active InputMask " + Groups[index], new Vector2(20, 40),Vector2.One, Color.White);
                render.RenderText("Combo Pressed", new Vector2(20, 60),Vector2.One, Color.White);


        public void g1(InputPlayableKeyBoard ipk)
            isGroup2 = false;
            isComboPressed = false;
            isGroup1 = true;
        public void g2(InputPlayableKeyBoard ipk)
            isGroup1 = false;
            isComboPressed = false;
            isGroup2 = true;

        InputMask[] Im = new InputMask[] { InputMask.G1, InputMask.G2, InputMask.GALL };
        string[] Groups = new string[] { "G1", "G2", "GALL" };
        int index = 2;
        public void ChangeGroup(InputPlayableKeyBoard ipk)
            index = (index + 1) % 3;

            ///Turn Off all Masks (we cant turn the GSYSTEM MASK, even if we try ....)
            TurnOffInputMaskCommand tof = new TurnOffInputMaskCommand(InputMask.GALL);

            ///Turn only the right mask
            ///Masks are Bit Field, You can turn more than one using InputMask.GALL | InputMask.G1 for example
            ///The TurnOnInputMaskCommand Just combine its actual mask with the mask provided, It does not TURN OFFthe active masks (this is the reason why i sent a turn off mask before)
            TurnOnInputMaskCommand tom = new TurnOnInputMaskCommand(Im[index] | InputMask.GNONE);

            isAllActive = false;
            isGroup1 = false;
            isGroup2 = false;
            isComboPressed = false;
            isChangeGroup = true;

        public void Multiple(InputPlayableKeyBoard ipk)
            isGroup1 = false;
            isGroup2 = false;
            isComboPressed = true;


The code for this demo can be found in our Introduction Demos package, you can download it here. (there are lots of others demos in this package, we will explain each of them in the next tutorials)

Any doubts, critics, suggestions, pls go to our forum or leave a comment here.

See you guys =P




, , , , , ,

  1. #1 by pron on 28 de junho de 2017 - 7:45 am

    Howdy very cool site!! Man .. Excellent .. Superb ..
    I will bookmark your web site and take the
    feeds also? I am glad to search out a lot of useful information here in the submit, we want work out more
    techniques on this regard, thank you for sharing. . . .

    . .

  2. #2 by nipslip on 28 de junho de 2017 - 7:48 am

    I do believe all the ideas you’ve offered for your post. They’re very convincing and will definitely work.
    Nonetheless, the posts are too quick for novices.
    Could you please lengthen them a little from
    subsequent time? Thanks for the post.

  3. #3 by camel toe on 28 de junho de 2017 - 7:56 am

    Hey there! I’m at work surfing around your blog from my new apple iphone!
    Just wanted to say I love reading through your blog and look forward to
    all your posts! Keep up the superb work!

  4. #4 by auto insurance in oxford MI on 28 de junho de 2017 - 7:57 am

    What a beautiful post this is.Truly so heartfelt, and such wonderful images to go with it.Thank you so much for sharing with us this week at The Creative Exchange.Have a wonderful evening!lisa.

  5. #5 by self catering accommodation in Botswana on 28 de junho de 2017 - 8:12 am

    I’ve recently started a website, the information you offer on this web site has helped me greatly. Thanks for all of your time & work.

  6. #6 by Arlington on 28 de junho de 2017 - 8:37 am

    Hi there everyone, it’s my first pay a visit at this website, and article is truly fruitful in favor of me, keep up posting such
    articles or reviews.

  7. #7 by Depeche Mode on 28 de junho de 2017 - 8:47 am

    Thank you for the auspicious writeup. It in fact was a amusement account
    it. Look advanced to more added agreeable from you!
    By the way, how could we communicate?

  8. #8 by bigfoot real on 28 de junho de 2017 - 9:04 am

    Excellent post. Keep writing such kind of info on your site.
    Im really impressed by your site.
    Hey there, You’ve performed a fantastic job. I will certainly digg it and individually suggest to my friends.
    I am sure they’ll be benefited from this site.

1 2.126 2.127 2.128
(não será publicado)