REGoth-project / REGoth

OpenSource-Reimplementation of the zEngine, used by the game "Gothic"
GNU General Public License v3.0
630 stars 62 forks source link

Two concepts for reworking input handling #343

Open markusobi opened 6 years ago

markusobi commented 6 years ago

To make our Input system more maintainable/extendable I have worked out two concepts, which might help in this regard.

Concept 1: Views handle Input.

InputActions will be handled by Views ONLY. The topmost View on the screen will receive all input. Each View can decide to handle the InputAction and/or consume it independently.

A View can decide to handle+consume only specifiy InputActions and let all others through. Whether or not a View will consume InputActions will in most cases depend on it's visibility. I.e. when the Loading Screen is visible no binding shall be fired, except for the console (which is on top of the Loading Screen). If there is no associated View for a binding, one can add a empty Pseudo-View at the right position in the View-Tree. i.e. to be able to open the stats screen while the inventory is open, an invisible view can be inserted before the inventory view.

Advantage: Implicit order of InputAction handling/consumption through View draw order. Disadvantages: bound to View/Vieworder. Visibility of Pseudoviews has to be set somewhere.

Concept 2: Passing Input through a separate Chain of InputHandler.

This Concept is very similar to Concept 1, but in this case we are not using the View-Tree, but a separate chain of InputActing handling objects. Analog to Concept 1, an InputAction is passed through the Chain, where each object can decide whether to consume and/or handle it. A draft of how such chain could look like is found here:keyhandling.dot.pdf.

Advantages: Independent of View-Tree.

Goal

The goal behind these concepts is to make it easier to add new input consuming Views without having to add isViewXYVisible all over the place (currently all over the place in different InputAction handlers).

Feedback is welcome.

frabert commented 6 years ago

I personally prefer the first approach, makes implementing Views easier and puts less responsibility on them

aaeberharter commented 6 years ago

I am not sure whether the Views are suitable for this. I thought about a hierarchy of context classes. If the parent context gets enabled (e.g. menu shows up) all child contexts receive no more input. This is more or less the same as your suggestion but uses a separate class. Then if a view (or any other gameplay component) requires input it would activate its context. Keep in mind that not all views actually listen to user input (e.g. health bar). Furthermore one could store variables in the context, for example the this pointer of the PlayerController.

Since my original implementation of the callback system is often not used as intended I would like to propose a change. I would split up RegisterAction into several functions: RegisterTrigger(actionType, void(bool pressed, int slope)) only gets called when pressed changes it's state. slope tells you whether the button was pressed (+1) or released (-1). This resembles more or less the regular button. RegisterTriggerContinuous(actionType, void(bool pressed, int slope)) gets called in every frame. slope is 0 if button state did not change. RegisterAxis(actionType, void(float state, float slope)) Represents an axis e.g. a locked mouse or a joystick. This function gets called every frame because there is little sense in determining whether a float value has changed. RegisterPointer(actionType, void(float2 state, float2 slope)) x and y coordinates of a free mouse or the Nintendo Wii thing or whatever. RegisterText(actionType, void(unicode character) Still have to think about how exactly you would do this.

In each of the function you could access it's globally declared context and use this information to determine which character has to move etc.

Just a suggestion. @\all Please get involved and tell us what you think about it.

markusobi commented 6 years ago

Keep in mind that not all views actually listen to user input (e.g. health bar).

Such Views would just pass all input through. It would be possible to define default behaviors via enums like:

In case of the Health Bar: consume_handled + No binding is registered on it → passthrough of all input.

Furthermore one could store variables in the context, for example the this pointer of the PlayerController.

Is it easier to update the context than to update the View, when the current controlled NPC changes or gets invalidated? What is the advantage of keeping the context up to date compared to a View? Where do the context objects live? Inside a ContextTree/ContextList? Also: You will still need to rebuild the whole ViewTree as ContextTree in order to get the order of Views right.

I would split up RegisterAction into several functions

I think this is a good idea. Luckily this change would be largely independent of the proposed change. So it should not be easier/harder to implement it with the current system than with the proposed system.

aaeberharter commented 6 years ago

Okay I'm in favor of your solution because you can avoid the redundancy of defining the view tree twice. I just wonder how you would handle the 3D scene. Somehow put it into a view too or rather create a dummy view which does the input handling for the 3D scene?

markusobi commented 6 years ago

Somehow put it into a view too or rather create a dummy view which does the input handling for the 3D scene?

I plan to use a dummy view for the player/camera controls.

ataulien commented 6 years ago

@markusobi I would not call it "dummy view", but rather see the main 3d-scene as an UI-View on it's own: The UI-View which shows the game, if that makes sense.

markusobi commented 6 years ago

@ataulien Should this 3d-Scene View actually draw the world? Should it directly call GameEngine::drawFrame ?

ataulien commented 6 years ago

@markusobi not sure about that. It probably could though! It would allow for picture-in-picture and all such fancy things.

markusobi commented 6 years ago

It would allow for picture-in-picture and all such fancy things.

Fancy things like mirrors :-)

frabert commented 6 years ago

And portals!!!

superdaveman commented 6 years ago

I sense a Gothic Portal Gun Mod. :D

tomedi commented 6 years ago

I'm all for portals and mirrors! So, I'm also for this 3d- Scene View- thing :D (is this Option 2, the separate Chain of InputHandler?)

ataulien commented 6 years ago

Well, mirrors, portals and other 3d-stuff is usually handled differently. This should not be the reason to chose any of the two options over the other one.