Начну делать реальную попытку развить общество условно открытого кода, начиная со своего кода и тестовой сцены.
Вы можете использовать эту сцену для обучения и/или тестирования своих прототипов. Данный репозиторий содержит, в составе проекта на Unity 6.3. URP библиотеки/компоненты исходного кода, которые могут облегчить начальную разработку ваших прототипов. Все компоненты, которые не рекомендуется изменять самостоятельно, включают в себя сокращение Tac, и находятся в соответствующих директориях/namespace/dll.
Особенностью библиотек Tac является их продуманная архитектура и легкость, благодаря чему их можно собирать как конструктор для разных прототипов игр. На данный момент можно выделить:
1. TacCamera - простейшая камера, которая позволяет перемещаться в игре, пролетая над поверхностью, т.н. "камера сверху"
2. TacItemCreate - минимальный набор, который позволяет перекрыть создание моделей в игре, выстроив правильную архитектуру "сущностей"
3. TacItemMove - позволяет перемещать/располагать/строить предметы в игре
4. TacStandart - общие компоненты, расширяющие функциональность классов Unity и требующиеся практически для каждого компонента
5. TacWireframe - Управление визуальной сеткой (wireframe), что позволяет отобразить пересечение модели, или прозрачность стен, заменяя модель сеткой определенного цвета
P.S. Конечно сердце этого проекта - это документация с видео туториалами, которые выложу со временем, тем быстрее, чем появится заинтересованные.
P.P.S. Конечно, это только начало ... за несколько лет накопилось много кода, который превращается в бесполезный, если его не выкладывать и не поддерживать. (на дискетах много кода, когда то выбросил) Гавнокод поддерживать не хочется, а красивые решения отдавать жалко :) Но я могу себе позволить, могу даже подискутировать на сколько решения красивые. Но призываю создавать движение в обе стороны, а не только я -> вам, но постарайтесь быть полезны и вы -> мне.
А вот как это начиналось, в ролике основы, но после этого ряд компонентов переосмыслены и оформлены лучше

Великий архитектор.
Пожалуйста, объясни мне, кривозубому крестьянину, зачем ты использовал ключевое слово partial в примере?
Я правда пытаюсь понять твою гениальную идею.
Код из твоего репозитория:
namespace Tac
{
public abstract class Item : MonoBehaviour
{
public int ObjectId;
public int GroupId = -1;
public string ModelName = "";
public ModelTypes ModelType = ModelTypes.Model;
}
}
namespace Tac.ItemMove
{
public partial class Item2 : Item
{
public bool AllowMove = true;
}
public class ItemMove
{
public void Move(Item2 item, ... ) { ... }
}
}
namespace Tac.ItemRotate
{
public partial class Item2 : Item
{
public bool AllowRotate = true;
}
public class ItemRotate
{
public void Rotate(Item2 item, ... ) { ... }
}
}Storm54
Спасибо за вопрос. Я запишу видео-урок, подождите немного.
Только вот давайте без ёрничества.
upd.

