InputHandler undergoing major changes

Hi all,



as handling input was quite complicated in the past the InputHandler is about to be revamped:

  • InputHandlers get organized in a tree structure to allow for composite handlers, e.g. FirstPersonHandler is composed from MouseLookHandler and KeyboardLookHandler
  • InputHandlers can be disabled, which cause them not to update any sub-handlers
  • InputActions are not divided into key and mouse actions any more, to easy controls configuration
  • actions can query the ActionEvent for the trigger that caused their invocation, and they get additional data about it (e.g. keyCode, axis position, button state)



    First changes will be checked in today. I tried to keep most of the old behaviour and have set methods to deprecated. The major effect on jME users are:
  • InputAction becomes an abstract class instead of an interface: 'implements InputAction' must be changed to 'extends InputAction'
  • MouseInputAction becomes a class (change 'implements', too)
  • InputAction got key and allowsRepeats deprecated, these trigger parameters are specified in InputHandler.addAction, now
  • InputHandler.setKeySpeed and setMouseSpeed cannot be used any more, but setActionSpeed



    Please post any questions and objections…

I assume the key and mouse speed can still be set seperately (by setting it on MouseLookHandler and KeyboardLookHandler I would think)?

After updating many tests are very "sluggish", keyboard control speed is significantly different. Some tests no longer even seem to move.

Sounds like the speed question i was asking…  mouse speed generally had to be significantly higher than you'd want key speed.

>>First changes will be checked in today.



I'm hesitant to update cvs now due to the work I had to fix last time, especially when you imply there will be more changes coming.



Could you explain a little more clearly what your refactoring path/timeline is likely to be?



Are you doing it in stages or in one go? if not in one go, fully tested, why not?



The reason I ask is that I am prototyping Jme with a number of alternate control interfaces (biofeedback and 6dof) so changes to the input system impact me greatly atm, and I'd just like to get a better idea of what is happening.



Thanks,

sv

The slow movement (and speed of some other actions) in the tests resulted from use of the deprecated setKeySpeed and from the change in FirstPersonHandler: it is composed from two handlers now, one for mouse, one for keyboard. setKeySpeed and setMouseSpeed thus were not able to set the speed. I have corrected the tests now (and Mojo has increased the default speed in SimpleGame). I will write about new stuff in InputHandler in the Wiki when the implementation is done…

@sonicviz: I have no timeline for the work on jME as it depends on how much spare time I have for it (as the other developers, I guess). But I want to get the input stuff done quickly - so I could say it's a matter of days, most probably below one week…



Another thing, sonicviz:  you seem to have very interesting controllers  :-o attached to your jME apps - do you have additional requirements for the input stuff in jME?

irrisor said:

@sonicviz: I have no timeline for the work on jME as it depends on how much spare time I have for it (as the other developers, I guess). But I want to get the input stuff done quickly - so I could say it's a matter of days, most probably below one week....

Another thing, sonicviz:  you seem to have very interesting controllers  :-o attached to your jME apps - do you have additional requirements for the input stuff in jME?


Thanks for the info. Could you make a post when it's all in and done?

re: controllers. yup...I'm really interested in pushing games into new interaction areas. Fortunately Nintendo thinks the same (ie: revolution), so hopefully all my hacking will be worth the effort:-)

re: requirements...not at the moment. I'll need to refactor based on your changes and then I'll have another look at generic issues that could be extracted. 



thanks,
sv

Input stuff is nearly complete. But I won't have time to finish it the next days (rl kicks in ;)). But I can already tell you that no further API changes are made. So don't fear any adaption work - only easier working with jME because of the new features  :smiley:

Thanks in advance Irrisor for all this work,



wiill you make a special post to explain the modification needed or the first post you made is enough ?

Perhaps I missed something clearly stated elsewhere, but I'm having problems using FirstPersonHandler now.  I changed to the following code and I still can't get the mouse look to work:


input = new FirstPersonHandler(camera, properties.getRenderer());
        input.getKeyboardLookHandler().setActionSpeed(50.0f);
        input.getMouseLookHandler().setActionSpeed(1.0f);
        input.setActionSpeed(1.0f);



Thanks,

darkfrog

SimpleGame does basically the same thing except for your last line.  Other than that it also updates the input every frame using the current tpf value.

Well, here's what I've been using:


package utility;

