В прошлых статьях мы настроили серверную физику (#2) и мультиплеер (#1). Скажу честно: писать сетевой код оказалось проще, чем заставить Godot 4 адекватно рендерить растительность. Я заказали карту у фрилансера. Карту прислали, собрана на базе мощного плагина Terrain3D. Вроде выглядит неплохо, но как обычно дьявол кроется в мелочах и наружу полезли артефакты рендера. Вместо того чтобы спокойно писать геймплей, я потратил 23 часа на технический аудит, дебаг и хакинг чужих шейдеров. В сети полно туториалов,...
GGG Studio
#2 - GODOT 4. ПРОДОЛЖАЕМ РАЗРАБОТКУ MULTIPLAYER — ДОБАВЛЯЕМ СЕТЕВУЮ ФИЗИКУ И ПИНАЕМ КУБИКИ Продолжаем работу над сетевой частью проекта. В прошлом посте мы разобрались с базовым движением: настроили контроллер так, чтобы клиент не спорил с сервером. Персонаж бегает и синхронизируется. Но мир пока статичен. Пришло время добавить интерактивности — физические объекты, которые можно толкать. В синглплеере (или если бы мы делали локальный контроллер) растолкать раскиданные по уровню коробки (RigidBody3D) проще простого. Посмотрите, как выглядит обычное толкание объектов при столкновении: func _physics_process(delta): move_and_slide() # Проверяем всё, с чем столкнулся игрок for i in get_slide_collision_count(): var collision = get_slide_collision(i) var collider = collision.get_collider() # Если это физический объект — толкаем его if collider is RigidBody3D: var push_dir = -collision.get_normal() collider.apply_central_impulse(push_dir * push_force) Всё интуитивно: сдвинулись, проверили коллизии, нашли физический объект, пнули его в противоположную от нормали сторону. Теперь возвращаемся к нашему мультиплееру. Если оставить этот код как есть, начнется хаос. Каждый подключенный клиент будет сам пинать кубик на своем экране. Из-за микрозадержек (пинга) у одного игрока кубик улетит вправо, у другого — влево, а у третьего вообще застрянет в текстурах. Поэтому мы снова вспоминаем наше золотое правило: сервер всегда прав. Адаптируем код. Клиент просто упирается в кубик (для него это стена), а расчетом импульса занимается исключительно сервер: func _physics_process(delta): move_and_slide() # Физику толкания просчитывает только сервер! if is_multiplayer_authority(): for i in get_slide_collision_count(): var collision = get_slide_collision(i) var collider = collision.get_collider() if collider is RigidBody3D: var push_dir = -collision.get_normal() # Небольшой секрет: даем легкий импульс вверх, # чтобы кубик оторвался от пола и красиво покатился, а не просто скользил push_dir.y = 0.3 collider.apply_central_impulse(push_dir.normalized() * push_force) Что здесь происходит? Сервер замечает, что игрок врезался в кубик. Сервер сам применяет силу (плюс мы добавили хитрость — push_dir.y = 0.3, небольшой "апперкот", чтобы коробка реалистично подпрыгивала, а не терлась пузом об пол). А что делает клиент? Клиент ничего не считает. Как и в случае с передвижением игрока, на самом кубике (RigidBody3D) висит наша любимая нода MultiplayerSynchronizer. Сервер пнул кубик -> физический движок сервера рассчитал его полет -> MultiplayerSynchronizer автоматически разослал новые координаты и угол поворота всем клиентам. В итоге у всех игроков на экранах кубик красиво и абсолютно синхронно отлетает в сторону. Никаких рассинхронов, сервер должен всё контролировать. В следующей статье по "сетевой физике" покажу мультиплеерный баг который обнаружил...
#1 - GODOT 4. НАЧАЛ РАЗРАБОТКУ МУЛЬТИПЛЕЙЕРА — ПЕРЕПИСЫВАЕМ СЕТЕВОЕ ДВИЖЕНИЕ: С ЛОКАЛЬНОГО КОНТРОЛЛЕРА НА СЕТЕВОЙ. Сегодня хотел приоткрыть завесу тайны своего проекта, над которым работаю продолжительное время. Проект перешёл в стадию начало разработки мультиплейера. Первым делом будем адаптировать контроллер управления игрока, но сперва нужно определиться с моделью — а именно, сервер всегда прав, а клиент... скажем так, не спорит. Посмотрите как выглядит обычный локальный контроллер на WASD: func _physics_process(delta): velocity.y -= grav * delta process_input() update_facing(delta) move_and_slide() Всё просто: гравитация, ввод с клавиатуры, поворот, движение. Персонаж слушается каждую кнопку мгновенно. Теперь мультиплеер. Тот же контроллер, но появляется проверка — кто я? Сервер или клиент? func _physics_process(delta): if is_server_authority(): velocity.y -= grav * delta process_input() update_facing(delta) move_and_slide() else: velocity = Vector3.ZERO move_and_slide() Сервер работает как раньше — считает физику, двигает, отправляет позицию остальным. Клиент — нет. Он обнуляет скорость и ждёт готовые координаты от MultiplayerSynchronizer. MultiplayerSynchronizer — это нода в Godot, которая сама следит за изменениями нужных переменных и рассылает их клиентам. Без ручных вызовов каждый кадр, без лишнего кода. Настроил один раз — и персонаж двигается плавно на всех подключённых клиентах. Правильный мультиплеер в Godot строится вокруг этой ноды — без неё не сделать хорошо работающую сетевую игру.
Пишем свой универсальный сканер Modbus RTU на Python.
В прошлой статье мы боролись с анемометром и чтобы применить его в хозяйстве, пришлось разрабатывать скрипт-сканер. Но, как оказалось, он совсем не универсальный и требует постоянной точечной настройки в самом коде Python практически под каждое новое устройство. Что ж, будем исправлять эту ситуацию. Проблема: есть датчик, которым уже пользовались. Его сняли, положили на склад, прошли месяцы. И вот он понадобился. А какой у него адрес? Какая скорость? Где взять таблицу регистров? Документация утеряна, шильдик закрашен, штатная программа не видит устройство...
Пишем защиту от дребезга для ПР205: плавающая зона гистерезиса на аналоговый датчик
Сегодня пойдёт речь о программировании контроллеров — другими словами, об автоматике и КИПиА. Постараюсь рассказать просто о сложном. «Проблема? Какая проблема?» — скажете вы, если вы профессиональный программист АСУ. А я вот нет. Я не программист АСУ. И что такое гистерезис, я узнал только когда столкнулся c дребезгом. Но об этом как-нибудь отдельно. Итак, мы в программе говорим: включи вентилятор, когда температура будет 21 градус. Всё работает, все счастливы. Пока матушка-природа не преподнесёт нам погоду в 20...
Подключаем анемометр RS-485 к контроллеру ПР200, или как приладить к хозяйству дурилку электронную. Часть 2
В первой части мы написали свой сканер регистров на Python в паре с нейросетью и прошили анемометр CYC-FS1-KV-W2 на адрес 124. Теперь — подключаем его к Owen ПР200. Распиновка датчика : Цвет Назначение Красный Питание +24В Зелёный GND (земля) Чёрный RS-485, линия A Жёлтый RS-485, линия B Схема подключения: Блок питания 24В подключаем: +24В → красный датчика GND → зелёный датчика RS-485: Чёрный (A) → D+ (А) ПР200 Жёлтый (B) → D- (В) ПР200 ⚠️ Подключение — только при отключённом питании...

