TVMLエンジンスクラッチ開発


UnityでDLLを使う

今までも何度かやった経験はあるのだが、Unityで外部DLLのライブラリをコールする方法について、書いておく。

今回、いくらやっても動かず、5時間ぐらい格闘してしまったので、また同じことにならないように備忘録として、また、初めてやる人の参考として簡単にまとめておく。

DLLはVisual StudioのC++でビルドして作り、これをUnityにインポートして、Unity ScriptからC++のファンクションコールする、という流れである。今回、あまりに動かないんで、最後の最後、もっとも簡単なコードにして確認した。もちろん、この簡単コードはUnityのマニュアルにもあって、それとそれほど変わるところは無いんだが。

DLL側のコード

extern "C" __declspec(dllexport) int Add(int a, int b)
{
     return(a + b);
}
  • Visual Studio 2015使用。C++のWin32アプリでDLLを選んでプロジェクトを作る(名前をdlltestとしよう)
  • ごちゃごちゃファイルができるがそのままやってもいいけど、今回、メインのdlltest.cpp以外のstdafx.hとかそういうのは全部消しちゃった。
  • 上記コードをdlltest.cppにコピペ(ヘッダーも何もなく、これだけ)
  • ビルドする。設定はReleaseのx64。つまり64ビットDLL
  • dlltest.dllができる

Unity側のコード

using UnityEngine;
using System.Runtime.InteropServices;

public class NewBehaviourScript : MonoBehaviour   {

     [DllImport("dlltest")]
     public static extern int Add(int a, int b);

     void Start ()   {
          Debug.Log(Add(3, 7));
     }
}
  • Unity 5.3.1 (64bit)を使用。
  • Assetsの下にPluginsというフォルダを作り、そこにさっきできたdlltest.dllをドラグアンドドロップ
  • C#のコード(NewBehaviourScript.cs) を作り、上記コードをコピペ
  • 適当なオブジェクトにコードをアタッチ
  • 再生ボタンを押して、コンソールに「10」が出たら成功(3足す7は10)

できてしまえば、まったく何でもないんだが、今回、

EntryPointNotFoundException: Add
NewBehaviourScript.Start () (at Assets/NewBehaviourScript.cs:10)

というエラーが出まくって、何をやっても消えてくれず参った。このエラーはつまり、DLLは読めたけどその中にAddというファンクションが見つからない、と言っているのである。なんで5時間もできなかったか、実はそれほどはっきりしないんだが、以下の通りである。

  • 「EntryPointNotFoundException Unity DLL」とかで検索して多くの英語エントリーを見て、そこに書かれた通りにやってみるのだが、みな、少しずつ違う。焦って中途半端にコピペしたり、自分で変更したりしてるうちに不整合が起こった(らしい)
  • DLLをUnityにドラグアンドドロップするのだが、既にあるDLLをまずDeleteして、そこに新しくビルドしたDLLをドロップするのだけど、いつからか古いDLLが消えなくなり、DLLを新しくしているつもりが、更新されておらず、ずっと旧いDLLのままチェックして、どうしても動かない、ってのをだいぶ長時間気が付かなかった。
  • このような場合は、Unityをいったん落として、Windowsのエクスプローラで、手動で削除とコピー(つまりDLL入れ替え)をして、それからUnityを起動し直す。
  • こういうハマり方をした場合は、まず強制的に数時間休憩を入れ、別のことをすること。これ鉄則なんだけど、今回、ついつい熱くなり5時間が無駄。

以上です~


リアル系でTVMLを動かしたところ

前の投稿で予告したので、リアル系のセットとキャラで今回のTVMLエンジンを動かしたところを貼っておく。

 

 

セットはUnity5のGlobal illuminationとかPhysical based shadingとか、わりとふんだんに使っている。キャラはフリーのキャラで、動きもフリーのBVH再生で、リアルというよりは気持ち悪いが、いつものPooni君ばかりでないことだけは分かってもらえるであろう。

