Если вы создавали игры в Unity, есть несколько ключевых отличий, которые вам нужно знать, чтобы начать работу в All Out!
Если вы переходите с Unity, вам будет привычно сущности + компоненты, иерархия, инспектор, и переиспользуемые префабы. Самое большое изменение мышления состоит в том, что игры All Out — в первую очередь многопользовательские: состояние игрового процесса синхронизируется за вас, и большую часть логики вы пишете так, как будто по умолчанию она сервер-авторитетная.
Кратко: что отличается от Unity
Мультиплеер по умолчанию: состояние игрового процесса автоматически реплицируется с сервера на клиентов. Обычно вы не пишете RPC, SyncVars или логику спавна Netcode.
Осознанно выбирайте, где выполняется код:
Используйте игровые входные данные/UI, влияющие на состояние, на сервере + локальном клиенте (см. is_local_or_server() паттерны в руководстве по авторингу CSL).
Используйте чисто косметическую логику только локально.
Избегайте глобального состояния singleton: несколько игроков подключаются к одной и той же сессии. Предпочитайте хранить состояние на игроке или в компонентах мира.
Сначала мобильные устройства: избегайте предположений только о клавиатуре, если только ваша игра явно не ориентирована на ПК.
Структура проекта: где находятся «скрипты» и «ресурсы»
Скрипты: код вашей игры находится в .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 вы обычно используете:
Глобальные proc в main.csl (для настройки на уровне игры)
Методы жизненного цикла компонентов в ваших компонентах
Система префабов находится в стадии развития. См. раздел ограничений в документации по префабам.
«Сериализуемые поля» (переменные, видимые в Inspector)
Unity использует [SerializeField] и публичные поля, чтобы отображать значения в Inspector. В CSL используйте @ao_serialize чтобы показать поле в редакторе.
Затем добавьте ваш компонент к сущности в Инспектор и настраивайте значения для каждой сущности отдельно.
Столкновения и триггеры: по умолчанию «без колбэков»
Если вы привыкли к OnTriggerEnter / OnCollisionEnter, учтите, что CSL не полагается на колбэки столкновений для игрового скриптинга. Распространённый паттерн — запрашивать близлежащие компоненты и проверять расстояния.
Мышление для мультиплеера: ввод, UI и «где выполняется код»
В Unity вы часто можете предполагать, что «мой клиент владеет моим персонажем». В All Out думайте в терминах server-authoritative:
Ввод/UI, влияющие на игровой процесс: выполняйте там, где это может влиять на авторитетное состояние (обычно сервер + локальный клиент)
Косметика: только локально
См. руководство по авторингу CSL с примерами использования is_local_or_server() vs is_local().
Частые подводные камни при переходе с Unity на All Out
Менеджеры-одиночки: предпочитайте состояние на уровне игрока или сущности вместо глобальных GameManager -подобных singleton.
Привычки импорта: импортируйте движок и папки из main.csl (не разбрасывайте импорты по многим файлам).
Предположение о singleplayer: всегда думайте: «что произойдёт, если подключены 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 {
// проверить расстояние / применить эффект / и т. д.
}