Экран ILI9341 на 2,4 дюйма, крестовина из четырёх кнопок и ещё две кнопки для A и B: вот это и есть карманная игровая консоль, и ESP32 для неё более чем достаточно. Мы сделали пять игр под такую раскладку, а затем шестую, Skythread, для более крупной консоли с аналоговым стиком, и поместили все шесть в галерею примеров Velxio. Каждая из них это скетч Arduino, и каждая запускается в браузере: нажмите Run и играйте, без платы, без экрана, без пайки.
| Игра | Плата | Тип игры | Частота кадров в Velxio |
|---|---|---|---|
| ESP32 Doom | ESP32 DevKit | шутер от первого лица | около 18 FPS |
| Sky Duel | Seeed XIAO ESP32-S3 | воздушный бой с видом сбоку | около 29 FPS |
| XIAO Quest | Seeed XIAO ESP32-S3 | RPG с видом сверху | около 32 FPS |
| Gear Hop | Seeed XIAO ESP32-S3 | платформер с боковой прокруткой | около 27 FPS |
| Circuit Defense | Seeed XIAO ESP32-S3 | tower defense | около 31 FPS |
| Skythread | Seeed XIAO ESP32-S3 Sense | трёхмерный полёт над сгенерированным ландшафтом | около 20 FPS |
Первая, Doom, появилась раньше всех и работает на классическом ESP32. Следующие четыре работают на Seeed Studio XIAO ESP32-S3, плате размером с большой палец от нашего партнёра Seeed, и используют один общий небольшой движок. Последняя, Skythread, работает на XIAO ESP32-S3 Sense, том же чипе с 8 МБ PSRAM, которые нужны ему, чтобы держать в памяти целый ландшафт. В этой статье рассказывается, как устроена каждая игра и как она работает внутри.
Как играть в них в Velxio
- Откройте любую игру из таблицы выше. Скетч, библиотеки и схема загружаются вместе.
- Нажмите Run. Velxio компилирует скетч и запускает его. Первая компиляция скетча занимает до минуты; после этого он берётся из кэша сборки.
- Каждая игра начинается с демо в режиме привлечения внимания, которое играет само по себе. Нажмите любую клавишу или кнопку, чтобы взять управление на себя.
- Играйте с клавиатуры: стрелки это крестовина, Z это A, а X это B. Каждая кнопка на холсте показывает свою клавишу в маленьком колпачке. Кнопки можно также нажимать мышью и удерживать, чтобы они оставались нажатыми. У Skythread есть аналоговый стик и больше кнопок; её управление описано в отдельном разделе ниже.
- Откройте монитор Serial, чтобы следить за логом игры: строка
STATс частотой кадров и строкаEVTдля каждого убийства, уровня, подобранного предмета и смерти. Skythread печатает свою строку каждые две секунды, с частотой кадров, временем отрисовки и отправки каждого кадра и счётом.
Любую кнопку в Velxio можно привязать к клавиатуре, не только эти. Щёлкните кнопку правой кнопкой мыши и используйте карточку Keyboard control: нажмите на неё, нажмите клавишу, и с этого момента удержание этой клавиши нажимает кнопку, пока идёт симуляция.
Симулятор выполняет саму прошивку, скомпилированную для настоящего чипа: Velxio эмулирует ESP32 и ESP32-S3 инструкция за инструкцией прямо в браузере и рисует всё, что скетч отправляет на ILI9341.
Карманная консоль

