アニメーション待ちのwaタグでフリーズするケース

はじめに

ティラノスクリプト ver471 の話です。

先日、アニメーション中にフリーズすることがあるという話があり、調査しました。
その結果、アニメーション待ちをするwaタグでフリーズしていました。

ティラノスクリプトのanimタグの動作は以下のようになっています。

  • アニメーション開始で 現在アニメーションしている数が +1
  • アニメーション完了で 現在アニメーションしている数が -1

waタグは「現在アニメーションしている数が 0 になるまで待つ」というタグなのですが、アニメーション完了が呼ばれないケースがあり、現在アニメーションしている数が一生 0 にならないことがあります。

そのため、waタグの待ちがずっと続いてしまい、フリーズしてしまうという現象がおきます。

フリーズした場合、ティラノライダーで確認してもwaコマンド移行が実行されていません。
waタグでストップしている

ケース1

キャラが2人以上表示されており、一人がアニメーションしていなくて、片方がアニメーションしているときに、chara_hide_allをした場合です。
例を下に示します。

1
[chara_show  name="akane" ]
2
[chara_show  name="yamato" ]
3
[anim name=akane left="-=30" time=1000 effect=jswing]
4
[anim name=akane left="+=30" time=1000 effect=jswing]
5
[chara_hide_all time="0"]
6
[wa]

ケース2

アニメーション中にfreeimageしても同様にフリーズします。

1
[chara_show  name="akane" ]
2
[anim name=akane left="-=30" time=1000 effect=jswing]
3
[anim name=akane left="+=30" time=1000 effect=jswing]
4
[freeimage layer=0]
5
[wa]

簡単な説明

簡単に言うと、chara_hide_allfreeimageもアニメーション中の対象を削除します。削除するのでアニメーション完了が呼ばれません(chara_hide_allに関してはもう少し特殊ですが)。

現状とれる対処

chara_hide_allfreeimageの前でwaを呼び、アニメーション完了を待ってから消す。

ピクチャの色あいを変えるプラグイン

概要

前回、色相を変えるプラグインを作りましたが、今回は色合いを変えます。
色合いを変えると以下の動画のように白い服に対して着色できます。

プラグインコマンド

SetTint arg0 arg1
arg0はピクチャ番号、arg1は色の値。下記2つは同じ

  • 例: SetTint 1 0xFF0000
  • 例: SetTint 1 16711680

引数には変数の制御文字が使えます。

  • 例: SetTint \V[3] \V[4]

RGBそれぞれに値を指定する場合は以下です。
SetTint arg0 arg1 arg2 arg3
arg0はピクチャ番号、arg1、arg2、arg3にはrgbの値を指定

  • 例: SetTint 1 255 0 0

引数には変数の制御文字が使えます。

  • 例: SetTint \V[3] \V[4] \V[5] \V[6]

リセットするには0xFFFFFFを指定します。

  • 例: SetTint 1 0xFFFFFF

ダウンロード

こちら

色相を変えるプラグインと同一です。
色相を変えるプラグインはRotateHue.jsでしたが、今回統合してPictureColorChange.jsになりました。
申し訳ないのですが、RotateHue.jsは削除してこちらに差し替えてください(プラグインコマンドは同じなのでファイル差し替えのみ)。

ピクチャ合成について

ピクチャ合成系のプラグインには対応していません。ピクチャ合成もピクチャを加工するため、色あいを変えて合成すると競合してしまいます。対応するにはピクチャ合成プラグインとこちらのプラグインを両方セットで調整しなければなりません。

ライセンス

Released under the MIT license

ピクチャの色相を変えるプラグイン

概要

ピクチャの色相を変えるプラグインを作りました。
デフォルトでは色調を変えることはできますが、色相は変更できません。
色相を変えると以下の動画のように自然な感じで色違いのピクチャを作れます。

以下のような色相環をぐるっと360度回す感じになります。
白や黒、モノクロの画像は色相を変えても色は変化しませんので注意が必要です。

色相環

ちなみに色調はtone、色相はhueです。

プラグインコマンド

RotateHue arg0 arg1
arg0はピクチャ番号、arg1はデフォルトからの色相の回転角度。単位は度

  • 例: RotateHue 1 180

引数には変数の制御文字が使えます。

  • 例: RotateHue \V[3] \V[4]

角度なので0~359が有効な値ですが、-180、360、720などを入力は可能です。
デフォルトからの回転角度なのでRotateHue 1 180を2回呼んでも360にはなりません。

ダウンロード

こちら

