Подробный гайд: Null-алиас (nkey) в CS 1.6: как скрипт убирает перехлёст клавиш WASD - Часть 1
1. Что это вообще такое
Null‑алиас — это набор консольных алиасов, который перехватывает клавиши движения (W/A/S/D) и делает так, чтобы две противоположные клавиши никогда не были активны одновременно. Нажали новую — старая автоматически «отжимается».
В русском комьюнити штука известна под кучей имён, и все они про одно и то же:
| Название | Откуда | Смысл |
|---|---|---|
| null / нулы | англ. null‑cancel | «обнуление» противоположного ввода |
| nkey | null key | то же самое, сленг KZ/HNS |
| 1key | «одна клавиша» | строгий вариант: активной может быть только одна клавиша направления |
| null‑cancel / null binds | Source/CS:GO/CS2 | то же |
| SOCD / snap‑tap | железо (Razer, Wooting) | аппаратный аналог того же поведения |
В русских гайдах по KZ/HNS это прямо называется «нулы»: «Во время нажатия W A S D отжимают противоположную кнопку. Благодаря чему намного легче попадать в стрейфы».
2. Зачем это нужно: механика GoldSrc «под капотом»
Всё упирается в функцию CL_CreateMove из клиентской DLL (cl_dll/input.cpp, репозиторий https://github.com/ValveSoftware/halflife/blob/master/cl_dll/input.cpp):
cmd->sidemove += cl_sidespeed->value * CL_KeyState(&in_moveright);
cmd->sidemove -= cl_sidespeed->value * CL_KeyState(&in_moveleft);
...
if ( !(in_klook.state & 1) )
{
cmd->forwardmove += cl_forwardspeed->value * CL_KeyState(&in_forward);
cmd->forwardmove -= cl_backspeed->value * CL_KeyState(&in_back);
}
cl_forwardspeed, cl_backspeed, cl_sidespeed по умолчанию = 400.
Следствие:
Зажали A и D → sidemove = 400 − 400 = 0. Персонаж встаёт на месте. Это и есть «перехлёст» (overlap), из‑за которого в KZ падают со стрейфов, а в обычном CS вас ловят на мгновение полной остановки.
Null‑алиас убирает саму возможность такого состояния на уровне ввода.
Побочный бонус:
CL_KeyState возвращает дробные значения (0.25 / 0.5 / 0.75 / 1.0) в зависимости от того, что произошло с кнопкой в этом кадре. Но поскольку 0.75 × 400 = 300 всё ещё больше sv_maxspeed (250) и вектор потом нормализуется к maxspeed, на скорости это не сказывается. Заметно только при ходьбе на +speed (cl_movespeedkey 0.3 → 120 × 0.75 = 90 на один кадр).
3. База: как работают bind и alias (без этого скрипт не понять)
Два факта из документации Valve Developer Community:
3.1. Правило «+ → −». Если строка, привязанная к клавише, начинается с +, то при отпускании клавиши движок выполняет ту же строку, заменив первый символ на −.
bind w +mfwd → нажатие: +mfwd | отпускание: -mfwd
Заменяется только первый символ всей строки. Поэтому bind w "+forward; +attack" при отпускании даст -forward; +attack — баг. Всегда оборачивайте в алиас.
3.2. Алиасы.
- Имя алиаса — до 31 символа, регистр не важен; значение — до 1023 символов.
alias имябез значения =alias имя ""— пустой алиас, при вызове не делает ничего.aliasбез аргументов печатает в консоль список всех алиасов (Current alias commands:).- Алиас не может называться так же, как существующая команда или cvar.
- Алиас может переопределять сам себя и другие алиасы прямо во время выполнения — на этом построены все тумблеры.
- Алиасы не сохраняются в
config.cfg— туда пишутся только бинды и cvar'ы. Это ключевой момент для установки (см. раздел 4).
3.3. Как движок хранит «нажатость». В kbutton_t есть два слота down[0] и down[1] под номера клавиш.
Важно:
void KeyUp (kbutton_t *b) {
c = Cmd_Argv(1);
if (c[0]) k = atoi(c);
else { // вызов БЕЗ аргумента (из алиаса/консоли)
b->down[0] = b->down[1] = 0;
b->state = 4; // «assume for unsticking, so clear all»
return;
}
...
}
Когда -forward вызывается из алиаса, аргумента (номера клавиши) у него нет → движок полностью сбрасывает кнопку. Именно поэтому null‑скрипт и работает, и именно поэтому он иногда «залипает» (см. раздел 10).
4. Установка: куда класть (это самое важное!)
Порядок загрузки конфигов в CS 1.6:
1. autoexec.cfg (если существует)
2. параметры запуска (+exec ...)
3. config.cfg (ВСЕГДА; перезаписывается игрой при каждом выходе)
4. userconfig.cfg (через строку "exec userconfig.cfg" в конце config.cfg)
Папка:
- Steam:
...\Steam\steamapps\common\Half-Life\cstrike\ - Non‑Steam:
...\папка_игры\cstrike\
Главная ошибка новичков
Положить null в autoexec.cfg. autoexec.cfg выполняется раньше config.cfg, а config.cfg перезаписывается игрой при выходе и содержит дефолтные bind "w" "+forward", которые вы же и накликали в меню Options. Итог — после перезапуска null молча слетает.
Правильно
Вариант А (простой) — всё прямо в cstrike\userconfig.cfg:
// Add custom configurations to the file "userconfig.cfg".
exec null.cfg
Вариант Б (чистый) — отдельный файл cstrike\null.cfg + строка exec null.cfg в userconfig.cfg. Рекомендую именно так.
Требования к файлу
- Кодировка ANSI / Windows‑1251, без BOM. UTF‑8 с BOM ломает первую строку.
- Переводы строк Windows (CRLF).
- Создавать обычным Блокнотом или Notepad++ (Кодировки → Windows‑1251).
- Файл
userconfig.cfgигра сама не создаёт — его нужно создать вручную.
5. Основной скрипт (классика: «последняя нажатая побеждает»)
Полный файл cstrike\null.cfg:
// ==========================================================
// NULL ALIAS / nkey — CS 1.6 (GoldSrc)
// Противоположные клавиши движения не могут быть зажаты
// одновременно. Последняя нажатая всегда в приоритете.
// ==========================================================
// ---------- служебные ----------
alias none "" // пустая заглушка
alias chkf none // «флаги»: зажата ли клавиша логически
alias chkb none
alias chkl none
alias chkr none
// ---------- W / S ----------
alias +mfwd "-back;+forward;alias chkf +forward"
alias -mfwd "-forward;chkb;alias chkf none"
alias +mback "-forward;+back;alias chkb +back"
alias -mback "-back;chkf;alias chkb none"
// ---------- A / D ----------
alias +mleft "-moveright;+moveleft;alias chkl +moveleft"
alias -mleft "-moveleft;chkr;alias chkl none"
alias +mright "-moveleft;+moveright;alias chkr +moveright"
alias -mright "-moveright;chkl;alias chkr none"
// ---------- тумблер ----------
alias nkey_on "bind w +mfwd;bind s +mback;bind a +mleft;bind d +mright;alias chkf none;alias chkb none;alias chkl none;alias chkr none;echo NULL (nkey) ON;alias nkey nkey_off"
alias nkey_off "bind w +forward;bind s +back;bind a +moveleft;bind d +moveright;-forward;-back;-moveleft;-moveright;echo NULL (nkey) OFF;alias nkey nkey_on"
alias nkey nkey_on
// ---------- автозапуск ----------
nkey_on
// ---------- по желанию ----------
// bind F6 nkey // горячая клавиша вкл/выкл
// bind UPARROW +mfwd // если ходите ещё и стрелками
// bind DOWNARROW +mback
// bind LEFTARROW +mleft
// bind RIGHTARROW +mright
И в cstrike\userconfig.cfg:
exec null.cfg
Построчный разбор
Возьмём пару A/D.
alias +mleft "-moveright;+moveleft;alias chkl +moveleft"
-moveright— вызван без аргумента →KeyUpполностью сбрасывает кнопкуin_moveright. Правый стрейф мгновенно выключается, даже если D физически зажата.+moveleft— включает левый стрейф.alias chkl +moveleft— ставим «флаг»: A логически зажата.
alias -mleft "-moveleft;chkr;alias chkl none"
-moveleft— отпускаем.chkr— если флаг D установлен (клавиша D всё ещё физически зажата), этот алиас сейчас содержит+moverightи возвращает движение вправо. Если D не зажата —chkr=none= пусто.alias chkl none— сбрасываем свой флаг.
Именно шаг chkr отличает нормальный null от «кривого»: без него после отпускания второй клавиши вы бы остались стоять, хотя первая всё ещё зажата.
Что происходит в игре
| Действие | Без null | С null |
|---|---|---|
| Держим A, нажали D | стоим на месте (sidemove = 0) | сразу стрейфим вправо |
| Держим A, нажали D, отпустили D | снова влево | снова влево (через chkr) |
| Держим W, нажали A | диагональ W+A | диагональ W+A (не ломается!) |
| Держим W, нажали S | стоим | идём назад |
Обратите внимание:
Классический null гасит только противоположные пары. Диагонали W+A, W+D и т.д. работают как обычно — иначе играть было бы невозможно.
6. Вариант «1key» — строгий: только одна клавиша движения
Если нужно буквально «не позволяет одновременно зажать клавиши движения» (диагонали тоже запрещены). Отличается от классики только тем, что +m* гасит все три остальные направления, а -m* проверяет все три флага.
alias none ""
alias chkf none
alias chkb none
alias chkl none
alias chkr none
alias +mfwd "-back;-moveleft;-moveright;+forward;alias chkf +forward"
alias -mfwd "-forward;chkb;chkl;chkr;alias chkf none"
alias +mback "-forward;-moveleft;-moveright;+back;alias chkb +back"
alias -mback "-back;chkf;chkl;chkr;alias chkb none"
alias +mleft "-forward;-back;-moveright;+moveleft;alias chkl +moveleft"
alias -mleft "-moveleft;chkf;chkb;chkr;alias chkl none"
alias +mright "-forward;-back;-moveleft;+moveright;alias chkr +moveright"
alias -mright "-moveright;chkf;chkb;chkl;alias chkr none"
bind w +mfwd
bind s +mback
bind a +mleft
bind d +mright
Когда это имеет смысл:
Тренировка чистых стрейфов, некоторые surf/bhop‑режимы, обучение «не ёрзать».
Когда вредно:
Обычный CS — теряете диагонали, а с ними и манёвренность. Ставьте только через тумблер.
Мы делимся этой технической информацией, чтобы помочь вам в решении задач — используйте её с пониманием. Статья носит рекомендательный характер, поэтому, пожалуйста, применяйте описанные методы осмотрительно.