実はこれまでのTVMLエンジンではこれがきちんとできなかったのである。今回、CGのセットやキャラ、照明などはすべてUnityのScene任せにしてTVMLからは一切コールしないことで、このようなことが格段にやりやすくなった。TVMLは演技を記述しているだけで、一切の初期設定は不要である。TVMLスクリプトには、最初からいきなり

character: look(name=Mia, target=camera, wait=no)
character: talk(name=Mia, text=”こんにちは、みなさん”)
character: talk(name=Mia, text=”これはまだリハーサルなのですが、お見せしています”)

と書けば、その通りに動く。

まだまだ、ショットとかぜんぜんイマイチなのだが、急ぎでやったのでご勘弁を。とにかくも、今回のエンジンのおかげでこんな絵も作りやすくなりました、ってことで。

ではでは。


TVMLが再生できた

TVMLスクリプトが一応再生できるようになった。もちろん仕様の一部だけど、こんなのが走るようになった。TVMLスクリプトはコレ

コマンドにして10個ぐらいをサポート。TVMLの仕様自体は7、80個ぐらいあったと思う。ただ、今回、従来のTVML仕様にこだわる気はなく、作りながらTVMLの方も変えていってしまう予定。今のところ、少ししか変えてないが、一か所、従来と大きな違いがある。

それは、CharacterデータをTVMLスクリプト内で読み込むのを止めたこと。従来は、たとえば

character: openmodel (model=POONI, filename=”Characters/pooni/pooni”)

みたいにfilenameに外部データファイル名を指定して、それをオープンして、それを使っていた。上述の「Characters/pooni/pooni」は、実際にはUnityのプロジェクト内のResourcesフォルダ内に配置したキャラクタデータだったんだよね(あるいはどこぞに配置したAssetBundle)。今回、このやり方は止めて、キャラクタとセット(実際はカメラも照明も全部)をUnityEditor上で最初からScene上に配置しておき、これをもとにTVMLスクリプトを動かすようにした。

UnityEditor上では最初からこうなっている。

Untitled-1

キャラクタ、セットはプレハブをドラグアンドドロップして、Editor上で配置を行う。照明やカメラも配置調整して絵作りをしてしまう。その後、このシーンに対してTVMLスクリプトを再生して、こいつらに演技させるのである。そのため、今回TVMLに「assign」というコマンドを追加した。こういう風に書く。

character: assign (name=A, gameobject=”pooni”)

pooniという名前のゲームオブジェクトに、TVML内の「A」というキャラクタ名をアサインし、以後、これを使う。

character: talk (name=A, text=”こにゃにゃちわ”)

という風になる。ということは、UnityEditor上でシーンさえ作っておけば、以下の正味2行のTVMLを書けば、キャラクタを動かすことができる。

character: assign (name=A, gameobject=”pooni”)
character: talk (name=A, text=”こにゃにゃちわ”)

と、ここまで書いて気が付いたが、2行も要らないな。

character: talk (name=pooni, text=”こにゃにゃちわ”)

でしゃべった方がいいに決まってるわな。そうすっか。検討しておこう。しかし、こうなるともう既にオブジェクト指向表記そのものだね。

pooni.talk(“こにゃにゃちわ”)

と書いているのと意味的にまったく変わらない。それじゃあ、オブジェクト指向にすりゃいいじゃないか、という考え方も確かにある。現にNHKではだいぶ前に、結局、Pythonを使ってそのようにしたと聞いている。でも、まあ、TVML元祖のオレとしては、一応、TVML表記にこだわっておくことにしよう。

それから、このやり方は、従来のようなスタンドアロンなTVMLプレイヤーと比較したとき、UnityEditorを前提としてしまっているところに制限があり過ぎるように思えるかもしれない。でも、オレの考えではこっちの方がたぶん絶対にいい。ここで何がいいかクダクダは書かないが、この次のブログエントリーかなんかで、こんなショボいキャラと板セットじゃない、Unity5のGIやらPBSやらの機能をふんだんに使ったTVMLシーンでも掲載して、「ほれ、この方がいいっしょ?」とするつもり。

それでは、また!


とりあえずトーク

一応、こんな風にしゃべっている。TVMLは何はともあれしゃべるのが基本なんで、最初はこれでしょう。

 

 

