DAZ Scriptにおける、イベント処理の実装方法

2019年1月18日DAZ ScriptDAZ Script, DAZ Studio, TypeScript

DAZ ScriptはJavaScriptと同じですので、JavaScriptにあるイベント処理方法がそのまま使えます。

こちらの記事「ダイアログボックスのボタンをクリックするたびに表示画像を切り替える」でも記載したように、connect関数を使ってイベントとイベント発生時に実行する関数とを紐付けることが出来ます。

この紐付ける関数には、

  • クロージャ
  • オブジェクトのメソッド

が使えます。

このとき、紐付ける関数のことをコールバック関数と呼び、イベント発生時に呼び出すことをコールバックと呼びます。

今回は、上記2つのタイプの関数をコールバック関数の指定する方法を説明します。
またTypeScriptのアロー関数を使うと、クラスを模してインスタンスのメソッドを紐付けることも出来るので、より柔軟性が増すと思います。その方法も解説します。

コールバック関数とは

コールバック関数とは、あるイベントが発生したときに、OSに対して予め呼び出すように指定した関数のことを言います。
DAZ Scriptの例でいうと、ボタンをクリックしたときに呼び出される関数のことをコールバック関数と呼びます。

以前の記事、「DAZ Scriptでウィンドウを表示する」がわかりやすいのですが、スクリプト実行中において、connect関数のところでボタンをクリックしたときに呼び出す関数をOSに指定しています。
そして、スクリプトのメインの処理はそのまま最後まで走り、終了します。

いったんスクリプトは終了していますが、その後ユーザーがボタンをクリックしたとき、connect関数で予め指定しておいた関数をOSが呼び出し、その関数の処理が実行されます。

これが、コールバック関数と一連の処理の流れです。

先の記事でも使用している下記の図では、doit関数がコールバック関数に相当します。

パターン1.クロージャをコールバック関数にする

こちらの記事「ダイアログボックスのボタンをクリックするたびに表示画像を切り替える」で紹介した方法ですね。

DAZ Scriptで例を示してみます。

function SwitchTimer1(in_x, in_y){
    var x = in_x;
    var y = in_y;
    function inner(){
        print( x + y );
    }
    return inner;  //上記で宣言したinner関数を返す。
}
(function(){
	var timer1 = SwitchTimer1(1,2);  //inner関数を取得
    var oTimer1 = new DzTimer();
    oTimer1.singleShot = true;
    connect( oTimer1, "timeout()", timer1);  //タイマーのイベントとinner関数を紐づけ
    oTimer1.start( 1000 );  //1秒後に"timeout"イベントを発火する
    sleep(3000);  //これを入れておかないと、"print( x + y )"が実行される前に、
                  //スクリプトが終了してしまう。
})();

実行結果は次のように「3」と出力されます。

DzTimerオブジェクトは、DAZ Scriptの標準ライブラリの機能です。
この例では、1000ミリ秒後に1度だけ「timeout」イベントを発生するようにしています。これを発火と呼んだりします。
スクリプト実行中、oTimer1.start(1000);の行で1秒後に「timeout」イベントが発火するように仕込みます。この行が実行されてから1秒後、「timeout」イベントが発火します。
すると、事前にconnect関数で紐づけていた関数(inner関数)が呼び出され、実行され、コンソール上に「3」と表示されます。

inner関数はtimeoutイベントのコールバック関数として実行されたわけです。

パターン2.オブジェクトのメソッドをコールバック関数にする

次に示す方法は、新しいオブジェクトを定義し、そこに定義したメソッドをコールバック関数として指定する方法です。
何だかややこしいですが、要はパターン1とは違う方法でもコールバック関数を指定できるということです。

クロージャが用意できるかどうかはケースバイケースですので、設計の都合上クロージャが作れないときはこの方法になります。

なお、コード中に記載のthisとは何か?については、こちら「DAZ Scriptにおけるthisの挙動」で解説しています。

var SwitchTimer1 = {};
SwitchTimer1.x = 1;
SwitchTimer1.y = 2;
SwitchTimer1.getInner = function (){
    var _this = this;
    return function (){
        print( _this.x + _this.y);
    }
};
(function(){
    var oTimer1 = new DzTimer();
    oTimer1.singleShot = true;
    connect( oTimer1, "timeout()", SwitchTimer1.getInner());
        //"print( _this.x + _this.y);"する関数(名前無し)を取得して紐付ける。
    oTimer1.start( 1000 );
    sleep(3000);
})();

実行結果は次のように、パターン1と同じく「3」と出力されます。

この方法では、先にオブジェクトを定義し、その中に含まれるメソッドをコールバック関数として紐づけています。JavaScriptエンジンから見ると、パターン1と同じに見えているのですが、コードを書く設計者側からすると「考え方」の観点でパターン1とパターン2には違いがあります。

