AltseedとF#ゲーム製作入門

AltseedでF#を使う入門をした,という記事です.

初めに



Altseedというゲームエンジンがあります.
複数のプログラミング言語に対応しており,C++や.NET (Mono)やJVMから使うことができます.
詳しくはこちらの公式ページをご覧ください.
ゲームエンジン - Altseed -

F#Microsoftの開発している関数型言語です.C#と同様に.Net Frameworkプログラミング言語です.

この記事は秋津早苗さんのこちらの記事を受けて,もう少し発展した内容を扱います.
akitsu-sanae.hatenablog.com


Altseedは,どの言語でもほとんど同じ記述で書けます.
言語間のオブジェクト指向パラダイムにおける文法の違いがわかれば,簡単に他の言語でもゲームを作ることができます.

開発環境はVisual Studio Community 2017 for Macを使っています.

今回のプロジェクトのソースコードはこちらです.説明が不要な方はどうぞ.
github.com

導入


プロジェクトの作成

開発環境はVisual Studioを使います.

  1. 新しいプロジェクトから,その他→.NETのコンソールプロジェクト F# を選んでください.
  2. プロジェクト名(以下Solution)を入力して作成を押すと,新しいプロジェクトが作成されます.
  3. ソリューションエクスプローラーからパッケージを右クリック.
  4. パッケージの追加を選び,Altseedを検索して,AltseedDotNetを追加します.

注:Windows Form Applicationへの変更

Mac
  1. 適当なエディタでVisualStudioProjects/Solution/Solution/Solution.fsprojを開きます.
  2. 7行目あたりにあるOutputTypeのExeをWinExeに変更します.(Windowアプリケーションへの変更
Windows

上述の方法でもできますが、Visual StudioからGUIで変更できるはずです。

コード


ウィンドウを表示する.

Program.fs

namespace test

module Program = 
    [<EntryPoint>]
    let main argv = 
        asd.Engine.Initialize ("test", 640, 480, new asd.EngineOption ())
            |> ignore

        let rec loop () =
            if asd.Engine.Keyboard.GetKeyState asd.Keys.Escape = asd.KeyState.Push then ()
            elif asd.Engine.DoEvents () then
                asd.Engine.Update ()
                loop ()
        loop ()

        asd.Engine.Terminate ()
        0

上述の秋津さんの記事を参考にしました.実行するとウィンドウを表示するコードです.
ESCキーを押したら終了します.
F#ではbreak文がありません.途中でループを抜けるためには,while文ではなく再帰関数(rec)を用いる必要があります.

qiita.com

シーンを作る.

Game.fs

namespace test

type Game() =
    inherit asd.Scene()

    let layer = new asd.Layer2D()

    override this.OnRegistered () =
        base.OnRegistered ()
        this.AddLayer layer

asd.Sceneクラスを継承したGameクラスです.
inheritで継承,letでフィールドを宣言します.

bleis-tift.hatenablog.com

Program.fsの一部を以下の様に書き換え,Gameシーンに遷移する様にします.
省略されている部分は適宜読み替えてください.

Program.fs

    and.Engine.Initialize ...
        ...

    let scene = new Game ()
    asd.Engine.ChangeScene scene

    let rec loop () =
        ...

F#ではファイルの順序が大切で,先に定義したものしか使えません.

F# プロジェクトでは、ファイルの順序が重要になります。 F# プロジェクトのファイルは、F# コンパイラによって順番に処理されます。 F# コンパイラでは、すべての構成要素が、使用される前に定義されている必要があります。したがって、F# の構成要素の定義が含まれているファイルは、プロジェクトのファイル リストで、その構成要素を使用するファイルより前にある必要があります。
Visual Studio による F# プログラムの作成

そのため,ソリューションエクスプローラーで,Game.fsをProgram.fsの上に持ってきてくださ.

グローバルな関数を用意する.

Altseedのボタン入力はコードが長いので,関数定義をします.
Global.fs

namespace test

module Global =
    let KeyPush key =
        asd.Engine.Keyboard.GetKeyState key = asd.KeyState.Push
    
    let KeyHold key =
        asd.Engine.Keyboard.GetKeyState key = asd.KeyState.Hold
    
    let KeyRelease key =
        asd.Engine.Keyboard.GetKeyState key = asd.KeyState.Release
    
    let KeyFree key =
        asd.Engine.Keyboard.GetKeyState key = asd.KeyState.Free

これをソリューションエクスプローラで*.fsのファイルの先頭に持ってきてください.

プレイヤーを作る.

Player.fs

namespace test

open Global

type Player() as this = 
    inherit asd.GeometryObject2D()

    let speed = 4.0f
    let size = new and.Vector2DF(50.0f, 50.0f)

    do
        this.Shape <- 
            new asd.RectangleShape (DrawingArea=new asd.RectF (-this.size / 2.0f, this.size))
        this.Color <- new asd.Color(255uy, 255uy, 255uy)
        this.Position <- 0.5f * asd.Engine.WindowSize.To2DF ()

    member private this.UpdatePosition dx dy  =
        this.Position <- new asd.Vector2DF (this.Position.X + dx, this.Position.Y + dy)

    member private this.ClampPosition () =
        let pos = this.Position
        let win = asd.Engine.WindowSize.To2DF ()
        let x = asd.MathHelper.Clamp (pos.X, win.X - size.X / 2.0f, size.X / 2.0f)
        let y = asd.MathHelper.Clamp (pos.Y, win.Y - size.Y / 2.0f, size.Y / 2.0f)
        this.Position <- new asd.Vector2DF (x, y)

    member this.Move ()  =
        if KeyHold asd.Keys.Up then
            this.UpdatePosition 0.0f -speed
        
        if KeyHold asd.Keys.Down then
            this.UpdatePosition 0.0f speed
        
        if KeyHold asd.Keys.Right then
            this.UpdatePosition speed 0.0f
        
        if KeyHold asd.Keys.Left then
            this.UpdatePosition -speed 0.0f
        
        this.ClampPosition ()

    override this.OnUpdate () =
        base.OnUpdate ()
        this.Move ()

memberがメンバー関数を示します.
コンストラクタで処理を行うには,doの後につなげます.

F#では,値の代入には<-演算子を用います.
Altseedでは,get_XX, set_XXという形でgetter/setterが用意されています.

また,Geme.fsを以下の様に書き換え,layerにPlayerクラスのインスタンスを追加します.
Game.fs

namespace test

type Game() =
    inherit asd.Scene()

    let layer = new asd.Layer2D()
    let player = new Player()

    override this.OnRegistered () =
        base.OnRegistered ()
        this.AddLayer layer
        layer.AddObject player

実行する.

現在のファイル順序は

  • Global.fs
  • Player.fs
  • Game.fs
  • Program.fs

である必要があります.
この状態で実行すると,十字キーで操作可能な白い矩形が表示されていると思います.

終わりに

F#はインデントを用いるので,コードの見た目がスッキリしています.
F#でゲームを作るための,オブジェクト指向の基本的な部分がわかったので,簡単なゲームを一つ作ってみたいと思います.

(* 2018/04/21追記 *)
作りました。
freegame-mugen.jp


次の新歓に向けて,ドキュメントと初心者講座の整備もできたら嬉しいですね.

Amusement Creatorsの皆さんは是非,この機会にF#でゲームを作ってみてもらいたいと思います.

では,AltseedとF#ゲーム製作入門でした.