2016年11月28日

Unity+MoonSharpで開発を進めている

Unity+MoonSharpで開発を進めている。

現在、次のようなLuaスクリプトが動かせるものが出来ている。


function main()
  表示文です。
  [名前タグ]はろーわーるど。
  >english print text.
  -- 下の二つは同じ意味
  [tag]text
  >[tag]text
  log("普通のLuaの記述")
  local x=0
  for i=1,10 do
    foo(i)
  end
end

NScripter系と同じ仕様の表示文を


___p("名前タグ","はろーわーるど")
というように行を一対一対応で置換して実行する。いわゆる外部DSLというやつだが、こうすることでLuaの文法に最小限の拡張をしてゲームのシナリオを記述できるようになった。Luaのエラーメッセージも行番号にズレがないのでちゃんと正しい場所を返してくれる。

ここからしばらく、勘違いで必要のなかった作業についての記述なので折りたたんでおきます。ただ、ビルド前処理の練習にはなりました。

問題はこのファイルの拡張子を.luaのままにしておきたいということだ。.txtにしたり.bytesにしたりすると、テキストエディタのシンタックスハイライト機能が効かなくなるからだ。
しかしUnityは元々登録されている拡張子のリソース以外は読み込んでくれない。StreamingAssetsにしておけば生ファイルを扱えるが、スクリプトファイルがそのままコピーされてしまうため、さすがにゲームのコードをダブルクリックでそのまま読めてしまうのは具合が悪い。そこで次の方法でとりあえずの解決を見た。

  • Assets外のフォルダにluaファイルを置いて、そこで編集し、「再生ボタンを押した時」もしくは「ビルド前」にAssets/Resourcesにテキストアセットの拡張子に変換したものをファイルコピーし、インポートさせる。コードからはこのテキストアセットのほうを読み込む。[PostProcessScene]を使って実装した。

(疑問点)再生ボタンを押すごとにluaスクリプトリソースがフル更新されるんだけどこういう設計でいいのか? また、このタイミングでリソース更新をかけた場合確実にリソースが更新されることは保証されているのか?
(考察)元々再生時にシーンまるごとメモリに読み込んでバックアップしてるようだし、これくらいはどうってことなさそうな気もする。リソースの更新についてはどうなんだろう。各アトリビュートのついた関数の実行タイミングが、リソースを追加していいタイミングなのかどうかってどこかで分かるんだろうか? とりあえず今のところ大丈夫のようだが。

ひとまず何か一本作ってみないと分からないかもしれない。この後簡単なギャルゲADV風のサンプルシステムを組んだら、何かゲームを一本作る。

(注釈)その後、たとえばAtomでは「Select Grammer」で、テキストファイルを開いたときでも文法をLuaで認識させることが出来るのが判明しました。これならもうテキストアセットでいいや……ただ、ビルド前やエディタ実行前に何かやる、という処理を書くのに役には立つのでいい経験になりました。

セーブロードの仕様について検討

セーブロードの機構をどうするかも悩み所。Luaの実行状態はセーブ出来ないからだ。ギャルゲADV以外では「シーンの冒頭まで巻き戻る」か、いっそ「宿屋やロビー画面でしかセーブ/ロードはさせない」で解決する(むしろそうしない場合、撤退できないイベントや場所で絶対に敵に勝てないレベルでセーブされると詰む、みたいな状況が頻発するので慎重な作業が必要になる。あと、特に3Dとかアクションゲームとかの場合、ゲームの実行状態を全部完全に保存するのが現実的でないこともしばしばあるので)。 ただ、ギャルゲ系の読み物ADVはどうしても、読んだところでちゃんとセーブして次はそこから始まって欲しいという需要がある。シーン単位だと、そのシーンが長大な時にかなり巻き戻ることになるからだ。これを実装するのは生のLuaスクリプトでは無理で、別のスクリプト言語が必要になると思われる。NScr2で言えばBASICだし、吉里吉里で言えばKAGだろう。この部分の文法をどうするか。うーん、やはりギャルゲADV界隈はどうしてもどこかで俺俺言語のくびきを逃れられないのだねえ。

今回のテーマは疎結合

