【画像編集】ImageMagick 7で画像を操作してみます(前編)

ImageMagickとは

ImageMagickは、コマンドライン上で画像の操作を行うフリーソフトウェアです。

一括で画像の操作が行える便利なツールです。

www.imagemagick.org

インストール

公式の"Download"の各OS(例えば”Windows Binary Release”)から、ダウンロードしてきてインストールします。

"Q8"あるいは"Q16"は、画像の各ピクセルを内部的に何ビットで計算しているかをあらわすようです。"HDRI"はHDRに対応しているか、"dll"および"static"はダイナミックリンクあるいはスタティックリンクを行っているかの違いです。"portable"はインストーラー無しで、コピーするだけで使えるバージョンです。(パスを通しません。)

旧バージョン(6系統まで)では一部のコマンドがWindowsのコマンドとバッティングするため困ったことになりましたが、7以降ではコマンド名が変更されているため、Windowsのconvertコマンドが隠されてしまうといった心配は無くなりました。

Windowsで試しに使うなら"ImageMagick-7.0.8-8-portable-Q16-x64.zip"を、普通に使うなら"ImageMagick-7.0.8-8-Q16-x64-dll.exe"辺りをダウンロードすればいいかなと思います。

また、デフォルトのオプションだとffmpegも(imagemagickフォルダの下に)インストールされます。

 

インストール完了後、

magick -version

を実行すればバージョンを確認できます。

f:id:chirotec:20180804094037p:plain

また、

imdisplay '画像ファイル名'

と入力すると、ウィンドウが開いて画像ファイルを確認できます。

旧バージョンではdisplayコマンドでしたが、これも7で変更されています。

f:id:chirotec:20180804094705p:plain

 

画像リサイズの例

画像を一括処理するには、mogrifyコマンドを使います。

inフォルダ以下にあるpngファイルを全てリサイズする例です。

