Forked from: o_healer's 炎エフェクト(Android検証用) diff:1 forked from: 炎エフェクト(Android検証用) rod1 forked:0favorite:0lines:211license : MIT License modified : 2012-04-27 23:46:32 Embed Tweet // forked from o_healer's 炎エフェクト(Android検証用) /* 炎エフェクト(Android検証用) 概要 ・Androidで動的な炎がどの程度実用的なのか確認するためのコード 検証用メモ ・ラベルをクリックするとオンオフの切替が可能 ・UseBlur ・ブラーを使うか否か ・ブラーを使うとより炎っぽくなり自然に見える ・基本的にブラーは重いはず ・UseSubtract ・パーリンノイズを加算で使うか減算で使うか ・OpenGLはベース部分では減算をサポートしてないので、減算だと遅くなるかもしれない 動作メモ(Desire HD) ・アプリ(ランチャー内のFireTest) ・https://play.google.com/store/apps/details?id=air.showohealer.game.airprototype ・全てオフにしたら十分な速度で動いた ・ただし、白バックの黒炎限定になるので使いどころが難しい ・ブラーをかけるだけでだいぶ重くなる ・減算にしても特に処理落ちしてなかった ・黒バックの炎になるので、これをさらにどこかに加算すれば良さそう 参考 ・以下のコードをさらに簡略化したもの ・http://wonderfl.net/c/dzI0 ・さらに元をたどれば以下を参考にしたもの ・http://wonderfl.net/c/rolo/ */ package { import flash.display.*; import flash.events.*; import flash.filters.*; import flash.geom.*; import flash.net.*; import flash.system.*; import flash.text.*; import flash.media.*; [SWF(width="465", height="465", frameRate="30", backgroundColor="0x000000")] public class FireTest extends Sprite { //==Const== //表示サイズ static public const VIEW_W:int = 465; static public const VIEW_H:int = 465; //テキスト兼ボタンを並べる際の間隔 static public const TEXT_OFFSET_Y:int = 32; //Utility static public const VIEW_RECT:Rectangle = new Rectangle(0,0,VIEW_W,VIEW_H); static public const POS_ZERO:Point = new Point(0,0); //==Var== //#炎の描画&処理まわり //実際の表示に使うビットマップ public var m_BitmapData_View:BitmapData = new BitmapData(VIEW_W, VIEW_H, true, 0x00000000); public var m_Bitmap_View:Bitmap = new Bitmap(m_BitmapData_View); //炎の状態を0x00~0xFFで保持するデータ public var m_BitmapData_Fire:BitmapData = new BitmapData(VIEW_W, VIEW_H, false, 0x00); public var m_Bitmap_Fire:Bitmap = new Bitmap(m_BitmapData_Fire); //Fire => Viewの変換のためのパレット(0x00~0xFFの値を、実際の炎の色に置き換える) // public var m_Palette_Fire_to_View:Array = new Array(256); //減衰に使うためのパーリンノイズ public var m_BitmapData_PerlinNoise:BitmapData = new BitmapData(VIEW_W * 2, VIEW_H * 2, false, 0x000000); //スクロールに使うマトリクス public var m_Mtx_PerlinNoise:Matrix = new Matrix(); //広げるためのブラーフィルター public var m_Filter_FireBlur:BlurFilter = new BlurFilter(4,4); //#発火部分の描画まわり static public const LINE_W:uint = 16; public var mouseX_Old:int = 0; public var mouseY_Old:int = 0; public var fireShape:Shape = new Shape(); public var fireShape_Fix:Shape = new Shape(); //#背景 public var m_BitmapData_BG:BitmapData = new BitmapData(VIEW_W, VIEW_H, false, 0x000000); //#検証用フラグ // public var m_UsePalette:Boolean = false; public var m_UseBlur:Boolean = false; public var m_UseSubtract:Boolean = false; //#テキスト兼ボタン public var m_Text_UseBlur:TextField = new TextField(); public var m_Text_UseSubtract:TextField = new TextField(); //==Function== //Init public function FireTest(){ if(stage != null){ Init(null); }else{ addEventListener( Event.ADDED_TO_STAGE,//ステージに追加されたら Init ); } } public function Init(e:Event):void { var i:int; // //Settings // { // //画面幅に合わせ、長い方が画面にフィットするように拡大 // //- 短い方は画面にフィットせず、もともと画面外だったものが内部表示されるかも // stage.scaleMode = StageScaleMode.SHOW_ALL; // } //m_BitmapData_PerlinNoise { //普通にパーリンノイズを生成して const PanelLen:int = 24;//火種のおおまかな大きさ(ドット絵に使うので、1マス=32ドットあたりを想定) const Octave:int = 2;//変化は雑でいい const stitch:Boolean = true;//端はループできる感じで const fractalNoise:Boolean = true;//なめらかな変化で const channel:uint = 7;//全てのチャンネルを使用して const grayScale:Boolean = true;//RGBは全て同じ値で m_BitmapData_PerlinNoise.perlinNoise(PanelLen,PanelLen, Octave, 1000*Math.random(), stitch, fractalNoise, channel, grayScale); //それを縦に2枚並べる形にして(スクロールするため。つなぎ目は気にしない) m_BitmapData_PerlinNoise.copyPixels(m_BitmapData_PerlinNoise, new Rectangle(0,0,VIEW_W*2,VIEW_H), new Point(0,VIEW_H)); //減衰に使うため値を抑制 const ratio:Number = 0.09;//小さくすると炎の伸びが大きくなる m_BitmapData_PerlinNoise.colorTransform( m_BitmapData_PerlinNoise.rect,//VIEW_RECTとは範囲が違うので、直のrectを使う new ColorTransform(ratio,ratio,ratio)//値を減衰させる ); } /* //m_Palette_Fire_to_View { for(i = 0; i < 256; i++){ //Cosによって発火部分と消える部分の境界を薄める //さらにPowによって減衰の速さを調整する(白→黄色→橙になるように) var r:uint = 0xFF * (0.5 - 0.5*Math.cos(Math.PI * Math.pow(i/255, 1.0))); var g:uint = 0xFF * (0.5 - 0.5*Math.cos(Math.PI * Math.pow(i/255, 1.5))); var b:uint = 0xFF * (0.5 - 0.5*Math.cos(Math.PI * Math.pow(i/255, 3.0))); m_Palette_Fire_to_View[i] = (0xFF<<24)|(r<<16)|(g<<8)|(b<<0); } } //*/ //背景 { addChild(new Bitmap(m_BitmapData_BG)); } //炎の表示 { //m_Bitmap_View.blendMode = BlendMode.ADD;//加算表現にすることによって、黒=透明として扱える // if(m_UsePalette){ // addChild(m_Bitmap_View); // }else{ addChild(m_Bitmap_Fire); // } } //マウスによる発火処理 { stage.addEventListener(MouseEvent.MOUSE_MOVE, DrawEmit); stage.addEventListener(MouseEvent.MOUSE_DOWN, OnMouseDown); } //Update { addEventListener(Event.ENTER_FRAME, Update); } //Text { m_Text_UseBlur.selectable = false; m_Text_UseBlur.autoSize = TextFieldAutoSize.LEFT; m_Text_UseBlur.defaultTextFormat = new TextFormat('Verdana', 24, 0xFFFFFF, true); m_Text_UseBlur.text = ''; m_Text_UseBlur.filters = [new GlowFilter(0x000000,1.0, 2,2)]; m_Text_UseBlur.y = VIEW_H - 2*TEXT_OFFSET_Y; addChild(m_Text_UseBlur); } { m_Text_UseSubtract.selectable = false; m_Text_UseSubtract.autoSize = TextFieldAutoSize.LEFT; m_Text_UseSubtract.defaultTextFormat = new TextFormat('Verdana', 24, 0xFFFFFF, true); m_Text_UseSubtract.text = ''; m_Text_UseSubtract.filters = [new GlowFilter(0x000000,1.0, 2,2)]; m_Text_UseSubtract.y = VIEW_H - 1*TEXT_OFFSET_Y; addChild(m_Text_UseSubtract); } //Flag { Refresh_UseBlur(); Refresh_UseSubtract(); } //OnEnd { addEventListener(Event.REMOVED_FROM_STAGE, Finish); } //Test // Switch_UseSubtract(); } //Finish public function Finish(e:Event):void{ removeEventListener(Event.ADDED_TO_STAGE, Init); removeEventListener(Event.ENTER_FRAME, Update); removeEventListener(Event.REMOVED_FROM_STAGE, Finish); } //Update public function Update(e:Event=null):void{ //var DeltaTime:Number = 1.0 / stage.frameRate; //発火部分の描画 Emit(); //炎表現 DrawFire(); } //Emit:マウスに応じて発火 public function DrawEmit(e:MouseEvent):void{ var color:uint = m_UseSubtract? 0xFF8800: 0x000000; if(e.buttonDown){//クリックされてる間だけ、固定発火の描画を行う var g:Graphics = fireShape_Fix.graphics; //マウスの動きに応じて線 { g.lineStyle(LINE_W,color,1.0); g.moveTo(mouseX_Old, mouseY_Old); g.lineTo(mouseX, mouseY); } } } public function Emit():void{ var color:uint = m_UseSubtract? 0xFF8800: 0x000000; var g:Graphics = fireShape.graphics; //マウス位置に円 { const RAD:uint = 8; g.lineStyle(0,0,0); g.beginFill(color, 1.0); g.drawCircle(mouseX, mouseY, RAD); g.endFill(); } //マウスの動きに応じて線 { g.lineStyle(LINE_W,color,1.0); g.moveTo(mouseX_Old, mouseY_Old); g.lineTo(mouseX, mouseY); } //発火元として描画 { m_BitmapData_Fire.draw(fireShape); m_BitmapData_Fire.draw(fireShape_Fix); } //次回用更新 { //揮発タイプは毎回リセット fireShape.graphics.clear(); mouseX_Old = mouseX; mouseY_Old = mouseY; } } //Button public function OnMouseDown(e:MouseEvent):void{ if(VIEW_H - 1*TEXT_OFFSET_Y < mouseY){ Switch_UseSubtract(); return; } if(VIEW_H - 2*TEXT_OFFSET_Y < mouseY){ Switch_UseBlur(); return; } } //DrawFire:炎の自動描画 public function DrawFire():void{ //描画処理 { //ブラーで炎を広げる if(m_UseBlur) { //薄めることで、上の方を細くする効果も兼ねる m_BitmapData_Fire.applyFilter(m_BitmapData_Fire, VIEW_RECT, POS_ZERO, m_Filter_FireBlur); } //全体的に沈静化 if(m_UseSubtract) { //パーリンノイズで減衰することで揺らぎを表現 m_BitmapData_Fire.draw(m_BitmapData_PerlinNoise, m_Mtx_PerlinNoise, null, BlendMode.SUBTRACT); } else { m_BitmapData_Fire.draw(m_BitmapData_PerlinNoise, m_Mtx_PerlinNoise, null, BlendMode.ADD); } // //そして0x00~0xFFの値を炎の色に置き換えて表示 // if(m_UsePalette) // { // m_BitmapData_View.paletteMap(m_BitmapData_Fire, VIEW_RECT, POS_ZERO, null,null,m_Palette_Fire_to_View); // } } //次回用の更新 { const ScrollVal:int = 2; //炎を上にスクロールさせて、燃え上がりを実現 { //切り捨てて良いスクロールなので、普通にscrollを呼ぶ m_BitmapData_Fire.scroll(0, -ScrollVal); } //パーリンノイズの採用位置を変更 { //横方向には少しだけ振動させ、上下方向は炎の上昇と合わせることで、それっぽい揺らぎを作り出す m_Mtx_PerlinNoise.tx += (Math.random() < 0.5)? 1: -1;//振動 m_Mtx_PerlinNoise.ty -= ScrollVal;//追随 //範囲チェック if(m_Mtx_PerlinNoise.tx > 0){m_Mtx_PerlinNoise.tx -= 2;}//範囲外は押し戻す if(m_Mtx_PerlinNoise.tx < -VIEW_W){m_Mtx_PerlinNoise.tx += 2;} if(m_Mtx_PerlinNoise.ty < -VIEW_H){m_Mtx_PerlinNoise.ty += VIEW_H;}//下にワープ } } } public function Switch_UseBlur():void{ m_UseBlur = !m_UseBlur; Refresh_UseBlur(); } public function Refresh_UseBlur():void{ if(m_UseBlur){ m_Text_UseBlur.text = "UseBlur : On"; }else{ m_Text_UseBlur.text = "UseBlur : Off"; } } /* public function Switch_UsePalette():void{ m_UsePalette = !m_UsePalette; //!!描画colorやPaletteの内容も変更が必要 if(m_UsePalette){ m_Bitmap_Fire.parent.addChild(m_Bitmap_View); m_Bitmap_Fire.parent.removeChild(m_Bitmap_Fire); }else{ m_Bitmap_View.parent.addChild(m_Bitmap_Fire); m_Bitmap_View.parent.removeChild(m_Bitmap_View); } } //*/ public function Switch_UseSubtract():void{ m_UseSubtract = !m_UseSubtract; Refresh_UseSubtract(); } public function Refresh_UseSubtract():void{ if(m_UseSubtract){ m_BitmapData_Fire.fillRect(m_BitmapData_Fire.rect, 0x000000); m_Text_UseSubtract.text = "UseSubtract : On"; }else{ m_BitmapData_Fire.fillRect(m_BitmapData_Fire.rect, 0xFFFFFF); m_Text_UseSubtract.text = "UseSubtract : Off"; } fireShape_Fix.graphics.clear(); } } } Code Fullscreen Preview Fullscreen