2013/08/31(土) [n年前の日記]
#1 [haxe] Haxe+OpenFLでハマった
Haxe + OpenFL で、svg 表示ができるかどうかが気になって試していたのだけど、ハマってしまったり。全然すんなり行かない…。
結論から言うと、svgの内容によっては、一応表示できなくもない感じ。ただし、いくつか注意点がありそう。
ちなみに、Inkscape で svg を作ってたのだけど。その場合、以下の点に注意して作業・保存したほうが良いかもしれず。
Haxe + OpenFL上で、svg表示を可能にするライブラリ?は、gm2d/svg と haxenme/svg の2種類があるようで。前者を試したらエラーが出て解決できなかったので、後者で試した。
haxelib.exe を使って、いくつかライブラリをインストールする必要あり。
application.xml
Main.hx
以下の3行が肝心の部分、なのかな。
結論から言うと、svgの内容によっては、一応表示できなくもない感じ。ただし、いくつか注意点がありそう。
- svg内で、ストロークは使えない。Flash や Neko 用に出力する場合は問題無く表示できるけど、HTML5用に出力した際は、ストロークを使ってない部分まで、ストロークを使ってる部分のストローク設定が反映されてしまって、滅茶苦茶な見た目になる。
- svg内で使えるグラデーションの種類に制限がある模様。単純な線形?グラデーションなら大丈夫っぽいけど、円状のグラデーションは色がおかしなことになる。これも、Flash用とNeko用は問題無く表示できるけど、HTML5用がダメ。
ちなみに、Inkscape で svg を作ってたのだけど。その場合、以下の点に注意して作業・保存したほうが良いかもしれず。
- Inkscape で保存する際、ファイル種類として「Inkscape SVG」で保存せず、「プレーンSVG」を選択して保存。「Inkscape SVG」で保存すると、後から Inkscape で編集するための情報も記録されてしまうので、parser が正常動作しない予感。
- 「最適化 SVG」で保存したほうが良さそうな気もするのだけど、今回試してみたところ、一部、色情報が欠落してしまった。
- グループ解除や、パス→オブジェクトをパスへ、を使って、テキストのパス化等を行う。
- パス→ストロークをパスに変換、を使って、ストロークの除去を試みる。
- 全てをグループ解除すればOK、というわけでもないようで、場合によっては、一部をグループ化したままのほうが、不具合が出ない時もあった。
Haxe + OpenFL上で、svg表示を可能にするライブラリ?は、gm2d/svg と haxenme/svg の2種類があるようで。前者を試したらエラーが出て解決できなかったので、後者で試した。
haxelib.exe を使って、いくつかライブラリをインストールする必要あり。
haxelib.exe install format haxelib.exe install svg haxelib.exe install swf最後のは要らないかもしれないけど、念のため入れてみたり。
application.xml
<?xml version="1.0" encoding="utf-8"?> <project> <!-- NMML reference: https://gist.github.com/1763850 --> <!-- metadata, make sure 'package' is at least 3 segments (ie. com.mycompany.myproject) --> <meta title="haxe_openfl_svg_disp_test" package="haxeopenflsvgdisptest" version="1.0.0" company="mieki256" /> <!-- output --> <app main="Main" file="haxeopenflsvgdisptest" path="bin" /> <window background="#FFFFFF" fps="60" /> <window width="640" height="360" unless="mobile" /> <window orientation="landscape" vsync="false" antialiasing="4" if="cpp" /> <!-- classpath, haxe libs --> <source path="src" /> <!-- <haxelib name="gm2d" /> --> <haxelib name="format" /> <haxelib name="svg" /> <haxelib name="openfl" /> <haxelib name="actuate" /> <!-- assets --> <icon path="assets/openfl.svg" /> <assets path="assets/img" rename="img" /> <!-- optimize output <haxeflag name="-dce full" /> --> </project>
- svgファイルは、application.xml 内の <assets path="assets/img" rename="img" /> で指定したフォルダの下に置いておく。
- <haxelib name="format" /> と <haxelib name="svg" /> を追加する。<haxelib name="openfl" /> の前に書いておかないと、コンパイル?時にエラーが出る模様。
- format と svg を追加することで、FlashDevelop 上でも関連メソッドの補完が効くようになった。
- antialiasing="4" とすると、アンチエイリアスがかかってくれるらしいのだけど、何をターゲットにして出力するかも絡むようで、どの出力時でも必ずアンチエイリアスがかかる、というわけでもないらしい。
Main.hx
package ; import flash.display.Shape; import flash.display.Sprite; import flash.events.Event; import flash.Lib; import openfl.Assets; import format.SVG; //import gm2d.svg.Svg; //import gm2d.reso.Resources; //import gm2d.svg.SvgRenderer; /** * SVG display test. use haxenme/svg * @author mieki256 */ class Main extends Sprite { var inited:Bool; var bg:Sprite; var count:Int; /* ENTRY POINT */ function resize(e) { if (!inited) init(); // else (resize or orientation change) } function init() { if (inited) return; inited = true; // (your code here) // Stage: // stage.stageWidth x stage.stageHeight @ stage.dpiScale // Assets: // nme.Assets.getBitmapData("img/assetname.jpg"); // svg setting var svg = new SVG(Assets.getText("img/bg.svg")); var shape = new Shape(); svg.render(shape.graphics); shape.cacheAsBitmap = true; //var svg = new SvgRenderer(Resources.loadSvg("img/bg.svg")); //var shape = svg.createShape(); //shape.cacheAsBitmap = true; bg = new Sprite(); this.addChild(bg); bg.addChild(shape); shape.x = -320; shape.y = -180; bg.x = 320; bg.y = 180; this.addEventListener(Event.ENTER_FRAME, onEnterFrame); count = 0; } private function onEnterFrame(e:Event):Void { //bg.rotation = 20 * Math.cos((count * 4) * Math.PI / 180.0); count++; } /* SETUP */ public function new() { super(); addEventListener(Event.ADDED_TO_STAGE, added); } function added(e) { removeEventListener(Event.ADDED_TO_STAGE, added); stage.addEventListener(Event.RESIZE, resize); #if ios haxe.Timer.delay(init, 100); // iOS 6 #else init(); #end } public static function main() { // static entry point Lib.current.stage.align = flash.display.StageAlign.TOP_LEFT; Lib.current.stage.scaleMode = flash.display.StageScaleMode.NO_SCALE; Lib.current.addChild(new Main()); } }
以下の3行が肝心の部分、なのかな。
var svg = new SVG(Assets.getText("img/bg.svg")); var shape = new Shape(); svg.render(shape.graphics);
◎ SVG表示の不具合について。 :
以下の画像のような感じになる。
グラデーションの色がおかしくなる件。
Flash ... OK。
Neko ... OK。
HTML5 ... NG。色がおかしい。
ストロークが入っていると表示がおかしくなる件。
Flash ... OK。
Neko ... OK。
HTML5 ... NG。ストロークが無いはずの部分にまでストロークがついている。
グラデーションの色がおかしくなる件。
Flash ... OK。
Neko ... OK。
HTML5 ... NG。色がおかしい。
ストロークが入っていると表示がおかしくなる件。
Flash ... OK。
Neko ... OK。
HTML5 ... NG。ストロークが無いはずの部分にまでストロークがついている。
◎ マウス座標の取得でハマったり。 :
画像を表示してマウスカーソルを追いかける、という処理を試しに書いていたのだけど。 Flash や Neko上ではすんなりと正常動作したものの、HTML5上ではうんともすんとも反応しなくてハマったり。
Spriteのプロパティ値を疑って、半日ほどアレコレ試していたけど。そこではなくて、マウス座標がそもそも取得できてない場面があったのが原因だった。undefined だか NaN だかの値をマウス座標として得てしまった際に、Sprite の x座標にもその値が入ってしまって、その後も、その計算不能な値を使って計算しようとするから、ずっと計算できずにピクリとも動かなくなっていた。
Main.hx
Spriteのプロパティ値を疑って、半日ほどアレコレ試していたけど。そこではなくて、マウス座標がそもそも取得できてない場面があったのが原因だった。undefined だか NaN だかの値をマウス座標として得てしまった際に、Sprite の x座標にもその値が入ってしまって、その後も、その計算不能な値を使って計算しようとするから、ずっと計算できずにピクリとも動かなくなっていた。
Main.hx
package ; import flash.display.Bitmap; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.Lib; import flash.text.TextField; import openfl.Assets; /** * OpenFLテスト。マウスカーソルを画像が追いかける。 * @author mieki256 */ class Main extends Sprite { var inited:Bool; var spr:Sprite; var bmp:Bitmap; var tf:TextField; var mousex:Float; var mousey:Float; /* ENTRY POINT */ function resize(e) { if (!inited) init(); // else (resize or orientation change) } /** * 初期化処理 */ function init() { if (inited) return; inited = true; mousex = 320; mousey = 180; // 画像を描画 spr = new Sprite(); bmp = new Bitmap(Assets.getBitmapData("img/chara.png")); bmp.x = - bmp.width / 2; bmp.y = - bmp.height / 2; spr.addChild(bmp); Lib.current.addChild(spr); // 文字列を描画 tf = new TextField(); tf.selectable = false; tf.width = 640; tf.height = 48; tf.textColor = 0x333333; tf.text = "start"; Lib.current.addChild(tf); // ステージ上でマウスが動いた際のイベントを登録 Lib.current.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove); // 毎フレームの処理を登録 this.addEventListener(Event.ENTER_FRAME, mainUpdate); } /** * マウスカーソルが動いた時の処理 * @param event */ function onMouseMove(event:MouseEvent):Void { mousex = event.stageX; mousey = event.stageY; } /** * メインループ部分相当 * @param e */ private function mainUpdate(e:Event):Void { var mx:Float; var my:Float; // マウス座標を取得 switch (1) { case 0: // html5上でも動く。 mx = mouseX; my = mouseY; case 1: // html5上でも動く。 mx = mousex; my = mousey; default: // html5上では動かない。Flash や Neko では動く。 mx = Lib.current.stage.mouseX; my = Lib.current.stage.mouseY; } // キャラクタ画像が、マウスカーソルを追いかける処理 var spd:Float = 0.04; var dx:Float = (mx - spr.x) * spd; var dy:Float = (my - spr.y) * spd; spr.x = spr.x + dx; spr.y = spr.y + dy; tf.text = 'mouse x,y = $mx, $my'; //tf.text = 'spr x,y = ${spr.x}, ${spr.y}'; } /* SETUP */ public function new() { super(); addEventListener(Event.ADDED_TO_STAGE, added); } function added(e) { removeEventListener(Event.ADDED_TO_STAGE, added); stage.addEventListener(Event.RESIZE, resize); #if ios haxe.Timer.delay(init, 100); // iOS 6 #else init(); #end } public static function main() { // static entry point Lib.current.stage.align = flash.display.StageAlign.TOP_LEFT; Lib.current.stage.scaleMode = flash.display.StageScaleMode.NO_SCALE; Lib.current.addChild(new Main()); } }
[ ツッコむ ]
以上、1 日分です。