OpenSiv3Dのスクリプト機能をはじめよう

はじめに

OpenSiv3D ver.0.3.0で新しく追加されたスクリプト機能。 長時間のコンパイル時間に悩まされることなくOpenSiv3Dでのプログラミングを楽しめるこの新機能、さっそく試してみたので簡単に使い方をまとめてみたいと思います。

f:id:movementi:20181005173355j:plain
今回のプログラムの実行結果

環境

  • OpenSiv3D ver.0.3.0
  • Windows10

最初にやること

OpenSiv3Dをインストールしてプロジェクトを作成する

OpenSiv3Dリファレンス

公式のリファレンス通りに進めればOK。

スクリプトエディタを用意する

自分はVisualStudioCodeを使用しています。asファイルを開くだけで「AngelScriptだな拡張機能いれるか?」って聞いてくれました。

スクリプトファイルを用意する。

exanpleフォルダやResource.rcと同じところにscript.asというフォルダを作成します。

フォルダ名は自分の好きなものでOK。拡張子のasはOpenSiv3Dのスクリプト機能で使われているAngelScriptのスクリプトファイル用拡張子です。

スクリプトにMain関数を書く場合

スクリプトにMain関数(void Main())を書く場合C++側は非常に簡潔に書くことができます。

//Main.cpp
# include <Siv3D.hpp> // OpenSiv3D v0.3.0

void Main()
{
    ManagedScript main(U"script.as");
    
    while (System::Update())
    {
        main.run();
    }
}
//script.as
void Main()
{
    //const Texture e1(Emoji(U"🌙"), TextureDesc::Mipped); //NG
    const Texture e1(Emoji("🌙"), TextureDesc::Mipped);
    const Texture e2(Emoji("🚢"), TextureDesc::Mipped);
    Vec2 pos(320, 440);

    //Print << true + 20; //NG cppファイルならOK(21が出力)

    while (System::Update())
    {
        const double speed = System::DeltaTime() * 320.0;
        Vec2 v(0, 0);

        //NG bool値をint(0or1)に変換してくれない。cppファイルならOK
        //v += Vec2(KeyRight.pressed() - KeyLeft.pressed(),
        // KeyDown.pressed() - KeyUp.pressed())
        // .setLength(System::DeltaTime() * 120.0);

        if (KeyUp.pressed())
        {
            v.y -= 1;
        }

        if (KeyDown.pressed())
        {
            v.y += 1;
        }

        if (KeyLeft.pressed())
        {
            v.x -= 1;
        }

        if (KeyRight.pressed())
        {
            v.x += 1;
        }

        v.setLength(speed);

        pos += v;

        Rect(0,350,Window::Width(),Window::Height()).draw(Palette::Darkblue);
 
        e1.drawAt(520, 120);
        e2.drawAt(pos);

    }
}

コピペするだけで動きます。

解説

  • ManagedScript main(U"script.as");
    ManagedScriptクラス。コンストラクタの引数にはスクリプトファイルのパスを入れます。

  • main.run();
    メインループでrunを呼ぶだけ。

  • const Texture e1(Emoji("🌙"), TextureDesc::Mipped);
    OpenSiv3Dで文字列を扱うときはUプレフィックスを使ってU"文字列"やU"🌙"としますがスクリプトファイルに記述するときはプレフィックスを使わずに"文字列"、"🌙"と記述します。

また、OpenSiv3DではCの仕様としてfalseは0、trueはそれ以外(基本的に1)とうまいこと相互に変換してくれます。

しかしAngelScriptはC#なんかと同じでintはintでありboolはboolと完全に区別されます。

if文やwhile文の条件はbool型にしないといけず、Print << true + 20;みたいなboolとintの足し算はエラーが出るので注意。(OpenSiv3Dのcppファイルに書けば21が出ます)

スクリプトファイルを書き換えて保存するだけであら不思議、プログラムがその場で変化するぞ!

スクリプトにMain関数を書かない場合

メインループの処理はOpenSiv3D本体及びC++に任せあくまでスクリプトは補助役として振る舞います。

//Main.cpp
# include <Siv3D.hpp> // OpenSiv3D v0.3.0

void Main()
{
    const Texture e1(Emoji(U"🌙"), TextureDesc::Mipped);
    const Texture e2(Emoji(U"🚢"), TextureDesc::Mipped);

    Script sc(U"script.as");
    auto func = sc.getFunction<Vec2()>(U"move");

    auto func2 = sc.getFunction<void(int32)>(U"printTest");
    func2(3);
    
    Vec2 pos(320, 440);

    while (System::Update())
    {       
        if (KeyR.down()) {
            sc.reload();
            func = sc.getFunction<Vec2()>(U"move");
            
            Print << U"Reload";
        }

        pos += func();

        Rect(0, 350, Window::Width(), Window::Height()).draw(Palette::Darkblue);

        e1.drawAt(520, 120);
        e2.drawAt(pos);
    }
}
//script.as
Vec2 move(){

    const double speed = System::DeltaTime() * 320.0;

    Vec2 v(0, 0);
    if (KeyUp.pressed())
        {
            v.y -= 1;
        }

    if (KeyDown.pressed())
        {
            v.y += 1;
        }

    if (KeyLeft.pressed())
        {
            v.x -= 1;
        }

    if (KeyRight.pressed())
        {
            v.x += 1;
        }

    return v.setLength(speed);
}

void printTest(int32 i){
    Print <<i;
}

こちらもコピペで動きます。

プログラムの内容はMain関数版と同様です。

解説

  • Script sc(U"script.as");
    Scriptクラス。コンストラクタの引数にはスクリプトファイルのパスを入れます。

  • auto func = sc.getFunction<Vec2()>(U"move");
    getFunctionはスクリプト内の関数を呼びます。
    スクリプト内にある返り値がVec2型の"move"という名前の関数をfuncから関数オブジェクト感覚で呼べる様になる…で説明あってるハズ(このへんまだよくわかってない)

  • auto func2 = sc.getFunction<void(int32)>(U"printTest");
    <void(int32)>はint32型の引数を持ち返り値はvoidを意味します。C++のルールですね。
    スクリプトへの参照渡しはこちらを参考にしてください。
    OpenSiv3D スクリプト機能で遊んでみる

  • sc.reload();
    こっちは自動リロードがない(はず)なので適宜呼びましょう。
    func = sc.getFunction<Vec2()>(U"move");インスタンスの更新も忘れずに。

さいごに

旧Siv3D時代から話は出ていたが結局出なかったスクリプト機能。

ついにOpenSiv3D ver.0.3.0で実装されましたね。荒削りに感じるところも多く、最初はかなり詰まったけれども慣れるとすごい楽しいぞ!!!

上のプログラムなら三日月の部分を別の絵文字にしてみたり、船の速度を変えてみたりしてみてください。

この記事がスクリプト機能を使ってみる手助けになれば幸いです。