magick mogrify -resize 256x256 in/*.png

この例ですと、inフォルダ以下のpngファイルの画像サイズが256ピクセルに拡大縮小され、ファイルが上書きされます。

 

バージョン7の違い

ここまでで示したように、ver7ではコマンドが変更されています。

旧バージョンでは"convert" "mogrify"などのコマンドを実行していましたが、

ver7では"magick" コマンドに変更され、"convert"などを引数で渡すようになりました。

過去の資料を探すときはご注意ください。

 

次回はこれらのコマンドを使って画像データを加工する事例を紹介いたします。

次回→【画像編集】ImageMagick 7で画像を操作してみます(後編) - chirotec’s blog

【VTuber】VTuberワークショップに参加させて頂きました

昨日7/21、御茶ノ水デジタルハリウッド大学で開催されましたVTuberワークショップに参加させて頂き、エンジョイしてきました。

vtuberjustdoit.peatix.com

 

オープニング

まずは株式会社キッズプレート茂出木謙太郎様より、御挨拶と事例紹介です。アメリカザリガニ平井善之さんが出演されている『ツルンッ!とたまご学園』の紹介もありました。なかなかユニークな試みです。

www.youtube.com

[360°Encyclopedia]Vol.23 バーチャルアイドルを作れないか? - PRONEWS

たまご学園では、近日中になんらかの発表があるらしいですよ。

要チェックです😎

 

基調講演 - ねこますさん

皆様ご存知「バーチャルのじゃロリ狐娘Youtuberおじさん」こと、ねこますさんのVR講演です。前方左右のプロジェクター画面に映し出されました。

私のメモでは概ねこのような内容でした。Twitter#vtuberjustdoitタグで語られていますのでそちらも参考にして下さい。

・技術選定は大事。

・使う技術はスケーリング出来るようにしておくと良いです。機材は最悪カメラだけでも使えるようにしておくと保険になり、環境によって使えないといった機会損失が無くなる。

・技術というものは問題解決でのみ価値を生むということを理解するべき。

・使用するツールも、メリットやデメリットを理解して選定しよう。DCCツールや動画配信ツールなど。

VRMという統一規格が出来たことで、ツール障壁が少なくなり、コラボレーションなどが行いやすくなった。

・画力は大切。

・迷走してたけどダメ元でVTuber。行動することは大切。

と、大変興味深いお話でした。VRメイド喫茶行きたいですね。

 

VRoid Studio

ピクシブ株式会社 のりお様より、VRoid Studioの開発状況のご紹介です。

vroid.pixiv.net

女の子の素体がすでに用意されており、これにパーツを描画したりパラメータを変更することでキャラクターを作成出来るようです。

最初に表示される素体はすっぽんぽんでちょっとえちい感じでしたが、製品版では検討中とのことです。まぁmakehumanとかにもアレなパーツはあるのですが

今回は髪型を作る機能をご紹介頂きました。頭の上に面をいくつか置き、その上からブラシのようなもので描画することによって、髪の毛を簡単に作成出来るようです。

また、3Dモデルに直接カーソルを当てて、顔や眼球などのテクスチャを描画出来るようです。

重要なことですが、このソフトウェアで作成したモデルは、ライセンスの制約は無く自由に使えるとのことです。ペイントツールで自分で描画した絵と同じように扱えるようです。ゲームなどでの利用にも適しているかもしれません。

 

ワークショップ

今回は「Blenderで楽しくキャラづくり」に挑戦してきました!

そもそもお前デザイナーじゃなくてプログラマーだろと言われそうだとか、Blenderは少し触ったけどUIが全然意味不だったとか、以前某入門サイト様でてんとう虫を作ったのはいいけど力尽きたとか、確かに難易度は相当高いです。ましてや人体モデリングなんて……

 

前半は三面図を参考に、モデリングの触りを駆け足で体験してきました。

うーんやはりBlender UI全然分からんです。昔にやった分も思い出せない……

でもポチポチと点を打ってみたり、にょーんと面を伸ばしていくのは楽しいです。Ctrl+Eで面を伸ばしていってー、こんな感じ!

f:id:chirotec:20180722204540p:plain

時間が無いので今回は顔の輪郭をなぞるくらいで終わりました。作業はどこまで進めるか、現在私の方で少しずつ進めております。

今回の講座の講師は「ワニでもわかるゼロからのBlender」のzen様です。ありがとうございます!

動画の方も非常に参考になりますので、是非とも紹介させて頂きたいです。第2回の中盤まで進めれば、上の画像になるかと思います。

www.youtube.com

後半はテクスチャの貼り方やポーズの付け方を学んできました。

テクスチャはUV展開したところに絵を書いたりすればいいのかな?

Blenderには自動ウェイト計算機能もありますが、あまり品質が高くないようなので注意が必要です。

また簡単にボーン・ウェイトやアニメーションを付けるサービスとして、AdobeMixamoの紹介もありました。

 

おわりに

突発で開催されたイベントでしたが、大変有意義なイベントでした。ゲームや映像作りにも参考になりそうな情報です。皆様、ありがとうございました!

地道な作業が次に繋がるかもしれません。モチベーションを大切にしたいですね。

【雑記】ちょっとした買い物

最近はPCを持たずにスマホで済ませている若者さんも多いらしいですが、殆どの方はなんらかのPCを持っていると思います。
私が昔Vista時代に使っていたノートPCは、使い潰して4年前くらいに遂に壊れたので廃棄してしまいました。
思えば当時はカジュアルにMMORPGを遊んでましたね。もうサービス終わってしまいましたがECOとか。

代わりに買ったのは、CPUがCore i5 760というギリギリi5を名乗れる中古PCです。
こちらはまだまだ現役です。ファンの音が気になるでしょうか。

中古品の魅力

秋葉原の店頭とかネット通販を眺めていると、多くの魅力的な中古PCが並んでいます。新品に比べて格安でPCを
またMicrosoftは中古再生品にもライセンスを提供しているので、ちゃんとした所で買えばWindowsの不正品を掴まされる恐れは少ないはずです。多分。
四年前、ノートPC壊れて困ってた私はWindows 7 Professional付きの自作っぽいPCの中古を5万円ほどで購入しました。
そしてメモリを8GB差して12GBに、HDDをSSDに交換しました。足りない物は買い足せば良いのです。これだけでとても快適に利用出来ていました。

壊れるパーツ、そして取り替え

当然中古品にはリスクが伴います。
まず2年ほど使っていたら、画面の表示が壊れて映らなくなってしまいました。どうやらグラボが昇天したようです。(Radeonの古いの、型番忘れた)
すぐさまパーツショップへ行き、安く売られていたGeforce 960を買ってきて差すことで事なきを得たようです。(今思うと奮発して970に手を伸ばすべきだったかも)
それからしばらくはトラブルは無かったのですが、時々電源が落ちるようになってしまいました。
BIOSや電源プランを弄ったりして騙し騙し対応していましたが、遂に
パーツショップに駆け込み、電源ユニットを買って交換することで完全に治りました。
ちなみに電源ユニットはコンデンサの中身が吹っ飛び、真っ白な粉まみれになっていました。今までよく頑張ってくれました合掌。

省電力PCが欲しいので

そんなわけで現在のPCには愛着があるのですが、いかんせんNeharem世代なので消費電力が気になるところです。
一日中つけっぱなしにすることもあるので、サブ機が欲しいなーと思ってました。

というわけで、今週これを買ってみました
akiba-pc.watch.impress.co.jp

リースアウト品っぽい、超小型PCです。小さいですが、重量はずっしりあります。
グラボが載らないことを除くとこっちのが性能上です。なってこったい

ちなみにまだ動作確認しかしていません😎
使い込んで気に入ったらまた感想書きたいなーと思います。

ExcelのPower Queryでアンケート結果を集計してみる

多くの人が仕事でExcelを使っていると思いますが、
多分99%以上の人が使っていないであろう機能がてんこ盛りだと思います。
なお私はExcelでセルを方眼紙のようにして書類を作るのは絶対に許せない派です。

今回はExcelに入っているPower Queryを使ってみました。

 

ことの始まり

大雑把にいうと、こういうことを行う必要がありました。
・オフラインでのアンケート集計機能を低コスト(重要)で作りたい
・アプリ中のおまけ機能なので、簡素な作りでいい
Excelで見れると便利だね

今回の解決方法はこちら
・回答者は共用マシンにインストールされたアプリでアンケートに答える
・アプリでは回答結果を1行ずつcsvファイルに書き出す
・そのファイルをExcelで読み込み、集計して表にする

しかし集計のために数式を書くのはまだしも、
データ更新のためにファイルを読み込んで整形したりそれを行うマクロを書いたりするのはちょっと面倒です。
そこでPower Queryを使うことにしました。

 

Power Queryとは

MicrosoftのPower BIとも関係があったりしますが、
今回ここで扱うのはMicrosoftが公開しているExcelのアドインです。

support.office.com


データを外部から取得してExcelで扱えるようにしたりする、なんか凄い奴です。(手抜き説明)

市販のパッケージではOffice 2013以降で無料で使用できます。Officeを買えば無料で使用できます。(Office 2010ではProfessional Plusというボリュームライセンスの製品でのみ使用できました)

Download Microsoft Power Query for Excel from Official Microsoft Download Center

また、Office 2016では「取得と変換」という標準機能となり、アドインをダウンロードする必要も無くなりました。

 

使用例 

CSVファイルの準備

最初にヘッダだけ書き込まれたCSVファイルを用意し、アプリ側でアンケートに答える度に、その結果をカンマ区切りで1件につき1行ずつ書き込むだけのシンプルな実装です。
シンプルイズベスト。

f:id:chirotec:20180704194803p:plain

ちゃんとするならDBにテーブル作っておいて1行ずつ書き込んだりするべきなのかなーと思いました。

 

Excelでの取り込み

まずExcelを起動し、空白のブックを作ります。

データリボンの「新しいクエリ > ファイルから > CSVファイルから」
を選択し、アンケートの結果が入った(という想定の)CSVファイルを取り込み先とします。

f:id:chirotec:20180704194800p:plain

f:id:chirotec:20180704194757p:plain

 

これでCSVファイルからデータを取得するクエリが作成され、
さらにテーブルの内容が書かれている新規シートが作成されました。

f:id:chirotec:20180704194754p:plain

 

今回はCSVファイルからデータを取得していますが、
SQL ServerODBCを通してRDBMSから取得するようにも出来ます。
というかそれが本来の使い方だと思います

 

シートの編集

数式で集計したり、見やすいように色を付けたりして結果を分かりやすくします。
今回はこれだけ編集しました。
・テーブルのA列のデータ部分を範囲選択して「クイック分析」→「合計>個数」
 (集計行がテーブルの下部に追加され、A7セルではSUBTOTAL関数でレコードの件数が表示されるようになりました。)
・COUNTIF関数で各項目の回答を集計し、その下ではA7で割って回答率を計算
・AVERAGE関数で点数項目の平均点を計算
・セルの書式設定を「0"件"」などにして見やすく

f:id:chirotec:20180704194750p:plain

 

データ更新
アンケートの結果が集まりました。CSVファイルが更新されています。(という設定)

f:id:chirotec:20180704195416p:plain


ここでデータの「すべて更新」を押すなどして、データを再取得します。

f:id:chirotec:20180704195412p:plain

書式などはそのままに、テーブルだけが更新されました。便利!


まとめ

このようにアンケート集計の仕組みを簡単に作成出来ました。

多分極めれば色々なことが出来ると思います。少なくともマクロを書いて弄るよりはずっと楽でした。

Power Queryは強力なツールです。200m先のコンビニに行くのにタクシーを呼ぶような贅沢感がありました。

【Unity】ネイティブプラグインを使った時のミス、ハマりポイント

前回の記事ですが、内容が多すぎて結構雑かなーと思いました。

補足説明をいれておきます。m(__)m

 

long型のサイズの違い

C言語C++ではlong型のサイズは実装系依存で、つまり実行する環境によって変わりますが、Visual Studioでx64アプリケーション(LPP64)をビルドした場合、long型は4バイトとなります。(LinuxmacOSなどのLP64環境では8バイトのようです。)

一方、C#のlong型は8バイトとして定義されています。

参考:整数型の一覧表 (C# リファレンス) | Microsoft Docs

名前が同じだからといってC++のlongをC#側でlongで受けてしまうと、例えば配列の前半部分に予期しない大きな値が入り、後半部分に0で埋まった値が入っているなどの予期せぬ動作が起こったりします。

C++でlong型で定義した関数は、C#側はint型(Int32型)で受けましょう。

C#側で文字列をstringとして使いたいとき

C++のconst char*型を、C#側でいくつかの方法でマーシャリングすることが出来ます。

参考:文字列に対する既定のマーシャリング | Microsoft Docs

参考:既定のマーシャリングの動作 | Microsoft Docs

C#側でstringとしてマーシャリングした場合は、stringのデコンストラクターが呼ばれるタイミングでメモリが解放されます。

前回malloc()でメモリ確保すると書きましたが、これだと本当にメモリリークが起きないのかが心配です。特にデバッグビルドで作成したdllを読み込ませた場合、_DEBUGが定義されているため、mallocが_malloc_dbgに書き換えられてしまうため、C#側で正しく解放できずUnityが落ちてしまうようです。

CoTaskMemAlloc()関数で確保するのが良いのでしょうか?

static const char* MallocString(const char* str) {

 size_t n = strlen(str);

 char* s = (char*)CoTaskMemAlloc(n+1);

 memcpy(s, str, n + 1);

 return s;

}

このようにした方が良いかもしれません。

VisualStudioでデバッグしたい

Visual StudioでUnity.exeのプロセスにアタッチすると、 VSのデバッガを使ってデバッグを行えます。ブレークポイントを貼れたりなど便利です。

dllでも同様にデバッグがしたいです。出来ればデバッグビルドでビルドしたdllを使いたいですが、 デフォルト設定のDebugビルドで作成したdllを読ませるとUnityが落ちてしまいます。

DLLのプロジェクトのプロパティで

 C/C++ > コード生成 > ランタイムライブラリでデバッグのdllを使っていると落ちるようです。

 

Unityのネイティブプラグインをはじめました

昨日に引き続き、今日はネイティブプラグインを扱います。

ここではUnity 2018 1.3f、Visual Studio 2017を使用しています。

おかげさまで絶好調です。

 

ネイティブプラグインとは

多分公式マニュアルを見た方が早いでしょうか?
docs.unity3d.com

既存のC,C++で書かれたライブラリを、 C#で扱うための仕組みです。

Visual Studioなど他のコンパイラでdllファイルを作成し、Unityでこのdllの関数を呼び出します。 

 

ネイティブプラグインの作成

dllの作成

まずはVisual Studio 2017でdllを作成するプロジェクトを作成します。

Visual C++ > Windows デスクトップ > ダイナミック リンク ライブラリ(.dll)のプロジェクトを作成します。

f:id:chirotec:20180617201500p:plain

 

初期状態では、以下のようなプロジェクトになっています。

 

f:id:chirotec:20180617201456p:plain

ここでは「NativePluginSample.cpp」のみを編集します。
 さらにdllをincludeするためのヘッダーファイル「NativePluginSample.h」をプロジェクトに追加します。

 

NativePluginSample.h

#pragma once
#ifdef NATIVEPLUGINSAMPLE_EXPORTS
#define SAMPLE_API __declspec(dllexport)
#else
#define SAMPLE_API __declspec(dllimport)
#endif

extern "C" {
	// 値渡し
	SAMPLE_API int SampleAPIInt(int i);
	SAMPLE_API float SampleAPIFloat(float f);
	SAMPLE_API double SampleAPIDouble(double d);

	// 参照渡し
	SAMPLE_API void SampleAPIInt2(int& i);
	SAMPLE_API void SampleAPIFloat2(float& f);
	SAMPLE_API void SampleAPIDouble2(double& d);

	// 配列の参照渡し
	SAMPLE_API void SampleAPIIntArray(int intArray[], int intArraySize);
	SAMPLE_API void SampleAPILongArray(long longArray[], int longArraySize);
	SAMPLE_API void SampleAPIFloatArray(float floatArray[], int floatArraySize);
	SAMPLE_API void SampleAPIDoubleArray(double doubleArray[], int doubleArraySize);

	// 文字列
	SAMPLE_API const char* SampleAPIString1();
	SAMPLE_API const char* SampleAPIString2(const char* str);
}

NativePluginSample.cpp

#include "stdafx.h"
#include "stdlib.h"
#include "NativePluginSample.h"

SAMPLE_API int SampleAPIInt(int i)
{
	return 123 + i;
}

SAMPLE_API float SampleAPIFloat(float f)
{
	return 123.456f + f;
}

SAMPLE_API double SampleAPIDouble(double d)
{
	return 123.456 + d;
}

SAMPLE_API void SampleAPIInt2(int& i)
{
	i = 123 + i;
}

SAMPLE_API void SampleAPIFloat2(float& f)
{
	f = 123.456f + f;
}

SAMPLE_API void SampleAPIDouble2(double& d)
{
	d = 123.456 + d;
}

SAMPLE_API void SampleAPIIntArray(int intArray[], int intArraySize)
{
	for (int i = 0; i < intArraySize; i++)
	{
		intArray[i] = i;
	}
}

SAMPLE_API void SampleAPILongArray(long longArray[], int longArraySize)
{
	for (int i = 0; i < longArraySize; i++)
	{
		longArray[i] = i;
	}
}

SAMPLE_API void SampleAPIFloatArray(float floatArray[], int floatArraySize)
{
	for (int i = 0; i < floatArraySize; i++)
	{
		floatArray[i] = 0.5f + i;
	}
}

SAMPLE_API void SampleAPIDoubleArray(double doubleArray[], int doubleArraySize)
{
	for (int i = 0; i < doubleArraySize; i++)
	{
		doubleArray[i] = 0.5 + i;
	}
}

SAMPLE_API char* MallocString(const char* str)
{
	size_t bufSize = strlen(str) + 1;
	char* buf = (char*)malloc(bufSize);
	strcpy_s(buf, bufSize, str);
	return buf;
}

SAMPLE_API const char* SampleAPIString1()
{
	return MallocString("Hello!");
}

SAMPLE_API const char* SampleAPIString2(const char* str)
{
	// 文字列の後ろに"Hello!"をくっ付けて返す
	const int bufferSize = 256;
	static char buffer[bufferSize];
	const char hello[] = "Hello!";

	strcpy_s(buffer, bufferSize, str);
	size_t n = strnlen(buffer, bufferSize);
	strcpy_s(buffer + n, bufferSize - 7, hello);
	return MallocString(buffer);
}

このプロジェクトを作成したときに、プロジェクトのプロパティ内で、プリプロセッサの定義に NATIVEPLUGINSAMPLE_EXPORTS が定義されています。これを利用して dllexport 属性またはdllimport 属性を使用出来ます。

正しくdllexport属性が使用できていれば、ビルド時にインポートライブラリ(*.libファイル)が*.dllファイルと共に作成されます。

注意するべき点は、const char* 型をC#側でstring型として扱う場合、malloc()でヒープ領域にメモリを確保して返す必要があることです。うっかりスタック領域を返してしまうと、最悪Unity Editorごと落ちます。malloc()で確保したメモリは、stringが解放されるタイミングで開放されます。

ポインタを扱う場合、C#側ではIntPtr型で扱えますが、マーシャライズなどを行う必要があるため今回説明は省略します。

 

dllのテストプログラムの作成

dllが正しく作られたかテストを行うために、先ほどのソリューションに追加する形で、Windows コンソールアプリケーションを作成します。

f:id:chirotec:20180617201452p:plain

 テストプログラムのプロジェクトが追加されたら、テストプログラムのプロジェクト側の「参照」に、先ほどのdllのプロジェクトを追加します。

f:id:chirotec:20180617201449p:plain

 構成はこのようになります。テストプログラムの方をスタートアッププロジェクトに設定しておけば、デバッグ実行を行うことが出来ます。

f:id:chirotec:20180617201443p:plain

 インクルードディレクトリにdllプロジェクトのヘッダファイルが入ったフォルダを追加しておきます。

f:id:chirotec:20180617201603p:plain

以下のようなチェック用プログラムを作成しました。

NativePluginSampleTest.cpp

#include "stdafx.h"
#include "stdlib.h"
#include "NativePluginSample.h"

int main()
{
	printf("int:    %d\n", SampleAPIInt(111));
	printf("float:  %f\n", SampleAPIFloat(111.111f));
	printf("double: %f\n", SampleAPIDouble(111.111));

	int    i = 222;
	float  f = 222.f;
	double d = 222.0;
	SampleAPIInt2(i);
	SampleAPIFloat2(f);
	SampleAPIDouble2(d);
	printf("int:    %d\n", i);
	printf("float:  %f\n", f);
	printf("double: %f\n", d);

	int    is[3] = { 111, 222, 333 };
	long   ls[3] = { 111, 222, 333 };
	float  fs[3] = { 111.f, 222.f, 333.f };
	double ds[3] = { 111.0, 222.0, 333.0 };
	SampleAPIIntArray(is, 3);
	SampleAPILongArray(ls, 3);
	SampleAPIFloatArray(fs, 3);
	SampleAPIDoubleArray(ds, 3);
	printf("int:    %d,%d,%d\n", is[0], is[1], is[2]);
	printf("long:   %d,%d,%d\n", ls[0], ls[1], ls[2]);
	printf("float:  %f,%f,%f\n", fs[0], fs[1], fs[2]);
	printf("double: %f,%f,%f\n", ds[0], ds[1], ds[2]);

	const char st[] = "MyTest ";
	const char* p1 = SampleAPIString1();
	const char* p2 = SampleAPIString2(st);
	printf("string1: %s\n", p1);
	printf("string2: %s\n", p2);
	free((void*)p1);
	free((void*)p2);

	return 0;
}

 このプログラムを実行すると、dllの関数が呼び出されていることが分かります。

f:id:chirotec:20180617201600p:plain

 

Unity側での使用

Unityのプロジェクトを作成します。 

f:id:chirotec:20180617210619p:plain

”Plugins”フォルダを作成し、中にReleaseビルドしたdllファイルを入れます。

(64bit版と32bit版を用意し、Platform settingsで振り分けます。)

f:id:chirotec:20180617211100p:plain

C#の関数にdllの関数を割り当てます。
lib.cs

using System.Runtime.InteropServices;

public class Lib {

    // 値渡し
    [DllImport("NativePluginSample")]
    public static extern int SampleAPIInt(int i);
    [DllImport("NativePluginSample")]
    public static extern float SampleAPIFloat(float f);
    [DllImport("NativePluginSample")]
    public static extern double SampleAPIDouble(double d);

    // 参照渡し
    [DllImport("NativePluginSample")]
    public static extern void SampleAPIInt2(ref int i);
    [DllImport("NativePluginSample")]
    public static extern void SampleAPIFloat2(ref float f);
    [DllImport("NativePluginSample")]
    public static extern void SampleAPIDouble2(ref double d);

    // 配列の参照渡し
    [DllImport("NativePluginSample")]
    public static extern void SampleAPIIntArray(int[] intArray, int intArraySize);
    [DllImport("NativePluginSample")]
    public static extern void SampleAPILongArray(int[] longArray, int longArraySize);
    [DllImport("NativePluginSample")]
    public static extern void SampleAPIFloatArray(float[] floatArray, int floatArraySize);
    [DllImport("NativePluginSample")]
    public static extern void SampleAPIDoubleArray(double[] doubleArray, int doubleArraySize);

    // 文字列
    [DllImport("NativePluginSample")]
    public static extern string SampleAPIString1();
    [DllImport("NativePluginSample")]
    public static extern string SampleAPIString2(string str);
}

テストプログラムを作成し、適当なオブジェクトにアタッチして実行します。

内容は先ほどのC++版のほぼ等価です。

TestObject.cpp

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestObject : MonoBehaviour {

	// Use this for initialization
	void Start () {
        Debug.Log("int:    " + Lib.SampleAPIInt(111));
        Debug.Log("float:  " + Lib.SampleAPIFloat(111.111f));
        Debug.Log("double: " + Lib.SampleAPIDouble(111.111));

        int i = 222;
        float f = 222.0f;
        double d = 222.0;
        Lib.SampleAPIInt2(ref i);
        Lib.SampleAPIFloat2(ref f);
        Lib.SampleAPIDouble2(ref d);
        Debug.Log("int:    " + i);
        Debug.Log("float:  " + f);
        Debug.Log("double: " + d);

        int[]    i_s = new int[] { 111, 222, 333 };
        int[]    l_s = new int[] { 111, 222, 333 };
        float[]  f_s = new float[] { 111.0f, 222.0f, 333.0f };
        double[] d_s = new double[] { 111.0, 222.0, 333.0 };
        Lib.SampleAPIIntArray(i_s, i_s.Length);
        Lib.SampleAPILongArray(l_s, l_s.Length);
        Lib.SampleAPIFloatArray(f_s, f_s.Length);
        Lib.SampleAPIDoubleArray(d_s, d_s.Length);
        Debug.LogFormat("int:    {0},{1},{2}", i_s[0], i_s[1], i_s[2]);
        Debug.LogFormat("long:   {0},{1},{2}", l_s[0], l_s[1], l_s[2]);
        Debug.LogFormat("float:  {0},{1},{2}", f_s[0], f_s[1], f_s[2]);
        Debug.LogFormat("double: {0},{1},{2}", d_s[0], d_s[1], d_s[2]);

        string st = "MyTest ";
        string s1 = Lib.SampleAPIString1();
        string s2 = Lib.SampleAPIString2(st);
        Debug.Log("string1: " + s1);
        Debug.Log("string2: " + s2);
    }
}

 

f:id:chirotec:20180617201553p:plain

 動作が確認できました。

 

Unityエディタ拡張はじめました

個人的にはやや不調ですので、リハビリに手短に行かせていただきます。m(__)m

少しは役に立つかな? という小ネタを書いてみます。 

 

Unityのエディタ拡張

UnityのEditorは、スクリプトで機能を追加することが出来ます。

Assetsフォルダの下のどこかに"Editor"という名前のフォルダを作成し、エディタ用のスクリプトをその中に置けばOKです。

今回は下の内容の"PageView.cs"ファイルを置きました。 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Linq;
using UnityEngine.SceneManagement;

// ページ切り替えを行うエディタ拡張
// メニューのEditor/PageViewerから呼び出します。
public class PageViewer : EditorWindow
{
    [MenuItem("Editor/MyPageViewer")]
    private static void Create()
    {
        // 生成
        GetWindow("MyPageViewer");
    }

    // MyPageViewerウィンドウが描画されるときの処理
    private void OnGUI()
    {
        // scene中のルートオブジェクトを全て取得
        Scene scene = SceneManager.GetActiveScene();
        GameObject[] rootObjects = scene.GetRootGameObjects();

        List pageObjects = new List();
        foreach (var rootObj in rootObjects)
        {
            // 子オブジェクトをすべて取得
            Transform[] childObjectsTransform = rootObj.GetComponentsInChildren(true);
            List childObjects = childObjectsTransform.Select(x => x.gameObject).ToList();
            // このうち、PageTagタグが付いているGameObjectを取得
            List taggedObjects = childObjects.Where(x => x.tag == "PageTop").ToList();
            // リストに追加
            pageObjects.AddRange(taggedObjects);
        }

        // PageTagタグが付いたGameObject一覧を表示
        foreach(GameObject pageObject in pageObjects)
        {
            // ボタンを表示
            if (GUILayout.Button(pageObject.name))
            {
                // ボタンが押された時の処理
                // ボタンに対応したpageObjectをアクティブに、それ以外のpageObjectを非アクティブにする
                pageObjects.ForEach(x => x.SetActive(x == pageObject));
            }
        }
    }
}

今回はUnityEditor.EditorWindowクラスを継承して、ウィンドウを作成しております。

関数に[MenuItem("Editor/MyPageViewer")]という属性をつけていますが、これにより"Editor"および"MyPageViewer"という名前の項目が追加され、これを選択した時にこの関数が呼び出されます。

f:id:chirotec:20180617003609p:plain

また、OnGUI()はウィンドウが描画されるたびに呼び出されます。

仕組みとしては昔のUnityのGUIに近く、GUILayout.Buttonなどの関数でGUIのボタンなどの部品を表示します。

f:id:chirotec:20180617003542p:plain

このスクリプトでは、シーン中のルートにあるオブジェクトより下の階層にあるゲームオブジェクトの中から、「"PageTop"」という名前のタグが付いたゲームオブジェクトを検索し、該当するオブジェクトの名前をボタンとして一覧表示しています。このボタンが押されたとき、ボタンに対応したオブジェクトがアクティブになり、その他のボタンに対応したオブジェクトが非アクティブ状態となります。

 

上記の画像の例では、「Page1」~「Page5」ボタンにPageTopタグを割り当てております。

「Page2」ボタンを押すと「Page2」がアクティブになり、その他の「Page1」などが非アクティブになります。

 

この例の検索の条件などを色々変えてみると、作業に役に立つ機能が作れるかもしれません。