正直NScr2のBASICはリッチすぎる。RPG風戦闘シーンとかミニパズルゲームまでこのBASICで組んじゃってるわけだが、そういうのは本来LuaやC++(Unityの場合ならC#)でやるべきだ。スプライトシステムがBASICの記述に最適化されているためBASICでやれることはBASICでやってるのが現状だが、今作ってるUnityのスクリプト環境のほうでは、「疎結合」をテーマにしたい(名前もLooseScriptにしようかなとか思ってる)。UnityはVisualC++でDirectXで組んでるときとは違い、アセットもコンポーネントもUIエディタも充実していて、こちらで組んだ方がLuaや自前言語で組むよりむしろ楽だし、JITの効くC#のほうが絶対に実行が速い。なので、そういう複雑なゲーム的な部分はUnity側で組んで、Luaやシナリオスクリプトはそれを大まかに制御するだけにしたいのだ。だから、シナリオ部分をスクリプト言語にするとしたら、出来るだけシンプルなタグ付けだけのものにしたい。その上で、演算したい場合はLuaコードをHTMLで言うscriptタグみたいな感じで呼べるようにするかな。

今回作ってるエンジンの方向性について。

一年以上グダグダ作っては作り直してますが、そろそろこれは形にする。相当ブラッシュアップされてきたので。
ひとまず、サンプルのADV環境は提供するけど、カスタマイズは生Unity環境でやるようなものになると思う。UnityのGUIエディタやC#スクリプトは使ってもらうし、ビルドプロセスはUnityのものになるので。「宴」ほどには統合環境的ではなく、あくまでプラグイン的な立ち位置。初心者には難しいかも知れない。その分生Unityに近く、疎結合で柔軟なため、SpineでもLive2Dでもなんでも、よそのコンポーネントは使い放題になると思う。
まあ、まだ出来てないようなものの青写真はともかくとして、うちの業界はクロスプラットフォーム開発環境への以降をぼちぼち考えはじめてる人も増えてるはずで、その流れに乗りたいです。
配布形態はオープンソースにして導入作業やサポートを引き受けた時にお金取るか、あるいはアセットとして売るか……お金は欲しい、のだが、今世の中見てると、もうエンジンそのものを売って商売するのは難しくなってきてる。だから組んで仕事出来る人が現れそうなら前者でいいかもしんない。名前も売りたいし求職活動にもなりそうならそのほうがいいかもね。シンプルな読み物ADV作る以外の用途では、プログラマはどうせ必要なんだから、そっちの実作業でお金取ればいい気もするのだ。

posted by NTak_Indies at 19:33| Comment(0) | TrackBack(0) | 日記

2016年11月26日

Unity C#とMoonSharpの実験

割と簡単なコードで、MoonSharpとC#とのコルーチンで処理をピンポンさせつつ合間にちゃんとUnityの他の処理も実行させることが出来ました。これで、ゲームのシナリオ進行やイベント演出等をMoonSharpで直列で書きつつ具体的な処理についてはUnityでまったく制約なく書くことが出来そうです。 MoonSharpはC#で書かれたLuaライクなスクリプト言語で、一部の機能を除いてLuaととてもよく似ています。APIもC#の機能を活用しつつLuaのAPIから類推出来る部分も多いので、Luaを使ったことがあるならすぐに使えるようになると思います。


    using UnityEngine;
    using System.Collections;
    using MoonSharp.Interpreter;

    public class NewBehaviourScript : MonoBehaviour {
      // Use this for initialization
      void Start () {
        StartCoroutine(CSharpFunc());
      }

      IEnumerator CSharpFunc()
      {
        string src = @"
        function wait(i)
          coroutine.yield(i)
        end
        return function ()
          for i=1,10 do
            wait(i)
          end
        end  
        --coroutine.yieldは関数の中からでも呼べる。
        ";
        Script s = new Script();
        DynValue luafunc = s.DoString(src);
        DynValue cr = s.CreateCoroutine(luafunc);//C#からでもコルーチンを作れる
        while (true)
        {
          DynValue x = cr.Coroutine.Resume();
          if (cr.Coroutine.State == CoroutineState.Dead) break;
          Debug.Log(x);
          yield return null;
        }
      }

      // Update is called once per frame
      int count = 0;
      void Update () {
        count++;
        if (count<15)
        {
          Debug.Log("Update");
        }
        //Updateで全部流れてしまわないように15回だけ表示している。
      }
    }
    //出力に数字とUpdateが入り交じり、C#のCSharpFunc、MoonSharpのコルーチン、Updateがそれぞれ呼ばれてるのが分かる。
posted by NTak_Indies at 08:19| Comment(2) | TrackBack(0) | 日記

2016年11月22日

Unityの研究中

少し更新期間が開いてしまいましたが色々やってます。

Unityでボクセルベースのテレインを作ってみる

色々作ってみたのだが、こういうのってレンダリングやマテリアルの管理などUnityがせっかくある程度自動でやってくれてる部分をいきなり自前で組む話になってるので、ちょっと初習者向きでは無いかな、といったんここでペンディング。まあ、メッシュとか自前で構成する勉強にはなった。

2016-11-21.png

クリスペで作画→Illustratorで線画→クリスペで塗り→Spineでリギング、アニメーション

イラレの線の引き方はだいぶ覚えた。Altでアンカーをクリックして折れ線を書けるのがミソ。Spineには課題が多い。メッシュ機能で頂点置いてウェイトを塗ることは覚えたのだが、元絵の作り方をもうちょっと工夫しないといけなさそう。身体はいっそ最初から一体にして、ちゃんと頂点置いて変形させるほうがいいかな。別オブジェクトに割ると連結部分が問題になる。あとまあアニメーションの作り方かな。この辺は3Dでも同じなのだけど、ちゃんとキーを打たないと直線的になる。(画像はアニメGIFにする際に容量節約のためフレームレートをかなり落としてあります。)

yukari.gif

IllustratorでUI仮素材デザイン→SVGで出力→node.jsからXMLとして読み込みLuaテーブル形式に変換→それを元にNScripter2で仮素材を作る

書いたとおり。IllustratorでSVGを弄る場合は、全ての単位をピクセルにし、線を無しにして長方形を置けば意図通りになる。ウィンドウの「変形」で数値入力できる。背景に画面サイズの長方形を置いておかないと出力時に透明部分がトリミングされるかも。あるいは、AtomでSVGを直書きしてしまうことも出来る。いずれにしろ、idをちゃんと振っておけばあとで取り出しやすい(Illustratorではレイヤ名やグループ名やパス名がidになる)。上手く行ったので、これからは仮素材作りはこれでやろうと思う。

Tiled map editorで作った2DマップデータをLuaテーブルを出力し、NScr2から取り込む

この辺の話を後に記事にするかも。単純にLuaテーブル形式で吐けるのでNScripter2では単にdostringして戻り値を取るだけでいい(内部ファイルの場合。外部から任意のファイルを取り込む場合はevalインジェクションがあるので避けた方がいい)。他にもJSONやCSVでも吐けます。

posted by NTak_Indies at 19:26| Comment(2) | TrackBack(0) | 日記