Альтернатива Unity: Urho3D + C# + Xamarin
Про Urho3D уже писали на хабре, это полностью открытый 3D движок на языке С++ с редактором и интересным набором 3rd parties: Box2D, Bullet, kNet, Recast/Detour, SDL, FreeType и т.п. У движка есть редактор (написанный на нем же), но к сожалению, он далеко от Unity, зато есть другие плюсы (о них далее). Движок имеет очень аккуратный API, что стало причиной обращения взгляда Xamarin для использования кодогенератора (С++ API to C#) для генерации байндингов поверх API на языке C#. В результате получился движок (UrhoSharp) со следующими плюсами (по сравнению с тем же Unity):
- Полная поддержка последней версии Mono 4.2.x, C# 6.0/F# с Xamarin Studio / Visual Studio — грубо говоря это обычное приложение с шаблонами проектов для Visual Studio
- Открытый код (однако, для запуска на iOS и Android понадобится как минимум Xamarin Indie лицензия поскольку именно Xamarin используется на этих ОС в качестве платформы)
- Наличие C# (Xamarin) контролов, которые могут быть интегрированы в существующие неигровые приложения.
- Все плюшки оригинального движка, описанные в большом списке тут.
- Распространяется через Nuget который содержит базовые ассеты и native библиотеки что делает его легко подключаемым
- Отличная производительность, минимальный оверхед от .NET/Mono
Эй, псс… не хочешь немного примеров?
Оригинальный Urho3D при помощи Emscripten запустили в WebGL, так что примеры можно глянуть даже через браузер, но работают они не шибко стабильно. Лучше посмотреть те же самые примеры на C# склонировав репозиторий и запустив на интересующей платформе. Примеры работают на всех основных платформах: Windows, Mac, iOS и Android. Несколько скриншотов примеров:
Ландшафт (карта высот) и вода:
Скелетная анимация:
Физика 3D (Bullet)
Физика 2D (Box2D)
Управление толпой (поиск пути для толпы — raycast / pathfinding / detour):
Встраивание в существующие обычные Xamarin (Forms) приложения:
И многое другое (около 40 примеров, включая собственный от Xamarin) — копию игры ShootySkies.
Немного кода примеров:
Urho3D имеет типичную нодо-компонентную архитектуру. C# API практически копирует C++ код за исключением добавления сахара: свойств, событий, async/await. Вот так выглядет простейшая 3Д сцена:
public class HabraSample : Application
{
protected override void Start()
{
// 3D scene with Octree
var scene = new Scene(Context);
scene.CreateComponent();
// Box
var boxNode = scene.CreateChild();
boxNode.Position = new Vector3(x: 0, y: 0, z: 5);
boxNode.SetScale(2f);
boxNode.Rotation = new Quaternion(x: 60, y: 0, z: 30);
var boxModelComponent = boxNode.CreateComponent();
boxModelComponent.SetMaterial(ResourceCache.GetMaterial("Materials/BoxMaterial.xml"));
boxModelComponent.Model = ResourceCache.GetModel("Models/BoxModel.mdl");
// Light
Node lightNode = scene.CreateChild(name: "light");
var lightComponent = lightNode.CreateComponent();
lightComponent.LightType = LightType.Point;
lightComponent.Range = 50;
// Camera
Node cameraNode = scene.CreateChild(name: "camera");
Camera cameraComponent = cameraNode.CreateComponent();
// Viewport
Renderer.SetViewport(0, new Viewport(Context, scene, cameraComponent, null));
}
}
Из проекта CocosSharp были нагло скопированы отдолжены Actions которые позволяют строить красивые сложные анимации декларативно в стиле «поверни объект на столько градусов за столько секунд, затем передвинь его туда-то параллельно уменьшая размера». Выглядет это как-то так:
// уменьшить масштаб до 0.5 за 1 секунду игрового времени
await boxNode.RunActionsAsync(new ScaleTo(duration: 1f, scale: 0.5f));
// передвинуть на новую позицию за 2 секунды
await boxNode.RunActionsAsync(new MoveBy(duration: 2f, position: new Vector3(x: 5, y: 0, z: 0)));
// перекрасить в желтый
await boxNode.RunActionsAsync(new TintTo(duration: 1f, red: 1, green: 1, blue: 0));
Эти действия можно группировать, запускать параллельно и обромлять в easing functions.
В качестве примера:
boxNode.SetScale(0); // сделаем нулевой масштаб для ящика
await boxNode.RunActionsAsync(new EaseBounceOut(new ScaleTo(duration: 1f, scale: 1)));
await boxNode.RunActionsAsync(new RepeatForever(
new RotateBy(duration: 1, deltaAngleX: 90, deltaAngleY: 0, deltaAngleZ: 0)));
Что означает «увечить масштаб с 0 до 1 за 1 секунду используя EaseBounceOut easing. Затем начнем бесконечное вращение по оси Х 90 градусов за 1 секунду.
Генерация C# для C++ кода
Процесс генерации довольно непрост и, если есть интерес, я могу раскрыть его в отдельной статье. Пока могу лишь в общих словах рассказать алгоритм:
- Компилирование всего C++ кода в PCH файл (AllUrho.cpp)
- При помощи проприетарной либки (враппер над Clang AST), которой скармливается этот PCH мы получаем дерево классов/членов (AST) С++ кода в виде managed объектов.
- Генерируем DllImport для всех методов всех публичных классов (на этом этапе возникает много мелких нюансов). Но в целом проект генератора получился простой (не слишком красивый, т.к. писался в стиле «быстро-быстро и в продакшн»).
Таким образом обеспечивается покрытие около 100% публичного C++ API.
Репозиторий проекта и примеров можно найти на github.