Author Topic: MODULE: ArrowSelect 0.6.0 (for usage with Keyboard and Gamepad)  (Read 1882 times)


ArrowSelect version 0.6.0

Select things using arrows keys or joystick hat, module for in point and click games made with Adventure Game Studio.

Requires AGS

Get Latest Release arrowselect.scm | GitHub Repo | Demo Windows | Demo Linux

Note: This module doesn't deal with printing things on screen, but if you want to do this, you may find it provides some helpful functions with the Interactives abstraction.

Basic usage

For basic usage with Keyboard, in your global script, add at game_start:

Code: Adventure Game Studio
  1.     ArrowSelect.enableKeyboardArrows();

Usage with joystick

If you are using a joystick or gamepad plugin, you will need to implement your own function to deal with. An example for hat is below.

Code: Adventure Game Studio
  1.     //pressed a hat
  2.     void pressedPov(int pov){
  3.       if(pov == ePOVCenter){
  4.       } else if(pov == ePOVDown){
  5.         ArrowSelect.moveCursorDirection(eDirectionDown);
  6.       } else if(pov == ePOVLeft){
  7.         ArrowSelect.moveCursorDirection(eDirectionLeft);
  8.       } else if(pov == ePOVRight){
  9.         ArrowSelect.moveCursorDirection(eDirectionRight);
  10.       } else if(pov == ePOVUp){
  11.         ArrowSelect.moveCursorDirection(eDirectionUp);
  12.       } else if(pov == ePOVDownLeft){
  13.         ArrowSelect.moveCursorDirection(eDirectionDownLeft);
  14.       } else if(pov == ePOVDownRight){
  15.         ArrowSelect.moveCursorDirection(eDirectionDownRight);
  16.       } else if(pov == ePOVUpLeft){
  17.         ArrowSelect.moveCursorDirection(eDirectionUpLeft);
  18.       } else if(pov == ePOVUpRight){
  19.         ArrowSelect.moveCursorDirection(eDirectionUpRight);
  20.       }
  21.       return;
  22.     }

What are Interactives ?

Interactives are things on screen that the player can interact with.
These are Objects, Characters, Hotspots, and GUI Controls like buttons and others.
This module only cares for their type, and a position that is similar to the thing center that mouse can click.

Note that some gotchas apply, for example, if you have three different Hotspots areas that map to the same Hotspot, instead of finding out they are different, it will erroneously find a point in the center of them.
So if you have, for example, two TVs in your background, that have the same interaction, create two different hostpots for them and just map the same interaction function to both, otherwise this module will fail.

