[TyranoScript]テキスト表示を高速化する


※この記事はティラノスクリプトV2.74についての記事です。
※ティラノスクリプトがどういうものかについてはティラノスクリプトの公式サイトを参照してください。

メンバーから、
「ティラノスクリプトで[nowait]タグ使っても、テキストの表示が1文字ずつで遅い。次のクリック待ちまで一気に表示できるようにしたい。ティラノスクリプトもオープンソース化されたしちょっと見てみてよ」
と言うようなことを言われたので、ためしに解析、改造アンドメモを残してみることにしました。

とりあえずこの辺?ということで/tyrano/plugins/kag/kag.tag.jsを見てみます。コードは圧縮されてるのでOnline JavaScript beautifier等を使って読める形にします。
そして、今回問題となるのはこの辺。テキスト部分の表示を行っている関数tyrano.plugin.kag.tag.text.showMessage()。
特に268行目以降(pcharの定義と実行)。

    showMessage: function (message_str) {
        var that = this;
        that.kag.ftag.hideNextImg();
        (function (jtext) {
            if (jtext.html() == "") jtext.append("<p class=''></p>");
            var _message_str = message_str;
            var current_str = "";
            if (jtext.find("p").find(".current_span").length !=
                0) current_str = jtext.find("p").find(".current_span").html();
            var index = 0;
            that.kag.checkMessage(jtext);
            var j_span = that.kag.getMessageCurrentSpan();
            j_span.css("color", that.kag.stat.font.color).css("font-weight", that.kag.stat.font.bold).css("font-size", that.kag.stat.font.size + "px").css("font-family", that.kag.stat.font.face);
            var pchar = function (pchar) {
                var c = _message_str.substring(index, ++index);
                if (that.kag.stat.ruby_str != "") {
                    c = "<ruby><rb>" + c + "</rb><rt>" + that.kag.stat.ruby_str + "</rt></ruby>";
                    that.kag.stat.ruby_str =
                        ""
                }
                current_str += c;
                that.kag.appendMessage(jtext, current_str);
                if (index <= _message_str.length) {
                    that.kag.stat.is_adding_text = true;
                    if (that.kag.stat.is_click_text == true || that.kag.stat.is_skip == true || that.kag.stat.is_nowait == true) setTimeout(function () {
                        pchar(pchar)
                    }, 0);
                    else setTimeout(function () {
                        pchar(pchar)
                    }, that.kag.stat.ch_speed)
                } else {
                    that.kag.stat.is_adding_text = false;
                    that.kag.stat.is_click_text = false;
                    if (that.kag.stat.is_stop != "true") that.kag.ftag.nextOrder();
                    else; if (that.kag.stat.flag_glyph == "false") {
                        $(".img_next").remove();
                        jtext.find("p").append("<img class='img_next' src='./tyrano/images/kag/nextpage.gif' />")
                    } else $("#glyph_image").show()
                }
            };
            pchar(pchar)
        })(this.kag.getMessageInnerLayer())
    },

pcharで何をやっているかと言うと、以下のような感じです。

  1. 前にがあったらルビのテキストを追加
  2. 1文字分画面表示(ルビがあったらルビもあわせて表示)
  3. 全テキスト表示終わってなかったらpchar(pchar)で次の文字を処理
  4. 全テキスト表示終わってたらグリフ表示して終了

最初はテキストの画面表示処理(that.kag.appendMessage(jtext, current_str))の実行タイミングを最後のグリフ表示前に持ってくればOKかな、と考えていましたが、次の文が長いと表示されるまで時間がかかります。表示以外の1文字ずつ処理している所も手を入れないと意味が無い。と言うわけでどうするかと言うと、下のハイライト部分のようなコードを追加することに。

    showMessage: function (message_str) {
        var that = this;
        that.kag.ftag.hideNextImg();
        (function (jtext) {
            if (jtext.html() == "") jtext.append("<p class=''></p>");
            var _message_str = message_str;
            var current_str = "";
            if (jtext.find("p").find(".current_span").length !=
                0) current_str = jtext.find("p").find(".current_span").html();
            var index = 0;
            that.kag.checkMessage(jtext);
            var j_span = that.kag.getMessageCurrentSpan();
            j_span.css("color", that.kag.stat.font.color).css("font-weight", that.kag.stat.font.bold).css("font-size", that.kag.stat.font.size + "px").css("font-family", that.kag.stat.font.face);
            // nowaitが効いてる時は1文字ずつ表示ではなくテキスト全てを一気に表示させる
            // 最終的にはnowaitとは別タグ用意して切り替えできるようにするつもり
            // 後、縦書きもいずれ対応する、つもり
            if(that.kag.stat.is_nowait == true){
                var str = _message_str;
                if (that.kag.stat.ruby_str != "") {
                    str = "<ruby><rb>" + _message_str.substring(0, 1) + "</rb><rt>" + that.kag.stat.ruby_str + "</rt></ruby>" + _message_str.substring(1, _message_str.length);
                    that.kag.stat.ruby_str = ""
                }
                current_str += str;
                that.kag.appendMessage(jtext, current_str);
                that.kag.stat.is_adding_text = false;
                that.kag.stat.is_click_text = false;
                if (that.kag.stat.is_stop != "true"){
                	that.kag.ftag.nextOrder();
                } else {
                }
                if (that.kag.stat.flag_glyph == "false") {
                    $(".img_next").remove();
                    jtext.find("p").append("<img class='img_next' src='./tyrano/images/kag/nextpage.gif' />")
                } else {
                    $("#glyph_image").show()
                }
                return;
            }
            var pchar = function (pchar) {
                var c = _message_str.substring(index, ++index);
                if (that.kag.stat.ruby_str != "") {
                    c = "<ruby><rb>" + c + "</rb><rt>" + that.kag.stat.ruby_str + "</rt></ruby>";
                    that.kag.stat.ruby_str =
                        ""
                }
                current_str += c;
                that.kag.appendMessage(jtext, current_str);
                if (index <= _message_str.length) {
                    that.kag.stat.is_adding_text = true;
                    if (that.kag.stat.is_click_text == true || that.kag.stat.is_skip == true || that.kag.stat.is_nowait == true) setTimeout(function () {
                        pchar(pchar)
                    }, 0);
                    else setTimeout(function () {
                        pchar(pchar)
                    }, that.kag.stat.ch_speed)
                } else {
                    that.kag.stat.is_adding_text = false;
                    that.kag.stat.is_click_text = false;
                    if (that.kag.stat.is_stop != "true") that.kag.ftag.nextOrder();
                    else; if (that.kag.stat.flag_glyph == "false") {
                        $(".img_next").remove();
                        jtext.find("p").append("<img class='img_next' src='./tyrano/images/kag/nextpage.gif' />")
                    } else $("#glyph_image").show()
                }
            };
            pchar(pchar)
        })(this.kag.getMessageInnerLayer())
    },

ルビの処理については、今のティラノスクリプトのつくりからすると、テキストの先頭だけチェックすればよいはず(ルビとルビの間のテキストを表示する処理なので、途中の文字でルビを振るような状態にはならない)。
そう考えると1文字ずつ処理する必要もなくなり、期待したとおりに高速表示ができるようになりました。

これでやりたいことはできましたが、エンジン本体に手を入れてよしとするのはどうかな?と言う思いもあるので、次はこれをプラグイン化してみようと思います。