🏄‍♂️

StateGoとGoでビジュアルプログラミング🏄🏄‍♀️🍤🌊

2021/01/18に公開

はじめに

テキストとビジュアルでプログラミングするStateGoプログラミング言語Goを追加しました。
すでに適応したのは昨年(2020年12月初め)ですが、実は詳細な記事を書かなくっちゃと構えてしまったのが原因か全然筆が進まず、年が明けてしまいました。

『これだと熱が冷めてしまう!』

というわけで、要点のみの簡単な記事で公開することにしました。

StateGoとは

StateGoはステート遷移図をつくり、そこにプログラムを埋め込み、プログラムを完成させます。
テキストだけで綴られる情報に比べ、図による情報による理解は比べ物になりません。
つい複雑化してしまうプログラミングと楽しく、そして、長く付き合うことが出来ます。
言語・フレームワークの対応を増やし、このツールの利便性をますます広げていきたいと考えています。

プログラミング言語Goとは

すでによい解説があるので、次を参照ください。
Go言語とはどんなプログラミング言語?特徴やできることを解説

Go言語への対応について

StateGoのGo言語対応は、その厳格なる表記法のため、他言語の時とは異なる対応を3つ迫られました。

1.命名則

変数名の命名則はキャメル式です。さらに、大文字始まりか小文字始まりでアクセスビリティが異なります。さらに、変数名中のアンダーバーはGoの文法チェッカーで警告され続け、使用するのが憚れました。
StateGoでは、ステートを"S_HOGE"と言う具合に『大文字+アンダーバー+文字列』で管理しています。
これまで適用してきたプログラミング言語では、そのまま利用することができました。
ですが、Go言語特有の命名則のため、StateGoに新しい機能の追加が必要でした。

キャメル変換機能追加

ステート名変換部分に新しい機能を追加しました。
[[state>>lc]] stateの値をローワーキャメル
[[state>>uc]] stateの値をアッパーキャメル

例 stateの内容を S_HogeHegeとしたとき

[[state>>lc]] ⇒ sHogeHege
[[state>>uc]] ⇒ SHogeHege

マクロにも同様な機能を追加しました。
$lc:文字列$ 文字列をローワーキャメル変換
$uc:文字列$ 文字列をアッパーキャメル変換

2.異なるステート管理の実装

汎用とebiten用の2つの実装例を用意しました。

汎用サンプル

汎用サンプルは、StateGo Go言語サンプルgeneralフォルダにあります。
ステートマシンを一つだけ定義し、それを実行します。
各ステートにint値のインクリメントIDを振りあてて、各ステートは関数として定義して、その関数の配列を先に定義したIDでアクセスするようにしました。
文字列による管理やマップ等の利用も考えましたが、パフォーマンスを取ることしました。

本サンプルでは、main関数より testControl関数を呼ぶだけで実行されます。

一つだけの実装であれば、他の言語とさほど変わることはありませんでしたが、問題は複数のステートを管理する場合でした。

ebitenサンプル

ebitenはGoで作られた2Dゲーム用フレームワークです。
ebitenのサンプル(sample1)は、StateGo Go言語サンプルebitenフォルダにあります。

汎用サンプルが一つのStateGoを扱うのに対しebitenのサンプルは複数のStateGoを扱います。
複数のStateGoを扱い方ですが、通常はstructかinterfaceで共通の管理クラス&ファイルを作成してインポートするのが定石です。

共通管理クラスは扱いが面倒です。

ツール側で用意した管理クラスのファイルを出力するのか/しないのかをユーザが尋ねることになり、さらには、Go特有のインポートパス名の厳格な指定が負担です。
また、開発当初のStateGoには共通管理クラスが必須だったのですが、管理の負担となったため削除した経緯がありました。

共通の管理クラスを持たずにどうするか・・・Go言語のためにとった方法は関数を返す関数の作成でした。

通常の手法では、利用者の利便性を考慮して、変換ファイルの内部に次の3つ関数が用意されます。

Start()\[開始\]
Update()\[更新\]
IsEnd()\[終了確認\]

今回は関数を返す機能を使い、それをユーザに利用してもらいます。
上記の3つを返すと冗長になるため、1つの関数にしました。
次の関数です。

Update(bFirst bool) bool \[更新\]

引数で開始を知らせて、終了時にはTrueを返してもらうものとしました。
つまり、

従来 Go用
Start() Update(true)
Update() Update(false)
IsEnd() if (Update(false)==true)
ということです。

サンプルのTestControlは次のように定義されています。

func TestControl(d *TestControlData) func(bool, *Game) bool {
   :
   return func(bFirst bool, ig Game) bool {
   :
   }
}

利用する側は、TestControl関数から更新関数を得て、その更新関数がtrueを返すまで呼び続ければいいわけです。
初期値に戻る場合には、更新関数のbFirstにtrueを設定すればよいわけです。

3.サブディレクトリ問題

これは、私が使ったGoのバージョンがたまたまそうだったのかもしれませんが、ローカルサブディレクトリを使う際は、Githubへの登録が必須でした。
サンプル利用程度でそのようなことは避けたいものです。
そのため、基本サンプルではサブディレクトリを使わないことにしました。

最後に

Goは、プログラムが読みやすく、環境構築も簡単で、ディープなファンに支えられたコミュニティもあり(リサーチ中もそのコミュニティに助けられました)と大変魅力的な言語かつエコシステムを持つ環境セットでもあります。

そのGo言語への適用は、StateGoの新変換仕様追加や、さらに出力ソース内へのStateGoによる変換が5ヵ所も必要とするこれまでにない最高の難易度でした。Goが持つ読みやすいソースコード追及の執念を感じさせられました(笑)。

本記事が楽しいプログラミングライフの一助になれば幸いです。

補足1:リンク一覧

Go https://golang.org/
ebiten https://ebiten.org/
StateGo https://statego.programanic.com
StateGo Go Sample https://github.com/NNNIC/psgg-go-sample

補足2:サンプルの実行画面


補足3:掲載イラストについて

イラストはTAJIBON氏によって描かれており、その中に使われるGopherはRenée French氏のデザインを元にしたものです。
The Gopher character is based on the Go mascot designed by Renée French.

Discussion