import java.util.*;
import java.util.logging.*;

import com.jme.app.*;
import com.jme.image.*;
import com.jme.input.*;
import com.jme.math.*;
import com.jme.renderer.*;
import com.jme.scene.*;
import com.jme.scene.state.*;
import com.jme.system.*;
import com.jme.util.*;
import com.jme.util.Timer;
import com.jmex.physics.*;
import com.jmex.sound.openAL.*;

/**
 * <code>PhysicsGame</code> is a implementation of AbstractGame to simplify the process
 * of physics game creation and management.
 *
 * @author Matthew D. Hicks
 */
public class PhysicsGame extends AbstractGame {
    private static String fontLocation = "/com/jme/app/defaultfont.tga";
   
    private Camera camera;
    private Timer timer;
    private Node rootNode;
    private Node fpsNode;
    private Text fps;
    private float timePerFrame;
    private PhysicsWorld physicsWorld;
   
    private FirstPersonHandler input;
   
    private ArrayList commands;
   
    private Game game;
   
    public PhysicsGame(Game game) {
        // TODO remove eventually - game should provide the display info.
        setDialogBehaviour(ALWAYS_SHOW_PROPS_DIALOG);
        this.game = game;
        commands = new ArrayList();
    }
   
    public Camera getCamera() {
        return camera;
    }

    public Game getGame() {
        return game;
    }

    public PhysicsWorld getPhysicsWorld() {
        return physicsWorld;
    }

    public Node getRootNode() {
        return rootNode;
    }

    public float getTimePerFrame() {
        return timePerFrame;
    }

    public Timer getTimer() {
        return timer;
    }

    protected void initSystem() {
        JoystickManager.initialize();
        display = DisplaySystem.getDisplaySystem(properties.getRenderer());
        display.createWindow(
                properties.getWidth(),
                properties.getHeight(),
                properties.getDepth(),
                properties.getFreq(),
                properties.getFullscreen());
        display.getRenderer().setBackgroundColor(ColorRGBA.black);
       
        camera = display.getRenderer().createCamera(display.getWidth(), display.getHeight());
        camera.setFrustumPerspective(
                45.0f,
                (float)display.getWidth() / (float)display.getHeight(),
                1.0f,
                1000.0f);
        Vector3f location = new Vector3f(0.0f, 0.0f, 0.0f);
        Vector3f left = new Vector3f(-1.0f, 0.0f, 0.0f);
        Vector3f up = new Vector3f(0.0f, 1.0f, 0.0f);
        Vector3f direction = new Vector3f(0.0f, 0.0f, -1.0f);
        camera.setFrame(location, left, up, direction);
        camera.update();
        display.getRenderer().setCamera(camera);
       
        input = new FirstPersonHandler(camera, properties.getRenderer());
        input.getKeyboardLookHandler().setActionSpeed(50f);
        input.getMouseLookHandler().setActionSpeed(1f);
       
        timer = Timer.getTimer(properties.getRenderer());
       
        game.setPhysicsGame(this);
    }
   
    protected void initGame() {
        rootNode = new Node("rootNode");
       
        ZBufferState buffer = display.getRenderer().createZBufferState();
        buffer.setEnabled(true);
        buffer.setFunction(ZBufferState.CF_LEQUAL);
        rootNode.setRenderState(buffer);
       
        // Frames Per Second stuff
        AlphaState as = display.getRenderer().createAlphaState();
        as.setBlendEnabled(true);
        as.setSrcFunction(AlphaState.SB_SRC_ALPHA);
        as.setDstFunction(AlphaState.DB_ONE);
        as.setTestEnabled(true);
        as.setTestFunction(AlphaState.TF_GREATER);
        as.setEnabled(true);
        TextureState font = display.getRenderer().createTextureState();
        font.setTexture(
                TextureManager.loadTexture(PhysicsGame.class.getResource(fontLocation),
                Texture.MM_LINEAR,
                Texture.FM_LINEAR));
        font.setEnabled(true);
        fps = new Text("FPS label", "");
        //fps.setForceView(true);
        fps.setTextureCombineMode(TextureState.REPLACE);
        fpsNode = new Node("FPS node");
        fpsNode.attachChild(fps);
        fpsNode.setRenderState(font);
        fpsNode.setRenderState(as);
        //fpsNode.setForceView(true);
       
        physicsWorld = PhysicsWorld.create();
        physicsWorld.setUpdateRate(100);
        physicsWorld.setStepSize(2.0f / 100.0f);
       
        game.init();
       
        rootNode.updateGeometricState(0.0f, true);
        rootNode.updateRenderState();
        fpsNode.updateGeometricState(0.0f, true);
        fpsNode.updateRenderState();
    }
   
