Если вы создавали игры на Unity, есть несколько ключевых отличий, которые вам нужно знать, чтобы начать работу в All Out!
Если вы переходите из Unity, вам будет комфортно с сущностями + компонентами, иерархией, инспектором, и переиспользуемыми префабами. Самое большое изменение мышления в том, что в All Out игры ориентированы на мультиплеер: состояние игрового процесса синхронизируется за вас, и вы пишете большую часть логики так, как будто сервер по умолчанию является авторитетным.
Кратко: что отличается от Unity
Мультиплеер — по умолчанию: состояние игрового процесса автоматически реплицируется с сервера на клиентов. Как правило, вы не пишете RPC, SyncVar или логику появления через Netcode.
Будьте намеренными в том, где выполняется код:
Используйте ввод/интерфейс, влияющие на состояние игрового процесса, на сервере + локальном клиенте (см. is_local_or_server() паттерны в руководстве по созданию CSL).
Используйте чисто косметическую логику только локально.
Избегайте глобального состояния-одиночки: несколько игроков подключаются к одной и той же сессии. Предпочитайте хранить состояние на игроке или в компонентах мира.
Сначала мобильные устройства: избегайте предположений только про клавиатуру, если ваша игра явно не ориентирована на ПК.
Скрипты: ваш игровой код хранится в .csl файлах. Новые проекты начинаются с main.csl который импортирует движок и определяет точки входа жизненного цикла. См. Начало работы с CSL.
АССЕТЫ: игровые ассеты хранятся в /res каталоге вашего проекта и обращаются по пути без префикса /res (пример: "ui/button.png"). См. Ассеты и ресурсы.
Импорты (важное отличие)
В Unity каждый C#-скрипт компилируется и может использовать свои собственные using директивы. В All Out держите импорты централизованными:
Импорт "core:ao" в main.csl
Если вы добавляете папку (например, ui/), импортируйте папку один раз в main.csl
Избегайте добавления импортов в других файлах
Пример:
Жизненный цикл: MonoBehaviour → CSL
В Unity вы обычно прикрепляете MonoBehaviour к GameObject и реализуете:
Start() / Awake()
Update() / LateUpdate()
OnDestroy()
В CSL вы обычно используете:
Глобальные процедуры в main.csl (для настройки на уровне игры)
Методы жизненного цикла компонентов в ваших компонентах
Система префабов развивается. См. раздел с ограничениями в документе о префабах.
«Сериализованные поля» (переменные, отображаемые в инспекторе)
Unity использует [SerializeField] и публичные поля для отображения значений в Инспекторе. В CSL используйте @ao_serialize чтобы открыть поле для редактирования в редакторе.
Затем добавьте ваш компонент к сущности в Инспектор и настройте значения для каждой сущности.
Коллизии и триггеры: по умолчанию «нет обратных вызовов»
Если вы привыкли к OnTriggerEnter / OnCollisionEnter, обратите внимание, что CSL не полагается на коллизионные колбэки для скриптинга игрового процесса. Обычный шаблон — запрашивать близлежащие компоненты и проверять расстояния.
Мышление в мультиплеере: ввод, UI и «где выполняется код»
В Unity вы часто можете предположить «мой клиент владеет моим персонажем». В All Out думайте сервер-авторитет:
Ввод/интерфейс, влияющие на игровой процесс: выполняется там, где он может повлиять на авторитетное состояние (обычно сервер + локальный клиент)
Косметика: только локально
См. руководство по созданию CSL для примеров с использованием is_local_or_server() против is_local().
Распространённые подводные камни при переходе из Unity в All Out
Менеджеры-одиночки: предпочитайте состояние на игроке или на сущности вместо глобального GameManager стиля одиночек.
Привычки импорта: импортируйте движок и папки из main.csl (не разбрасывайте импорты по множеству файлов).
Предположение о синглплеере: всегда думайте «что произойдёт при подключении 10 игроков?»
Жёстко заданный вход для десктопа: избегайте требования клавиатуры/мыши, если это не является осознанным решением.
DamageOnTouch :: class : Component {
@ao_serialize
damage: int = 10;
}
// Создание и уничтожение сущностей
e := Scene.create_entity();
e->set_local_position({0, 0});
e->destroy();
// Перебор сущностей (когда вам действительно нужно "всё")
foreach e2: entity_iterator() {
}
// Перебор компонентов определённого типа
foreach player: component_iterator(My_Player) {
}
nearby: [..]Pickup;
Scene.get_all_components_in_range(entity.local_position, 2.0, ref nearby);
for p: nearby {
// проверить расстояние / применить эффект / и т.д.
}