У XIAO всего 11 выводов GPIO, а экрану и шести кнопкам нужно двенадцать. Хитрость в том, что экрану не нужны две его обычные линии: с него никогда ничего не считывается, поэтому MISO остаётся неподключённым, а его вывод сброса привязан к 3,3 В, так что он сбрасывается вместе с платой. Этого хватает ровно.
| Вывод XIAO | Куда идёт | Вывод XIAO | Куда идёт | |
|---|---|---|---|---|
| D8 | экран SCK | D0 | крестовина ВВЕРХ | |
| D10 | экран MOSI | D1 | крестовина ВНИЗ | |
| D7 | экран CS | D2 | крестовина ВЛЕВО | |
| D9 | экран D/C | D3 | крестовина ВПРАВО | |
| 3V3 | экран VCC, LED и RST | D4 | A | |
| GND | экран GND, все кнопки | D5 | B |
Каждая кнопка соединяет свой вывод с землёй. Скетч включает внутренние подтягивающие резисторы чипа через pinMode(pin, INPUT_PULLUP), поэтому вывод читается как HIGH, когда кнопка отпущена, и как LOW, когда она нажата. Резисторы не нужны ни на холсте, ни на настоящей макетной плате. Doom использует ту же идею на более крупном ESP32 DevKit, со своими выводами (они перечислены в его разделе ниже).
Почему XIAO ESP32-S3
Четыре игры для XIAO сначала рисуют всю картинку в памяти, а затем отправляют её на экран за один раз. Картинка 320 x 240 при двух байтах на пиксель это 153 600 байт, и она должна лежать в одном непрерывном блоке RAM.
Именно здесь два чипа различаются. Мы напечатали самый большой свободный блок из скетча на каждом из них: 110 592 байта на классическом ESP32 и 278 528 байт на ESP32-S3. S3 может вместить весь кадр с запасом; классический ESP32 не может, поэтому Doom рисует картинку по одному столбцу за раз (подробнее об этом ниже).
S3 также быстрее из двух в симуляторе. При одном и том же тесте кадрового буфера на обеих платах движок Velxio в браузере выполнял ESP32 на 81–90 миллионов инструкций в секунду, а ESP32-S3 на 96–106 миллионов, и S3 выдавал 33 полных кадра в секунду против 31 у ESP32.
Один движок для четырёх игр
Четыре скетча для XIAO начинаются с одних и тех же 200 строк: движка. Он владеет кадровым буфером, кнопками, горсткой функций рисования и циклом кадра.

Рисование в RAM означает, что порядок не важен. Игра может нарисовать небо, затем холмы, затем самолёты, затем взрывы поверх всего, и экран увидит только готовую картинку, так что ничего не мерцает. Отправка это три вызова библиотеки Adafruit ILI9341:
static void present() {
tft.startWrite();
tft.setAddrWindow(0, 0, SCR_W, SCR_H); // the whole 320 x 240 screen
tft.writePixels(fb, SCR_W * SCR_H); // 153,600 bytes in one burst
tft.endWrite();
}
На 40 МГц эти 153 600 байт занимают около 31 мс на проводах SPI, так что одна только передача задаёт потолок около 30 кадров в секунду. Остальная часть кадра, логика игры и рисование, должна уложиться в то, что осталось, поэтому функции рисования простые: залитые прямоугольники, линии, круги, текст шрифтом 5 x 7, который поставляется с Adafruit GFX, и спрайты.
Спрайты записаны как текст, один символ на пиксель, с . для прозрачных пикселей и палитрой из 128 записей для остальных. Вот робот из Gear Hop:
"........a.......",
"....mmmmmmmm....",
"...mMMMMMMMMm...",
"...mMllMMllMm...", // l = the eyes, light cyan
"...mMllMMllMm...",
"...mMMMMMMMMm...",
"....mmmrrmmm....", // r = the red mouth
Ничто в этих играх не берётся из файла изображения. Тайлы карты, текстуры и эффекты рисуются кодом в RAM при запуске платы, так что каждый скетч это простой текст, который можно читать и менять в редакторе.
Sky Duel: воздушный бой с моделью полёта
Вы летите на биплане над миром шириной в четыре экрана, который зациклен: улетев за правый край, вы появляетесь слева. Красная эскадрилья охотится за вами, на земле ждут зенитки и грузовики, и каждая волна приносит ещё один вражеский самолёт.
Как играть. Направляйте D-pad туда, куда хотите лететь, включая диагонали; самолёт поворачивает в этом направлении. Удерживайте A для пулемётов и нажмите B, чтобы сбросить бомбу (шесть на волну). Бомбы это единственный способ уничтожить наземные цели. Радар вверху показывает весь мир, так что вы можете найти самолёты за пределами экрана.
Как это работает. Самолёт не движется с фиксированной скоростью. Каждый кадр скетч прогоняет небольшую модель полёта:

Скорость стремится к крейсерскому значению, но набор высоты отнимает скорость, а пикирование её добавляет, пропорционально углу. Если слишком долго тянуть строго вверх, самолёт опускается ниже скорости сваливания: нос сам по себе падает к земле, и вам приходится пикировать, чтобы снова набрать ход. Вражеские пилоты летают по той же модели, с тем же сваливанием, и именно поэтому это воздушный бой, а не тир.
Самолёт это один спрайт 16 x 16, обращённый вправо. При загрузке скетч поворачивает его в 32 направления и сохраняет все, зеркально отражая те, что смотрят влево, чтобы самолёт не летал вверх ногами. Тогда отрисовка самолёта это просто поиск в таблице с двукратным увеличением на экране. Враги направляются к точке чуть впереди вас, стреляют, когда вы оказываетесь перед их пушками, и отворачивают после близкого пролёта, чтобы не таранить вас.
XIAO Quest: небольшая RPG
Герой, деревня, лес и пещера. Старейшина у фонтана рассказывает, что Король Теней унёс кристалл деревни в старую пещеру, и даёт вам ключ от ворот, преграждающих путь. В лесу и пещере на вас нападают монстры, и вы сражаетесь с ними по очереди.
Как играть. Ходите с помощью D-pad. A говорит с людьми, читает таблички, открывает сундуки и подтверждает; B показывает ваши характеристики и предметы. Фонтан лечит вас, торговец продаёт зелья за 10 золота, а в трёх сундуках лежат два зелья, железный меч и серебряное кольцо. В бою выбирайте с помощью D-pad и подтверждайте кнопкой A: атака, огонь (3 MP, двойной урон, игнорирует защиту), выпить зелье или убежать.
Как это работает. Игра это небольшой конечный автомат. У каждого состояния своя обработка ввода и своя отрисовка, и вот события, которые переводят его из одного в другое:

Три карты это 20 x 14 тайлов по 16 пикселей, записанных в скетче строками, по одному символу на тайл: # это дерево, ~ вода, = тропа, K запертые ворота. Герой движется по одному тайлу за раз и скользит между тайлами за 0,16 секунды, так что движение ощущается плавным, а коллизии остаются простыми: шаг либо разрешён, либо нет.
В боях используются небольшие формулы с целыми числами. Атака наносит ваш показатель атаки плюс бросок от 0 до 3, минус защита врага. Опыт даёт уровни, и каждый уровень добавляет здоровье, магию, атаку и защиту. Пещера тёмная: после отрисовки карты скетч приглушает каждый пиксель на расстоянии от 50 до 70 пикселей от героя до половины яркости, а всё дальше до четверти, так что вы несёте фонарь. Вычисление расстояния для каждого пикселя снизило частоту кадров примерно с 27 до 16. Если вычислять его один раз на строку, как освещённый участок, тусклый участок и тёмный остаток, частота возвращается примерно к 22.
Gear Hop: платформер
Маленький робот бежит через два уровня, собирая шестерёнки. Жуки ходят по земле, дроны парят в воздухе, некоторые полы усеяны шипами, а самые широкие пропасти можно пересечь только на движущихся платформах. Доберитесь до флага в конце каждого уровня.
Как играть. Ходите кнопками LEFT и RIGHT, удерживайте B, чтобы бежать, нажмите A, чтобы прыгнуть. Удерживайте A дольше для более высокого прыжка. Приземлитесь на врага, чтобы раздавить его, и прыгните в блок ? снизу, чтобы выбить из него шестерёнку. У вас три сердца; шипы и враги отнимают одно, как и падение в яму.
Как это работает. Уровни это 128 x 14 тайлов, сгенерированных небольшим скриптом и вставленных в скетч строками. Робот это прямоугольник 10 x 14 пикселей, и каждый кадр он движется в два шага: сначала вбок, выталкиваясь обратно из любой стены, внутри которой оказался; затем по вертикали, приземляясь на землю, на одностороннюю доску или на движущуюся платформу, либо ударяясь головой о блок. Раздельная обработка двух осей не даёт роботу прилипать к стенам или проскальзывать сквозь углы.
Три небольших правила делают прыжки правильными. Прыжок, нажатый за 0,12 секунды до приземления, всё равно срабатывает, когда робот касается земли (буфер прыжка). Прыжок, нажатый в течение 0,08 секунды после схода с края, всё ещё считается (время койота). А раннее отпускание A обрезает скорость вверх, и именно поэтому удержание кнопки даёт более высокий прыжок. Два слоя холмов прокручиваются со скоростью одной пятой и чуть меньше половины скорости камеры, что придаёт сцене глубину.
Circuit Defense: tower defense на печатной плате
Жуки ползут по печатной плате к чипу. Вы строите на плате электронные детали, чтобы их остановить: светодиод бьёт быстрым лазером по одному жуку, конденсатор разряжается во всех жуков вокруг себя, а катушка замедляет жуков. Каждый жук, добравшийся до чипа, отнимает у него единицу здоровья, а волны становятся всё сильнее.
Как играть. Двигайте курсор крестовиной. B выбирает деталь, A строит её, а A на уже построенной детали улучшает её до уровня 3. Уничтоженные жуки приносят монеты, а за каждую пройденную волну дают бонус. Круг вокруг курсора показывает радиус действия детали, которую вы собираетесь построить.
Как это работает. Жуки не идут по фиксированному маршруту. Они ходят по свободному месту на плате, и вы можете направлять их тем, что строите: медная дорожка на плате всегда будет их маршрутом. За этим стоит поиск в ширину, который запускается заново после каждой постройки:

Поиск начинается с клеток рядом с чипом, нумерует их 0 и расходится наружу: каждый свободный сосед пронумерованной клетки получает её номер плюс один. Когда поиск закончен, в каждой клетке записано расстояние до чипа, включая стены из башен. Жук, стоящий на клетке, просто шагает к любому соседу с меньшим номером. Никакого планирования маршрута для каждого жука нет: один поиск на 260 клеток обслуживает всех.
Те же числа позволяют легко соблюсти единственное правило игры: путь можно удлинять, но нельзя перекрывать. Когда вы строите, скетч ставит деталь, запускает поиск заново и смотрит на клетку входа. Если у неё больше нет номера, жуки не смогли бы пройти: деталь убирается, и игра пишет THAT BLOCKS THE PATH.
ESP32 Doom: шутер от первого лица
Самая старая из пяти игр работает на классическом ESP32 DevKit. Вы бродите по лабиринту из текстурных стен, стреляете по роботам, которые вас преследуют, и по слизням, которые плюются кислотой, открываете двери, подбираете аптечки и боезапас и отбиваете волну за волной.
Как играть. Крестовина отвечает за ходьбу и повороты, A стреляет (удерживайте для автоматической стрельбы), B открывает дверь перед вами, а B вместе с LEFT или RIGHT даёт стрейф. Пины отличаются от игр для XIAO: UP на GPIO 32, DOWN 33, LEFT 25, RIGHT 15, A на 22, B на 21, а экран на шине VSPI ESP32 (SCK 18, MOSI 23, MISO 19, CS 5, D/C 2, RST 4).
Как это работает. Doom это рейкастер, техника Wolfenstein 3D (1992): для каждого столбца экрана он пускает луч от игрока в карту-сетку 20 x 20 и останавливает его на первой стене. Расстояние задаёт высоту отрисовываемого среза стены. Поскольку у классического ESP32 нет места для полного кадра, скетч собирает по одному столбцу в буфере на 200 пикселей, где пол, потолок, стена, враги, оружие и эффекты уже скомпонованы, и отправляет его одним пакетом:
Роботы идут прямо на вас и бьют когтями на 5 здоровья в секунду; слизни держатся на расстоянии и плюются кислотой, которая отнимает 7. Выстрел попадает в ближайшего врага под прицелом, если только какая-нибудь стена не ближе. За каждым зачищенным врагом приходит следующая волна с двумя новыми, чуть быстрее и чуть крепче.
Skythread: трёхмерный полёт над сгенерированным миром
Doom это 3D в помещении. Skythread это 3D на открытом воздухе: вы летите на дельтаплане низко над открытой местностью, вдоль речных долин, мимо гор и над озёрами, с туманом у горизонта и небом, которое проходит путь от дня к золотому часу и к ночи. Впереди в воздухе висят кольца, а часы идут. Вы начинаете с 45 секундами, и каждое кольцо добавляет ещё несколько. Каждое кольцо подряд повышает комбо, вплоть до пятикратных очков, а пролёт через середину кольца даёт PERFECT. Промахнётесь мимо кольца, и комбо начинается заново; коснётесь земли, и вы отскочите от неё, медленнее и беднее на три секунды. Как и остальные, игра начинается с демо, которое летает само.
Карманная консоль. Skythread написана для консоли, на которой работают наши порты Metal Gear Solid и Castlevania, а не для раскладки с крестовиной, как у остальных четырёх: Seeed Studio XIAO ESP32-S3 Sense, тот же экран ILI9341, двухосевой аналоговый стик, две кнопки на собственных пинах и ещё шесть на одном пине. В Velxio пример открывается в раскладке этой консоли: стик слева, экран посередине, кнопки справа и под экраном.
| Пин XIAO | Куда идёт | Пин XIAO | Куда идёт | |
|---|---|---|---|---|
| D8 | экран SCK | D0 | стик X (влево и вправо) | |
| D10 | экран MOSI | D1 | стик Y (вверх и вниз) | |
| D9 | экран MISO | D2 | шесть кнопок через резисторную лестницу | |
| D3 | экран CS | D5 | FIRE | |
| D6 | экран D/C | D7 | BRAKE | |
| D4 | экран RST | 3V3, GND | питание экрана, стика и лестницы |
Как играть. Управляйте стиком: кликните по нему на холсте и используйте стрелки, или удерживайте мышь на одной из его стрелок. Тяните назад (вниз), чтобы набрать высоту, толкайте вперёд, чтобы спикировать, а влево или вправо, чтобы накрениться и повернуть. FIRE (Z) это ускорение, которое тратит синюю полосу, а также запускает игру. BRAKE (X) замедляет вас для крутых поворотов. START (Enter) запускает и ставит на паузу, CROSS (C) тоже запускает игру, SELECT (Q) возвращает к демо, CIRCLE (T) меняет время суток, TRIANGLE (W) строит новый мир, а L1 (F) показывает частоту кадров.
Шесть кнопок на одном пине. У XIAO 11 пинов, а экрану, стику и восьми кнопкам нужно было бы 16. Приём для шести из кнопок это резисторная лестница. Резистор на 10 кОм подтягивает D2 к 3,3 В, и каждая кнопка соединяет D2 с землёй через свой резистор, так что каждая образует свой делитель напряжения. Пин читает 3,3 В × R / (R + 10 кОм):
| Кнопка | На землю через | Ожидается | Измерено в Velxio |
|---|---|---|---|
| CIRCLE | 0 Ом | 0 В | 0 мВ |
| CROSS | 2,2 кОм | 0,595 В | 612 мВ |
| TRIANGLE | 4,7 кОм | 1,055 В | 1065 мВ |
| L1 | 10 кОм | 1,650 В | 1651 мВ |
| START | 22 кОм | 2,269 В | 2258 мВ |
| SELECT | 47 кОм | 2,721 В | 2665 мВ |
Скетч читает D2 через analogReadMilliVolts() и выбирает кнопку с ближайшим напряжением, с порогами посередине между соседями (300, 824, 1350, 1960, 2496 и 2960 мВ). Velxio рассчитывает лестницу как настоящую схему: последний столбец это то, что скетч напечатал в мониторе порта, когда мы нажимали каждую кнопку на холсте. Лестница читает по одной кнопке за раз, поскольку две нажатые вместе выглядят как одна кнопка с меньшим сопротивлением. Поэтому игра держит FIRE и BRAKE, те две, которые вы удерживаете во время полёта, на собственных пинах. Она также игнорирует лестницу, пока не увидит её отпущенной хотя бы раз, так что D2, оставленный неподключённым, никогда не сможет нажать фантомные кнопки.
Как это работает. Мир генерируется при запуске платы: карта ландшафта 512 x 512, сделанная шумом diamond-square, где каждая ячейка занимает два байта, один для высоты и один для цвета из палитры. Вместе с копией половинного размера для дальних расстояний и двумя буферами кадра это около 940 КБ, поэтому Skythread нужна модель Sense с её PSRAM. В Velxio на построение мира уходит 3 секунды симулированного времени, а первый кадр доходит до экрана через 3,4 секунды после включения.
Отрисовка использует колоночный рейкастинг воксельных полётных игр начала 1990-х. Для каждого столбца экрана скетч ведёт луч от камеры по карте, от ближнего к дальнему, до 160 шагов и 340 ячеек вдаль; после 110 ячеек он читает вместо этого карту половинного размера. На каждом шаге он превращает высоту рельефа в этом месте в строку на экране. Если эта строка выше всего, что уже нарисовано в столбце, он заполняет промежуток цветом этой ячейки и запоминает новый верх:

Этот запомненный верх для каждого столбца называется y-буфером. Ближняя земля всегда рисуется первой, поэтому дальняя земля может показаться только над ней, и ни один пиксель никогда не рисуется дважды. Туман это 20 палитр, каждая смешана чуть сильнее к цвету неба, по одной на полосу расстояния. Крен это сдвиг: горизонт каждого столбца смещается вверх или вниз пропорционально его расстоянию от центра экрана, что выглядит как крен. Кольца, тень дельтаплана и солнце проверяются по тому же y-буферу на своём расстоянии, так что гора перед кольцом скрывает его.
Кадр хранится столбец за столбцом, уже в том порядке байтов, который ожидает ILI9341. В собственной портретной адресации панели столбец картинки ландшафта это одна из её строк, поэтому весь кадр уходит одним пакетом. С двумя буферами кадра два ядра чипа работают одновременно: задача на ядре 0 отправляет один кадр на экран по SPI на 40 МГц, пока ядро 1 рисует следующий.
Скетч также следит за собственной скоростью. Когда отрисовка кадра занимает в среднем больше 22 мс, он пускает 160 лучей вместо 320 и рисует каждый столбец шириной в два пикселя, а ниже 7 мс возвращается к полной ширине. После 45 мс, случай, который его комментарии оставляют для медленного хоста, такого как симулятор в браузере, он перестаёт перекрывать два ядра и ждёт, пока каждый кадр дойдёт до конца, прежде чем рисовать следующий, так что симулированная панель всегда получает целые кадры. В Velxio он всё время работал в этом режиме, на 160 столбцах, рисуя каждый кадр за 33–50 мс симулированного времени.
Частота кадров
Мы измерили каждую игру на velxio.dev в headless-браузере Chrome на нашем сервере, играя примерно по минуте в каждую.
| Игра | Сообщает скетч (медиана, диапазон) | Кадров в секунду реального времени |
|---|---|---|
| ESP32 Doom | 17,6 (13–24) | 16,1 |
| Sky Duel | 28,9 (23–38) | 27,2 |
| XIAO Quest | 32,2 (21–39) | 30,8 |
| Gear Hop | 27,0 (21–35) | 23,4 |
| Circuit Defense | 31,3 (23–40) | 29,4 |
| Skythread | 20,4 (16–23) | 17,7 |
Два числа на игру. Первое это то, что игра сама сообщает в мониторе порта, по собственному millis() чипа: частота кадров симулированной платы. Второе это сколько кадров браузер на самом деле показал за секунду реального времени. Они расходятся, когда компьютер, запускающий симуляцию, не может держать симулированный чип на полной скорости; симулированное время тогда идёт немного медленнее настенных часов. Ваши собственные числа зависят от вашего компьютера.
Четыре двухмерные игры для XIAO работают почти вдвое быстрее Doom. Часть этого это работа: рейкастер пускает 320 лучей и вычисляет пол и потолок пиксель за пикселем. Часть этого это передача: один пакет на кадр вместо 320 маленьких, каждый со своим окном адресации. Skythread, единственная, что рисует трёхмерный ландшафт, держит около 20 кадров в секунду, пуская 160 лучей вместо 320, ту самую уступку, которую делает её собственный код всякий раз, когда отрисовка кадра занимает больше 22 мс.
Сделайте их своими
Каждая игра это один скетч, а код организован блоками: движок, графика, состояние игры, обновление и отрисовка. Несколько изменений, которые займут минуту:
- Sky Duel: измените крейсерскую скорость (125) или скорость сваливания (50) в
fly()и почувствуйте, как меняется самолёт. - XIAO Quest: карты это строки в
MAP_V,MAP_FиMAP_C; параметры монстров находятся в таблицеFOES. - Gear Hop: сила прыжка это
-335вmovePlayer(), гравитация950; попробуйте лунную. - Circuit Defense: добавьте четвёртую деталь в
TW_NAME,TW_COSTиupdateTowers(). - Doom:
MAP_SRCэто 20 строк по 20 цифр; каждая цифра это текстура стены, а 9 это дверь. - Skythread:
WORLD_SEEDв начале скетча выбирает мир, в который вы загружаетесь, аPITCH_PULL_TO_CLIMBсо значением 0 заставляет стик поднимать нос самолёта при наклоне вперёд.
Нажмите Run после изменения, и Velxio скомпилирует изменённый скетч, так что вы можете поменять число и увидеть результат через несколько секунд. Когда всё работает так, как вам нравится, тот же скетч запускается на настоящем XIAO ESP32-S3 с ILI9341 и шестью кнопками, подключёнными как описано выше, а Skythread на XIAO ESP32-S3 Sense со стиком и кнопками из его раздела.