色あいを変えるプラグインと同一です。
色相を変えるプラグインはRotateHue.jsでしたが、今回統合してPictureColorChange.jsになりました。
申し訳ないのですが、RotateHue.jsは削除してこちらに差し替えてください(プラグインコマンドは同じなのでファイル差し替えのみ)。

ピクチャ合成について

ピクチャ合成系のプラグインには対応していません。ピクチャ合成もピクチャを加工するため、色相を変えて合成すると競合してしまいます。対応するにはピクチャ合成プラグインとこちらのプラグインを両方セットで調整しなければなりません。

ライセンス

Released under the MIT license

技術的な解説

色相を変えるメソッドはデフォルトで実装はされていて、Bitmap.prototype.rotateHue がそのメソッドです。 敵キャラの色相変更で利用されています。
これを呼べば簡単にできるのですが落とし穴があり、Bitmapをそのまま書き換えます。
何が問題になるかというと、キャッシュです。画像はロードするとキャッシュされますが、色相をいじるとキャッシュした画像をそのまま書き換えてしまい、その画像はずっと色相が変わったままになってしまいます。

一つの解決策としてロード時に色相を指定することが挙げられます。
ImageManager.loadNormalBitmap = function(path, hue)とあるようにhueを指定してBitmapをロードできます。実はデフォルトでは色相つきでキャッシュされています。敵キャラの色相の変更がそうなっています。
以下の箇所でキャッシュのキーを生成しているのですが、画像のパスと色相でキーを作っています。この組み合わせのキーでキャッシュを検索して、存在すればキャッシュの画像を使用し、なければロードするということです。

1
ImageManager._generateCacheKey = function(path, hue){
2
    return  path + ':' + hue;
3
};

一見、hue付きでロードすれば解決するように見えますが、ちらつきの問題があります。
プリロードしようにも画像+色相の組み合わせをプリロードするのは無理があります。
なので今回は、色相を弄る前の画像をコピーして色相を変える方法でこれを回避しています。

スプレッドシートからjsonデータを読み込む

概要

結構前にスプレッドシートからデータを読み込むの記事でOAuth2の認証をしてスプレッドシートを読む実装をしました。
ですが、こんなことしなくてもスプレッドシート側でGETのリクエストを受け取ってデータをjsonにして返せば大体事が足ります。
今回はビルド前にスプレッドシートからデータを読み込み、保存するエディタ拡張を作ります。

スプレッドシート側の実装

スプレッドシート側は「Google Spreadsheet を簡易 Webサーバーとして動かして、手軽にWebHookを受け取る方法」の記事を参考にして実装します。
今回は翻訳データを例に、以下のようにGETの処理を実装しました。

1
function doGet(e){
2
  //本当はスプレッドシートからデータを作るけれど、今回は適当にデータを用意
3
  var test = {
4
    bullet : {Ja: "弾丸", En: "Bullets"},
5
    shot: {Ja: "ショット", En: "Shot"}
6
  }
7
  var output = ContentService.createTextOutput(JSON.stringify(test));
8
  output.setMimeType(ContentService.MimeType.JSON);
9
  return output;
10
}

Unity側でエディタ拡張として実装

以下のようにエディタ拡張として実装します。Editorフォルダを作り、GetWordJson.csとして保存します。
WWWクラスを使用して、レスポンスを受け取ります。
受け取った結果はLitJsonを使用して、クラスに変換します。
このとき、レスポンスはwww.textで受け取れるのですがこれを使用しようとすると、Unsupported encoding: 'UTF-8,text/html'とエンコーディングのエラーが出るのでwww.bytesからUTF8に変換して渡します。

1
using System.Text;
2
using UnityEditor;
3
using UnityEngine;
4
5
public class GetWordJson {
6
    public class Word {
7
      public string Ja;
8
      public string En;
9
    }
10
11
    //メニューのtest に GetWordJsonを追加
12
    [MenuItem("test/GetWordJson")]
13
    public static void Get() {
14
      WWW www = new WWW("https://script.google.com/macros/s/AKfycby-XVXs5qJl1v26Y1xxxxxxxxxxxxxxxxxxxxxxxxVx8inwe/exec");
15
      while (!www.isDone);
16
      if (www.error != null) Debug.LogError(www.error);
17
      else {
18
        var obj = LitJson.JsonMapper.ToObject<Dictionary<string, Word>>(Encoding.UTF8.GetString(www.bytes));
19
      }
20
    }
21
  }

以下のようにobjに翻訳辞書データが入っているのがわかります。

jsonを受け取り変換した結果

