Подробный гайд: Null-алиас (nkey) в CS 1.6: как скрипт убирает перехлёст клавиш WASD - Часть 1

Полный гайд по null-алиасу (nkey) в CS 1.6: как скрипт убирает перехлёст клавиш WASD, правильная настройка конфигов, баги и легальность на KZ-серверах.

2026.10.30                  


Подробный гайд: Null-алиас (nkey) в CS 1.6: как скрипт убирает перехлёст клавиш WASD - Часть 1Подробный гайд: 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"
  1. -moveright — вызван без аргумента → KeyUp полностью сбрасывает кнопку in_moveright. Правый стрейф мгновенно выключается, даже если D физически зажата.
  2. +moveleft — включает левый стрейф.
  3. 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 — теряете диагонали, а с ними и манёвренность. Ставьте только через тумблер.


Мы делимся этой технической информацией, чтобы помочь вам в решении задач — используйте её с пониманием. Статья носит рекомендательный характер, поэтому, пожалуйста, применяйте описанные методы осмотрительно.


Статью подготовил: Аверко Денис Сергеевич @Nymexis г. Омск (специалист по ЗИ)

Комментарии

Загрузка...
Если комментарии не загружаются, можете попробовать отключить блокировщик рекламы для этого сайта