F# — это функциональный язык программирования (но, если точнее, на самом деле, это — мультипарадигмальный язык, т.к., кроме функционального программирования, он поддерживает как императивное программирование, так и объектно-ориентированное программирование) под платформу .NET Framework. Своего рода Scala в мире JVM, только для CLR. F# предоставляет много интересных возможностей, и во многих сценариях он на две головы выше своего брата C#. Пожалуй, чтобы описать все преимущества F# над C#, потребуется целая статья (кстати, хорошая идея для следующей статьи), поэтому давайте не будем останавливаться на этом вопросе сейчас. Тем более, никто не обязывает нас писать проект полностью на F#. Если в каких-то местах есть необходимость (или более выгодно) писать на C#, нам никто не запретит это сделать. Таким образом, можно использовать сильные стороны обоих языков программирования!
Основным языком программирования в движке Unity, как, думаю, многим должно быть известно, является C#. И, несмотря на то, что F# движком Unity официально не поддерживается, нам попадалась информация от одного из сотрудников Unity в твиттере о том, что у них есть тесты некоторой кодовой базы на F#.
Другими словами, не стоит бояться писать на F# под Unity, только из-за того, что что-то может не заработать или не скомпилиться, т.к. F# официально не поддерживается. Все-таки, и C# и F# работают поверх CLR и компилируются в промежуточное представление (IL), которое и выполняется либо Mono, либо компилируется в нативный код посредством IL2CPP. Таким образом, для Unity, теоретически, абсолютно не важно, на каком языке вы будете писать.
Настройку проекта мы будем выполнять в Windows. У нас установлена Unity версии 2019.1.8f1, но, думаю, может подойти и чуть более старая версия. Само собой, это также должно сработать и на более новых версиях Unity. В качестве IDE мы будем использовать IDE от JetBrains — Rider. По идее, нет особой разницы, какую IDE использовать, так что если у вас Visual Studio, то все описанные действия можно адаптировать и под нее.
Создаем новый Unity проект (пускай он будет называться FSharpUnityProject).
Далее создаем папку Scripts внутри папки Assets, после чего создаем тестовый скрипт на C# (TestScript) и открываем его в Rider-е.
Далее, в папке основного проекта создаем подпапку для проекта на F# — FSharpLib. Затем из Rider-а создаем новый F# проект типа .Net Core Class Library (.Net Core уже должен быть установлен на вашем компьютере; если это не так, то самое время исправить это досадное недоразумение) в этой папке с аналогичным именем. В качестве языка программирования указываем F#, а в качестве фреймворка — .NetStandard2.0.
Далее из консоли (терминала), прямо не выходя из Rider-а, запускаем команду для сборки проекта: dotnet publish . После успешной сборки проекта создаем папку Assets/Libraries и копируем в нее из папки FSharpLib\bin\Debug\netstandard2.0\publish файлы FSharp.Core.dll (основная библиотека языка F#, без нее у нас не скомпилируется проект).
Теперь необходимо подключить Unity зависимости к нашей F# библиотеке. Для этого переходим к структуре проекта, выбираем Dependecies, затем выполняем RMB -> Add Reference... -> Add From и добавляем C# библиотеку Library/ScriptAssemblies/Assembly-CSharp.dll в качестве зависимости (не обязательный пункт; он нужен, чтобы мы могли обратиться к C# части проекта из F#). Теперь нужно подключить завимости от движка Unity из папки с установкой. В нашем случае это C:\Program Files\Unity\Hub\Editor\2019.1.8f1\Editor\Data\Managed .
Выбираем все необходимые зависимости (можно выбрать все библиотеки). В нашем случае достаточно UnityEngine.dll и UnityEngine\UnityEngine.CoreModule.dll .
Теперь давайте создадим наш первый Unity скрипт на F#. Для этого переключаемся обратно в Rider в проект с библиотекой и пишем вместо уже имеющегося примерно следующий код:
1 2 3 4 5 6 7 |
namespace FSharpLib open UnityEngine type HelloWorldScript() = inherit MonoBehaviour() member this.Start() = Debug.Log("F#: Hello World!") |
Сохраняем и пересобираем проект Build -> Build Solution . Затем переходим в папку FSharpLib\bin\Debug\netstandard2.0 и копируем следующие файлы: FSharpLib.dll (библиотека с кодом выше) и FSharpLib.pdb (файл с отладочной информацией, чтобы можно было подключиться с дебаггером; необязательный шаг) в папку Libraries . Переходим в Unity, создаем пустой игровой объект на сцене, вешаем скрипт HelloWorldScript на него и запускаем проект. В консоли должна показаться заветная фраза. Поздравляю! Нам удалось подружить Unity с F#.
Для примера взаимодействия с C# частью проекта давайте изменим тестовый скрипт следующим образом:
1 2 3 4 5 6 7 8 9 |
using UnityEngine; public class TestScript : MonoBehaviour { public static string Say(string value) { return "C#: " + value; } } |
Вызовем новый метод на стороне F#:
1 2 3 4 5 6 7 8 9 |
namespace FSharpLib open UnityEngine type HelloWorldScript() = inherit MonoBehaviour() member this.Start() = Debug.Log("F#: Hello World!") Debug.Log(TestScript.Say("Hi!")) |
Сохраняем, пересобираем, снова копируем и запускаем:
Копировать файлы после пересборки проекта вручную довольно утомительно и ошибкоопасно. Поэтому стоит этот момент автоматизировать. К сожалению, в отличие от Visual Studio, в Rider-е нет возможности настроить действия после сборки приложения прямо из пользовательского интерфейса. Но не беда, мы можем это сделать вручную. Для этого открываем файл проекта библиотеки FSharpLib\FSharpLib.fsproj в блокноте и добавляем следующие строки:
1 2 3 4 |
<Target Name="PostBuild" AfterTargets="PostBuildEvent"> <Exec Command="copy /y "$(TargetPath)" "$(ProjectDir)\..\Assets\"" /> <Exec Command="copy /y "$(TargetDir)$(TargetName).pdb" "$(ProjectDir)\..\Assets\"" /> </Target> |
Благодаря этому при каждой пересборке проекта автоматически будут скопированы файл библиотеки и отладочный файл.
Таким образом, мы не только подружили Unity и F#, наладили взаимодействие между C# и F#, но и автоматизировали часть процесса сборки. В общем, потрудились на славу. Полный исходный код проекта можно скачать здесь. Если есть какие-то вопросы, то милости просим в комментарии к этой статье. Всем спасибо за внимание и до скорых встреч!