Author Topic: MODULE: DoubleClick + KeyListener: detect double clicks and more  (Read 1847 times)

Crimson Wizard

  • AGS Project Tracker Admins
    • Best Innovation Award Winner 2013, for spearheading the AGS 3.3.0 project
    •  
    • Lifetime Achievement Award Winner
    •  
    • Crimson Wizard worked on a game that was nominated for an AGS Award!
      Crimson Wizard worked on a game that won an AGS Award!
DOWNLOAD DoubleClick 1.0.0 MODULE
DOWNLOAD KeyListener 0.9.7 MODULE

Both modules require AGS 3.2.1 or higher.

Module sources:
DoubleClick
KeyListener
Git clone address: https://ivan-mogilko@bitbucket.org/ivan-mogilko/ags-script-modules.git



Double Click

As of now, AGS does not have built-in mouse double-click events. DoubleClick module solves this by detecting when player made two consecutive clicks within short duration.

Usage is very simple:

Code: Adventure Game Studio
  1. function on_mouse_click(MouseButton mb)
  2. {
  3.   if (mb = eMouseLeft)
  4.   {
  5.     if (DoubleClick.Event[eMouseLeft])
  6.     {
  7.       player.SayBackground("Double-clicked!");
  8.     }
  9.     else
  10.     {
  11.       // regular click
  12.     }
  13.   }
  14. }
  15.  

DoubleClick supports claiming events similar to standart ClaimEvent() function. If you are detecting double clicks in several modules, you may use DoubleClick.ClaimThisEvent() to not let double-click "through" to lower scripts.

DoubleClick.Timeout property is used to set up max timeout (in milliseconds) which may pass between two clicks for them to be considered "double click". Default is 300 ms.



Key Listener

KeyListener is a more sophisticated script module, that keeps track and record of the particular key and mouse button states in your game. Since AGS is limited in which key and mouse events it reports to the script, this module may be a useful workaround.

With KeyListener you do:
  • Setup which keys or mouse buttons to listen (track) and change that list dynamically at any moment in the game;
  • Optionally configure deduction parameters, such as maximal time between two key presses while they still counted as a sequence;
  • Enable and disable listener work at will.

With KeyListener you acquire following information about any of the listened keys and mouse buttons at any time:
  • Is key currently up or down;
  • Was the key just released or pushed;
  • Has the player double clicked a mouse button;
  • How long was the key up or down (in milliseconds);
  • How much times was the key tapped in a sequence;
  • How much times was the key "auto-repeated" while being held down;

NOTE that if you are using KeyListener, you do not need DoubleClick module.


Concepts

On sequencing.
Sequencing is pressing same key (or mouse button) over and over again, when every next press was made within certain time limit since the previous one. KeyListener lets you configure the maximal waiting time (timeout), default value being 300 ms. You are able to know when the key was "sequenced" another once, and how many times it was pressed in sequence already ("sequence length").

On key autorepeats.
In KeyListener terminology, a key's autorepeats start to occur periodically when the key is being held down for some time. This simulates the common operating system behavior, which can be easily observed in any text editor: when you press and hold the key first only one letter is typed, but after small amount of time it starts typing same letter again and again automatically, without you releasing and pressing key again.
You may configure two parameters related to key repeats: the first delay, which has to pass before key begins "repeating", and the (usually shorter) period between repeat count increments.
You may get when autorepeat occurs, and how many times this key was autorepeated.

Signal properties
Since AGS does not support custom callbacks (calling your own functions when something happened in script module), KeyListener tells you about immediate events by setting certain signal properties. These properties are easily distinguished by "Evt*" prefix.
The positive values on signal properties only set for 1 game tick (update), and are reset immediately afterwards. They literally mean that the key event has just occured, and you should react to that. The next tick they will be reset until some key event occurs again.
You will have to check these properties periodically, in the "repeatedly_execute" or "repeatedly_execute_always" functions of GlobalScript or your own custom script to know when the related events occur.

Lifetime of key records
Another important peculiarity is that some properties are kept for just 1 tick more after key state changes, before getting reset. These are KeyUpTime, KeyDownTime and KeyAutoRepeatCount. This is done so to allow you get their final value after being notified about key state change. For example, you may want to know how long the key was held down right after it was released.


Module API

Following are properties of the KeyListener class, that are exposed for use in your scripts:

