炎エフェクト(Android検証用)
概要
・Androidで動的な炎がどの程度実用的なのか確認するためのコード
検証用メモ
・ラベルをクリックするとオンオフの切替が可能
・UseBlur
・ブラーを使うか否か
・ブラーを使うとより炎っぽくなり自然に見える
・基本的にブラーは重いはず
・UseSubtract
・パーリンノイズを加算で使うか減算で使うか
・OpenGLはベース部分では減算をサポートしてないので、減算だと遅くなるかもしれない
動作メモ(Desire HD)
・アプリ(ランチャー内のFireTest)
・https://play.google.com/store/apps/details?id=air.showohealer.game.airprototype
/**
* Copyright o_healer ( http://wonderfl.net/user/o_healer )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/bYBE
*/
/*
炎エフェクト(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();
}
}
}