Code: [Select]
enum InteractiveType{
  eInteractiveTypeNothing = eLocationNothing,
  eInteractiveTypeObject = eLocationObject,
  eInteractiveTypeCharacter = eLocationCharacter,
  eInteractiveTypeHotspot = eLocationHotspot,

managed struct Interactive{
  int x;
  int y;
  int ID;
  InteractiveType type;

ArrowSelect API

bool ArrowSelect.moveCursorDirection(CharacterDirection dir)
Moves cursor to the interactive available at a direction. Returns true if the cursor is successfully moved.

Interactive* ArrowSelect.getNearestInteractivePointA tDirection(CharacterDirection dir)
Get the nearest interactive available at a direction. Returns null if there is none.

Point* ArrowSelect.getNearestInteractivePointA tDirection(CharacterDirection dir)
Get point of the nearest interactive available at a direction. Returns null if there is none.

bool attribute ArrowSelect.UseMouseAsOrigin
If true, Mouse position is used as origin in getNearestInteractiveAtDirection and related functions. Default is true.

Point* attribute ArrowSelect.Origin
Point used as origin if UseMouseAsOrigin is false.

void filterInteractiveType(InteractiveType interactiveType, InteractiveFilter filter=0)
Filters or not a interactive type for cursor moveCursorDirection and getNearestInteractivePointAtDirection.

bool ArrowSelect.areKeyboardArrowsEnable()
Returns true if regular keyboard arrows are enabled for cursor movements.

bool ArrowSelect.enableKeyboardArrows(bool isKeyboardArrowsEnabled = 1)
Enables or disables (by passing false) regular keyboard arrows handled by this module.

Triangle* ArrowSelect.triangleFromOriginAngleAndD irection(Point* origin, int direction, int spreadAngle=90)
Returns a Triangle instance with one point at the origin points and the two other points separated by spreadAngle, and at the direction angle

int ArrowSelect.distanceInteractivePoint(Interactive* s, Point* a)
Retuns the distance between an interactive and a point.

Interactive* ArrowSelect.closestValidInteractivePoin t(Interactive* Interactives[], Point* a)
Returns the closest interactive to a point.

Interactive*[] ArrowSelect.getInteractives()
Get a list of all interactives on screen.

bool ArrowSelect.isInteractiveInsideTriangle(Interactive* p, Point* a, Point* b, Point* c)
Returns true if an interactive is inside a triangle defined by three points.

Interactive*[] ArrowSelect.whichInteractivesInTriangle(Interactive* Interactives[], Point* a, Point* b, Point* c)
Returns a list of which triangles are inside a triangle defined by three points.

Implementation details

This is just the detail on how things works on this module

By using keyboard arrow keys or joystick directional hat, select between
clickable things on screen.

When the player press an arrow button do as follow:

1 .get the x,y position of each thing on screen,

2 .select only things on arrow button direction (example:at right of current
  cursor position, when player press right arrow button),

3 .calculate distance from cursor to things there, and get what has the smaller

Solution details
For 2, the key is figuring out the right angle and then create a triangle that
extends to screen border, the things inside the triangle can be figured with the
function below
Code: C
  1. public static bool PointInTriangle(Point p, Point p0, Point p1, Point p2)
  2. {
  3.     var s = p0.Y * p2.X - p0.X * p2.Y + (p2.Y - p0.Y) * p.X + (p0.X - p2.X) * p.Y;
  4.     var t = p0.X * p1.Y - p0.Y * p1.X + (p0.Y - p1.Y) * p.X + (p1.X - p0.X) * p.Y;
  6.    if ((s < 0) != (t < 0))
  7.     return false;
  9. var A = -p1.Y * p2.X + p0.Y * (p2.X - p1.X) + p0.X * (p1.Y - p2.Y) + p1.X * p2.Y;
  11. return A < 0 ?
  12.         (s <= 0 &amp;&amp; s + t >= A) :
  13.         (s >= 0 &amp;&amp; s + t <= A);
  14. }


Made by eri0o


Distributed under MIT license. See LICENSE for more information.
« Last Edit: 10 Nov 2020, 00:27 by eri0o »

Re: MODULE: ArrowSelect 0.1.2
« Reply #1 on: 05 Aug 2019, 02:09 »
Did not try it yet, but looks very nice.

I can think of some use cases for this to make console playing more convenient.
I think the main game-play should still be with a manual moving of the cursor using the analog sticks on the controller. Otherwise, the "point and click" part of the adventure game is missing.
But things like navigating menus, complex RPG style UIs, closeup puzzles could really benefit from it.

I have an improvement suggestion. Let the user of the module to selectively enable which interactives should be part of the system. Then it will be possible to use it with characters, inventory, room exit points etc, but leave the puzzles for manual searching. Maybe someone would like decide that for his game, any interactive that was clicked at least once in the game could be quickly reached using the arrows.


Re: MODULE: ArrowSelect 0.1.2
« Reply #2 on: 05 Aug 2019, 18:57 »
I will try to figure out how to filter things out. Each block of the algorithm is also exposed if someone wants to do anything creative with them too. :)

I really want people to try it out because there may be lots of use cases and bugs that I haven't predicted here.

Edit: I need to figure out how to navigate list GUI controls.
« Last Edit: 08 Aug 2019, 13:34 by eri0o »

Monsieur OUXX

  • Mittens Vassal
  • Cavefish
  • Mittens Half Initiate
    • I can help with proof reading
    • I can help with translating
    • I can help with voice acting
    • Monsieur OUXX worked on one or more games that won an AGS Award!
    • Monsieur OUXX worked on one or more games that was nominated for an AGS Award!
Re: MODULE: ArrowSelect 0.1.2
« Reply #3 on: 09 Aug 2019, 08:22 »
Could you clarify in your first post that you mean keyboard arrow keys? I was very confused at first read, I thought your module was replacing the game's mouse icons with the same icon, just with an added arrow


Hey Monsieur OUXX, I added a thing on the subject and added keyboard and gamepad mention on the first line, see if it's clearer now. :)

I have an issue I couldn't quite figure out yet which is when there's a clickable GUI on top of some stuff on screen, if someone has ideas or want to fork and pull request on GitHub a solution :P
« Last Edit: 09 Aug 2019, 12:21 by eri0o »

I have an issue I couldn't quite figure out yet which is when there's a clickable GUI on top of some stuff on screen, if someone has ideas or want to fork and pull request on GitHub a solution :P
What is the problem? Clicking on something, or checking that something is under? There's e.g. Room.ProcessClick that ignores GUI.


I need to know before the click if the thing will actually be clickable by ProcessClick, with this information I could invalidate the ocluded interactives.

Edit: I created a issue for this.

Since this problem is more relevant for GUIs and things hidden by GUIs, I will solve by filtering out points that , when hit with GUI.GetAtScreenXY return a value different than null and are not GUI controls, and for GUI Controls, check if the owning GUI matches.

Edit2: Fixed and new release.  8-)

