Устроившись снова на работу, с довольно удобным графиком 2/2 по 5 часов, с возможностью подработки решил начать долгострой, на этот раз казуальный выживач с элементами рпг, квестами и открытым миром большие локации, между которыми нужно перемещаться по глобальной карте, типа как в классических фоллаутах.
Пока что скрестил элементы из пары проектов и немного смоделил пушки из мешинстанстов. Думаю ещё начну постепенно глубже изучать блендер, чтобы делать более приятную глазу картинку если не задушусь, в таком случае всё будет как будет
>>1105099 Ого, ты вернулся, а то я думал, куда ты пропал...
А что с тем киберпанк-шутером со световыми мечами? Почему бы не добавить выживание, РПГ-прокачку и квесты в открытом мире прямо туда? Мне кажется, киберпанк-сеттинг с крысами-мутантами и всякими киборгами интереснее, чем очередной "казуальный выживач" в лесу с охотничьим ружьём.
Но ладно. По видео могу сказать: если это глухой лес и игрок будет проводить в нём кучу времени, ища ресурсы и борясь с животными/монстрами, то тебе обязательно нужен ландшафт с холмами, оврагами, обрывами, речушками, какими-нибудь пещерами/канализацией (лол, рядом с городом бывает и такое: идёшь по лесу, а там раз - бетонная дырка канализации посреди ничего, как какой-то артефакт древней цивилизации) и другими подобными деталями местности. Потому что бегать по плоскости будет очень скучно, и это довольно нереалистично, на мой взгляд. Поэтому первым делом нужно сделать/скачать и протестировать ландшафт. Не обязательно воксельный, на картах высот можно сделать много чего интересного. Взаимодействие с ландшафтом в 3D выживалке очень важно.
А "приятная глазу картинка" складывается в основном из работы с текстурами (особенно если это стилизованная игра) и написания кастомных шейдеров. На одних только мешах из блендера ты не уедешь далеко, и, ИМХО, работать по ААА-пайплайну (хайполи печётся в лоуполи) намного дольше и сложнее, чем просто покрасить лоуполи/мидлполи мультяшными/пережатыми текстурками и потом щедро обмазаться динамичными шейдерами в движке. В общем-то, твоя киберпанк игра уже неплохо выглядела для своей ниши, а с этим выживанием в лесу ты непонятно чего добиваешься...
>>1105126 Грубо говоря так и будет! А крафт будет выглядеть как просто скидывание категоризированных предметов в кучу две штуки второго тира кожи соединяем с первым тиром ветки и получаем палатку в которой восстанавливаем здоровье и силы
>>1105125 >Ого, ты вернулся, а то я думал, куда ты пропал... >А что с тем киберпанк-шутером со световыми мечами? А я как раз им и занимался. В целом. Всё основное что хотел там сделать, было сделано кроме мультиплеера, но на него уже не осталось моральных сил и после демо фестиваля в стиме в октябре она выйдет. Компания и сюжет есть. Боевка и прогрессия есть. Фарм валюты для покупки скинов и пара арен для аутирования тоже. Можно было бы дальше игру шлифовать и наполнять контентом, но я уже немного устал от неё и есть ощущение бессмысленности перед последующими стараниями, поэтому пусть всё будет как будет, посмотрю отзывы в будущем и сделаю выводы для будущей части хочу развивать эту серию и в целом вселенную с широким таймлайном
>Почему бы не добавить выживание, РПГ-прокачку и квесты в открытом мире прямо туда? Мне кажется, киберпанк-сеттинг с крысами-мутантами и всякими киборгами интереснее, чем очередной "казуальный выживач" в лесу с охотничьим ружьём. Вообщеееее... Изначально так и планировал. И собсна кирпичики складывал ещё в самурайской игре. Но по нескольким причинам решил взять за основу другой контроллер персонажа: 1) Я в целом устал от прошлого контроллера, поэтому нужно переключиться на что-то новое. 2) Я хотел добавить больше стилей борьбы и развитие боев на мечах, рукопашку, больше пушек, выживание как в метал гире 3, а так же улучшить графику. Но, честно говоря, думаю этот "вес" я пока не смогу осилить речь не только про программирование, но и модели и левел дизайн, и будет правильней отработать часть механик и подтянуть скилы на проекте по проще. Поэтому в общем чет посидев и покурив, вспомнил про старый иммерсив сим темплейт для годота, COGITO, в целом давно его заприметил, и так же давно хотел начать пробовать на нём что-то запилить, но был занят самураем. В итоге скачал его и посмотрел. В целом прикольный аддон, но потыкавшись, геймплейно мне он показался каким-то топорным, да и те вещи, которые хотелось бы украсть, можно впринципе и самому и понятней для себя сделать, поэтому взглянул на один свой джемовый проект. Ну и тут собственно появился образ будущей игры, иммерсивный выживач, где в безопасных деревнях можно пить пиво и делать квесты, а за их пределами преодолевать многокиллометровые расстояния и выживать в дикой местности.
>Мне кажется, киберпанк-сеттинг с крысами-мутантами и всякими киборгами интереснее, чем очередной "казуальный выживач" в лесу с охотничьим ружьём. Игра планируется как приквел, в котором закину удочку на эволюцию крыс. Грубо говоря это постапокалипсис в тайге с мутантами, и военными структурами подчиняющими местных голожопых аборигенов. В целом мне кажется этот сеттинг не менее интересным, и развить его можно прям вообще в любую сторону.
>если это глухой лес и игрок будет проводить в нём кучу времени, ища ресурсы и борясь с животными/монстрами, то тебе обязательно нужен ландшафт с холмами, оврагами, обрывами, речушками, какими-нибудь пещерами/канализацией (лол, рядом с городом бывает и такое: идёшь по лесу, а там раз - бетонная дырка канализации посреди ничего, как какой-то артефакт древней цивилизации) и другими подобными деталями местности Спасибо что сказал про обрывы и канализации, добавил в список. А так да. Грубо говоря часть времени нужно будет выживать на природе, не только леса, горы, пустыши, развалины, речки.
>Поэтому первым делом нужно сделать/скачать и протестировать ландшафт. Не обязательно воксельный, на картах высот можно сделать много чего интересного. Взаимодействие с ландшафтом в 3D выживалке очень важно. Хорошо, понял. Щас в ближайшее время закончу с функционалом пушек, и начну гуглить в эту сторону.
>А "приятная глазу картинка" складывается в основном из работы с текстурами (особенно если это стилизованная игра) и написания кастомных шейдеров Вообще, так погуглив, насколько понял важна работа с материалами для текстур пик, это типа базовый минимум для каждой модели, который щас все делают. Ну и сами текстуры конечно нужны.
>На одних только мешах из блендера ты не уедешь далеко, и, ИМХО, работать по ААА-пайплайну (хайполи печётся в лоуполи) намного дольше и сложнее, чем просто покрасить лоуполи/мидлполи мультяшными/пережатыми текстурками и потом щедро обмазаться динамичными шейдерами в движке. В целом, самостоятельно я все равно не вывезу хайполи, да и через чур много времени будет на модели уходить, поэтому продолжаю склонятся к лоу поли, но с большим числом полигонов и нормально выделеными материалами.
>В общем-то, твоя киберпанк игра уже неплохо выглядела для своей ниши Спасибо! >а с этим выживанием в лесу ты непонятно чего добиваешься... В целом, я примерно такой образ представляю, будто в первой халфе моддер делает камерную природную локацию, но со вкраплениями бетона типа как когда выходишь к каньону в первой халфе, вроде ощущается масштаб, но и камерность ощущается. Но в целом, посмотрим что получится, свой корявый стиль конечно же органично интегрирую
Анонасы, кто-нибудь знаток ArrayMesh? В частности, функция add_surface_from_arrays, которая принимает lods в качестве аргумента...а это словарь, где ключ это float и примерно пропорционально расстоянию, на котором начинает использоваться lod, а значение это, собственно array_index, которые используются в этом lod. К чему спрашиваю. Эта херня работает по принципу automatic lod, т.е. движок сам определяет какой array_index использовать. По идее, это должно быть лучше чем, допустим, сделать как обычно несколько lod для одного дерева и задать каждому свой visibility range. 1. Тут не будет никакого popin-popout. 2. Объект 1, а не равен количеству lod. Единственный минус, что памяти немного больше займёт. Я чёт подводных не вижу. Или они есть?
>>1105171 И да, понятное дело что количество материалов должно быть одинаковым для всех lods, а также array_tex_uv, array_normal и прочие должны тоже присутствовать (ну или заполнить ничем лол) для всех lods.
>>1105171 Я не понял о чём ты спрашиваешь и что сделать хочешь. Модельки, которые ты импортируешь в Godot из любого формата сами превращаются в ArrayMesh. По умолчанию в импортере стоит галочка Generate LOD которая и LODы из модели генерирует, https://docs.godotengine.org/en/4.7/tutorials/3d/mesh_lod.html
>>1105203 Можно и свои, но придётся чутка напрячься - или написать свой импортер, или выключить генерацию LODов и подставлять разные модели, в зависимости от расстояния до камеры, или воспользоваться аддоном https://github.com/puchik/godot-importance-lod или послушать свежую лекцию Кейси Муратори (пик), который показывает что ещё в 1974г Дональд Кнут писал в статье, что нефиг оптимизировать раньше времени.
>>1105209 Так вот я и спрашиваю, нет ли подводных. >в зависимости от расстояния до камеры Там немного не так работает. Оценивается не расстояние до камеры, а какой screen ratio объект на экране занимает.
>>1105171 >Тут не будет никакого popin-popout Почему так считаешь? Там же чётко сказано: нужно указывать float, который примерно пропорционален расстоянию, на котором LOD используется. То есть, указываешь неправильное число - будет заметно невооружённым глазом или будет бестолковым.
>Объект 1, а не равен количеству lod Щас бы экономить на объектах... >минус, что памяти немного больше займёт Ты же сам сказал, что объектов меньше, лол.
>Я чёт подводных не вижу. Или они есть? Подводные в том, что ты неправильно понимаешь: >сделать как обычно несколько lod для одного дерева и задать каждому свой visibility range. Настройка visibility range на нодах необходима для автоматической подмены нескольких нод одной. Ну, помнишь пасту Кирилла про корованы? Там он писал наподобие "деревья вдалеке картинкой" - так раньше повсеместно было, да и сейчас, думаю, тоже юзают.
Например, у тебя есть город. Когда игрок на улице, то детальные модели домов грузятся по отдельности и отображаются как отдельные ноды. Но когда игрок забирается в горы или на самолёте летит, весь город отображается одним лоуполи мешем - одной нодой.
С помощью LOD в ArrayMesh ты такого в принципе не сделаешь, потому что там ARRAY_INDEX - порядковые номера в ARRAY_VERTEX, ARRAY_UV и т.д. - т.е. у тебя уникальный массив точек, который РЕДУЦИРУЕТСЯ механизмом Level of Detail, а не просто подменяется.
Кроме того, использовать эту фичу через блендер не настолько просто, как кажется. Думаю, это больше процедурной геометрии подходит: чанков ландшафта, например, или чего-то подобного. Через Blender тебе придётся как-то... синхронизировать индексы мешей.
Пример: ты сделал восьмиугольник, шестиугольник и квадрат. У них разное число точек, но точки эти не совпадают друг с другом. Если квадрат ещё как-то вписывается в восьмиугольник (если увеличить), то шестиугольник имеет 2 вершины, которых у него нет.
Ещё нагрузка на GPU не столько от вершин, сколько материалов и текстур. Уменьшить нагрузку лучше с помощью упрощения шейдера и уменьшения числа обращения к текстурам. Но LOD в ArrayMesh будет использовать один и тот же материал с теми же UV.
Т.е. LOD через ArrayMesh - узкоспециализированная функция, хорошо подходящая только для простых хайпольных моделек, которые достаточно упрощать уменьшением массива индексов, без объединения с окружающими мешами и без изменений материала.
Алсо, для большой игры типа GTA 5 в любом случае потребуется писать велосипед - Godot пока не умеет в "asset streaming", который будет необходим, когда вес ассетов игры превышает объём RAM/VRAM у игрока. Жирные 150+ ГБ игры этим непрерывно занимаются.
P.S. Знаю это от интереса к процедурной генерации.
>>1105219 >То есть, указываешь неправильное число - будет заметно невооружённым глазом или будет бестолковым. Ну так вот надо пытаться правильное. >Ты же сам сказал, что объектов меньше, лол. Ну так у тебя 1 базовый меш и 3 lod. 10к вершин, 2к, 500 и 20, итого 12520 вершин, памяти чуть больше займёт (хотя по факту также, т.к. память от упрощенных мешей теперь в одном). А объектов меньше, да. >Через Blender тебе придётся как-то... синхронизировать индексы мешей. Можно сделать в самом годо через скрипт. Тебе ж надо по сути сделать так [lod0..........][lod1......][lod2...][lod3.] Т.е. тупо присобачить нужные тебе вершины с нужным офсетом для конкретного lod. Вроде не сложно. Главное, как ты и написал, если в lod0 есть например uv2, то он во всех других должен быть. Короче че пиздеть, завтра буду пробовать. А пока надо смену на заводе дожить.
>>1105222 Ты так и так вынужден подгонять числа, если хочешь смастерить свои собственные LODы. Избавиться от подгонки позволяет только механизм auto-LOD.
Память: отдельные меши имеют свои собственные ARRAY_VERTEX и т.п., а один меш будет иметь общие, поэтому памяти МЕНЬШЕ, чем если ты создаёшь LOD посредством отдельных объектов (mesh instance).
>[lod0..........][lod1......][lod2...][lod3.] Хмм... Но тогда ты не экономишь память, и вообще непонятно, зачем ты это делаешь. Чтобы на нодах сэкономить, что ли? А почему тебе auto-LOD здесь не подходит? Выдаёт сильно некачественный вариант?
>завтра буду пробовать У тебя есть, с чем работать? Я согласен с >>1105209 >нефиг оптимизировать раньше времени. Если у тебя этот >>1105099 проект, то, мне кажется, рановато ты заботишься о LOD. Те же деревья... Ну, допустим, это самый маленький 3D LOD, ниже - это подменять 3D модели 2D спрайтами, не иначе...
>>1105228 >А почему тебе auto-LOD здесь не подходит? Выдаёт сильно некачественный вариант? Именно. Хочу чтобы был 1 объект и о нем заботился auto lod. Но при этом lodы мои.
>>1105236 Ой, да делай как хочешь. Только потом не ной, что ты потратил две недели на код, который оказался тупо медленнее/жирнее по памяти/хуже по результату на экране... Тыщу раз такое было: придумываешь свой велосипед, долго его отлаживаешь, потом смотришь - колёса-то квадратные и заменить никак не выходит. Поэтому такие оптимизационные велосипеды стоит откладывать на потом, чтоб не растерять энтузиазм.
>>1105239 Основная причина, почему я хочу сделать с помощью auto lod, это чтобы выбор lod'a зависел от размера на экране, а не от расстояния до камеры. И он это как раз делает. А т.к. само редуцирование часто из говна, то было бы неплохо попробовать сделать свои.
>>1104969 (OP) Даже не знаю, с чего начать, но есть смутный вопрос/тема для обсуждения...
Не знаю, остались ли тут те, кто помнит, но в ноябре 2023 я где-то десяток дней потратил на свой собственный аддон для создания... хм, ветвящихся последовательностей чего-то? Задумывалось это как редактор на базе GraphEdit, который может быть полезен как для визуальной новеллы, так и для штук вроде катсцен и даже поведения NPC. Но это не скриптовый язык... Хотелось чего-то упрощённого, минимального, но позволяющего быстро накидать то, что роится в голове.
Основная фича: где не нужно ветвление, можно не создавать отдельные плавающие блоки и не возиться с ниточками: достаточно добавлять новые элементы в линейный блок-список. При этом элементы возможно перетаскивать из одного списка в другой мышкой и легко создавать новый, дропнув элемент в пустое пространство. Меньше развилок - граф меньше в ширину и больше в высоту. Также эти блоки-списки можно сворачивать в заголовок, чтобы можно было свернуть длинную портянку из десятков элементов и видеть только её входящие и исходящие связи. А у элементов списка могут быть теги для каких-то дополнительных функций (например, если это ВН, то удобно добавить тег-имя говорящего и тег-эмоцию, или тег-фон). Всё это GUI в той или иной степени реализовано на достаточно удобном уровне, и я вроде кидал скриншоты. Также была идея с созданием блоков-агрегатов, чтобы скрыть не только линейные портянки, но и все внутренние развилки, оставив только свободные точки входа и выхода из агрегата, но так и не дошёл до этой стадии, т.к. даже с сохранением/загрузкой возникли какие-то проблемы.
Как нетрудно догадаться, я забил на этот проект почти на три года. А на днях опять загорелось сделать что-то вроде визуальной новеллы/виртуального питомца, и я подумал - почему бы не попытаться довести этот аддон до юзабельного состояния, учитывая, что я сделал 80% от всего минимально необходимого. Начал изучать, что я наделал... И вспомнил одну проблему UI/UX, которую я тогда толком не смог решить концептуально: что делать с условными выражениями?
Но сначала поясню, зачем вообще нужны эти условия: смысл аддона в создании разветвлённых графов, а не просто линейных портянок; самый простой, базовый граф не имеет в себе никаких условий, и может быть использован для простой ВНки, где игроку доступны все выборы сразу; однако, более сложные сюжетные ветвления требуют условий разблокировки, как, например, "если есть зонтик и идёт дождь" разрешает ветку "предложить пойти вместе под одним зонтом" - необходимо где-то и как-то обозначить это условие, не так ли? Но... где?
Потенциальные варианты: - сделать сложную форму для добавления условий (готово на 80%); - сделать просто поле для ввода GDScript кода, и потом eval() его; - сделать некий способ подключения функций на GDScript к графу. Последнее я в целом планировал и без условий, то есть чтобы можно было вызывать функции, созданные на GDScript, прямо из графа, но без возврата значений; если использовать это для условных выражений, нужно будет возвращать и проверять bool для разблокировки ветки.
Изначально я начал делать форму с выпадающими списками и т.п., даже как отдельный блок, а потом подумал "зачем блок, если он будет использоваться только перед блоком-списком" и упаковал его в начало блока-списка, но потом... Что-то я засомневался, стоит ли эмулировать функциональность GDScript через такой GUI? Но если делать по-другому, не будет ли это как-то скрывать то, что реально вычисляется кодом, от внешнего представления графа в редакторе?
Даже не знаю, как иначе описать эту проблему. Мне кажется, я забил на этот проект в первую очередь из-за этой неопределённости с условиями... Где их отображать, как обрабатывать и т.д. Много неопределённого, что не так-то просто протестировать без сложного демо-проекта.
Знаю, что есть готовые решения для подобного, и я их смотрел - они мне как-то не нравятся. Текстовые скрипты мне не нравятся тем же, чем не нравится запись сценария на GDScript: все развилки становятся закопаны где-то в слоях линейного текста. Лапшичные редакторы зачастую страдают тем, что там одна реплика = один блок, и самый простой диалог вырастает в ширину очень быстро, что крайне неудобно, особенно когда каждый блок с кучей своих свистелок.
Что думаете? Чем вы пользуетесь для подобного? Как ощущения?
https://godotengine.org/article/dev-snapshot-godot-4-8-dev-4/ - Новая нода Trail3D для спецэффекта "ленточка" (Line3D могут добавить в будущем). - Группировка нод визуальных шейдеров, позволяющая переиспользовать куски кода. - Конвертация плагинов "главного экрана" в "доки" (docks); все экраны можно двигать. - Список методов в редакторе скриптов теперь представлен в виде аккуратного дерева. - Label и RichTextLabel смогут автоматически масштабировать текст, чтоб он умещался. - Аппроксимация окружающего затенения (AO) со множественными отскоками света. - Информация об отражённом свете (specular) теперь может запекаться в LightmapGI. - Поддержка декалей в режиме Compatibility (есть несколько ограничений на число). - Автоматическое скачивание и установка Android и Java SDK для экспорта на Android. - Улучшение редактирования Polygon2D (по сути, фикс ошибки). - Что-то связанное с анимациями - я не понял, лень разбираться... - Фикс избыточного потребления ресурсов CPU в фоне (CoreAudio). - Ускорение доступа к полям Object до x1.6 раз благодаря GDType. - Превью замены текста во множестве файлов функции Find in Files. - Кнопка для скрытия оверлея с метаданными в превью у текстуры. - Очистка/упрощение заголовка инспектора (кнопки в бургер-меню). - GDScript: предупреждение антипаттерна """строк-комментариев""". - Разрешено провозглашать тост во время застолья с окошками!🥂
>>1105292 > Информация об отражённом свете (specular) теперь может запекаться в LightmapGI. А ведь когда-то допрут до того, что запекать можно и вектор нормаля
>>1105278 Я бы рекомендовал посмотреть на старый добрый редактор квестов для Космических Рейнджеров (2), скорее всего он натолкнёт тебя на правильные мысли. Там конечно будут гейм-специфик константы, типа списка рас, такие вещи тебе у себя придётся перепридумать, как некие глобальные теги или переменные.
>>1105313 Нечто переписывает код китайца на классы годота, используя ИИ (конечно я верю что он использовался только для поиска по коду, как оно пишет в первом сообщении) В обзоре пишут что китаец taking a stab (пукнул-сренькнул, в контексте) в процессе своей реализации, но указывают что практически просто взяли его алгоритмы
Помогать нечту пришли все, только что сам Хуан не пришёл, все мегазанятые контрибуторы нашли время чтобы указать на проблемы в коде. Как починить что-то - так у них времени нет, постоянно пишут в статьях про это, как вычитывать кумовской нейрослоп - так пожалуйста, ведь надо помочь закрыть такой важный пропосал от этого нечто.
>>1105222 Вести с полей. Расстановка 23788 деревьев. Без использования MultiMesh. Без нод, всё на RenderingServer. Попробовал 3 способа. Autolod, создание 5 объектов с своим visibility_range (назовём VLOD) и моё предположение, назовём AutolodCastom. VLOD никаких фэйдинов и фэйдаутов нет, прост резкая замена. В общем и целом по местам: 1. VLOD / AutolodCastom. 2. Autolod. Суть в чём, если конечно сильно снизить порог threshold_pixel, то autolod станет более агрессивен, фпс сильно вырастет (как собственно и во всех других способах), но визуально будет так себе, слишком резкие попины/попауты. VLOD грузит bvh-дерево, поэтому проц. время повыше. К чему я это. Да просто.
>>1105380 >>1105367 >Но могут ли деревья расти? Другой вопрос - будет ли игрок летать? Это точно игра, а не развлекатель скуки? Зачем такая отрисовка того чего не увидит игрок?
>>1105367 Блин, меня во всех этих лодах всегда раздражает видимая горизонтальная полоса границы на экране. Я такое и в ААА играх вижу постоянно. МОжет, как то поэкспериментировать с рандомностью попинов
>>1105391 >С другой стороны, это много где надо, даже просто в катсценах какой-то рпг. Не спорю что классно и сочно, но насколько практично? Сильно было если дальние деревья превращались в спрайты. А так вместо далеких деревьев хотелось бы оставить ресурсов на то что поближе (травушку, кустики, камушки, грибочки, лося, зайчика, врага итд).
Вот оптимизированная трава как-будто более необходима, потому что она скрывает плоскую землю, а пушистость вообще делает дорого богато.
>>1105401 crusader kings 3 - из-за моделек деревьев видюха греет комнату (в максимум вентиляторы на северных регионах), выключаешь и видюха тупо спит. И вот думаешь - какого хрена, любители 3д деревьев на контурной карте.
>>1105509 Ну, это само собой разумеется, так я и сделал, потому что так было принято в Delphi/FreePascal (там нет требования именно так называть конструктор, просто все стандартные классы имеют именно такое название конструктора). Но в GDScript экземпляры классов создаются через new(), а не create(), и это вызывает некоторый диссонанс, когда ты делаешь что-то типа: >var button1 := MySimpleButton.new() # нода-кнопка >var button2 := MyComplexButton.create() # сложная сцена с кучей нод внутри Чувствуешь? Идеологически это одна и та же операция: создать экземпляр кусочка дерева сцены с какими-то настройками. Но в первом случае вызывается new() и всё работает, а у второго класса тоже есть new(), но вместо него приходится вызывать create(), иначе класс пукнет и обмякнет из-за того, что полагается на связанную с ним .tscn, а new() создаёт лишь ноду-корень.
При этом GDScript позволяет перезагрузить метод _init(), который вызывается при обращении к new(), и даже может иметь любое количество аргументов, которые будет нужно передать в new()... но не может ничего вернуть, потому что это не "конструктор экземпляра класса", а просто обработчик события "ты уже был создан, делай с этой информацией что хочешь".
Конечно, можно попытаться придумать костыль, когда корневая нода дожидается своего _ready() и только потом подгружает остальные свои ноды из отдельного .tscn. Но это неудобный костыль, требующий дополнительную корневую ноду (которой не может быть основная нода), и создающий диссонанс с привычной инициализацией объектов/сцен, принятой в Godot...
Но всё это не так уж и важно... Важно то, КАКОГО ХРЕНА МОЖНО ОБЪЯВИТЬ new(), ДАЖЕ static, И НЕ ПОЛУЧИТЬ СООБЩЕНИЯ ОБ ОШИБКЕ, ХОТЯ ЭТО, ОЧЕВИДНО, ОШИБОЧНАЯ СИТУАЦИЯ (объявленный new() не будет вызываться)? В Godot 4.8-dev4! Они уже кучу предупреждений добавили, в последней сборке даже "строки-комменты" помечают, а new() никого не волнует?
Т.е. я заранее понимал, что это должно быть неправильно, но ожидал, что IDE предупредит или Godot крашнется. Но нет...
>>1105519 Суть проблемы в том, что ООП в Godot размазано между императивным GDScript и декларативным PackedScene/Resource. Ты можешь много чего создать в Godot, не создавая GDScript-скрипты, а только лишь создавая сцены .tscn и соединяя внутри них ноды и сигналы. Создавать сцены как .tscn удобно, потому что у тебя есть WYSIWYG редактор и удобный инспектор свойств. Альтернатива - создавать сцены из кода, что очень неудобно, ведь ты не видишь промежуточные результаты, и сложная сцена растягивается на много несвязанных строк кода. Поэтому, разрабатывая какой-то компонент для своей игры, программы или плагина, ты сталкиваешься с проблемой, когда тебе нужно создать экземпляр какого-то сложного класса, но этот экземпляр должен забрать с диска .tscn файл и создать экземпляр сцены, иначе он не будет полностью работоспособен. Но как это сделать? Есть несколько разных путей, но Godot до сих пор не даёт однозначно лучший вариант. Приходится ломать голову над этим организационным моментом.
Вот простой пример: тебе нужен список сохранений в игре, который может увеличиваться и уменьшаться (разное число слотов), и каждое сохранение имеет иконку, название, дату сохранения, иконки и названия режима игры и прочую шаблонную информацию. Ты, естественно, создаёшь базовый контейнер через VBoxContainer в ScrollContainer, но дальше тебе нужна сцена-"слот сохранения". Ты создаёшь эту сцену-слот, и она у тебя имеет десятка два различных Control-нод с кучей настроек в инспекторе, которые ты бы замучился перечислять в коде GDScript, не видя того, каким получится результат. Дальше тебе нужен способ добавить сцену-слот в свой контейнер, когда игрок создаёт новое сохранение или когда игра сканирует папку сохранений и обновляет список сохранений на экране.
Самое очевидное решение будет: >var save_slot_scene: PackedScene = load("res://gui/save_slot.tscn") >var save_slot: Control = save_slot.instantiate() >save_slot.data = save_data >saves_list.add_child(save_slot) Но это куча строчек для БАНАЛЬНОГО действия "создать объект, заполнить данными и сунуть в дерево".
В идеале, ты бы хотел что-то вроде такого: >saves_list.add_child(SaveSlot.new(save_data)) Просто и понятно, не так ли? Мы добавляем в список свежесозданный слот с указанными данными. И нас не волнует, откуда с диска всё это считывается, как инстанциируется и куда там внутри помещаются наши данные - мы используем принцип инкапсуляции из ООП и поэтому код простой и понятный... вот только Godot препятствует так делать.
Да, можно перезагрузить _init() и добавить в него любые аргументы, но если ты добавишь аргумент в него, то ты уже не можешь добавить свою ноду в дерево сцены через ctrl+A, если у аргументов нет значений по умолчанию. Ладно, добавил, создал сцену, сохранил в .tscn... а как/когда теперь грузить её с диска? В момент вызова new()? Или когда будет уже _ready()? Но если эта нода потребуется снаружи до того, как она готова... и как вообще добавить в НОДУ заранее подготовленную СЦЕНУ, если у СЦЕНЫ на диске обязательно должна быть одна корневая нода? То есть у нас получается два корня: один создаётся new(), другой мы хотим считать из .tscn и поместить в дерево... Но мы не можем вернуть ссылку на .tscn через new()... Мы можем только создать свой статический метод, возвращающий экземпляр загруженной сцены. Но он не может называться new(), потому что такой метод невозможно вызвать.
>>1105529 > Суть проблемы в том, что ООП в Godot размазано между императивным GDScript и декларативным PackedScene/Resource. Эммм... Нет. Пакед сцены/ресурсы - это сериализация. Не имеет прямого отношения к ООП. > Ты можешь много чего создать в Godot, не создавая GDScript-скрипты, а только лишь создавая сцены .tscn и соединяя внутри них ноды и сигналы. Создавать сцены как .tscn удобно, потому что у тебя есть WYSIWYG редактор и удобный инспектор свойств. От тебя спрятали под капот создание инстансов, от этого ООП никуда не размазался. > В идеале, ты бы хотел что-то вроде такого: > >saves_list.add_child(SaveSlot.new(save_data)) > Просто и понятно, не так ли? Мы добавляем в список свежесозданный слот с указанными данными. И нас не волнует, откуда с диска всё это считывается, как инстанциируется и куда там внутри помещаются наши данные - мы используем принцип инкапсуляции из ООП и поэтому код простой и понятный... вот только Godot препятствует так делать. Нет, не препятствует.
Дальнейший текст вообще не вижу смысла комментировать там каша дичайшая из терминов и концепций без понимания сути.
А суть в том, что если ты хочешь из кода одной строчкой добавлять десериализованные объекты в дерево, то просто инкапсулируй вышеуказанные тобой же длинные строчки десериализации в один метод, который и вызывай. >var save_slot_scene: PackedScene = load("res://gui/save_slot.tscn") > ... >func get_slot(slot_data: Resource) -> Control: >____var save_slot: Control = save_slot.instantiate() >____save_slot.data = slot_data >____saves_list.add_child(save_slot) >____return save_slot > ... >saves_list.add_child(get_slot(save_data))
За сериализацию приходится расплачиваться в коде вот такими вот функциями-обёртками.
>>1105529 То, что ты описываешь, называется отложенная инициализация. Именно поэтому стадия instantiate() и add_child() разнесены. Может быть, разраб хочет только создать сцену, что-то рассчитать, а добавлять ее только тогда, когда какой-то юнит выстрелит пулькой. Поэтому, это так и выглядит var a = obj.new() ... obj.config(params) ... add_child(obj) А вот как раз если ты слепишь все в один вызов new(), то тебе надо знать все параметры заранее
>>1105516 >Т.е. я заранее понимал, что это должно быть неправильно, но ожидал, что IDE предупредит или Godot крашнется. Но нет... Вероятно, с точки зрения языкового сервера, ты можешь создать функцию new потому, что её не существует в GDScript. Это надстройка на уровне парсера, алиас, который выделяет под объект память и вызывает init(), ближайшая аналогия - оператор new в С++.
Потом ещё дорастёшь в своём познании до конвеерных функций, это когда > func do_some(): > ____ #do some > ____ return self и тогда вообще преисполнишься как тот идущий к реке. Будешь хуярить конвеерный код как профи: > var obj = MyObject.new().confgure(ObjectData.new().sync(SaveData)).reload().hide()
>>1105535 >Нет, не препятствует >приходится расплачиваться Сам себе противоречишь в одном посте... >сериализация не имеет прямого отношения к ООП >От тебя спрятали под капот создание инстансов Ты не понимаешь. Godot выше по уровню абстракции...
>>1105537 >Может быть, разраб хочет только создать сцену... Ты, похоже, тоже не понял, о чём речь идёт. Вот здесь: >obj.new() Уже ожидается "создание сцены", целиком и полностью (все ноды). А это: >obj.config(params) Делает САМ ДВИЖОК, когда ты делаешь load("scene.tscn").instantiate().
>>1105538 Да какая разница. Юзер может допустить ошибку - юзера нужно предупредить...
>>1105540 Я пробовал так делать - мне не понравилось. Слишком неудобно выглядит...
>>1105543 Неудобно, что кто-то думает и пишет больше тебя? К школе готовься.
>>1105545 > Ты не понимаешь. Godot выше по уровню абстракции... Нет там ничего принципиально отличающегося от остальных тулкитов и фреймворков, которые десериализуют сериализованные ранее через редактор данные, формы/объекты/конфиги/сцены, да что угодно. Как это я не понимаю-то? Да как же я не понимаю-то после 20 лет в формошлёпстве?
Между TSCN и модным нынче XAML нет фундаментальной разницы, только язык другой. А разница исключительно в глубине запрятывания от тебя процесса создания нового инстанса, прохода парсером по файлу, и заполнения полей инстанса данными из файла.
>>1105546 ... в том числе, если работа парсера предполагает создание отдельного дерева, создание инстансов и добавления их в дерево, это абсолютно ничего не меняет.
>>1105529 Если ты полез в DI -Во первых надо смериться что будет много ручного бойлерплейта. Но оно того стоит, если проект будет больше змейки.
-Модуль у тебя всегда сцена - внутренние скрипты сцены не модули, инжектить в них не нужно, только в главный скрипт модуля (owner).
-Не использовать (никогда даже для extends RefCounted) базовый конструктор _init().
- Придумать свой "конструктор" модулей - я предлагал имя setup(). Проверять на вызов не нужно, если забыл дернуть его - все нуллами посыпиться и так.
-Тебе придется держать сервис локатор, иначе ты сума сойдешь когда игрока будешь просовывать через 3-4 модуля. Локатор лучше использовать глобальный класс. Да можно и в Main держать - но тупо просто удобнее
Насчет метода-конструктора setup - это самый правильных подход. Покажется избыточным пробрасывать туда, особенно когда больше 5 зависимостей. Но в реале тебе гдискрипт не даст что-то забыть передать (модуль либо работает либо нет, не будет какой-то магической баги, когда null дернет в середине игры).
Всем кто планирует проекты на годы вперед, рекомендую DI - нудятина, но потом увидите - что даже открывая модуль вы будете видеть по setup - от чего зависит модуль который вы написали годы назад (его контракт зависимостей).
>>1105556 >Тебе придется держать сервис локатор, иначе ты сума сойдешь когда игрока будешь просовывать через 3-4 модуля. Локатор лучше использовать глобальный класс.
Можно еще группы юзать, вместо сервис локатора. Только средствами движка (а не кодом задавать) иначе там может быть хрень с _ready. Но мне проще синглтон.
>>1105546>>1105548 Нет, ты не понимаешь. Если ты на Delphi/Lazarus/какой-то похожей RAD IDE сделаешь новый компонент, он будет компилироваться в саму IDE и использоваться как родной. В Godot такое поведение возможно только если ты сам, своими руками, спавнишь все нужные тебе ноды в дерево сцены (именно через код, а не через редактор - это очень важное отличие, нужно держать это в голове). Для этого даже есть концепция "internal nodes", что не отображаются в дереве сцены и по умолчанию не выдаются функцией get_children(). По-хорошему, так и надо делать...
Но Godot выше по абстракции, чем простые формошлёпалки, и тебе часто нужно МНОГО разных мелких нод, которые нужно по отдельности настраивать, и подгонять все эти настройки без визуального редактора сложно. А визуальный редактор не генерирует код на GDScript, он может только сохранить сцену как tscn/scn, и всё. То есть твой "новый компонент" разрывается между скриптом "что делать" в .gd и скриптом "как выглядит" в .tscn. И ты вынужден выбирать: либо загружать сцену по пути в файловой системе и надеяться, что по этому пути файл всё-таки есть и он нужного тебе класса, или как-то прикручивать .tscn внутрь .gd, чтобы обращаться по имени класса через базу данных классов. Это создаёт лишнее трение, разве непонятно? ...а ещё не забывай, что у .tscn есть своя иерархия и композиция, что живёт отдельно от скриптов, и поэтому их следует рассматривать как отдельную ООП систему, что вводит путаницу.
Поэтому хочется, чтобы new() могла возвращать не одну ноду, а целую сцену, которую движок прочитает с диска: >@scene("my_complex_class.tscn") >class_name MyComplexClass >extends BaseClass Здесь строчка "@scene()" должна указать Godot, что для работы этого класса необходимо сначала прочитать данные в указанном файле и создать все ноды, а только потом - вернуть экземпляр. Или даже проще, если указан тег @scene, тогда Godot автоматически ищет файл имя.tscn рядом с файлом имя.gd, без указания пути до файла (ведь очевидно, что такой скрипт будет лежать в одной папке с тем же именем, что и файл сцены, т.к. он с ней связан).
>>1105556 >Если ты полез в DI Какое отношение эта проблема имеет к DI? У тебя только DI на уме? >будет много ручного бойлерплейта Проблема не бойлерплейте, а в том, что ты юзаешь что-то вместо new() с ролью new(), а new() ломается. >Модуль у тебя всегда сцена Это тоже не имеет отношения к теме. "Модулем" может быть и всего одна нода - сцена не обязательна. >Не использовать (никогда даже для extends RefCounted) базовый конструктор _init() Это вообще какая-то шиза. Почему нет? Тем более для RefCounted... Нет, _init с аргументами - это база. >конструктор... setup() Семантически, "setup" - "настройка (того, что есть)", например "set up computer" - "настроить компьютер" (а "computer setup" - это "(уже) настроенный компьютер"). Конструктор в ООП является создателем экземпляра по заданным шаблонам/чертежам (называемым обычно "классами"), поэтому для конструктора больше подходит имена типа "construct", "create", "build", "make", "new" и т.п. У нас в GDScript уже есть "new", поэтому логично его перезагрузить... но не получается... >придется держать сервис локатор, иначе ты сума сойдешь когда игрока будешь просовывать Задолбал со своими сервисами. Если у тебя нет сервисов, то не нужно их и искать. Зачем ты куда-то игрока "просовываешь"? Мобам, например, не нужно ничего об игроке знать, пока Area не коснётся, а там они и сами разберутся, кого и где увидели. У тебя просто какая-то профдеформация с этими сервисами, которые ты постоянно хочешь куда-то куда не нужно просунуть. Об игроке большинство игровых сущностей знать не должно абсолютно ничего, как будто игрока и не существует...
>>1105559 Страшно, но в реале можно и его выкинуть, в Main все сервисы положить. Если есть main. Знать об игроке, знать о текущем мире - это как раз область ответственности главной ноды.
>>1105564 Я кодить начал лет 20 назад, почти сразу в контексте важных для видеоигр тем.
>>1105566 Ты превращаешь свой main в глобальную свалку данных, а в этом 50% вреда синглтонов.
>Знать об игроке, знать о текущем мире - это как раз область ответственности главной ноды. Зависит от игры, конечно, но обычно - нет, главная нода не должна знать о мире и игроке.
Вообще. Обычно игрок знает больше всех, ибо это точка взаимодействия с игроком.
>>1105561 >Проблема не бойлерплейте, а в том, что ты юзаешь что-то вместо new() с ролью new(), а new() ломается. У тебя обострение, прими что ПНД прописали.
Я пытался понять что за бредовый поток мысли, возможно ты путаешь два мира - мир дерева нод и мир кода? Это все ваше компонентная разработка - она тебя и свела с ума. Хватит воспринимать скрипты - нодами.
Модуль у тебя только сцена. Родитель сетапит всех детей, даже если это не сцены. new() - занято, придумай свое инициализирующий метод. Синглтон - это нормально. Но если ты коснулся DI - то выкинь. Сервисы - удобнее отлаживать чем сигналы. Но сигналы нужны. Небо - синие Трава - зеленая
>>1105568 > Зависит от игры, конечно Если мне позволит сравнения уважаемая модерация, но в Unreal Engine именно так, как тебе выше взрослый дядя расписал. И ничего. Как то пишутся там ААА тйтлы.
>>1105568 >Ты превращаешь свой main в глобальную свалку данных, а в этом 50% вреда синглтонов. Проблема синглтонов вообще не касается геймдев разработки. Хз откуда эти пугалки, у тебя не будет никогда смены контекста, даже юнит тесты не пишет никто.
То что локатором будет main - не делает его god object. God object - это про другое.
Так что нет проблем если главный класс отвечает за все сервисы в игре. Главная ошибка - если ты будешь из сцен (других модулей) добавлять внутренние "кишки" в main.
Почему я выкинул синглтон - потому что стал писать общую библиотеку для всех тайловых игр. И стал выносить код для юнит тестов. Кто тут вообще пишет юнит тесты в играх?
>>1105573 >Не я один такой. Годот просто испещрен WTF-решениями. Приходиться только адаптироваться. Я говорил что организация модульного кода ппц как сложна.
Что мешает? const MY_SCENE = preload("uid://fsdfsfsdfsf") // наш модуль var my_obj := MY_SCENE .instantiate() my_obj.setup(self, di1, di2, di3) // Инжектим все зависимости add_child(my_obj)
Если у тебя просто скрипт лежит var my_obj := MyClass.new() my_obj.setup(self, di1, di2, di3) my_obj.create_super_game()
Воспринимай new как оператор в шарпах, просто с другой стороны. new MyClass()
>>1105577 Опять начинается... >не касается геймдев разработки Ммм, расскажешь потом, как искал односимвольную ошибку по всему проекту, из-за того, что не можешь отловить, кто и откуда насрал в глобальное хранилище данных ошибками, которые не крашат игру, но делают её работу некорректной с точки зрения реального пользователя... Хотя, если ты обмазался тестами, то до игры ты тебе ещё долго двигаться... >откуда эти пугалки Опыт, сынок, опыт - сын ошибок трудных... >God object При чём тут God Object, если речь про то, что любая мразь может бесшумно насрать в общий колодец? >если ты будешь из сцен (других модулей) добавлять внутренние "кишки" в main Они у тебя уже давно перепутались своими кишками, если делают что-то вроде: >get_tree().service.foo(bar) В рандомном месте проекта. Это нисколько не лучше "синглтона" (autoload). >юнит тесты Да что ты к ним прицепился-то? Вред в любом случае огромный.
>>1105582 >Что мешает? Ничто не мешает, но хотелось бы больше так не делать... Выглядит просто ужасно.
>>1105583 >Ничто не мешает, но хотелось бы больше так не делать... Выглядит просто ужасно. Выглядит логично (сцену надо с диска поднять и инстанцировать всю макаронину), а вот где реально бойлерплейт начинается - пик. Чтобы внести одну зависимость надо три раза её прописать, еще в родителе прокинуть.
>Опыт, сынок, опыт - сын ошибок трудных... Ну твою ошибку мы знаем, ты повесил одни данные в разных местах несвязанных местах. Это была даже не проблема синглтона, но ты не хочешь учиться и принять свою ошибку.
Ты в курсе что передача по ссылки несет в себе глобальное состояние? Когда передаем юзера setup(user) мы не клонируем объект а передаем по ссылке. И поменяв user.nick = "Ваня" изменится везде глобально где ты передал user целиком. Так что больше половины работают с глобальными состояними и в ус не дуют. Больше скажу - в игре нужно работать только с глобальными состояниями. Игра никогда не будет масштабировать между нескольких машин - проблема глобального состояния игры не касается никак вообще.
Кто-то реально принес проблему из мира бэкенда и все долбятся об неё сейчас.
>>1105593 У тебя "инъекция" задом наперёд. Ты объявил public property, ты владеешь жизнью этого объекта - так зачем тебе какие-то дополнительные костыли? Просто делай player.map = map, и дело в шляпе. В твоём конкретном случае тебе никакой setup() вообще не нужен. Высосал из пальца и жалуешься.
>>1105592 >сцену надо с диска поднять и инстанцировать всю макаронину Это детали имплементации. Сегодня - с диска, завтра - сгенерирую в RAM с нуля, послезавтра - буду вообще из интернета скачивать. В отличие от внешних зависимостей (DI), за внутренние штуковины вроде данных из .tscn должен отвечать сам объект, а не тот, кому он нужен в рабочем состоянии.
Пример для сравнения: есть инвалид на костылях и автомобиль в таксопарке. Костыли у этого инвалида должны быть изначально при нём, задолго до его выхода из квартиры, а вот автомобиль к нему снаружи подъезжает, потому что он позвонил в таксопарк и попросил подогнать подходящий автомобиль. Так что костыли - это детали имплементации (сегодня нужны, а завтра он здоров, а после завтра ему вообще кресло-каталка потребуется), а автомобиль как бы внедряется снаружи, но по запросу объекта, т.к. без автомобиля он не сможет добраться куда ему надо, но автомобиль его деталью не является. В нашем случае "запросом" является существование публичного свойства с null вместо нужного объекта, и мы должны присвоить ему то, что там требуется, тем или иным способом (лучше всего через инспектор). А вот как мы загружаем (и загружаем ли) сцену с диска - это нас беспокоить вообще не должно...
>изменится везде глобально где ты передал user целиком Ты что-то путаешь. Глобальное состояние - влияющее глобально на всю программу в целом. Если совершенно любой твой объект может просто так, ни с того, ни с сего, написать Player.health = 0 и игрок умрёт, то твой Player - это глобальное состояние. Если 99.99% объектов должны сначала написать "а скиньте мне кто-нибудь ссылку на player, я хочу с ним кое-что сделать", и только потом они смогут внести изменение в его состояние, то это не глобальное состояние - это локальное состояние 0.01% объектов, запросивших и получивших ссылку на общий для их локальной группы Player.
Сравни это с переменными в классе. Если переменная в методе класса, то это, безусловно, локальное состояние конкретного участка метода класса. Если переменная в полях класса, то это "глобальное состояние для методов класса", но это "локальное состояние среди объектов приложения", потому что другие объекты не имеют прямого доступа к этому полю. То есть всё относительно, да, но когда говорят "синглтон - это плохо", подразумевают, в числе прочего, что ты светишь одной точкой на глазах у всех методов всех объектов одновременно, и все они одновременно имеют неограниченный доступ.
>не проблема синглтона Зачем ты используешь строгую типизацию в GDScript (или даже C#/C++), зачем тебе какие-то паттерны ООП? Почему бы не использовать чистый ассемблер и менять биты и байты в памяти компьютера так, как тебе сегодня захочется, не думая о последствиях? Ты же можешь, я уверен, запомнить каждое своё действие и не допустить ни единой ошибки, какой бы сложной ни была твоя задача. Тебе не нужны все эти детские ограничения, сдерживающие творческий потенциал программиста. Так почему ты ещё не пишешь на ассемблере, а лучше - намагниченной иголкой на поверхности жёсткого диска?
Синглтон имеет две проблемы: он почём зря запрещает создавать копии себя, как будто ты случайно можешь создать копию и не заметить этого, но он также даёт полный и неограниченный доступ к определённым данным и функциям, как будто ты можешь гарантировать безошибочность всех своих действий. Только новичок без опыта будет отмахиваться и стрелять себе в ногу таким способом...
>>1105545 Скорее ты не понял. В Node.new() создается ненастроенная нона, потом у нее вызывается метод config или setup(param). Так на практике всегда и происходит. Инстанциируется сцена пульки. А потом менеджером ей задается ее глобальная позиция, вектор и скорость, цвет. Да пулька может вообще из пула переиспользуется, а не создается новая. Поэтому конфигурация это отдельный от создания шаг.
>>1105598 А так не бывает, потому что таксист приедет, увидит что там коляска, скажет места нет и уедет. Нужен менеджер, который знает о костылях, и он вызовет именно такси с местом под костыли.
>>1105583 >Просто делай player.map = map, и дело в шляпе. Если я забуду вызвать setup - упадет практически сразу, потому что будут null
А если я буду полями инициализировать 1) Непонятно какое поле просто паблик, какое инъекция. А я хочу видит условия - как использовать модуль - все его зависимости. 2) Если я инициализирую 5 полей, а 6 забуду и оно вызывается фиг пойми когда - я получу плавающую ошибку в середине игры. В случае setup я свалюсь сразу (я даже локальные сцене ноды там прописываю, поэтому упаду быстро).
>Пик Если ты хотел сделать цепочку вызовов как на пикче, то ты можешь возвращать self.
>Много текста Я не хочу спорить за DI это надо раз понять, что это. Оно даже помогает правильно ответственность распределить, когда сам не видишь (главное не передавать везде один main -а передавать именно сервисы/менеджеры. У меня main передает не как локатор, мне там надо получить позицию мышки от main).
Про глобальное состояние тоже спорить не хочу, там все правильно я написал. Хочешь реально увидеть локальное состояние - иди в раст с иммутабельными переменными и владением (ну или в любой ФП язык).
>Зачем ты используешь строгую типизацию в GDScript Если честно только ради автокомплита. Пока не будет jit компиляции и из сорцев не уберут конвертацию в variant, особо не вижу смысла использовать (кроме циклов). Все узкие места (циклогонялки) я с тестами перепишу на С++, все равно гдскрипт поддерживает вложенность типизированных массивов только на 1 уровне. Да я знаю про ту статью о 30%, не надо кидать ссылку.
>>1105598 Ладно, специально для тебя любимого сделал пример (пик, если не отвалится)
В реально разработке в DI передаются только интерфейсы (мы можем только абстрактные классы, что тоже самое). А объекты передаются через полиморфизм. У нас появляются чистые объекты, на манер чистых функций (чистые функции - термин гуглиться).
>>1105609 Почему я постоянно ною за интерфейсы Потому что нельзя сделать такое >class Ежик extends IМожетХодить, IМожетЕсть, IМожетВзаимодействовать, IМожетЛетать Почему они добавили абстрактные классы, а интерфейсы не добавили я хз.
>>1105592 >Игра никогда не будет масштабировать между нескольких машин - проблема глобального состояния игры не касается никак вообще. У тебя не будет - поэтому тебе плохие практики не мешают. Любой функционал - типа реплеев, мультиплеера, банально рассчет ходов наперед - требует дублировать мир.
>>1105616 Клиент игры будет всегда только на одной машине. может когда игры будут только в облаках и клиенты надо будет масштабировать.
Проблема серверного мира, если есть глобальное состояние. клиент - запрос - сервер А - сессия (одни состояния) через минуту клиент - запрос - сервер Б - сессия (вообще другие состояния)
>>1105615 Потому что они идут на поводу ноющих перекатунов на форумах, и добавляют фичи, которые СКРИПТУ(!) не нужны. И абстрактные классы не нужны, и аннотации, и явная типизация, и ещё куча фич, которые они понавводили. Они забыли, для чего был нужен скрипт: легкая автоматизация для непрограммистов. Для жёсткого кодинга всей игры нужен был отдельный полнотьюринговый язык. Они всунули туда шарп. Я с этим тоже не согласен. Нужно было сделать свой собственный GDLang, в котором были бы и абстракты, и интерфейсы, и лямбды, и типизация, и всё остальное, о чём ноют на форумах. Таким образом было бы чёткое разделение: либо создаётся файл скрипта, который является ресурсом в обьектной системе, подцепляется к скриптам, прост и понятен новичкам; либо создаётся файл компилируемого модуля, результатом компиляции которого является полноценная нода в объектной системе движка. Как в GDExtension, только прямо в редакторе, без сторонних IDE и фреймворков, все целевые DLL компилируются при экспорте в целевую платформу, либо вшиваются в экспортный wasm, совместимость полностью независима от сторонней платформы (как в случае с шарпом).
И этот GDLang уже мог бы выглядеть более профессионально, без оглядки на новичков: > class Ежик inherits Mammal implements IМожетХодить, IМожетЕсть, IМожетВзаимодействовать, IМожетЛетать
>>1105672 Да им и сейчас нужен такой язык. По хорошему неплохо было иметь два мира как во фронтенде. Базовый js где все еще можешь указать типы для IDE через jsDoc и ts - для хардкора с компиляцией, дженериками и шлюхами интерфейсами (правда ts компилится в js, что тот еще мир абсурда).
Нельзя просто добавить типы и сказать - дальше сами епитесь как хотите. Если полез в эту область - долбись до конца. Но в реале мы долбимся в кадре, а в кадре мы можем долбиться в списки. В общем, для геймдева перебирать динамические типы налету - кажется плохим решением всегда. И зачем делать ставку на новичков, которые отваляться завтра - непонятно.
Что касается шарпов, то он там смотрится криво, как не родной. Плюс еще два сборщика мусора сверху вылезают (а ты даже одному не рад).
Но уже ничего не поделать, мы слишком избалованы gdscript
>>1105681 > Но уже ничего не поделать Мир несовершенен, постоянно такое наблюдал всю жизнь, простое и очевидное решение всплывает, когда годы потрачены на развитие и поддержку хуйни. И потом включается принцип трамвая: ты можешь повернуть рычаг, но тогда, но тогда.
>>1105681 >Да им и сейчас нужен такой язык. Хотя я сейчас подергал ИИшку и она говорит что у плюсов будет минимум оверхеда на GDExtension C++.
>Да им и сейчас нужен такой язык. В общем, такой язык у годота есть - это С++
Ну и не забываем что у нас есть сорцы, мы можем нагадить прям в движок без оверхедов. никто этого делать не будет, но максималисты могут спать спокойно
>>1105685 >Мир несовершенен, постоянно такое наблюдал всю жизнь, Это проблема разработки снизу вверх. Когда ты пишешь практичный код, потом рядом еще, еще и когда оглядываешься оказывается что уже склад костылей.
Но ты тоже не можешь сразу сделать идеальное API, только с оглядкой на опыт. К версии Godot 17 все сделают.
>Пик1 и Пик2 В общем, я так покапал и оказалось это прям не рокет сайнс и делается не сложно. Что касается затрат, если дублировать классы в С++ (скажем получать свойства не через Variant, а по средствам каста в существующий С++ класс), то выходит тоже не дорого. В теории, можно даже GDScript -> C# если в числодробилке не дергать вызов API движка.
>Пик3 НО оказалось годот работает с пакетными массивами почти прозрачно между GDSript и С++. Этакий ECS поневоле получается. Так что с учётом того что вызов API у GDSript дешевый и все данные на дробилку завернуть packed - связка GDSript и С++ получается убер мощная (но надо тестить).
>>1105694 > опять с++ учить, в 20 раз В конечном итоге мы все выучим кресты (а потом и чистый си). Все эти шарпы, жавы, дельфи, питоны - полумеры, жалкая попытка лентяя отсрочить неизбежное.
>>1105604 >увидит что там коляска, скажет места нет и уедет Демагогия... Менеджер таксопарка = программист.
>>1105603 Опять ты какую-то чепуху несёшь. >В Node.new() создается ненастроенная нода Настроенная, если ты настроишь через _init(). >Инстанциируется сцена пульки. А потом... "Инстанциация сцены" - это в т.ч. "настройка".
>>1105605 >упадет практически сразу, потому что будут null Модульный проект не должен падать, т.к. модули по определению сущности автономные - способны по отдельности существовать, без инъекций, но будут бездействовать в некоторых ситуациях (как когда отсутствует карта для поиска пути для движения).
>какое поле просто паблик, какое инъекция Что такое "просто паблик"? Просто дырка какая-то, существующая без цели, без предназначения? Опять демагогия - "Что если я создал поле и не знаю, зачем создал, как я узнаю, что оно мне зачем-то нужно".
>инициализирую 5 полей, а 6 забуду Если твой Player требует одновременно шесть (!!!) независимых инъекций, то это уже труп, и не нужно заниматься некрофилией, просто закопай его и иди нормальную архитектуру проектировать.
Скорее всего, твои шесть независимых инъекций одновременно относятся к одной и той же области конкретного проекта или пусть даже к двум (world, отвечающий за физический мир и всё, что в нём теоретически может существовать, и GUI), так что, предоставляя к ним доступ, ты просто затягиваешь клубочек потуже. Нужно реверсировать контроль и принуждать игрока ЯВНО взаимодействовать с его окружением, а не давать ему кольцо всевластия.
>спорить не хочу, там все правильно я написал Ты просто упёртый как школьник...
>Если честно только ради автокомплита Смысл строгой типизации в том, чтоб в коде не было неуловимых ошибок, когда ЯП неявно конвертирует требуемые данные в какой-то неожиданный формат. Неявность - главное зло, и глобальное состояние - это главный источник неявности в коде. Потому что когда происходит взаимодействие с глобально доступными ячейками памяти, это неявно действует на тысячи неоднозначных позиций в коде, хочешь ты того или нет, знаешь ты об этом или нет (в сложном проекте).
>>1105735 >демагогия >чепуха >упертый школьник Выглядит так, что аргументов у тебя нет, и ты просто пытаешься "победить" в споре токсичностью, сделав его неприятным для остальных участников, чтобы они перестали тебе отвечать и таким образом за тобой останется последнее слово и ты якобы от этого становишься прав.
>>1105609 >сделал пример Лол, а почему не сделал так? >class Машина: >_ func move(how: Привод) -> Машина: how.how(); return self И тогда ты мог бы собрать такую шизу: >Машина.new().move(Колёса.new()).move(Лыжи.new()).move(Гусеницы.new()) В одну строчку - твоим любимым конвейером.
Я это к чему? Твой пример - слишком абстрактный, оторванный от реальной жизни. В реальной жизни в большинстве случаев ты не нуждаешься в подобном переобувании на каждом шагу, а если даже внезапно потребуется переобуться, тебе нужен reset()/clear(), сбрасывающий/очищающий состояние объекта к изначальному, а не просто setup(), потому что своим переобуванием ты можешь создать неожиданное состояние... Типа если твои "колёса" повёрнуты на 45 градусов, а у гусениц поворота нет, и теперь вездеход двигается строго по диагоналям, и ты потом можешь придумать костыль "после установки гусениц нужно запустить 1 гусеницу на 3.5 секунды и дождаться разворачивания на -44.9999456789 градусов"...
В реальности ты используешь то, что тебе удобнее.
>>1105615 >Потому что нельзя сделать такое Они планировали добавить Traits в GDScript, но это оказалось слишком сложным на текущей базе, и они переделывают GDScript с ClassDB на GDType, чтобы разрешить все эти проблемы. Когда переведут на этот GDType, тогда и реализуют тебе эти Traits. Ждём.
>>1105672 >фичи, которые СКРИПТУ(!) не нужны Если тебе нужен огрызок из игр 00-х - можешь сам реализовать и использовать. Это совсем нетрудно (разрабатывал свой скриптовый язык за пару дней).
>Нужно было сделать свой собственный GDLang >все целевые DLL компилируются при экспорте Зачем второй ЯП, если GDScript компилируемый?
>уже мог бы выглядеть более профессионально Самый профессиональный ЯП - Python/Cython...
>>1105681 >Нельзя просто добавить типы и сказать... Школьник, Godot - некоммерческий проект. Хочешь свистоперделку - делай сам, не можешь сам - иди и задонать, чтоб наняли того, кто сможет сделать. Если посмотреть на историю всех старых языков и т.п. - то большинство постепенно набирали функционал, не рождаясь из вакуума сразу с миллионом багофич. Типизированные языки были до ООП/объектов, это совершенно независимые концепции информатики.
>>1105688 >минимум оверхеда на GDExtension C++ Насколько я это всё понимаю, из-за того, как сегодня GDScript/ClassDB устроен, GDExtension не оптимален. Имеются предложения вынести GDScript из движка и превратить его в компилируемый язык... но тогда утрачивается где-то 50~75% киллер-фич GDScript. Но окончательного решения до начала Godot 5 не будет.
Алсо, помни: LLM чатботы основывают все свои предположения на информации, доступной онлайн, а собирается эта информация с разных форумов, где школьники срут своими предрассудками, а также с официального гитхаба, где часто бывают смутные предположения и фантазии контрибуторов о том, как замечательно будет в гипотетическом будущем, если мейнтейнеры соизволят принять их слоп-реквест... Соответственно, доверия к LLM на тему Godot даже поменьше, чем на тему каких-нибудь ИРЛ событий. Фактически ты спрашиваешь у кривого зеркала, что отражает выдумки фантазёров с умным видом.
И да, мы (постеры ИТТ) тоже часто заблуждаемся.
>>1105736 >таким образом за тобой останется последнее слово и ты якобы от этого становишься прав Ты про эту фразу говоришь: >>1105605 >спорить не хочу, там все правильно я написал ? Согласен, довольно глупо, словно в детском саду.
>токсичностью Какая же ты снежинка, если даже тут "токсичность"...
Но ты прав в том, что я в плохом настроении сегодня.
>>1105735 >Модульный проект не должен падать, т.к. модули по определению сущности автономные С чего вдруг? Что за бредятина. Модуль который фулл автономен - не несет сайд эффекта, а значит бесполезен (как программа, которая не исполняется). Отличие модуля от лапши - минимальная точка входа и контракт - пик.
>Что такое "просто паблик"? Публичное свойство (публичное API класса), которое не является контрактом инжекта. Неожиданно - родительский скрипт - owner - очень выгодно делать контекстом для всех остальных скриптов в сцене/модуле.
>Просто дырка какая-то, существующая без цели Дырка у тебя в голове. С пропертями (сахар над геттерами и сеттерами) проблем инкапсуляции уже не существует (разве только в старой джаве без Project Lombok).
>Школьник, Godot - некоммерческий проект. Язык тесно интегрируется со средой - совершенно нормально хотеть и требовать фичи. Ты потребитель, а не фанбой на страже веры, хватит технологи возводить в догму. Мы все тут с годотом работаем и хотим только лучшего, потому что буквально сами этим пользуемся. Поэтому не путай движкосрачную критику от нытья по фичам (у тебя контузия от движкосраника, ты взрываешься по любой херне).
>Имеются предложения вынести GDScript из движка и превратить его в компилируемый язык... но тогда утрачивается где-то 50~75% киллер-фич GDScript Каких? У тебя твой язык - ты волен делать любую магию (сильно насрать в ключевые слова уже не получится, но @аннотации бесконечны).
Ничего не будет, станет только лучше. Надо просто выкинуть variant (о чем скажут спасибо все типизированные языки) и сделать JIT cо спекулятивной оптимизацией (тогда другие типизированные языки станут не нужны). Но это невероятно много работы, как минимум годот 17 (или годот 9 если китайцы помогут). Сейчас по сути кроме packed массивов в гдсрипте ничего нет, типизация поверхностна. А прикинь было бы круто анонсировать свою структуру и поместить потом в упакованный массив структуры - тебе тогда вообще не нужно С++ и эта шляпа сразу закинеться на кэш процессора.
@struct MyStruct: var v1: int var v2: float
var mypack: PackedArray[MyStruct] = ....
И у тебя в памяти: [MyStruct][MyStruct][MyStruct][MyStruct] Вместо: [ptr][ptr][ptr][ptr] ptr - указатели, не могу имя со звездочкой написать