この結果を元にScriptableObjectにしても良いですし、そもそもjsonのまま保存して実行時にパースしても良いです。
jsonのまま保存する場合は以下のようになります。

1
var path = Application.dataPath + "/Words.json";
2
StreamWriter writer = new StreamWriter(path);
3
writer.WriteLine(Encoding.UTF8.GetString(www.bytes));
4
writer.Close();
5
AssetDatabase.Refresh();

BGSを保存・再開するスクリプト(VXAce版)

この記事はVXAce版です。MV版はこちら

概要

BGMと同様にBGSについても保存と再開を可能にするスクリプトです。

スクリプトコマンド

BGSの保存: $game_system.save_bgs
BGSの再開: $game_system.replay_bgs

ダウンロード

ライセンス

Released under the MIT license

ランダムにアイテムを取得するスクリプト(VXAce版)

概要

仕様についてはMV版の記事を参照してください

ランダムにアイテムを取得するプラグイン(MV版)

導入方法

まず、スクリプトがこれ
導入したら、スクリプトコマンドで Random_Treasure::reset を実行する。
このコマンド直後のショップ処理で登録したアイテムがランダムに手に入るアイテム群となる。ショップは開かれない。
アイテムごとの価格の設定がくじの本数となる。

アイテム群の設定
出現確率

そして、次に宝箱側の設定だ。スクリプトコマンドで Random_Treasure::get を実行するとランダムに1個手に入る。

ランダムに1個取得

入手したアイテムの表示方法

アイテム名格納変数とアイテムアイコン番号格納変数を用意した。
ここに変数番号を入れておくと、その番号の変数にアイテム名とアイコン番号が自動的に入る。

1
module RANDOMTREASURE
2
  NAME_VAR = 10   #取得したアイテム名を格納する変数番号
3
  ICON_VAR = 20   #取得したアイテムのアイコン番号を格納する変数番号
4
end

なので、これを使って、メッセージウィンドウに以下のように入力すれば入手したアイテムとアイコンが表示できる。
(変数番号は上記で指定した値)

1
\I[\V[20]] \V[10]を手に入れた

※ 入手インフォメーションスクリプトの インフォメーションfor RGSS3 Ver2.03-β に対応しています

DL先

https://raw.githubusercontent.com/kido0617/rpgmakerVXAce-plugin/master/random_treasure/random_treasure.rb

ライセンス

Released under the MIT license

ビジュアルノベルアセットFungusにコマンドを追加してカスタマイズする

この記事はUnity アセット真夏のアドベントカレンダー 2018 Summer!の2日目の記事です。

Fungusについて

Fungus はフリーのビジュアルノベルアセットで、高機能・高評価のアセットです。
簡単な概要についてはフリーのビジュアルノベルアセットのFungusが便利で紹介しています。
また、応用として、フリーのビジュアルノベルアセットFungusを使ってRPGのイベントを作る でも紹介しています。

概要

Fungusはビジュアルノベルアセットですが、RPGの会話機能に使ったり、フローチャートの機能を使ってイベントを制御したりすることができます。

Fungusのフローチャート

Fungusのコマンドは以下のようにたくさん用意されていますが、独自のコマンドを追加することにより、プロジェクトに適した使い方ができます。

Fungusのコマンド群

コマンドの追加方法

コマンドのソースはFungus/Scripts/Commandsに配置されています。
例えば、音楽を再生するPlay Musicコマンドを抜粋すると以下のようになっています。

1
[CommandInfo("Audio",
2
              "Play Music",
3
              "Plays looping game music. If any game music is already playing, it is stopped. Game music will continue playing across scene loads.")]