Edit3: Added a way to filter things out as per artium request. I also added enter as click just on the demo global script for ease of testing it out.

Edit4: Added support for list box!

Edit5: Now intermediary points of a slider are available too! Also fixed a crash that happened in the previous version.

Edit6: Added support for InvWindow and Inventory Items!

« Last Edit: 11 Aug 2019, 15:39 by eri0o »


  • Creator, Mutator and Defecator
    • I can help with proof reading
    • I can help with scripting
    • I can help with story design
    • I can help with translating
    • Babar worked on one or more games that won an AGS Award!
    • Babar worked on one or more games that was nominated for an AGS Award!
Hi eri0o!
morgan directed me to this for a problem I was having. I'll download and investigate it tomorrow, but I am curious, does this also cater to dialogue options (I care more about default, it seems with your GUI handling, it probably does custom).
The ultimate Professional Amateur

Now, with his very own game: Alien Time Zone


Hey Babar!

I actually never ever used the default dialog options thing from AGS, so I would say no. I would need to investigate if it's possible. Basically the problem is figuring out if it's on or not, and once this is figured out, what's the minimal list of points where the cursor needs to be.


  • Zzz...
    • I can help with making music
    • I can help with story design
Hi there eri0o :) Is there a way to disable the player from using the mouse and just control the cursor via keyboard?
Follow me on twitter:


Hi Stranga,

That is a super interesting use case. I looked into and is not really easy to accomplish now since it currently picks the origin from the mouse cursor position. I need to do some changes to allow for that.


Created a new version! I made that the demo game can allow a fake cursor (you can check it by clicking the red orb, but you should really look the demo project source code)

The gist is in the room script:

Code: Adventure Game Studio
  1. function on_key_press(eKeyCode keycode)
  2. {
  3.   if(!global_bool_fake_cursor) return;
  5.   if(keycode == eKeyReturn)
  6.   {
  7.     _doFakeClick();
  8.     return;
  9.   }
  11.   Interactive* interactive;
  12.   Point* p;
  14.   p = new Point;
  15.   p.x = gFakeCursor.X;
  16.   p.y = gFakeCursor.Y;
  17.   ArrowSelect.Origin = p;
  19.   if (keycode == eKeyDownArrow) {
  20.     interactive = ArrowSelect.getNearestInteractiveAtDirection(eDirectionDown);
  21.   } else if (keycode == eKeyUpArrow) {
  22.     interactive = ArrowSelect.getNearestInteractiveAtDirection(eDirectionUp);
  23.   } else if (keycode == eKeyLeftArrow) {
  24.     interactive = ArrowSelect.getNearestInteractiveAtDirection(eDirectionLeft);
  25.   } else if (keycode == eKeyRightArrow) {
  26.     interactive = ArrowSelect.getNearestInteractiveAtDirection(eDirectionRight);
  27.   } else return;
  29.   if(interactive == null) return;
  31.   gFakeCursor.X = interactive.x;
  32.   gFakeCursor.Y = interactive.y;
  33. }

Unfortunately by going this route, other things in the "game" ui that relies on mouse position breaks. If you can write your GUI code avoiding it - like avoiding YPopPosition and other mouse dependent code in the GUI, then sure, it will work.

I think this module need a good revamp on the API eventually so it's simpler to understand how to use but for now I think I will keep adding things in it and leave the revamped API whenever a 1.0.0 version happens someday in the future.
« Last Edit: 10 Nov 2020, 01:15 by eri0o »


  • Zzz...
    • I can help with making music
    • I can help with story design
This is perfect!  :grin: Exactly what I need! Thank you very much eri0o! Most of my games have been coded around using any mouse functions so this fine.
Follow me on twitter: