OnPlayerKeyStateChange RU
From SA-MP Wiki
Contents |
OnPlayerKeyStateChange
Описание:
Примечание | Этот обратный вызов также может быть вызван NPC. |
playerid | ID игрока, нажавшего клавишу. |
newkeys | Нажатая клавиша управления. Смотрите обозначенные клавиши здесь. |
oldkeys | Предыдущая нажатая клавиша управления. Смотрите обозначенные клавиши здесь. |
Возвращаемые значения:
- Это всегда вызывается первым из gamemode.
Совет | Клавиши управления не вызывают OnPlayerKeyStateChange (up/down/left/right). Они могут быть обнаружены только через GetPlayerKeys (в OnPlayerUpdate или таймере). |
Вступление
Данная функция не отслеживает, какая именно клавиша на клавиатуре была нажата - она опознает только "команды" для управления игроком, т.е.данная функция, для примера: не может отследить, нажал ли игрок клавишу 'F1' или 'F2'. Если в настройках игрока на F1 задано "прыжок", то функция определит клавишу не как F1, а именно как "KEY_JUMP".
Как НЕ следует указывать keystate
Если Вы хотите, назначить какой-то скрипт при нажатии клавиши, можно использовать данное условие:
if (newkeys == KEY_FIRE)
При таком условии в коде скрипт будет срабатывать ТОЛЬКО когда нажата именно одна клавиша (огонь в данном случае). Т.е. если у Вас при этом зажата клавиша прицеливания или какая-либо другая, скрипт не сработает!
Как рекомендуется использовать keystate
Для срабатывания скрипта при нажатии клавиши в сочетании с другими, нужно использовать такое условие:
if (newkeys & KEY_FIRE)
В данный момент символ '&' используется не в качестве логического "И" (а для чего? не знаю как сформулировать, кто-нить поправит :D)
Чето лень стало переводить, отложим, на неопределенный срок перевод :D
Now if you test this code it will work whether you are crouching or standing when you press the fire key. However there is still one slight problem - it will fire as long as you are holding the key. OnPlayerKeyStateChange is called every time a key changes and that code is true whenever the the fire key is held down. If you press fire the code will fire, if that key is held and you press crouch - that code will fire again because a key (crouch) has changed and fire is still held down How do you detect when a key is first pressed, but not trigger again when it's still held and another key changes?
How to check for a key that has been pressed
This is where "oldkeys" comes in. To check if a key has just been pressed you need to first check whether it is set in "newkeys" - meaning it's held down, and then check that it's NOT in "oldkeys" - meaning it's only just been held down. The following code does this:
if ((newkeys & KEY_FIRE) && !(oldkeys & KEY_FIRE))
That will ONLY be true when the FIRE key is first pressed, not when it's held and another key changes.
How to check for a key being released
Exactly the same principle as above, but reversed:
if ((oldkeys & KEY_FIRE) && !(newkeys & KEY_FIRE))
How to check for multiple keys
If you want to check for players HOLDING crouch and fire then the following code will work fine:
if ((newkeys & KEY_FIRE) && (newkeys & KEY_CROUCH))
However if you want to detect when they FIRST press fire and crouch the following code WILL NOT work. It will work if they manage to press the two keys at exactly the same time, but if they're fractionally out (far less than half a second) it won't:
if ((newkeys & KEY_FIRE) && !(oldkeys & KEY_FIRE) && (newkeys & KEY_CROUCH) && !(oldkeys & KEY_CROUCH))
Why not? Because OnPlayerKeyStateChange is called every time a single key changes. So they press "KEY_FIRE" - OnPlayerKeyStateChange is called with "KEY_FIRE" in "newkeys" and not in "oldkeys", then they press "KEY_CROUCH" - OnPlayerKeyStateChange is called with "KEY_CROUCH" and "KEY_FIRE" in "newkeys", but "KEY_FIRE" is now also in "oldkeys" as it's already been pressed, so "!(oldkeys & KEY_FIRE)" will fail. Fortunately the solution is very simple (in fact simpler than the original code):
if ((newkeys & (KEY_FIRE | KEY_CROUCH)) == (KEY_FIRE | KEY_CROUCH) && (oldkeys & (KEY_FIRE | KEY_CROUCH)) != (KEY_FIRE | KEY_CROUCH))
This may look complicated, but it checks that both keys are set in "newkeys" and that both the keys were not set in "oldkeys", if one of them was set in "oldkeys" that doesn't matter as not both of them were. All these things can be simplified greatly with defines.
Simplification
Detecting holding a key
The define:
// HOLDING(keys) #define HOLDING(%0) \ ((newkeys & (%0)) == (%0))
Holding one key:
if (HOLDING( KEY_FIRE ))
Holding multiple keys:
if (HOLDING( KEY_FIRE | KEY_CROUCH ))
Detecting first pressing a key
The define:
// PRESSED(keys) #define PRESSED(%0) \ (((newkeys & (%0)) == (%0)) && ((oldkeys & (%0)) != (%0)))
Pressed one key:
if (PRESSED( KEY_FIRE ))
Pressed multiple keys:
if (PRESSED( KEY_FIRE | KEY_CROUCH ))
Detecting if a player is pressing a key currently
The define:
// PRESSING(keyVariable, keys) #define PRESSING(%0,%1) \ (%0 & (%1))
Pressing one key:
if (PRESSING( newkeys, KEY_FIRE ))
Pressing multiple keys:
if (PRESSING( newkeys, KEY_FIRE | KEY_CROUCH ))
Detecting releasing a key
The define:
// RELEASED(keys) #define RELEASED(%0) \ (((newkeys & (%0)) != (%0)) && ((oldkeys & (%0)) == (%0)))
Released one key:
if (RELEASED( KEY_FIRE ))
Released multiple keys:
if (RELEASED( KEY_FIRE | KEY_CROUCH ))
Examples
Attach NOS when the player presses fire
public OnPlayerKeyStateChange(playerid, newkeys, oldkeys) { if (PRESSED(KEY_FIRE)) { if (IsPlayerInAnyVehicle(playerid)) { AddVehicleComponent(GetPlayerVehicleID(playerid), 1010); } } return 1; }
Super jump
public OnPlayerKeyStateChange(playerid, newkeys, oldkeys) { if (PRESSED(KEY_JUMP)) { new Float:x, Float:y, Float:z; GetPlayerPos(playerid, x, y, z); SetPlayerPos(playerid, x, y, z + 10.0); } return 1; }
God mode while holding use
new Float:gPlayerHealth[MAX_PLAYERS]; #if !defined INFINITY #define INFINITY (Float:0x7F800000) #endif public OnPlayerKeyStateChange(playerid, newkeys, oldkeys) { if (PRESSED(KEY_ACTION)) { // They just pressed the action key, save their // old health for restoration. GetPlayerHealth(playerid, gPlayerHealth[playerid]); SetPlayerHealth(playerid, INFINITY); } else if (RELEASED(KEY_ACTION)) { // They just let go of action - restore // their old health again. SetPlayerHealth(playerid, gPlayerHealth[playerid]); } return 1; }
Explanation
You don't need to worry about HOW it's done, just that it is. HOLDING detects if they're pressing a key (or keys), regardless of wether they were pressing it before, PRESSED detects if they only just pressed the key(s) and RELEASED detects if they just released a key(s). However if you want to know more - read on.
The reason why you need to do it this way, not just using & or ==, is to detect exactly the keys you want while ignoring others which may or may not be pressed. In binary KEY_SPRINT is:
0b00001000
and KEY_JUMP is:
0b00100000
thus ORing them into the wanted keys (we could also add them in this example but that's not always the case) gives:
0b00101000
If we were only using & and OnPlayerKeyStateChange was called for a player pressing jump we would get the following code:
newkeys = 0b00100000 wanted = 0b00101000 ANDed = 0b00100000
The AND of the two numbers is not 0, thus the result of the check is true, which isn't what we want.
If we only used == the two numbers are clearly not the same thus the check would fail, which is what we want.
If the player was pressing jump, sprint and crouch we would get the following code:
newkeys = 0b00101010 wanted = 0b00101000 ANDed = 0b00101000
The ANDed version is the same as the required keys and also not 0, thus will give the correct answer, however the two original numbers are not the same so == will fail. In both the examples one of the two has given the right answer and one has given the wrong answer. If we compare the first one using & and == we get:
newkeys = 0b00100000 wanted = 0b00101000 ANDed = 0b00100000
Obviously wanted and ANDed are not the same so the check fails, which is correct. For the second example:
newkeys = 0b00101010 wanted = 0b00101000 ANDed = 0b00101000
Wanted and ANDed are the same so comparing them as equal will result in a true result, which again is correct.
So using this method we can accurately check if certain keys are pressed ignoring all other keys which may or may not be pressed. the oldkeys check just uses != instead of == to ensure that the required keys were not previously pressed, so we know one of them was just pressed.
Связанные функции
Следующие функции могут быть полезны, т.к. они так или иначе связаны с текущей функцией.
- OnPlayerStateChange: Вызывается, когда положение игрока меняется.
Связанные автовызываемые функции
Эти автовызываемые функции могут оказаться полезными, т.к. они связаны с текущей функцией.
- GetPlayerKeys: Check what keys a player is holding.