Code: Adventure Game Studio
  1. struct KeyListener
  2. {
  3.   ///////////////////////////////////////////////////////////////////////////
  4.   //
  5.   // Setting up
  6.   // ------------------------------------------------------------------------
  7.   // Functions and properties meant to configure the listener's behavior.
  8.   //
  9.   ///////////////////////////////////////////////////////////////////////////
  10.  
  11.   // Enables or disables KeyListener, without cancelling tracked keys settings
  12.   import static attribute bool Enabled;
  13.  
  14.   /// Commences or ceases to listen keycode
  15.   import static void ListenKey(eKeyCode keycode, bool enable = true);
  16.   import static void ListenMouse(bool enable = true);
  17.   /// Stops listening all keycodes
  18.   import static void StopListenAllKeys();
  19.  
  20.   /// Resets all listener parameters to default values
  21.   import static void ResetToDefaults();
  22.  
  23.   /// Get/set minimal time (in milliseconds) for a held down key to be down to be considered "repeating"
  24.   import static attribute int KeyAutoRepeatDelay;
  25.   /// Get/set period (in milliseconds) between each "repeating" key count
  26.   import static attribute int KeyAutoRepeatPeriod;
  27.   /// Get/set maximal period (in milliseconds) between two key taps while they still considered a sequence
  28.   import static attribute int KeySequenceTimeout;
  29.  
  30.  
  31.   ///////////////////////////////////////////////////////////////////////////
  32.   //
  33.   // Event signals
  34.   // ------------------------------------------------------------------------
  35.   // Properties meant to signal user about keys events.
  36.   //
  37.   ///////////////////////////////////////////////////////////////////////////
  38.  
  39.   /// Gets if key was just pushed
  40.   readonly import static attribute bool EvtKeyPushed[];
  41.   readonly import static attribute bool EvtMousePushed[];
  42.   /// Gets if key was just released after being held down
  43.   readonly import static attribute bool EvtKeyReleased[];
  44.   readonly import static attribute bool EvtMouseReleased[];
  45.   /// Gets if key command was repeated for a held down key
  46.   readonly import static attribute bool EvtKeyAutoRepeated[];
  47.   readonly import static attribute bool EvtMouseAutoRepeated[];
  48.   /// Gets if key was tapped another time in sequence
  49.   readonly import static attribute bool EvtKeySequenced[];
  50.   readonly import static attribute bool EvtMouseSequenced[];
  51.   /// Gets if key was tapped twice in sequence just now
  52.   readonly import static attribute bool EvtKeyDoubleTap[];
  53.   readonly import static attribute bool EvtMouseDoubleClick[];
  54.  
  55.  
  56.   ///////////////////////////////////////////////////////////////////////////
  57.   //
  58.   // Key records
  59.   // ------------------------------------------------------------------------
  60.   // Properties meant to tell gathered information about keys the listener
  61.   // is listening to.
  62.   //
  63.   ///////////////////////////////////////////////////////////////////////////
  64.  
  65.   /// Gets if key is currently held down
  66.   readonly import static attribute bool IsKeyDown[];
  67.   readonly import static attribute bool IsMouseDown[];
  68.   /// Gets how many times the pressed down key command was repeated
  69.   readonly import static attribute int  KeyAutoRepeatCount[];
  70.   readonly import static attribute int  MouseAutoRepeatCount[];
  71.   /// Gets how many times the key was tapped in sequence
  72.   readonly import static attribute int  KeySequenceLength[];
  73.   readonly import static attribute int  MouseSequenceLength[];
  74.   /// Gets how long (in milliseconds) the key was not pressed
  75.   readonly import static attribute int  KeyUpTime[];
  76.   readonly import static attribute int  MouseUpTime[];
  77.   /// Gets how long (in milliseconds) the key was held down
  78.   readonly import static attribute int  KeyDownTime[];
  79.   readonly import static attribute int  MouseDownTime[];
  80. };
  81.  


Examples of use

Example 1: Some simple actions.
Add spoiler tag for Hidden:
Code: Adventure Game Studio
  1. function game_start()
  2. {
  3.   // We are going to track spacebar and the mouse buttons
  4.   KeyListener.ListenKey(eKeySpace);
  5.   KeyListener.ListenMouse();
  6.   // Start tracking
  7.   KeyListener.Enabled = true;
  8. }
  9.  
