KMCとPiet,そして最強のPietの為にdllを動的にC#で読みこむ話
はじめに
この記事はKMC Advent Calendar 2015 18日目の記事です。 前回の記事はwalkureさんによる京大マイコンクラブと旅するわたし - (。・ω・。)ノ・☆':*;':*でした。
KMCID: murataです。
最近Pietのことしか考えてないのでこの記事を書いたら半年ほどPiet禁します。
タイトルの「C#でDllを動的に読み込む話」はC#とかいう難しい言語でちょっとよくわかりにくいので
先に簡単で分かりやすい言語であるPietの話から始めますね。
最近僕が描いたPiet
3つ程描きました。
あぁ^~心がぴょんぴょんするんじゃぁ^~とは (アァーココロガピョンピョンスルンジャァーとは) [単語記事] - ニコニコ大百科に、 他のプログラミング言語はいっぱいあるのにPietだけなかったので描きました。 「あ~心がぴょんぴょんするんじゃ~」と出力します。 Pietはプログラミング出来い人にも画像という冗長性の手段によってコードの意味を伝えられる素晴らしい言語なんです。Pietで「ああ~心がぴょんぴょんするんじゃ~」と出力するチノちゃん描きました! pic.twitter.com/jRg80hWlC4
— むらため𒄆✘˵╹◡╹˶✘ (@paradigm_9) 2015年12月17日
髪の毛で72を取ってそれを顔を縦横無尽に横断しながらCHIHAYAという文字列を作って出力し続けています。72CHIHAYA と出力し続けるPietのプログラムのコードを書きました! pic.twitter.com/tDvZQlgxdx
— むらため𒄆✘˵╹◡╹˶✘ (@paradigm_9) 2015年10月3日
gyazo.com
これはここに描いているとおりの文字列を出力するコードです。
好きな文字列をわかりやすく表現出来ますね。
Pietは柔軟にコードを手軽に描ける良い言語なんです。
今年のKMCとPietの活動を振り返る
この記事はKMCアドベントカレンダーなので、 一応今年(2015年)のKMCの大きなPiet活動を簡単に振り返ろうと思います。
時系列順にPiet活動を振り返る
- 3月 Pietの統合開発環境(IDE)であるPidetPietのエディタを作った話が発表される
- これにより、開発がスムーズになりPietの開発が加速されました。
- 5月 Pietを描く新入生プロジェクト発足
- KMCの新入生に初めてのプログラミングでPietを教える…正気の沙汰ではないですね。 kmc.hatenablog.jp
- 8月 YAPCでPietの講座が部員によって行われる
- YAPC,Perlの凄い規模のカンファレンスでした。 その大舞台で KMCID:hnagamin がPietの講座をしました。 Perl , Python , PHP ,Piet 合わせて4Pと呼ばれる時代が来るのではないかと思いました。 (ちなみに、僕はYAPCでは銅鑼パーソンを担当させてもらいました! 今までで一番人生観が変わった出来事でした!) http://yapcasia.org/2015/talk/show/1da55daa-0deb-11e5-944c-67dc7d574c3ayapcasia.org
- 11月 NFでPietが展示される
- NF(京大の学祭)でPietを展示しました。 文字でしかプログラミング出来ないという常識を吹き飛ばすのがなかなか新鮮なのか意外と好評でした。
KMCの部員が開発しているツールを探してみた
- @dama さんの最強のPietのIDE、Pidetのリポジトリです。
GitHub - dnek/Pidet: IDE for Piet. - @noname774 さんはPiet関連のリポジトリが多く、おすすめです。
Search · user:nna774 Piet · GitHub - @primenumber さんもPiet関連のリポジトリが多く、おすすめです。
Search · user:primenumber Piet · GitHub - @utgw さんの、Pietをgo言語で動かすリポジトリです。
GitHub - utgwkk/Piet-on-Go: A Piet interpreter written in Go
Pietの中間言語やコンパイラ、テストユーティリティ、インタプリタ、IDE…十分な開発環境が用意されてますね。 ここまでPietの開発が進んでいるのはKMCだけではないでしょうか?
僕の考えた最強のPiet
こんなに夢のあるPietをさらに実用的な言語にしようと、 僕は二週間くらいかけて最強のPiet拡張を考案しました。 概要だけ言うと数字だけでなくスタックを積んだりDllを扱えて、 ゲームやWebアプリケーションを手軽に作れるようになる予定です。 @Noname774さんのサークルにその仕様書を寄稿させてもらったのでよければお手に取り下さい。
今回初参加のサークル「いっと☆わーくす!」で、木曜日(三日目) 東-ム55bに配置されました。全てがうまくいけばおそらくPietに関する本が出ると思います。需要は無いでしょうがよろしくお願いします。 #C89WebCatalog https://t.co/Spsotf9ewt
— 久我山 菜々🐇🥕🏰🕛 (@nonamea774) 2015年10月30日
C#でDllを動的に読み込む
僕の考えた最強のPietはC#で作られたPidetをベースに作る予定なので、 同じくC#で実装します。 僕の考えた最強のPietではdllを読み込む機能を搭載する予定です。 そのためにまずはC#で動的にdllを読み込める必要があります。 今回の記事のメインはその読み込み方についてです。 静的に(つまりコンパイル前に予め)読み込むのなら、 DllImport属性を利用することで簡単に利用することが出来ます。 (解説は他所に譲ります)。 しかし、この方法ではコンパイル後にそれで定義していない新しいDllの関数を動的に呼び出すことは出来ません。 ではどうすればいいのでしょうか?
.Net製のDllの場合
.Net製Dll、つまりC#やVB.Netで作成されたdllの場合は比較的楽に呼び出すことが出来ます。
public static object Load_DotNet_Dll(string dllpath,string ClassName ,string FuncName, object[] Params) { try { var t = Assembly.LoadFrom(dllpath).GetType(ClassName); var obj = Activator.CreateInstance(t); return t.GetMethod(FuncName).Invoke(obj, Params); } catch {return 0;} }
(usingは省略しました)。 使うときには、
Load_DotNet_Dll("Plugins/TestDLL.dll", "TestDll.CsDll.LangA",new object[] { 5, "RA" });
このように文字列と引数の配列だけを用います。 これで動的に好きなdllを呼び出せるようになります。 (この例では自作DllをPlugins下にTestDLL.dllという名前で配置していて、 そのDll内で定義された、TestDll名前空間のCsDllクラスのLangA関数を引数 5 と "RA" で呼び出しています。) .Net製dllは型など色々な情報がそのまま入っているため、扱いが容易なのですね。
C製のDllの場合
C製のDll(kernel32.dll,winmm.dllなど)はちょっと手間が必要です。 関数ポインタを得る為に kernel32.dllからLoadLibrary,FreeLibrary,GetProcAddressを呼び出す必要があります。 さらに引数に応じた関数デリゲードは動的に作成しなければならないのでなんかめんどかったです。 デリゲードをジェネリックで作成すると一見成功するように思えますが、そうすると いざ使う時に型がジェネリックのままなので使うことが出来ないです。
C#でLoadLibraryを使用してアンマネージDLLを使用する - 閑古鳥 C# GetDelegateForFunctionPointer with generic delegate - Stack Overflow
[DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern IntPtr LoadLibrary(string lpFileName); [DllImport("kernel32", SetLastError = true)] internal static extern bool FreeLibrary(IntPtr hModule); [DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = false)] internal static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName); public static class DelegateCreator { private static readonly Func<Type[], Type> MakeNewCustomDelegate = (Func<Type[], Type>)Delegate.CreateDelegate( typeof(Func<Type[], Type>), typeof(Expression) .Assembly .GetType("System.Linq.Expressions.Compiler.DelegateHelpers") .GetMethod("MakeNewCustomDelegate", BindingFlags.NonPublic | BindingFlags.Static)); public static Type NewDelegateType(Type ret, Type[] Params) { Type[] args = new Type[Params.Length+1]; Params.CopyTo(args, 0); args[args.Length - 1] = ret; return MakeNewCustomDelegate(args); } } public static object Load_C_Dll(string dllpath,string func , object[] Params) { try { var Types = Params.Select(arg => arg.GetType()).ToArray(); IntPtr handle = LoadLibrary(dllpath); IntPtr fadd = GetProcAddress(handle, func); Type t = DelegateCreator.NewDelegateType(typeof(IntPtr), Types); object res = Marshal.GetDelegateForFunctionPointer(fadd, t).DynamicInvoke(Params); FreeLibrary(handle); return res; } catch { return 0; } }
私達はこの関数を定義したのでもうこれからは、 Load_C_Dll("user32.dll", "MessageBoxA", new object[] { IntPtr.Zero, "Hello World.", "Caption", (uint)0 }) のようにすることでコンパイル後でも、実行中の好きな時に好きなDllを呼び出すことが出来ます!
まとめ
このDll機能を利用して僕は最強のPietを作るつもりなので乞うご期待ですよ。
ついでに宣伝
https://play.google.com/store/apps/details?id=com.Paradigm.Ikada 最近運ゲー排除マインスイーパーをAndroidアプリにした話と市場に対する雑感 - DNEK's blogみたいに自作ゲームを宣伝するのが流行っているっぽいので僕も最近作ったパズルゲームのAndroid版を公開します。 よかったら遊んでステージ作れるんで投稿して下さい。
最後に
明日の記事は、Kazakamiさんによる「shellチャンネルについて」です。 好きなコマンドが何もかも気にせず好き放題打てる夢の様なチャンネルShellチャンネルの話ですね! 期待しています!!!!!
シェーダ周りを色々調べたのでまとめてみた
KMCでのグラフィックのプロジェクトでは、 毎回担当者が講座をします。 僕は今回担当でシェーダーについて調べてたので軽くブログに書こうかな ということで書きます。
Shader?
応用例
トゥーンシェーディング
- 影を段階的につけたり輪郭をつけたりしてセル画風にする表現形式
- 例:アイドルマスターOFA ofa.idolmaster.jp
水面の表現
- 反射・水紋・屈折などを計算してリアルな水面を再現している
- 例:World of Warships worldofwarships.com
基本的な仕組み
- WebGL(ブラウザで高性能描画が出来るやつ)の解説サイトが凄く良かったです。 wgld.org
- w020 ~ w024, w042 ~ w048 あたりを見れば基本は分かりますし、それ以外にも参考になるものはとても多いサイトです。
絵を描く時に活かす
- 三角形を描画するしか脳のないパソコンでもShaderの技術を使えばリアルな表現が出来る → ましてや私達人間もShaderの技術を理解すれば よりリアルな表現が出来るようになるかも
- 絵を描く人向けに3DCGの原理概念から応用方法を書いてて分かりやすいです oekakigakusyuu.blog97.fc2.com
- Flesnel reflection (フレネル反射)に関しては↓も参考になります http://marupeke296.com/DXPS_PS_No7_FresnelReflection.htmlmarupeke296.com
どのくらいShaderを使うと変わるの
- Shader及び先ほど紹介した技法を使うとどれくらい変わるものなのかを紹介した素晴らしいスライドがありました www.slideshare.net
実際のゲームへの応用
- グラフィックメインで Guilty Gear Xrd の解説記事がありました。 www.4gamer.net
- アイドルマスターというゲームでも解説記事がありました。 www.4gamer.net
Unityで活かす
- 先に概念がわかっていれば、↓の本が英語ですが実装するのに参考になりました
- この本は、Amazonで買うとデータが付属していない?ので本家サイトのの方がいいかもです www.amazon.co.jp
- 他にもUnityにおいては下記サイトが参考になりました。 http://marupeke296.com/UNI_S_No2_ShaderLab.htmlmarupeke296.com unitech.hatenablog.com
- ComputeShaderを頑張れば↓のようなことも出来るようです qiita.com
CCDxLib72を使って過去に作ったゲーム3種をAndroidに移植してみた
はじめに
前回の記事で、Cocos2dxで動くDxLibの関数群のライブラリであるCCDxLib72を公開しました!
今回の記事は、それで実際にGooglePlayに出したところまで解説します!
chy72.hatenablog.com
完成品
最初に完成品を見せるのが一番だろうということで。
①GreedGreen
https://play.google.com/store/apps/details?id=suken.greedgreen
高校生の後輩がDxLibで作成したゲームです。
全体的にすっきりとしていて、操作ももともとマウス操作なのでスマホにそのまま移植しても綺麗に動きました。
紹介する三作品の中で一番スマホで遊びやすくてオススメです。
②Celestial
https://play.google.com/store/apps/details?id=suken.celestial&hl=ja
僕が高校3年の頃にDxLibで作成したゲームです。
とりあえずWindows版のまま殆ど手を加えずに移植したので、操作性・見やすさは難ありです。
(絵も音楽も自分で作ったのと、受験期だったという理由で、作品のレベルに関してはご容赦ください)
③日本史クイズ
https://play.google.com/store/apps/details?id=suken.historyquiz
これも後輩がDxLibで作成したゲームです。
操作はもともとマウス操作だったので、スマホにそのまま移植しても操作はできてよかったのですが、スマホの小さい画面では見づらいかもしれないです。
元となるゲームを用意
github.com
(残り2つの作品は、もともと自分が作ったゲームではない(後輩が作った)ので、コードを公開するのも気が引けるので…)
まずは、元となるDxLibで動くゲームを用意します。
移行
まずは、cocosのプロジェクトを作成します。
最初のHelloWorldSceneがAndroidでも動くところまでは自力でお願いします。
移行した時に躓いたところを中心に列挙します。
コードのリファクタリング
- Data という名前の変数をもともと使用していました。しかしCocosにもDataというクラスはあるので、そのDataという変数を一括でcDataという名前に置き換えました。
- ShiftJisでもともとコードを保存していましたが、DrawStringで文字化けが発生するのでUTF-8(コードはBOMあり、読み込むテキストファイルはBOMなし)に変更しました。
- #define RED とかしていたのですが、それがCocosのColor3Bクラスの REDと競合したので sRED とかに変えました
- windows.h(ShellExeCuteに利用)など、Windows専用のヘッダーはincludeしないようにしました
- main.cpp というファイルを使っていたのですが、cocosのmain.cppと競合したので、main2.cppという名前に変更しました。
bmpをpngに
- cocos2dxではbmpファイルを読み込めないので、pngファイルに変換します。
- http://toktakaomi.hatenablog.com/entry/2015/02/24/004729を利用させてもらいました。
- 透過色を指定とかはないので、透過済みのPNGファイルに変換します。
BGMの読み込み
- 内部ではSimpleAudioEngineというものを利用して音を鳴らしているのですが、メモリに先立って読み込めるのは確か10秒程度までの効果音なので、10秒以下の効果音を読み込む前に SetCreateSoundDataType(DX_SOUNDDATATYPE_MEMNOPRESS) を、10秒以上のBGMを読み込むときはSetCreateSoundDataType(DX_SOUNDDATATYPE_MEMPRESS)を宣言します。
ファイル入出力変更
- 普通にfopenなどをしたいところですが、Resourcesに同梱してアプリに入れるデータは、読み込みしかできないこと、AndroidのManagerを利用してしか読み込めないということがあるので、それ専用の機能としたFileRead~~ の関数に書き換えます。
- また、書き換えるデータ(セーブデータなど)は、専用の場所に新しくファイルを作成するため、専用の関数UserDatafopenにfopenを変更します。(移行は普通にfseekとかもできます)fopenをしたくない場合は、getUserDataPath()を利用して、書き換え可能なパスを取得して使います。
操作方法変更
ロジック変更
- InitやCCDxStartやCCDxLoopを使う形に変更します。
- ProcessMessage,ScreenFlipでループを作って利用しているコードがあると思いますが、CCDxLib72では、ProcessMessageに当たるものがなく、CCDxLoopを抜けるということでしかProcessMessageやScreenFlipすることができません。
- そういう部分がもしもあれば、ロジックを考えて書き直すしかありません。(通常は、ゲーム全体として一つの大きなループでしかないと思うので、その場合は必要ありません)
- 理由としては、cocos2dxの方へCCDxLoopを抜けたあとに制御を返して、他の処理(イベント処理やFPS制御やDxLibでいうProcessMessageや他のSceneの処理)をしてもらって描画するという形をとっているからです。
- ProcessMessageに当たる関数を作成することも考えたのですが、それをすると他のcocos2dxの機能(Widgetなど)が使えなくなったり、バージョンアップで動かなくなったり、マルチプラットフォーム対応が難しくなったりしそうだったので、これに関してはご容赦ください。代わりにただのSceneとしてしかCCDxLibは存在しないので、他のcocosの機能は使い放題だと喜んでください!
- メモ:この処理をうまく非同期にできれば、可能かもしれません、強い方、実践してください。
完成
以上の変更(cocosの機能に関する変換(png,BGM,UTF8),マルチプラットフォームに関する変換(fopen,Emulate),ProcessMessage)を行い、cocos compile -p android で、コンパイルして、Googleに3000円投げてアカウント開いてGooglePlayにapkを投げて必要事項を記入したらすぐ公開です!
思ったこと
DxLibで作成したゲームは、どうしてもPC用に最適化されていて、コードをそのまま写しただけだとどうしても操作性がやや難になってしまいます。画面も小さくなるので意識していないと見辛くなることもしばしばです。なので、このライブラリでは移植の大部分を手助けすることはできますが、根幹の操作性だとかユーザビリティというところまではどうしても対処できないということを思いました。
やはり、ユーザーのことを考えるなら、苦を覚悟して手をかなり加える必要があるのかもしれません
スマホ向けにDxLibのコードがそのまま動く! Cocos2dxで動かせるDxLib、「CCDxLib72」を公開します!
2017/10/25追記
DXライブラリ置き場 Androidアプリ開発の注意点など
公式でAndroid版が出たようなので、そちらを使用すべきです。
僕が作った以下で紹介するライブラリはもうメンテナンスされておらず、
今後もするかどうかも未定である点からも公式のものを使うべきです。
しかしながら、cocos2dxでラップした以下の版を使いたい方も
いるやもしれませんのでこの記事はこのまま残しておきます。
これは何?
Cocos2dxの機能でDxLibの関数群(現在72個)をラップしたものです。
何が出来るの?
DxLibは通常Windowsでしかビルド出来ません。
しかしこれを使えば、同じC++で書かれているcocos2dxを利用して、
DxLibのコードを殆どそのままでスマホ向け等にゲームをビルド出来ます!
使用許諾などはいらないので、好きに使ってください。
開発環境の整え方
リポジトリ https://github.com/Muratam/CCDxLib72 のReadMeに記入していますが、
例えばAndroidに向けてビルドする手順の概略としては、
- python,cocos2dxをインストールして、cocos2dxのプロジェクトを新規作成する。
- CCDxLib72 をダウンロードして Classesに入れて、同梱のButtonExample.pngをResourcesに入れる。
- 書いたDxLibのコードをClassesフォルダに入れて、ゲームで読み込むファイルをResourcesフォルダに入れる。
- 同梱のReadMe.mdを見ながらコードをマルチプラットフォーム用に書き直す。(例えばファイル入出力やユーザーの入力操作)
- cocos2dxへの移行のヒントはReadMe.md にも書いてあります。
- cocos compile -p android でAndroid向けにビルドで完成!
cocos2dxのui::Button で一つの画像で半透明とかするコード
cocos2dxのui::Button は割と便利なんですが、作成時の引数には(スプライトではなくて)画像の名前で指定します。
押していないときは半透明、押しているときはそのまま描画、みたいにするコードです。
Cocos2dxでにゃんにゃんする
初めに
DxLib http://homepage2.nifty.com/natupaji/DxLib/ の関数をcocosでエミュレートできるようにラッパー関数を作っています。
昔はDxLibでゲームを作っていたので、そのコードをそのまま改変することなくAndroid,Macintosh,ios,linux,...にもビルド出来るようになれたらと思い、作成しています。
一度JavaでAndroid用にDxLibを作っていたのですが、C++ ->Java で結構リファクタリングする必要があるのであまり需要もないし面倒だったので、今回C++であるcoco2dxsにしようとした次第です。github.com
今、半分くらいの関数をcocos2dxでエミュレート出来ているので、もう少しで公開出来そうです。
その過程でつまづいたところを先にメモしておきます。
cocosのバージョン
cocos2dxのversionは3.8です(cocos2d.cppより)
cocos --version は2.0だった
開発環境
windows10 + Visual Studio 2013 Professional です。
基本的には、VisualStudioでWin32用でテストしていって、Android実機で試すということをしていってます。
開発環境導入
pythonがいりますね、僕はPietとRubyは入れていたのですが…
cocos2dxもcocosも落としてsetup.pyしてwin32のslnをVisualStudioで開いて10分くらいかけてコンパイルしたらまずHelloWorldのゲームシーンが出来ますね。
VisualStudioだとスクリプトの文字コードの警告が多いので、BOMとかUnicodeとか頑張ってください。あくまでもWarningなので問題はないですし。
一度ビルドしてしまえば以降は楽になります。
Androidビルド
EclipseやAndroidStudioなくても、SDK Ant NDK があればいいですよね。
画像などのファイルは最上階層のResourcesにいれておけば勝手にコンパイルするときにAndroidの方いじらなくてもassetsに入れてくれます(解像度とか気にしなければ)。
書いたコードを他のプラットフォームでもコンパイルできるようにするためには、最上階層のClassesの中にコードをいれておきましょう。
proj.android/jni/Android.mk を書き換えましょう。
../../Classes/HelloWorldScene.cpp \
のところを整合性を揃えてないとエラーになります。
VisualStudioでてきとーに作っていて BYTE とか TRUE std::_pi とか使っていても、そういうのがないってこのAndroidビルドの時に怒られて気づけます。
cocos
Windows版のpreloadBackgroundMusic()の中何もないのおもしろすぎでしょ。
あとAutoBatchingがあるからBatchNodeが非推奨になっているのとか。