読者です 読者をやめる 読者になる 読者になる

F# で作成したライブラリをUnityのスクリプト上で利用する

ゲームのほうのUnityの話です。
UnityはMonoを基盤としており、スクリプト内で.NETなライブラリはもちろん、自作ライブラリをUnity側で読み込んで使うことだってできます。

そのライブラリ、F#で作ろうず!

こうなると、ロジック部分はF#で作りたいですよね?
特に非同期プログラミングとか、副作用のすくないコードとか。
では、作ってしまいましょう!

とりあえずサンプルを作る

サンプルなので、簡単なCounterみたいな?

namespace FsCounter

open System

module Counter =

  let Zero = 0

  let Increment counter = counter + 1

  let Decrement counter = counter - 1

  let Reset max =
    let rand = new Random()
    match rand.Next(max) , rand.Next(max) with
    | _ , 0 | 0 , _ -> Zero
    | x , y when x > y -> x % y
    | x , y -> - ( y % x )

Reset関数以外はテストコードも書いてみました。

module FsCounterScenario

open NaturalSpec
open FsCounter

[<Scenario>]
let ``初期値0のカウンターを生成できる ``() =
  Given Counter.Zero
  |> It should equal 0
  |> Verify

[<Scenario>]
let ``初期状態のカウンターをインクリメントするとカウントが1増える`` () =
  Given Counter.Zero
  |> When Counter.Increment
  |> It should equal 1
  |> Verify

[<Scenario>]
let ``カウントが1のカウンターをインクリメントするとカウントが2になる`` () =
  Given Counter.Zero |> Counter.Increment
  |> When Counter.Increment
  |> It should equal 2
  |> Verify

[<Scenario>]
let ``初期状態のカウンターをデクリメントするとカウントが1減る`` () =
  Given Counter.Zero
  |> When Counter.Decrement
  |> It should equal -1
  |> Verify

[<Scenario>]
let ``カウントが-1のカウンターをデクリメントするとカウントが-2になる`` () =
  Given Counter.Zero |> Counter.Decrement
  |> When Counter.Decrement
  |> It should equal -2
  |> Verify

Unity側で使う

上記コードをビルドして出来上がったdllを、Unityプロジェクト下にあるAssetsフォルダのどこかに放り込みましょう。Assets下ならどこでもよさそうなので、外部ライブラリだとわかりやすいようにフォルダでまとめておくのがお勧めです。
ただ、F#ライブラリの場合はこれだけだと"FSharp.Core.dllがないよ"とかで怒られるので、F#関連のdllも放り込みます。

追記:コンパイラオプションで--standaloneを指定してコンパイルすれば自作ライブラリのdllだけで動くようになります(@さん、情報ありがとうございます!)

さて、それではC#スクリプトを書いてみましょう。

using UnityEngine;
using System.Collections;
using FsCounter;

public class CountViewer : MonoBehaviour
{
    int max = 100;
    int min = -100;
    int data = Counter.Zero;

    void OnGUI()
    {
        if (data >= max || data <= min) data = Counter.Reset(max);
        
        if (data > 0) data = Counter.Increment(data);
        else data = Counter.Decrement(data);
        
        GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height / 2, 100, 60), new GUIContent("Counter : " + data));
    }
}

カウンタをラベルに表示するだけの簡単なものです。
あとはこのスクリプトをEmpty Objectにリンクさせれば動くようになります。

実行

画面中央にカウンタの数値が高速で表示されます。


外部ライブラリにする利点とか

  • 再利用しやすくなる
  • ビューとロジックを分離
  • テストが書きやすい
  • 非同期プログラミングとか
  • ゲームの一部を関数型言語で作れるという心の癒し
  • でも毎度F#のdllを追加しないといけないのはだるい…

C#でいいじゃんと思う人はC#でもいいと思います。
でも私はF#を使いたいのでこういうスタイルでUnityを触っています、ただそれだけです。