| 10-20-2011, 10:04 AM | #1 | |
If you want to centralize all the arrow key events in your map and keep your workload as short and sweet as possible, I have designed this library for you. JASS:library ArrowKeyEvent /* ========================================================================= ArrowKeyEvent version 1.0.0.0 ========================================================================= Credits: ------------------------------------------------------------------------- - Written by Bribe. - Earth-Fury for providing a lot of inspiration for the development of this system (especially documentation) via his KeyAction resource. - tooltiperror & Sgqvur for providing very helpful, constructive advice. ========================================================================= Introduction: ------------------------------------------------------------------------- Easy to use, efficient system for handling all arrow key events. It uses arrays and GetHandleId lookups to avoid the trap of many cloned functions that so many arrow key systems suffer from. ========================================================================= API Guide: ------------------------------------------------------------------------- To help centralize and make everything understandable, I originally used the constants appointed by Blizzard (bj_KEYEVENTKEY_LEFT/RIGHT/etc). But there was a lot of criticism over their ugliness so I made the following constants to correspond accordingly. They have the same values as the BJ constants, so you can use whichever is more appealing for you. Their purpose is to be passed as arguments so you are able to query such things as "is this key pressed" or simply to help make sense of what key was pressed from an event response and interpret it as an integer. constant integer ARROW_KEY_LEFT = 0 constant integer ARROW_KEY_RIGHT = 1 constant integer ARROW_KEY_DOWN = 2 constant integer ARROW_KEY_UP = 3 ------------------------------------------------------------------------- As I was developing this resource, is was mostly written in vanilla JASS. I had since converted it to OOP syntax but it has been requested to bring it back. I have made it available once again. function IsArrowKeyPressed takes player whichPlayer, integer arrowKey returns boolean To find out if the arrow key was pressed, you need to first ask which player is holding down the key, and then pass an ARROW_KEY constant to represent the key you are querying for. function RegisterArrowKeyEvent takes code onEvent returns nothing Instead of making up to 8 different functions for 8 different events, you can use this instead. Just specify a function that you want to be called whenever any key is pressed. That function must "return false". For event responses, you reference one of the following 3 functions: function GetEventArrowKeyPlayerId takes nothing returns integer This is more of an optimization benefit than not. GetTriggerPlayer() will get you the player who pressed the key, but most of the time you just need to know the player ID of that person. function GetEventArrowKey takes nothing returns integer Call this function to find out which key was pressed. It will return one of the four ARROW_KEY constants, of course. function IsEventArrowKeyPressed takes nothing returns boolean This is also here more for optimization's benefit. You can certainly use "IsArrowKeyPressed(GetEventArrowKey(GetEventArrowKeyPlayerId()))" but this is much more convenient I must say. ------------------------------------------------------------------------- "implement ArrowKey" ArrowKey API is accessible as if it were part of the implementing struct itself. As a bonus, you can create a method "onArrowKeyEvent" that gets called automatically when any key is pressed. The method musn't be static, because the member "this" is a player ID (to mirror the ArrowKey struct's own API). It must take an integer as its first argument to represent the pressed or released key. As a final argument, it must take a boolean to determine if the key was pressed (true) or released (false). The member "this" taken by the method is a player ID. */ //======================================================================= // // System Code // //======================================================================= globals //------------------------------------------------------------------- // Yo dawg, I herd you like constant variables so I gave you some new // constant variables so you can have some constant variables to go // with Blizzard's constant variables. // constant integer ARROW_KEY_LEFT = bj_KEYEVENTKEY_LEFT constant integer ARROW_KEY_RIGHT = bj_KEYEVENTKEY_RIGHT constant integer ARROW_KEY_DOWN = bj_KEYEVENTKEY_DOWN constant integer ARROW_KEY_UP = bj_KEYEVENTKEY_UP endglobals //======================================================================= // Ugly modules are needed to prevent initialization bugs. // private module Init private static method onInit takes nothing returns nothing local player p local integer i = 12 local trigger t = .trig loop set i = i - 1 set p = Player(i) if GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(p) == MAP_CONTROL_USER then //Register arrow key events for playing players call TriggerRegisterPlayerEvent(t, p, EVENT_PLAYER_ARROW_LEFT_DOWN) call TriggerRegisterPlayerEvent(t, p, EVENT_PLAYER_ARROW_LEFT_UP) call TriggerRegisterPlayerEvent(t, p, EVENT_PLAYER_ARROW_RIGHT_DOWN) call TriggerRegisterPlayerEvent(t, p, EVENT_PLAYER_ARROW_RIGHT_UP) call TriggerRegisterPlayerEvent(t, p, EVENT_PLAYER_ARROW_DOWN_DOWN) call TriggerRegisterPlayerEvent(t, p, EVENT_PLAYER_ARROW_DOWN_UP) call TriggerRegisterPlayerEvent(t, p, EVENT_PLAYER_ARROW_UP_DOWN) call TriggerRegisterPlayerEvent(t, p, EVENT_PLAYER_ARROW_UP_UP) //Run if library ArrowKey is found in the map. //! runtextmacro optional INIT_ARROW_KEY_AA() endif exitwhen i == 0 endloop //Run if library ArrowKey is found in the map. //! runtextmacro optional INIT_ARROW_KEY_CALL_LINK() call TriggerAddCondition(t, Filter(function ArrowKey.actions)) set p = null set t = null endmethod endmodule //======================================================================= // A central struct to handle all ArrowKey mechanics. This has its uses, // giving you slightly more control over the inner system functionality. // struct ArrowKey extends array //------------------------------------------------------------------- // Event responses // readonly static integer eventKey = 0 //Arrow key that triggered the event. readonly static boolean eventKeyPressed = false //Was the arrow key pressed? readonly static ArrowKey eventPlayerId = 0 //The id of the player who pressed the key. //Run if library ArrowKey is found in the map. //! runtextmacro optional ARROW_KEY_DECLARE_ARRAYS() //------------------------------------------------------------------- // System variables // private static trigger trig = CreateTrigger() //Handles all events. private static boolean array press //Is key pressed? //=================================================================== // User-friendly typecasting. // static method operator [] takes player who returns ArrowKey return GetPlayerId(who) endmethod //=================================================================== // Great for simplifying arrow key events - this function runs when // any player presses or releases an arrow key. The code passed is // expected to "return false". // static method registerEvent takes code onEvent returns nothing call TriggerAddCondition(trig, Filter(onEvent)) endmethod //=================================================================== // Returns true if the key is pressed, false if it is released. // method isPressed takes integer arrow returns boolean return press[this + arrow * 12] endmethod //Run if library ArrowKey is found in the map. //! runtextmacro optional ARROW_KEY_AXIS_METHODS() //=================================================================== // If you are running debug tests, this might come in handy. // static if DEBUG_MODE then static method getKeyName takes integer arrow returns string if arrow == ARROW_KEY_LEFT then return "LEFT" elseif arrow == ARROW_KEY_RIGHT then return "RIGHT" elseif arrow == ARROW_KEY_DOWN then return "DOWN" elseif arrow == ARROW_KEY_UP then return "UP" endif return "--" endmethod endif //=================================================================== // // Private Components // //=================================================================== //=================================================================== // Key event handler. // private static method actions takes nothing returns boolean local integer id = GetHandleId(GetTriggerEventId()) - 261 set .eventPlayerId = GetPlayerId(GetTriggerPlayer()) //If id is an even number, the key was pressed. set .eventKey = id / 2 set .eventKeyPressed = .eventKey * 2 == id set .press[.eventPlayerId + .eventKey * 12] = .eventKeyPressed //Run if library ArrowKey is found in the map. //! runtextmacro optional ARROW_KEY_SETUP() return false endmethod //Run if library ArrowKey is found in the map. //! runtextmacro optional INIT_ARROW_KEY_LINK() //Initialize the system via module implement Init endstruct //======================================================================= // // Event Responses // //======================================================================= //======================================================================= function IsArrowKeyPressed takes player whichPlayer, integer arrowKey returns boolean return ArrowKey[whichPlayer].isPressed(arrowKey) endfunction //======================================================================= function RegisterArrowKeyEvent takes code onEvent returns nothing call ArrowKey.registerEvent(onEvent) endfunction //======================================================================= function GetEventArrowKeyPlayerId takes nothing returns integer return ArrowKey.eventPlayerId endfunction //======================================================================= function GetEventArrowKey takes nothing returns integer return ArrowKey.eventKey endfunction //======================================================================= function IsEventArrowKeyPressed takes nothing returns boolean return ArrowKey.eventKeyPressed endfunction //======================================================================= // Implementation of this module allows you to make your struct "extend" // the ArrowKey struct. As a bonus feature, you can create a method named // "onArrowKeyEvent" that is called whenever a key is pressed or released. // The method needs "takes integer arrow, boolean pressed", and must be // positioned *above* the "implement ArrowKey" statement (this way it is // detected by the static if and doesn't compile to a function interface). // module ArrowKey //Delegates are fun, you should try them out. private delegate ArrowKey AK //=================================================================== // Please call this method from *below the module implement statement // if you know what's good for you. // static method operator [] takes player who returns thistype return GetPlayerId(who) endmethod static if thistype.onArrowKeyEvent.exists then private static method eventProxy takes nothing returns boolean call thistype(.eventPlayerId).onArrowKeyEvent(.eventKey, .eventKeyPressed) return false endmethod endif private static method onInit takes nothing returns nothing local thistype i = 12 loop set i = i - 1 set i.AK = i //Delegates require some delegation of course. exitwhen i == 0 endloop static if thistype.onArrowKeyEvent.exists then call ArrowKey.registerEvent(function thistype.eventProxy) endif endmethod endmodule endlibrary Optional functionality add-on. If you want to make use of it, make sure your library requires ArrowKey. JASS:library ArrowKey requires ArrowKeyEvent /* ========================================================================= ArrowKey version 1.0.0.0 ========================================================================= Credits: ------------------------------------------------------------------------- Same as credits for ArrowKeyEvent ========================================================================= API Guide: ========================================================================= ArrowKey provides more than just event responses, it expects you to have some other purposes for those values. If you have an arrow-key movement system, for example, you might want to have certain values to know how to offset the unit's coordinates. The following constants provide those for you: constant integer ARROW_KEY_NEG_VALUE = -1 constant integer ARROW_KEY_NIL_VALUE = 0 constant integer ARROW_KEY_POS_VALUE = 1 How you get those values in the first place is first you must query one of the X or Y axis, because both axis have positive and negative values of their own of course. You can reference these constant variables for that purpose: constant integer ARROW_KEY_X_AXIS = 0 constant integer ARROW_KEY_Y_AXIS = 1 I imagine you must be wondering what this is all good for. Here are the functions you would pass these constants into for arguments or receive as return values: function GetArrowKeyAxis takes integer arrowKey returns integer Pass an ARROW_KEY_LEFT/RIGHT/etc. constant to this function to return the axial constant they correspond to. LEFT & RIGHT belong to the X axis, DOWN/UP to the Y axis of course. This is useful if you're work- ing with the values dynamically. function GetArrowKeyValue takes integer arrowKey returns integer I am sure you are aware that along any decent graph you have negative values on the left/lower axis and positive values on the right/upper axis. This is why this function exists. It will return -1 for down or left, 1 for up or right. This is very useful if you use coordinates & want to offset by the direction of an arrow key. function GetOpposingArrowKey takes integer arrowKey returns integer This is mostly used by the system to avoid cloned functions, but if you want to use it or want to know if you should use it, this simply returns whatever key is opposite the key you passed. LEFT for RIGHT, for example. function GetAxisArrowKey takes player whichPlayer, integer axis returns integer This is a really good one. First, of course you must pass a player argument to let the system know which player's key you want info on. Second, you pass it the X or Y axis value. If you want to know if the player is holding down the LEFT or RIGHT key, suppose they are both pressed? This will let you know which key was pressed LAST. If the last key pressed is released, but the other is still held down, this function intelligently knows to go back to that other key. If there is no key being held down, it returns the following constant: constant integer ARROW_KEY_NONE = -1 function GetAxisArrowKeyValue takes player whichPlayer, integer axis returns integer This is a wrapper of sorts for the above function, however it filters out the ARROW_KEY_NONE value and returns ARROW_KEY_NIL_VALUE in that case. This is useful because if you are doing math you can multiply by 1 to go up/right, -1 to go down/left, or 0 to remain stationary on that axis. */ //======================================================================= // // System Code // //======================================================================= globals //------------------------------------------------------------------- // Axis are important for a parameter in GetAxisArrowKey, a function // that you determine which axis key the user is focusing on. // // If no key is pressed, the value ARROW_KEY_NONE will be returned. // constant integer ARROW_KEY_X_AXIS = 0 constant integer ARROW_KEY_Y_AXIS = 1 constant integer ARROW_KEY_NONE = -1 //------------------------------------------------------------------- // These help to interpret arrow key values returned by the system. // They are very useful for coordinate-based math. // constant integer ARROW_KEY_NEG_VALUE = -1 constant integer ARROW_KEY_NIL_VALUE = 0 constant integer ARROW_KEY_POS_VALUE = 1 endglobals function GetOpposingArrowKey takes integer arrowKey returns integer return ArrowKey.oppositeKey[arrowKey] endfunction function GetArrowKeyAxis takes integer arrowKey returns integer return ArrowKey.keyAxis[arrowKey] endfunction function GetArrowKeyValue takes integer arrowKey returns integer return ArrowKey.keyValue[arrowKey] endfunction function GetAxisArrowKey takes player whichPlayer, integer axis returns integer return ArrowKey[whichPlayer].getAxisKey(axis) endfunction function GetAxisArrowKeyValue takes player whichPlayer, integer axis returns integer return ArrowKey[whichPlayer].getAxisKeyValue(axis) endfunction //! textmacro ARROW_KEY_DECLARE_ARRAYS //------------------------------------------------------------------- // Useful reference variables. Pass one of the arrow key constants // ARROW_KEY_UP, ARROW_KEY_DOWN, ARROW_KEY_LEFT or ARROW_KEY_RIGHT. // readonly static integer array oppositeKey //Axis-opposite of key (.LEFT<->.RIGHT, .UP<->.DOWN) readonly static integer array keyAxis //Axis of key (.X_AXIS or .Y_AXIS) readonly static integer array keyValue //Key value (.NEG_VALUE or .POS_VALUE) //Needed for storing the active axis key private static integer array aaKey //! endtextmacro //! textmacro ARROW_KEY_AXIS_METHODS //=================================================================== // Returns ARROW_KEY_NONE if there is no key pressed for that axis. // The return value is whatever key the user focuses on. If a player // holds down an arrow key, that is the active key. But if the player // presses the opposite key, that will be the new active key. If that // key is released before the other key is released, then the other // key will be the return value as that one is still being held down. // method getAxisKey takes integer axis returns integer return .aaKey[this * 2 + axis] endmethod //=================================================================== // This method is more safe than the one above because it filters out // the unsafe ARROW_KEY_NONE return. It returns ARROW_KEY_NIL_VALUE // if there is no active key for the queried axis. I recommend this // method for something like an arrow key movement system because you // can use for seamless math. // // Example use: // // local ArrowKey ak = ArrowKey[GetTriggerPlayer()] // local real x = GetUnitX(hero[ak]) + ak.getAxisKeyValue(ArrowKey.X_AXIS) * polarOffset // local real y = GetUnitX(hero[ak]) + ak.getAxisKeyValue(ArrowKey.Y_AXIS) * polarOffset // call IssuePointOrder(hero[ak], "move", x, y) // method getAxisKeyValue takes integer axis returns integer local integer i = this.getAxisKey(axis) if i == ARROW_KEY_NONE then return ARROW_KEY_NIL_VALUE endif return .keyValue[i] endmethod //! endtextmacro //! textmacro ARROW_KEY_SETUP if .eventKeyPressed then //The active axis key is the one that was just pressed. set id = .eventKey elseif .eventPlayerId.isPressed(.oppositeKey[.eventKey]) then //The active axis key is the opposite of this released one. set id = .oppositeKey[.eventKey] else //There is no active axis key. set id = ARROW_KEY_NONE endif //Assign the active arrow key to the player's relevant key axis. set .aaKey[.eventPlayerId * 2 + .keyAxis[.eventKey]] = id //! endtextmacro //! textmacro INIT_ARROW_KEY_LINK private static method linkKeys takes integer a, integer b, integer axis returns nothing set .keyAxis[a] = axis set .keyAxis[b] = axis set .keyValue[a] = ARROW_KEY_NEG_VALUE set .keyValue[b] = ARROW_KEY_POS_VALUE set .oppositeKey[a] = b set .oppositeKey[b] = a endmethod //! endtextmacro //! textmacro INIT_ARROW_KEY_AA //Default active axis keys to ARROW_KEY_NONE set .aaKey[i] = ARROW_KEY_NONE set .aaKey[i + 1] = ARROW_KEY_NONE //! endtextmacro //! textmacro INIT_ARROW_KEY_CALL_LINK call .linkKeys(ARROW_KEY_LEFT, ARROW_KEY_RIGHT, ARROW_KEY_X_AXIS) call .linkKeys(ARROW_KEY_DOWN, ARROW_KEY_UP, ARROW_KEY_Y_AXIS) //! endtextmacro endlibrary
|
| 10-20-2011, 02:09 PM | #2 |
I think that you should provide your own constants, just for the sake of their names. bj_KEYEVENTKEY_LEFT is an eyesore to me. Something like ARROW_KEY_LEFT = bj_KEYEVENTKEY_LEFT would suffice. But even with that being said, I don't see myself using this snippet over Earth-Fury's KeyAction. |
| 10-20-2011, 02:34 PM | #3 |
KeyAction could use ArrowKeyEvent as its base. For the event detection, this does it in 1/20th the code length and (worst case scenario for KeyAction) 1/20th the handle count. The rest of the features KeyAction provides is a minimal amount of code. Maybe I would I update this to include said features if that is seriously the comparison you're getting at here. Edit: Looking further at the code of KeyAction, besides providing some constants, 1 extra function getter and an insanely huge module, this could easily deprecate that system. Edit 2: Added some less ugly constants. |
| 10-21-2011, 02:35 PM | #4 |
Code fully restructured and given more features. This now does more than KeyAction does, save for that "loop through all struct instances on event" that his module has (I don't see the benefit, and I could find no systems implementing his module). |
| 10-24-2011, 07:51 AM | #5 |
Updates thanks to tooltiperrors's advice: - Heavily invested in the documentation. - Re-added function wrappers. - Split the library into two instead of the static if: ArrowKeyEvent and ArrowKey. I suppose I could remove the requirement to put "return false" from the event response now, but I haven't tested it yet to see if it compiles with pJass so for now I advise keeping as the documentation recommends. |