4
[AddComponentMenu("")]
5
public class PlayMusic : Command
6
{
7
    [SerializeField] protected AudioClip musicClip;
8
    [SerializeField] protected float atTime;
9
    [SerializeField] protected bool loop = true;
10
    [SerializeField] protected float fadeDuration = 1f;
11
12
    public override void OnEnter(){
13
        var musicManager = FungusManager.Instance.MusicManager;
14
        float startTime = Mathf.Max(0, atTime);
15
        musicManager.PlayMusic(musicClip, loop, fadeDuration, startTime);
16
        Continue();
17
    }
18
    public override string GetSummary(){
19
      if (musicClip == null){
20
        return "Error: No music clip selected";
21
      }
22
      return musicClip.name;
23
    }

このソースからわかるようにCommandクラスを継承したクラスを作成し、CommandInfoを追加することによって、コマンドとして認識されるようです。
CommandInfoの第一引数がカテゴリ、第二引数が表示名、第三引数がヘルプのテキストです。
クラスのメンバにSerializeFieldを追加した変数はInspectorから設定できるようになります。

OnEnterメソッドが実際にコマンドとして実行されるメソッドです。最後にContinue()を呼ぶことにより、次のコマンドに遷移します。

GetSummaryメソッドを実装するとコマンドリストに表示されるサマリを変更できます。Play Musicに関してはAudio Clipがあればその音楽ファイル名を表示しています。

もし、プロジェクトで独自のSoundManagerクラスなどを実装している場合、このPlay MusicPlay SEを参考に作り直すと良いです。
ちなみにFungusはデフォルトだと音楽再生などをするとFungusManagerというDontDestroyOnloadされたマネージャが生成されます。

Play Musicコマンド

if コマンドを追加する

コマンドの中でもifは特殊なコマンドです。
ここでは仮にDEBUG_HOGEがdefineされていたらifが成立するようなデバッグ用のif コマンドを作ってみます。
CommandクラスではなくConditionを継承し、bool EvaluateCondition()bool HasNeededProperties()を実装すればif コマンドが作れます。
できたのが以下です。EvaluateCondition()の返り値がtrueだとif内に入ります。
HasNeededProperties()についてはこれがfalseを返すと条件に関係なくifの中に入ってしまうので、ここでは常にtrueを返しておきます。

1
namespace Fungus {
2
  /// <summary>
3
  /// If the test expression is true, execute the following command block.
4
  /// </summary>
5
  [CommandInfo("Flow", "DebugIf", "If define DEBUG_HOGE")]
6
  [AddComponentMenu("")]
7
  public class DebugIf : Condition {
8
9
    protected override bool EvaluateCondition() {
10
#if DEBUG_HOGE
11
      return true;
12
#else
13
      return false;
14
#endif
15
    }
16
    protected override bool HasNeededProperties() {
17
      return true;
18
    }
19
  }
20
}

DEBUGIfコマンド

まとめ

Fungusはオープンソースでもあるのでソースコードが見やすく、他の人が拡張しやすいように設計されています。
そのため、今回のようにコマンドを追加したりして、自身のプロジェクトに合うようにカスタマイズするととても便利になります。
ただ、あまり本体のコードを書き換えるとバージョンアップに対応できないので注意が必要です。
私は2017年9月からVer.3.6を使用していますが、今回の検証のためVer.3.9.1をダウンロードしたところ、コマンドが増えていたりとバージョンアップは頻繁に行われているようです。

使用したバージョン

  • Unity 2018.2.0f2
  • Fungus 3.9.1

ティラノスクリプトのプラグインの作り方-タグを追加する

はじめに

ティラノスクリプトのプラグインをjsを書いて作ります。
公式のプラグインを自作して公開しようにはプラグインの作り方が記載されています。フォルダ構造などはここで学べますが、ここに記載されているのはティラノスクリプトのデフォルトのタグを使って作るようなプラグインであって、jsを書いて作るタイプのプラグインの説明はされていません(2018/08/05現在)。
そこで今回は、jsを書いて新しいタグを作るプラグインの作り方を説明します。

準備

開発環境として用意するのは最新のスタンダードパッケージとデバッグ用のティラノライダーです。ティラノライダーを使うことにより、いつものデベロッパーツールを開けるのでhtml構造を確認したり、コンソールを見ることができます。
また、ティラノスクリプトのインタープリタやデフォルトのタグ類はスタンダードパッケージ内の tyrano/plugins/kag フォルダ内にありますが、minifyされているので、Githubリポジトリからminifyされていないファイルをダウンロードしてくることをおすすめします。

jsをロードするまで

まず、公式のプラグイン作成のマニュアルどおり、data\others\pluginフォルダに今回作成するプラグインのフォルダを作ります。
今回はプラグイン名をmypluginとし、その名前でフォルダを作りました。
そして、フォルダ内にinit.ksmain.jsファイルを作ります。
init.ksファイル内で以下のようにjsファイルをロードすればプラグイン内でjsを扱うことができます。

1
@loadjs storage=plugin/myplugin/main.js
2
@return

最後にゲーム起動時に呼ばれるfirst.ksファイルで以下のプラグインのロードコマンドを追加します。
[plugin name=myplugin]

これで準備は整いました。

事前知識

ティラノスクリプトはCanvasなどは使わずにDOMで構成されています。使用されている主なライブラリはjQueryです。
tyrano/libsフォルダにライブラリはあるので確認してください。

セーブデータはTYRANO.kag.statに保存できます。保存時はJSON.stringifyされてテキストとして保存されます。
TYRANO.kag.tmpにテンポラリなデータは保存できます。

基本形

新しいタグを追加するときの基本的な形は以下のようになります。

1
(function(){
2
  var myTag = {
3
    vital : [],
4
    pm: {},
5
    start : function(pm) {
6
      console.log("テスト");
7
8
      //終わったら次のタグへ
9
      this.kag.ftag.nextOrder();
10
    }
11
  };
12
13
  TYRANO.kag.ftag.master_tag.myTag = object(myTag);
14
  TYRANO.kag.ftag.master_tag.myTag.kag = TYRANO.kag;
15
})();

TYRANO.kag.ftag.master_tagにタグが管理されているのでそこにタグを作って追加する形になります。
タグの主な処理はstartメソッド内に記述します。処理が終わったら、最後にkag.ftag.nextOrder()を呼べば次の処理に移ります。
このタグの場合、[myTag]と記述してシナリオファイルから呼ぶと、consoleにテストと表示されます。

パラメータの処理

vitalpmについて説明します。vitalは必須パラメータの指定です。
vital : ['text']とすれば、textパラメータが必須となります。必須パラメータ無しでタグを呼ぶと以下のようにエラーが出るようになります。

パラメータ不足のエラー

pmはデフォルトパラメータを指定します。例えば、pmを以下のように定義し、タグの呼び出しで[myTag layer=2]とすると、
startメソッドに{layer:2, color: "#000"}として渡されます。

1
pm: {
2
      layer: 1,
3
      color: "#000",
4
}

渡されたパラメータによってエラーを出力したいときは以下のようにkag.errorメソッドを呼びます。

1
this.kag.error("エラー!");

レイヤーについて

画面に要素を追加するにはレイヤーの構造を理解する必要があります。
レイヤーはdivであり、レイヤー番号と前面、背面を組み合わせたclassで指定したレイヤーを取得できます。
例えば、レイヤー1の前面を取得したい場合は以下のようにします。

1
var layer1Fore = $(".1_fore");

レイヤー

まとめ

プラグインでjsを書いて新しいタグを作る方法を簡単にですが説明しました。
デフォルトのタグはtyrano/plugins/kagにあるのでこれを参考にすると新しいタグも作りやすいと思います。

使用バージョン

ティラノスクリプト Ver.450

Skinned Mesh Renderer の Boundsについて

カメラの端にいくと、SkinnedMeshRendererのオブジェクトの描画がされないということがあった。
以下の動画のような感じ。

これはどういうことかというと、SkinneMeshRendererのBoundsが正しく設定されていないためだった。
このオブジェクトのBoundsは以下のようになっている。白い線で描画されているボックスがBoundsである。

Bounds

SkinneMeshRendererはBoundsがカメラに入っていない場合、パフォーマンスのため描画しない。
そのため、メッシュとBoundsのサイズに違いがあると動画のような現象が起こる。

詳しくは以下の「オフスクリーン時の更新」に書かれている。

スキンメッシュレンダラー

自分専用のFirefoxアドオンを作る

概要

Firefox Add-ons - MozillaでWeb公開せず、自分だけ使えればいいアドオンを作りたくなったので作りました。
そのときの手順をメモします。

アドオンそのものについては初めての拡張機能を参照して作ります。
デバッグ時は一時的にアドオンを有効化できますが、再起動すると使えなくなってしまうのでインストールして永続的に利用可能にする必要があります。
今回は作ったあとのパッケージ化からインストールまでを説明します。

パッケージ化・署名

作ったあと作成したファイルをzip化します。
その際、ディレクトリごとではなく、ファイルを直接zip化する必要があります (拡張機能をパッケージ化する)
zip化したフォルダの拡張子をzipからxpiに単純に変更します。

アドオンは署名をする必要があります (アドオンを署名して配布する)。
FirefoxのDeveloper Edition、Nightly(プレリリース版)は署名なくてもインストール可能にできるそうですが、通常版を利用しているので署名が必要です。

https://addons.mozilla.org (AMO)で開発者登録をします (アドオンを投稿する)。その後は作成した.xpiファイルをアップロードすれば良いです。
その際、Webに公開しないオプションを選択しましょう。審査はないのですぐに完了します。

Webに公開しない

インストール

署名が完了すると、署名完了後のファイルをダウンロードできるのでそれをダウンロードします。Firefoxのアドオンウィンドウを開いて、ファイルからアドオンをインストールからダウンロードしたファイルを選択すればインストールできます。

![ファイルからインストール]](/img/2018-07-22-firefox-ext/install.png)