Когда начинаешь делать открытую карту в Godot, быстро становится понятно, что просто поставить красивый ландшафт недостаточно. Игрок должен нормально взаимодействовать с миром: не проходить сквозь деревья, упираться в крупные камни, обходить препятствия и при этом не застревать на каждом маленьком объекте. Для работы с ландшафтом я использую плагин Terrain3D. Он удобен для создания карты, покраски земли, расстановки травы, деревьев, камней и других объектов. Но с коллизиями деревьев возникла отдельная история, которую пришлось решать не самым очевидным способом...
GGG Studio
#3 GODOT 4 - Devblog. ДНЕВНИК РАЗРАБОТКИ — Работаю над багами карты, TERRAIN3D и 23 часа борьбы с рендером
В прошлых статьях мы настроили серверную физику (#2) и мультиплеер (#1). Скажу честно: писать сетевой код оказалось проще, чем заставить Godot 4 адекватно рендерить растительность. Я заказали карту у фрилансера. Карту прислали, собрана на базе мощного плагина Terrain3D. Вроде выглядит неплохо, но как обычно дьявол кроется в мелочах и наружу полезли артефакты рендера. Вместо того чтобы спокойно писать геймплей, я потратил 23 часа на технический аудит, дебаг и хакинг чужих шейдеров. В сети полно туториалов,...
#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 практически под каждое новое устройство. Что ж, будем исправлять эту ситуацию. Проблема: есть датчик, которым уже пользовались. Его сняли, положили на склад, прошли месяцы. И вот он понадобился. А какой у него адрес? Какая скорость? Где взять таблицу регистров? Документация утеряна, шильдик закрашен, штатная программа не видит устройство...