Ок, отвечу серьезно.
Досмотрел до 18 минуты ответ видео. Все доводы DeepSeek абсолютно правильны, SRP и полиморфизм страдает, тут добавить нечего.
Просто вернемся к коду из репозитория:
namespace Tac
{
public abstract class Item : MonoBehaviour
{
public int ObjectId;
public int GroupId = -1;
public string ModelName = "";
public ModelTypes ModelType = ModelTypes.Model;
}
}
namespace Tac.ItemMove
{
public partial class Item2 : Item
{
public bool AllowMove = true;
}
public class ItemMove
{
public void Move(Item2 item, ... ) { ... }
}
}
namespace Tac.ItemRotate
{
public partial class Item2 : Item
{
public bool AllowRotate = true;
}
public class ItemRotate
{
public void Rotate(Item2 item, ... ) { ... }
}
}В данном случае здесь два независимых класса Item2, а именно: Tac.ItemMove.Item2 и Tac.ItemRotate.Item2.
Каждый из этих классов не содержит поля другого.
Вы просто не разобрались в логике работы ключевого слова partial - читайте документацию.
Оно делает крайне простую вещь: Позволяет объявлять части класса в нескольких файлах (как, например, в C++, когда можно реализацию каждого метода вынести в отдельный файл). В C# это нужно было для кодогенерации, например, в WinForms, когда часть класса окна с визуальными компонентами генерировалась в отдельном файле.
Т.е. в вашей архитектуре Вы предложили просто сложить всю логику и данные в один огромный класс.
Идея глупая, непонятно на что Вы вообще надеялись, когда это опубликовали.
Storm54
> В данном случае здесь два независимых класса Item2, а именно: Tac.ItemMove.Item2 и Tac.ItemRotate.Item2.
Там была описка, это один и тот же класс, я уже поправил.
Остальное видимо нет смысла комментировать, думайте, может поймете. Пока много самоуверенности и не знание предмета.
Storm54
> Досмотрел до 18 минуты
Если посмотрели бы дальше, поняли бы (хотя не факт) свои ошибки и дипсика.
Storm54
> Т.е. в вашей архитектуре Вы предложили просто сложить всю логику и данные в один огромный класс.
это придумали вы, я такого не предлагал.
Что касается SRP, в трех частях (может поймете, что это само по себе глупо, и для структурщиков )
Рассматриваем книгу Б. Мартина "Быстрая разработка программ", конкретно пытаясь понять как им и Б. Косом программировалась игра "Боулинг" (этому посвящена одна из глав). Делаем это, чтобы понять вначале контекст как возник и кто тот человек, который предложил принцип единой ответственности. Понимаем, что это "заядлый структурщик", который пробывал себя в роли ООП`шника, подробное рассмотрение "игры в боулинг" однозначно приводит нас к этому пониманию. Подтверждается, что этот принцип (единой ответственности) позволяет немного сгладить эффекты, когда на объектно-ориентированном языке программируют те, кто думают исключительно структурно. Получается немного лучше, чем полностью в структурной (процедурной) парадигме, но все же далеко, если думать сразу в терминах ООП. Показывается пример того, если программировать "игру боулинг" изначально с помощью ООП, и становится понятно чем это отличается.
Непосредственно рассматриваем принцип единой ответственности и его связь с ролями и полнотой класса. Приходим к выводу, что принцип сформулирован не четко и для своего применения имеет мало примеров и случаев, является скорее критерием того, когда имеет смысл разделять класс на части в зависимости от ролей. А зачастую приводимые примеры не выдерживают ни какой критики.
Пример, чем заменить принцип единой ответственности, чтобы решить вопрос связанности классов вместо разделения класса по ролям. Подведение итогов и предложение заменить принцип единой ответственности - другим ..
tac
> это придумали вы, я такого не предлагал.
В реальной программе вариаций компонентов будет крайне много. У каких-то объектов будут одни компоненты, у каких-то объектов - другие.
В ECS это решается понятным образом - есть изолированный компонент с данными и система, которая обрабатывает один или несколько компонентов. В итоге, в программе можно собирать логику каждой сущности, оперируя разными наборами компонентов.
В Вашем же примере создается один убер класс (в данном случае Item2), который содержит в себе все поля. Это приведет к одному огромному монолиту, просто потому, что логика по работает с определенным компонентом уже находится в этом классе, не получится создать другой класс (например, ItemX, который наследуется напрямую от Item) и при этом переиспользовать логику из Item2 - отсутствие полиморфизма, дипсик про это и написал.
Итого: это вообще никак не применимо в реальном проекте. Ровно тоже самое, что и описать всю логику в одном классе. Попробуйте доказать обратное на простом примере, сделать два класса: Monster и Building и в них добавить поле со здовьем и взаимодействием с ним. Без явного или косвенного дублирования кода это сделать не получится
Storm54
> В реальной программе вариаций компонентов будет крайне много.
Мы можем обсуждать не в вакууме, и не с вашими предположениями, которые совершенно не верны (просто в каждом вашем слове, но у меня нет желания переубеждать человека, который не уважительно общается). Можно просто открыть репозиторий и посмотреть как организованны реальные Tac-компоненты.
Если хотите задать вопрос: уважительно опишите вашу проблему и опасения.
tac
> Мы можем обсуждать не в вакууме, и не с вашими предположениями, которые совершенно не верны (просто в каждом вашем слове, но у меня нет желания переубеждать человека, который не уважительно общается). Можно просто открыть репозиторий и посмотреть как организованны реальные Tac-компоненты.
Ковыряться в репозитории ноунейма - много чести, уж извините. Я итак потратил время и посмотрел половину 40-минутного ролика, в котором весь контент - озвучивание текста на экране без аргументации, а с возгласами в духе "не согласен с дипсиком".
Просто приведу самый краткий пример кода, который только возможен и который раскрывает проблему.
Напишите решение этой проблемы и вопрос закрыт.
public class Entity
{
public int EntityId;
public string Name;
}
// Monster file 1
public partial class Monster : Entity
{
public int Health;
public void Damage() { Health--; }
}
// Monster file 2
public partial class Monster : Entity
{
public Vector2 MoveSpeed;
public void StartMove() { MoveSpeed = new Vector2(10, 0); }
}
// Building file 1
public partial class Bulding : Entity
{
public int Health;
public void Damage() { Health--; }
}
// Building file 2
public partial class Bulding : Entity
{
public int WallLevel;
public void IncreaseWallLevel() { WallLevel++; }
}Monster и Building имеют здоровье, при этом монстр умеет двигаться в точку, а у строения может улучшаться уровень стены. Легко заметить, что код здоровья дублируется - его просто никак не написать иначе, т.к. поля Health находятся в разных классах и не связаны, следовательно, я не могу написать в одном месте логику, а приходится дублировать ее.
Как мне быть?
tac
> Остальное видимо нет смысла комментировать, думайте, может поймете. Пока много самоуверенности и не знание предмета.
Без кода с решением проблемы - пустая болтовня.
Storm54
> Как мне быть?
Например, вот так. Но ваш пример перпендикулярен и слабо показывает особенности partial-подхода. Не надо думать, что появление partial-подхода, удаляет ООП, я не такой же "идеолог" как ECSники, которые готовы уничтожить ООП и обязать всех страдать ерундой. Возможно не очевидно, но разница между AgentMover и Agent, в том, что AgentMover управляет сразу множеством Agent, так же как BuildingCreator строит разные Building. Аналогично и DamageControl. В реальности, скорее там будет не передача параметра, а просто просмотр своего списка всех агентов, строений и т.п.. А также, возможно и появление методов в самом Agent/Building - причем Agent с большой вероятностью, а Building скорее нет. Это связано с тем, что агент перемещается сам, а здание само не строится..
> Легко заметить, что код здоровья дублируется - его просто никак не написать иначе, т.к. поля Health находятся в разных классах и не связаны, следовательно, я не могу написать в одном месте логику, а приходится дублировать ее.
Это я не понял, видимо вы рассуждаете в каком то своем контексте, который мне не известен. Скорее всего, вы что-то не доформализовали. Если у вас единая логика для Health, то и поля легко объединить. Если вы хотели сказать, что Здоровье монстра и Прочность здание это разное и их требуется различать, то можно сделать интерфейс, очень напрашивалось, но я не сделал, т.к. не ясен контекст. Поэтому если логика одинакова - нужно объединить в одно понятие. , а если понятия все же разные, у них будут различия в реализации. Поэтому микро примеры плохо отражают. Но когда у вас спадет спесь, стоит посмотреть реальные примеры.
// MyGame.dll
public class MyMonster : Agent
{
}
public class MyBuilding : Building
{
}
// TacLibrary.dll
// BuildingCreator
public class BuildingCreator
{
public void OnIncreaseWallLevel(Bulding building) { building.WallLevel++; }
}
public class Bulding : Item2
{
public int WallLevel;
}
// DamageControl.cs
public class DamageControl
{
public void OnDamage(Item2 entity) { entity.Health--; }
}
// AgentMover.cs
public class AgentMover
{
public void StartMove(Agent agent) { agent.MoveSpeed = new Vector2(10, 0); }
}
public class Agent : Item2
{
public Vector2 MoveSpeed;
}
// Common.cs
public partial class Item2 : EntityId
{
public int Health;
}
// TacStandart.dll
public class EntityId
{
public int Id;
public string Name;
}P.S. у меня в рукаве еще много решений - заходите еще, только вытирайте ноги и мойте рот.
tac
> Это я не понял, видимо вы рассуждаете в каком то своем контексте, который мне не известен. Скорее всего, вы что-то не доформализовали.
Что именно не было понятно? Health находился в двух классах, это и было дублированием логики.
В коде выше Вы просто перенесли Health в базовый класс, это было предсказуемо.
Что будем делать, когда еще несколько полей появится? Будем все в базовый класс переносить?
Storm54
> Что будем делать, когда еще несколько полей появится? Будем все в базовый класс переносить?
Тут важно сказать каких "несколько" полей. Для разных полей будет разный подход. Да, что-то попадет в общий класс. Но это будет не так много. Вы очень преувеличиваете количество полей, которые общи с различными сущностями. Более того, это будет удобно ровно до того момента, чтобы легко настроить префаб, т.е. не сами поля, а их значения для разных моделей. И это как раз удобно делать в одном месте.
Но у меня есть и еще пару трюков. Логику можно разделить на ту которая относится к 3d - пространству и взаимодействию в нем, и прочую бизнес-логику. Поэтому мне не кто не мешает создать два уровня логики, наделив если нужно префаб модели двумя или более классами данных, которые не будут пересекаться.
Storm54
Свой бред оставь себе, и приходи когда разберешься и что-то будешь иметь сказать по сути. Терпеть твои оскорбления тут никто не будет. Я вижу лишь то, что ты не понимаешь ООП от слова совсем, поэтому обсуждать с тобой продвинутые вещи рано.
То есть ты потер сообщение, где я указал, что ты не понимаешь понятие абстракции, после чего оставил сообщение, где указал, что я не понимаю ООП ?
Сильный ход, сразу видно уверенного в себе специалиста. Это твой фокус из рукава?)