このキャラはPooniと言う。TVMLオリジナルキャラはBobだが、その子供にKidというキャラがいて、その現代版がPooni。デザインは家のカミさん。黄色いうんち色のおむつをしているのでPooniと言う名前なのである。あと知っている人は知っているが、このボブマリキャラはポリゴンが見えているところに特徴があるのだが、このPooniはそれが無くてスムーズ。Unity5になってデフォルトのシェーダーがPhong shadingになり、プラスチックの質感とかきれいに出るんで、ポリゴン頭はやめー! スムーズ路線に切り替えた。そのうちスムーズBobも出すことにしよう。

そういや、この前スウェーデンの授業でなんかのついでにこのPooniと、BoinというキャラとFoggというキャラの寸劇を見せたら学生の一人が超笑い転げていたが、そんなに面白いか、これが。

開発ブログなんでテクニカルノートを書こう。

音声合成はRTVoice(会社:crosstales)という売り物でUnity Asset Storeで見つけて買った。たしか二千円ぐらい。このRTVoiceは別に自分でTTSエンジンを持っているわけではなく、使っているマシンにプリインストールされているTTSエンジンをサーチして、それをUnityから使えるようにしている。オレの今のマシンはWindows10で、Windows10には英語のTTSが最初からあり、それで言語設定を日本語にするとどうやら日本語TTSが一つ入るようで、現在はその「日本女性」の声でしゃべっている。

それからリップシンクは、今回、やはりAsset storeで買ったSALSA(会社:Crazy Minnow Studio)というソフトを使っている。これも二千円ぐらい。Unity上で鳴っている音をリアルタイム処理して、3段階のリップシンクデータを自動的に出力する、という簡単なもの。受け取った僕のコード側では、この不連続な3つのデータにローパスフィルタにかけ、スムージングし、滑らかなリップシンクデータに変換して使っている。

このSALSAだが、アナウンスによるともうすぐ、Unity上キャラに表情をリアルタイムで自由に与えられるパッケージも出すそうで、それが出たらそれを組み込んで表情を変えていろいろやってみたい。もちろん、このバカキャラのままやるつもりだが。

あ、あと、見ての通りセリフの字幕も付けている。フォントはゴシックだと味気ないので、cinecaption227というシネマ調のフォントをどっかから落としてきて、それを使った。

というわけで、第一報でした。


TVMLエンジンスクラッチ開発を始めた

TVMLのエンジンをUnity5で新規にスクラッチで作り始めた。ほとんど、いや、完全に趣味である。

始めたきっかけだが、学校の授業でClean codeっていう「いかにきれいなコードを書くか」という、自分におよそふさわしくないテーマで講義をしているのだが、そこで使うちょっとした課題を作ったのが始まりだった。昔作ったキャラを引っ張り出してきて、Unity5の上に登場させ、ごく基本的なアニメーションをC#で書いて仕込んだ。このコードを学生に渡し、コードをきれいにしなさい、とやったわけだ。A4でほんの一枚以下のC#のコードだ。

キャラが動いて声を発するのが面白くて、ちょこっといじってこんなのを作った。

 

 

このコードはひと月ほど放置されていたのだが、ちょっとヒマができた時になんとなく取り出し、以前に買っておいた、Unity5で動くTTS(音声合成)と自動リップシンクソフトをやはり引っ張り出し、つないで、セリフをしゃべらせてみた。

コードに打ち込んだセリフをこいつが初めてしゃべったとき、思わず微笑んでしまった。すぐに、およそ20年近く前、同じようなCGキャラが初めて合成音でしゃべったときのことがフラッシュバックした。あれが最初の感動だったな、と思い、しばらく感慨にふけった。

と、まあ、そういうわけで、ヒマができたときだけ、非常にノロノロと、このコードをあれこれいじり始め、いろいろ考えてはみたのだが、結局、このままTVMLエンジンをゼロから作ってみるか、と思うようになった。そして、今に至る。

当面、ビジョンは無い。途中で放置する可能性も大である。というわけだが、自分はなぐさみで文を書くのも趣味なのでこのようなブログとともに開発してみようかな、と思っているのである。