    public void start() {
        LoggingSystem.getLogger().log(Level.INFO, "Application started.");
        try {
            getAttributes();
            initSystem();
            assertDisplayCreated();
            initGame();
           
            while ((!finished) && (!display.isClosing())) {
                update(-1.0f);
                render(-1.0f);
                display.getRenderer().displayBackBuffer();
                Thread.yield();
            }
        } catch (Throwable t) {
            t.printStackTrace();
        }

        cleanup();
        LoggingSystem.getLogger().log(Level.INFO, "Application ending.");

        if (display != null) display.reset();
        quit();
    }
   
    protected void update(float interpolation) {
        timer.update();
        timePerFrame = timer.getTimePerFrame();
       
        input.update(timePerFrame);
       
        fps.print("FPS: " + (int)timer.getFrameRate());
       
        physicsWorld.update(timePerFrame);
               
        game.update();
       
        SoundSystem.update(timePerFrame);
       
        rootNode.updateGeometricState(timePerFrame, true);
    }
   
    protected void render(float interpolation) {
        display.getRenderer().clearStatistics();
        display.getRenderer().clearBuffers();
        display.getRenderer().draw(rootNode);
        display.getRenderer().draw(fpsNode);

        game.render();
    }
       
    protected void reinit() {
        // TODO implement
    }
   
    protected void cleanup() {
        // TODO implement
    }
   
    protected void quit() {
        if (display != null) {
            display.close();
        }
        System.exit(0);
    }

    public PropertiesIO getProperties() {
        return properties;
    }
}



I'm sure I'm doing something wrong, but I haven't been able to determine what yet.

Thanks,

darkfrog

I had a similar problem with a custom input handler, tho not sure if this is your problem.



Check your input handler update method:

        if (mouse != null) {

            MouseInput.get().update(); //Important replaces event.setMouse(mouse.getMouseInput());

            mouse.update();

            //System.out.println(this.getClass().getName() + " x: " + mouse.getLocalTranslation().x + " time: " + time);

            for (int i = 0; i < mouseActions.size(); i++) {

                eventList.add(((InputAction) mouseActions.get(i)).getKey());

                actionList.add(mouseActions.get(i));

            }

        }



especially line:

MouseInput.get().update(); //Important replaces event.setMouse(mouse.getMouseInput());


Well, I wouldn't think that would be the problem since I'm using FirstPersonHandler, which is also successfully being used by SimpleGame.



darkfrog

One thing different from SimpleGame is your use of Joystick.  What class is JoystickManager?

True, I wrote that implementation of joystick support.  I tried commenting it out and it didn't make any difference at all.  I can use the keyboard to move around with the arrow keys, but mouse support is not functioning in my game it would seem.



I'm running the most recent build of jME from CVS.  Do I need to initialize anything to do with mouse support?



I think I'll have to start over from scratch on this thing and just pull everything from SimpleGame and BaseGame and just pulling junk out in order to figure out what the problem is.



darkfrog

out of curiosity replace:



input.getMouseLookHandler().setActionSpeed(1f);



with



nput.getMouseLookHandler().setActionSpeed(100f);

Made no difference at all.  It's not that it's moving incredibly slow, it's just not moving at all.



I haven't spent a whole lot of time debugging this.  I need to create a test case and get down to the problem.  I'm not sure what my problem is, but it would seem this is a bug of some sort since I've got all the required aspects in order to make mouse support work, correct?



darkfrog

After quite a bit of looking I finally figured out the problem.  This is something that will need to be put into the wiki when Irrisor gets to that, but with the new input system the following call has to be made:


InputSystem.update();



It is in the while loop in BaseGame and since I extend AbstractGame and wrote this previous to the re-write I did not have this in my code.

Hope this keeps someone else from having the headache I've had. :o

What do you think about some warning message if any calls are made to request information from the InputSystem before InputSystem.update() is called for the first time?  This would be a great way to keep people from having the same problem I had and not knowing how to resolve it.

darkfrog