OnPlayerKeyStateChange RU

From SA-MP Wiki

Jump to: navigation, search

Contents

OnPlayerKeyStateChange

Описание:

Данный обратный вызов вызывается при изменении состояния клавиш (нажимание/удерживание) управления игрока (up/down/left/right).


Image:32px-Ambox_warning_orange.png

Примечание

Этот обратный вызов также может быть вызван NPC.


Параметры:
(playerid, newkeys, oldkeys)
playeridID игрока, нажавшего клавишу.
newkeysНажатая клавиша управления. Смотрите обозначенные клавиши здесь.
oldkeysПредыдущая нажатая клавиша управления. Смотрите обозначенные клавиши здесь.


Возвращаемые значения:

Этот обратный вызов ничего не возвращает.
  • Это всегда вызывается первым из gamemode.


Совет

Image:Light_bulb_icon.png

Клавиши управления не вызывают OnPlayerKeyStateChange (up/down/left/right). Они могут быть обнаружены только через GetPlayerKeysOnPlayerUpdate или таймере).


Вступление

Данная функция не отслеживает, какая именно клавиша на клавиатуре была нажата - она опознает только "команды" для управления игроком, т.е.данная функция, для примера: не может отследить, нажал ли игрок клавишу '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.

Связанные функции

Следующие функции могут быть полезны, т.к. они так или иначе связаны с текущей функцией.


Связанные автовызываемые функции

Эти автовызываемые функции могут оказаться полезными, т.к. они связаны с текущей функцией.

Personal tools
Navigation
Toolbox
In other languages