パターン1の場合はオブジェクトという概念は関係なく、ただの処理のまとまりを渡しているだけと捉えてください。

パターン2の場合は、繰り返しますが先にオブジェクトを定義し、その中に含まれるメソッドを渡しています。

またこれは、次のように書いても同じです。

var SwitchTimer1 = {
    x : 1,
    y : 2,
    getInner : function (){
        var _this = this;
        return function (){
            print( _this.x + _this.y);
        }
    }
};
(function(){
    var oTimer1 = new DzTimer();
    oTimer1.singleShot = true;
    connect( oTimer1, "timeout()", SwitchTimer1.getInner());
        //"print( _this.x + _this.y);"する関数(名前無し)を取得して紐付ける。
    oTimer1.start( 1000 );
    sleep(3000);
})();

実行結果は先程と同等「3」と出力されます。

パターン3.TypeScriptを用いて、インスタンスのメソッドをコールバック関数にする

こちらの記事「DAZ Script開発環境の構築方法」で説明しているTypeScriptによるDAZ Scriptの開発環境が必要となりますが、パターン2はTypeScriptのアロー関数を用いることで、簡潔に実装することが出来ます。
特にオブジェクト指向に慣れている方は、インスタンスのメソッドをコールバック関数に指定できる方が、直感的にわかりやすいと思いますので、この方法がおすすめです。

ただ、TypeScriptから変換したDAZ Scriptはかなり複雑なものになりますので、後から編集したいときは、Typescript経由になってしまうのを覚悟しておく必要があります。

今回は、インスタンスが別れていることを明示するため、DzTimerオブジェクトを2つ用意して、それぞれ別々のタイミングで「timeout」イベントが発生するようにしてみます。

TypeScriptで次のように書きます。

class SwitchTimer {
    x: number;
    y: number;
    constructor(in_x: number, in_y: number) {
        this.x = in_x;
        this.y = in_y;
    }
    //アロー関数を用いたオブジェクトのメソッド。
    //timeoutイベント時に実行するコールバック関数にする
    inner = (): void => {
        console.log(this.x + this.y);
    }
}
(function () {
    let timer1 = new SwitchTimer(1, 2);
    let timer2 = new SwitchTimer(3, 4);
    var oTimer1 = new DzTimer();
    var oTimer2 = new DzTimer();
    oTimer1.singleShot = true;
    oTimer2.singleShot = true;
    //timeoutイベント時に実行するコールバック関数を設定
    connect(oTimer1, "timeout()", timer1.inner);
    connect(oTimer2, "timeout()", timer2.inner);
    oTimer1.start(1000); //1秒後に3を表示
    oTimer2.start(3000); //3秒後に7を表示
    sleep(5000);
})();

これがJavaScript(DAZ Script)に変換されると、次のようになります。

var SwitchTimer = /** @class */ (function () {
    function SwitchTimer(in_x, in_y) {
        var _this = this;
        //アロー関数を用いたオブジェクトのメソッド。
        //timeoutイベント時に実行するコールバック関数にする
        this.inner = function () {
            print(_this.x + _this.y);
        };
        this.x = in_x;
        this.y = in_y;
    }
    return SwitchTimer;
}());
(function () {
    var timer1 = new SwitchTimer(1, 2);
    var timer2 = new SwitchTimer(3, 4);
    var oTimer1 = new DzTimer();
    var oTimer2 = new DzTimer();
    oTimer1.singleShot = true;
    oTimer2.singleShot = true;
    //timeoutイベント時に実行するコールバック関数を設定
    connect(oTimer1, "timeout()", timer1.inner);
    connect(oTimer2, "timeout()", timer2.inner);
    oTimer1.start(1000); //1秒後に3を表示
    oTimer2.start(3000); //3秒後に7を表示
    sleep(5000);
})();

実行結果は次のようになります。

JavaScript上でインスタンスを模すために、コンストラクタなどややこしい機能を使っていますが、いざコールバックされたときの機構はパターン2と何ら変わりません。

まとめ

DAZ Scriptでのイベント処理の実装方法について、

  • パターン1.クロージャをコールバック関数にする
  • パターン2.オブジェクトのメソッドをコールバック関数にする

の2パターンを紹介しました。

また、パターン2をTypeScriptでアロー関数を用いて実装する方法も紹介しました。
この方法については、次の記事「TypeScriptのアロー関数を用いて、DAZ Scriptのクリック処理を実装する」で詳細に解説しています。

尚、これらの機構はJavaScriptと同じですので、ネット上にあるJavaScriptについて解説した記事がそのまま参考にできます。今回登場したthisやコンストラクタについては「DAZ Scriptにおけるthisの挙動」で解説していますが、『JavaScript thisキーワード』などでググると、参考になる情報がでてきますので、そちらの方がおすすめです。

Posted by lowpolysnow