Code: Adventure Game Studio
  1. function repeatedly_execute()
  2. {
  3.   if (KeyListener.EvtMouseDoubleClick[eMouseLeft])
  4.   {
  5.     // player double clicked with mouse; do something
  6.   }
  7.  
  8.   if (KeyListener.EvtMousePushed[eMouseLeft] && KeyListener.MouseSequenceLength[eMouseLeft] == 2)
  9.   {
  10.     // this is essentially the same as above
  11.   }
  12.  
  13.   if (KeyListener.IsKeyDown[eKeySpace] && KeyListener.KeyDownTime[eKeySpace] > 1000)
  14.   {
  15.     // player is holding space bar for more than 1 second
  16.   }
  17. }
  18.  

Example 2: displaying particular key records on GUI.
Add spoiler tag for Hidden:
Code: Adventure Game Studio
  1. int key;
  2. function game_start()
  3. {
  4.   key = eKeyA;
  5.   KeyListener.ListenKey(key);
  6.   KeyListener.Enabled = true;
  7. }
  8.  
  9. function repeatedly_execute()
  10. {
  11.   Label1.Text = String.Format("Key is down: %d", KeyListener.IsKeyDown[key]);
  12.   Label2.Text = String.Format("Key just pushed: %d", KeyListener.EvtKeyPushed[key]);
  13.   Label3.Text = String.Format("Key just released: %d", KeyListener.EvtKeyReleased[key]);
  14.   Label4.Text = String.Format("Key uptime: %d", KeyListener.KeyUpTime[key]);
  15.   Label5.Text = String.Format("Key downtime: %d", KeyListener.KeyDownTime[key]);
  16.   Label6.Text = String.Format("Key repeats: %d", KeyListener.KeyAutoRepeatCount[key]);
  17.   Label7.Text = String.Format("Key taps: %d", KeyListener.KeySequenceLength[key]);
  18.   Label8.Text = String.Format("Key double tap: %d", KeyListener.EvtKeyDoubleTap[key]);
  19.   Label9.Text = String.Format("Key sequenced: %d", KeyListener.EvtKeySequenced[key]);
  20.   Label10.Text = String.Format("Key repeated: %d", KeyListener.EvtKeyAutoRepeated[key]);
  21. }
  22.  


The module is currently in BETA stage and needs extensive testing.
Comments, critism, suggestions and bug reports are welcome.
« Last Edit: 23 Jan 2018, 01:55 by Crimson Wizard »

Crimson Wizard

  • AGS Project Tracker Admins
    • Best Innovation Award Winner 2013, for spearheading the AGS 3.3.0 project
    •  
    • Lifetime Achievement Award Winner
    •  
    • Crimson Wizard worked on a game that was nominated for an AGS Award!
      Crimson Wizard worked on a game that won an AGS Award!
Shortly after I posted this module here, I realized its program interface was somewhat raw and inconsistent. I took time to improve it to make it look better and more convenient to use.
There are also some changes to the state recording logic.

New version of the module 0.9.5:
https://bitbucket.org/ivan-mogilko/ags-script-modules/downloads/KeyListener_0.9.5.scm
Source code location is the same.

Changes 0.9.0 -> 0.9.5:

* The signal properties of the KeyListener class now share similar naming convention that makes them stand-out and easier to find and remember. All of them start with "Evt" prefix now (meaning - obviously - "Event").
Code: Adventure Game Studio
  1.   /// Gets if key was just pushed
  2.   readonly import static attribute bool EvtKeyPushed[];
  3.   readonly import static attribute bool EvtMousePushed[];
  4.   /// Gets if key was just released after being held down
  5.   readonly import static attribute bool EvtKeyReleased[];
  6.   readonly import static attribute bool EvtMouseReleased[];
  7.   /// Gets if key command was repeated for a held down key
  8.   readonly import static attribute bool EvtKeyAutoRepeated[];
  9.   readonly import static attribute bool EvtMouseAutoRepeated[];
  10.   /// Gets if key was tapped another time in sequence
  11.   readonly import static attribute bool EvtKeySequenced[];
  12.   readonly import static attribute bool EvtMouseSequenced[];
  13.   /// Gets if key was tapped twice in sequence just now
  14.   readonly import static attribute bool EvtKeyDoubleTap[];
  15.   readonly import static attribute bool EvtMouseDoubleClick[];
  16.  

* Key sequencing is now broken by any other key of same type (keyboard or mouse) pressed. This means that a sequential taps are now only remembered for only one key and one mouse button at a time.

