Альтернатива 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


830b6e00cf14404a8b558cc9e5fb9881.png

Эй, псс… не хочешь немного примеров?


Оригинальный Urho3D при помощи Emscripten запустили в WebGL, так что примеры можно глянуть даже через браузер, но работают они не шибко стабильно. Лучше посмотреть те же самые примеры на C# склонировав репозиторий и запустив на интересующей платформе. Примеры работают на всех основных платформах: Windows, Mac, iOS и Android. Несколько скриншотов примеров:

Ландшафт (карта высот) и вода:
e3e8f180d8b54f0989d9448c98eacd5b.png

Скелетная анимация:
9400ed9d08aa40239bca9ebed58ce058.png

Физика 3D (Bullet)
ec115969ff2e434ba41534ac41679232.gif

Физика 2D (Box2D)
d770606980874fb6a15484d04bea6dd6.gif

Управление толпой (поиск пути для толпы — raycast / pathfinding / detour):
9b886405d78f4d0bbf8d994f4f098cfa.png

Встраивание в существующие обычные Xamarin (Forms) приложения:
1ed3f5228b8d4b52a0f6433036f6a62c.png

И многое другое (около 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 секунду.

97d3ce5c139c4938875938d56b729002.gif

Генерация C# для C++ кода


Процесс генерации довольно непрост и, если есть интерес, я могу раскрыть его в отдельной статье. Пока могу лишь в общих словах рассказать алгоритм:

  • Компилирование всего C++ кода в PCH файл (AllUrho.cpp)
  • При помощи проприетарной либки (враппер над Clang AST), которой скармливается этот PCH мы получаем дерево классов/членов (AST) С++ кода в виде managed объектов.
  • Генерируем DllImport для всех методов всех публичных классов (на этом этапе возникает много мелких нюансов). Но в целом проект генератора получился простой (не слишком красивый, т.к. писался в стиле «быстро-быстро и в продакшн»).


Таким образом обеспечивается покрытие около 100% публичного C++ API.

Репозиторий проекта и примеров можно найти на github.

© Habrahabr.ru