[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):

COMPONENT COMP = new COMPONENT();
engine.addcomponent(COMP);

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

To send a Commmand, just use:

CommandProcessor.getCommandProcessor().SendCommandAssyncronous(COMMAND);

or

CommandProcessor.getCommandProcessor().SendCommandSyncronous(COMMAND);

 

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
  • BindKeyCommand -> BIND A KEYBOARD 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);
CommandProcessor.getCommandProcessor().SendCommandAssyncronous(bk);

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);
this.BindInput(ik2);

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);
                this.World.AddObject(obj3);
            }

            #endregion

            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;
            this.World.AddLight(ld1);
            this.World.AddLight(ld2);
            this.World.AddLight(ld3);
            this.World.AddLight(ld4);
            this.World.AddLight(ld5);
            #endregion

            this.World.CameraManager.AddCamera(cam);            

            ///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
            this.BindInput(ik1);            

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

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

            ///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);
            this.BindInput(ik4);

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

            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.RenderBegin(Matrix.Identity);
            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);
            if(isAllActive)
                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);
            if(isGroup2)
                render.RenderText("Group2", new Vector2(100, 20),Vector2.One, Color.White);
            if(isChangeGroup)
                render.RenderText("Active InputMask " + Groups[index], new Vector2(20, 40),Vector2.One, Color.White);
            if(isComboPressed)
                render.RenderText("Combo Pressed", new Vector2(20, 60),Vector2.One, Color.White);
            render.RenderEnd();

        }

        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);
            CommandProcessor.getCommandProcessor().SendCommandAssyncronous(tof);

            ///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);
            CommandProcessor.getCommandProcessor().SendCommandAssyncronous(tom);

            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

Links

 

 

, , , , , ,

  1. #1 by webpage on 19 de agosto de 2017 - 8:24 pm

    Fantastic goods from you, man. I’ve understand your stuff previous to
    and you’re just extremely fantastic. I really like what you have acquired here, really
    like what you’re saying and the way in which you say it.
    You make it entertaining and you still take care
    of to keep it smart. I can not wait to read
    much more from you. This is actually a great web site.

  2. #2 by michael kors factory outlet online store on 19 de agosto de 2017 - 8:32 pm

    Depending on yourself to make the decisions can really be upsetting and frustrating. Many of us develop this ability over the course of our life. Frankly it takes more than just happening to happen.
    michael kors factory outlet online store http://www.factorymichaelkorssale.com

  3. #3 by patagonia outlet ohio on 19 de agosto de 2017 - 8:33 pm

    Youre not the regular blog author, man. You definitely have something important to contribute to the World Wide Web. Such a wonderful blog. I will revisit again for more.
    patagonia outlet ohio http://hartlaubinsurance.com/patagonia/

  4. #4 by ecco shoes for men outlet on 19 de agosto de 2017 - 8:33 pm

    Im acquiring a weird error on the top of all your pages ==> Warning: require_once(../wp-includes/wordpress/entry.php) [function.require-once]: failed to available stream
    ecco shoes for men outlet http://www.destany.co.uk

  5. #5 by new balance classics sale on 19 de agosto de 2017 - 8:33 pm

    When I view your RSS feed it puts up a bunch of garbage, is the issue on my end?
    new balance classics sale http://www.swetheart.co.uk

  6. #6 by victoria secret promotions in store on 19 de agosto de 2017 - 8:34 pm

    Ive been meaning to read this and just never obtained a chance. Its an issue that Im incredibly interested in, I just started reading and Im glad I did. Youre a terrific blogger, one of the best that Ive seen. This blog certainly has some info on topic that I just wasnt aware of. Thanks for bringing this stuff to light.
    victoria secret promotions in store http://www.victoriassecretoutlet.store

  7. #7 by chanel outlet store locations on 19 de agosto de 2017 - 8:34 pm

    Thank you very much.
    chanel outlet store locations http://www.unilorites.com/bags-outlet/

  8. #8 by michael kors online shop on 19 de agosto de 2017 - 8:34 pm

    You must stop smoking read about the effects.
    michael kors online shop http://www.tmearegion26.com/michael-kors/

  9. #9 by stuart weitzman sale flats on 19 de agosto de 2017 - 8:34 pm

    Im getting all dressed up this morning.
    stuart weitzman sale flats http://www.dolux.co.uk

  10. #10 by nfl shop online on 19 de agosto de 2017 - 8:35 pm

    Hi – in truth huge site you have created. I enjoyed reading this posting. I did want to publish a remark to tell you that the design of this post is very aesthetically delightful. I used to be a graphic designer, now I am a copy editor. I have always enjoyed functioning with information processing systems and am trying to learn code in my free time.
    nfl shop online http://www.nflsale.online

  11. #11 by callaway drivers by year on 19 de agosto de 2017 - 8:35 pm

    Hello, I saw a 3 of your attention-grabbing posted posts and wished to ask if you would be keen on reciprocal pages? Group have weblog about alexis texas ass! Anyway, in my language, there will not be a lot good supply like this.
    callaway drivers by year http://www.callawaygolfsale.online

  12. #12 by Hayford on 19 de agosto de 2017 - 9:08 pm

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

  13. #13 by Hayford on 19 de agosto de 2017 - 9:15 pm

    Oh my goodness! Impressive article dude! Thank you, However I am encountering troubles with your RSS.
    I don’t understand the reason why I am unable to join it.
    Is there anybody having the same RSS problems? Anyone that knows the solution can you kindly respond?

    Thanks!!

1 2.220 2.221 2.222
(não será publicado)