* Mouse buttons now register for (and unregister from) listening all at once. This was done not for convenience though, but for technical reasons; there are few unfortunate quirks in how AGS reports mouse clicks in script that caused trouble unless all mouse buttons are recorded at once, and I felt it would be unnecessary hassle to try overcoming them. Besides, unlike keyboard keys, mouse buttons are few in numbers.

* There are few more convenience fixes; for example EvtMouseDoubleClick now does not require to check if the mouse was "just released" as well - it does so internally.

* Properties and constants related to automatic key repeating received "Auto" prefix to their names to make it more clear that they refer to Automatic Repeat (as opposed to repeatedly pressed by user).


NOTE: will update the first post to reflect these changes shortly.


EDIT: had to upload another fixed version (0.9.6) after finding a bug.
« Last Edit: 24 Feb 2016, 19:35 by Crimson Wizard »

Monsieur OUXX

  • Cavefish
  • Mittens Vassal
  • Mittens Half Initiate
    • I can help with proof reading
    •  
    • I can help with translating
    •  
    • I can help with voice acting
    •  
Cool. I had some home-made primitive version of this for my own purpose (some of it embedded in newer unpublished versions of StandaloneClick) but I was spending too much time maintaining it for the limited features it offered.

This is cool and should be considered by newbies for every new game.

EDIT: you should consider adding events related to drag and drop : mouse being held and how much it has been moved along X and Y since previous game loop.

This could come very handy for fighting games as well.

« Last Edit: 01 Mar 2016, 15:25 by Monsieur OUXX »
 

arj0n

  • Mittens Vassal
  • art consists in drawing a line somewhere
    • arj0n worked on a game that was nominated for an AGS Award!
Nice!

Crimson Wizard

  • AGS Project Tracker Admins
    • Best Innovation Award Winner 2013, for spearheading the AGS 3.3.0 project
    •  
    • Lifetime Achievement Award Winner
    •  
    • Crimson Wizard worked on a game that was nominated for an AGS Award!
      Crimson Wizard worked on a game that won an AGS Award!
Thank you.

EDIT: you should consider adding events related to drag and drop : mouse being held and how much it has been moved along X and Y since previous game loop.
I am working on drag & drop module now; I had one I picked out from my ancient project, but it uses such awful workarounds that I decided to completely rewrite it.

Crimson Wizard

  • AGS Project Tracker Admins
    • Best Innovation Award Winner 2013, for spearheading the AGS 3.3.0 project
    •  
    • Lifetime Achievement Award Winner
    •  
    • Crimson Wizard worked on a game that was nominated for an AGS Award!
      Crimson Wizard worked on a game that won an AGS Award!
A very small update, just some cosmetic changes + safety fixes.
KeyListener 0.9.7

Crimson Wizard

  • AGS Project Tracker Admins
    • Best Innovation Award Winner 2013, for spearheading the AGS 3.3.0 project
    •  
    • Lifetime Achievement Award Winner
    •  
    • Crimson Wizard worked on a game that was nominated for an AGS Award!
      Crimson Wizard worked on a game that won an AGS Award!
DoubleClick - is a stripped version of KeyListener that only detects mouse double clicks.
If you do not need full functionality of KeyListener, that could be a better option.

Download module: https://bitbucket.org/ivan-mogilko/ags-script-modules/downloads/DoubleClick_1.0.0.scm
Source: https://bitbucket.org/ivan-mogilko/ags-script-modules/src/05c8084142e7e50cc0e9a4390db88d4405812fba/ui/DoubleClick/?at=master

Usage is very simple:

Code: Adventure Game Studio
  1. function on_mouse_click(MouseButton mb)
  2. {
  3.   if (mb = eMouseLeft)
  4.   {
  5.     if (DoubleClick.Event[eMouseLeft])
  6.     {
  7.       player.SayBackground("Double-clicked!");
  8.     }
  9.     else
  10.     {
  11.       // regular click
  12.     }
  13.   }
  14. }
  15.  

DoubleClick supports claiming events similar to standart ClaimEvent() function. If you are detecting double clicks in several modules, you may use DoubleClick.ClaimThisEvent() to not let double-click "through" to lower scripts.

DoubleClick.Timeout property is used to set up max timeout (in milliseconds) which may pass between two clicks for them to be considered "double click". Default is 300 ms.
« Last Edit: 23 Jan 2018, 01:44 by Crimson Wizard »

This is going to be useful, I'll try it later. Thank you, Crimson Wizard!