炎々
炎を絶やさないように延々とブロックを継ぎ続けるパズル
操作方法
- 十字キー
-- 移動
- SPACE
-- 回転
/**
* 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/9ygD
*/
/*
「炎々」
・for wonderfl
概要
・炎を絶やさないようにブロックを延々と繋げていくゲーム
・以前に作ったやつの移植シリーズ
・ついでに、延焼の「アルゴリズム」と「描画」を改善した
操作方法
・←→:移動
・↓:高速落下
・SPACE:回転
アルゴリズム
・BitmapDataでパネルデータを管理し、ビットマップ操作だけで延焼を実現している
・RGBの役割
・R:発火の開始方向を表す(PANEL_FIRE_UなどをORでつなげたもの)
・G:不使用
・B:ブロックの有無を表す(PANEL_NONEかPANEL_BLOCKか)
・延焼アルゴリズム
・Rが存在すればそれをConvolutionFilterで上下左右に拡大して、ブロックがあるところだけ採用する
炎と木目の描画は以下のやつを利用
・http://wonderfl.net/c/dzI0
未対応
・ハイスコア管理
・炎が全て消えた場合のゲームオーバー処理
・ブロック回転時に画像が再生成されてしまっている問題の修正
*/
package
{
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import flash.net.*;
import flash.text.*;
import flash.filters.*;
import flash.ui.*;
import flash.system.*;
[SWF(width="465", height="465", frameRate="30", backgroundColor="0x000000")]
public class GameMain extends Sprite
{
//==Const==
//*
static public const URL_BLOCKS:String = "http://assets.wonderfl.net/images/related_images/e/e4/e466/e4667aa771860a2002c45728da23c52285814e86";
/*/
static public const URL_BLOCKS:String = "Blocks.png";
//*/
/*
■:I
■
■
■
■■:O
■■
■
■
■■:L
■
■
■■:R
■■■:T
■
■■:S
■■
■■
■■:Z
*/
static public var BlockTypeIter:int = 0;
static public const BLOCK_TYPE_I:int = BlockTypeIter++;
static public const BLOCK_TYPE_O:int = BlockTypeIter++;
static public const BLOCK_TYPE_L:int = BlockTypeIter++;
static public const BLOCK_TYPE_R:int = BlockTypeIter++;
static public const BLOCK_TYPE_T:int = BlockTypeIter++;
static public const BLOCK_TYPE_S:int = BlockTypeIter++;
static public const BLOCK_TYPE_Z:int = BlockTypeIter++;
static public const BLOCK_TYPE_NUM:int = BlockTypeIter;
//回転量(時計回り方向)
static public var RotIter:int = 0;
static public const ROT_0:uint = RotIter++;
static public const ROT_90:uint = RotIter++;
static public const ROT_180:uint = RotIter++;
static public const ROT_270:uint = RotIter++;
static public const ROT_NUM:uint = RotIter;
static public const BLOCK_FORM:Array = [
//I
[
//ROT_0
[
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
],
//ROT_90
[
[0, 0, 0, 0],
[0, 0, 0, 0],
[1, 1, 1, 1],
[0, 0, 0, 0],
],
//ROT_180
[
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
],
//ROT_270
[
[0, 0, 0, 0],
[0, 0, 0, 0],
[1, 1, 1, 1],
[0, 0, 0, 0],
],
],
//O
[
//ROT_0
[
[0, 0, 0, 0],
[0, 1, 1, 0],
[0, 1, 1, 0],
[0, 0, 0, 0],
],
//ROT_90
[
[0, 0, 0, 0],
[0, 1, 1, 0],
[0, 1, 1, 0],
[0, 0, 0, 0],
],
//ROT_180
[
[0, 0, 0, 0],
[0, 1, 1, 0],
[0, 1, 1, 0],
[0, 0, 0, 0],
],
//ROT_270
[
[0, 0, 0, 0],
[0, 1, 1, 0],
[0, 1, 1, 0],
[0, 0, 0, 0],
],
],
//L
[
//ROT_0
[
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 1, 0],
[0, 0, 0, 0],
],
//ROT_90
[
[0, 0, 0, 0],
[0, 1, 1, 1],
[0, 1, 0, 0],
[0, 0, 0, 0],
],
//ROT_180
[
[0, 0, 0, 0],
[0, 1, 1, 0],
[0, 0, 1, 0],
[0, 0, 1, 0],
],
//ROT_270
[
[0, 0, 0, 0],
[0, 0, 1, 0],
[1, 1, 1, 0],
[0, 0, 0, 0],
],
],
//R
[
//ROT_0
[
[0, 0, 1, 0],
[0, 0, 1, 0],
[0, 1, 1, 0],
[0, 0, 0, 0],
],
//ROT_90
[
[0, 0, 0, 0],
[0, 1, 0, 0],
[0, 1, 1, 1],
[0, 0, 0, 0],
],
//ROT_180
[
[0, 0, 0, 0],
[0, 1, 1, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
],
//ROT_270
[
[0, 0, 0, 0],
[1, 1, 1, 0],
[0, 0, 1, 0],
[0, 0, 0, 0],
],
],
//T
[
//ROT_0
[
[0, 0, 0, 0],
[1, 1, 1, 0],
[0, 1, 0, 0],
[0, 0, 0, 0],
],
//ROT_90
[
[0, 1, 0, 0],
[1, 1, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 0],
],
//ROT_180
[
[0, 1, 0, 0],
[1, 1, 1, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
],
//ROT_270
[
[0, 1, 0, 0],
[0, 1, 1, 0],
[0, 1, 0, 0],
[0, 0, 0, 0],
],
],
//S
[
//ROT_0
[
[0, 0, 0, 0],
[0, 1, 1, 0],
[1, 1, 0, 0],
[0, 0, 0, 0],
],
//ROT_90
[
[1, 0, 0, 0],
[1, 1, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 0],
],
//ROT_180
[
[0, 0, 0, 0],
[0, 1, 1, 0],
[1, 1, 0, 0],
[0, 0, 0, 0],
],
//ROT_270
[
[1, 0, 0, 0],
[1, 1, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 0],
],
],
//Z
[
//ROT_0
[
[0, 0, 0, 0],
[1, 1, 0, 0],
[0, 1, 1, 0],
[0, 0, 0, 0],
],
//ROT_90
[
[0, 1, 0, 0],
[1, 1, 0, 0],
[1, 0, 0, 0],
[0, 0, 0, 0],
],
//ROT_180
[
[0, 0, 0, 0],
[1, 1, 0, 0],
[0, 1, 1, 0],
[0, 0, 0, 0],
],
//ROT_270
[
[0, 1, 0, 0],
[1, 1, 0, 0],
[1, 0, 0, 0],
[0, 0, 0, 0],
],
],
];
//擬似マップ用
static public var m_MapIter:int = 0;
static public const O:int = m_MapIter++;//空白
static public const W:int = m_MapIter++;//壁
//Stage
static public const VIEW_W:int = 465;
static public const VIEW_H:int = 465;
//Panel
static public const PANEL_LEN:int = 32;
//Map
static public const MAP_NUM_X:int = 12;
static public const MAP_NUM_Y:int = 14;
//Block Param
static public const FALL_VEL:Number = 15.0;
static public const FALL_ACCEL_RATIO:Number = 20.0;
//Panel : B : ブロック
static public const PANEL_NONE:int = 0x00;
static public const PANEL_BLOCK:int = 0x01;
//Panel : R : 炎
static public const PANEL_FIRE_U:int = 0x01;
static public const PANEL_FIRE_D:int = 0x02;
static public const PANEL_FIRE_L:int = 0x04;
static public const PANEL_FIRE_R:int = 0x08;
//Input
static public var InputIter:int = 0;
static public const INPUT_U:int = InputIter++;
static public const INPUT_D:int = InputIter++;
static public const INPUT_L:int = InputIter++;
static public const INPUT_R:int = InputIter++;
static public const INPUT_ROT:int = InputIter++;
static public const INPUT_NUM:int = InputIter;
//Util
static public const VIEW_RECT:Rectangle = new Rectangle(0,0, VIEW_W,VIEW_H);
static public const DATA_RECT:Rectangle = new Rectangle(0,0, MAP_NUM_X,MAP_NUM_Y);
static public const PANEL_RECT:Rectangle = new Rectangle(0,0, PANEL_LEN,PANEL_LEN);
static public const POS_ZERO:Point = new Point(0,0);
//燃え広がる速さ
public var INTERVAL_MIN:Number = 0.05;//最速
public var INTERVAL_MAX:Number = 2.0;//最遅
//Debug
static public const IS_DEBUG:Boolean = false;
//==Var==
//--基本
//Root
public var m_Root:Sprite = new Sprite();
//BMP * BG
public var m_Bitmap_BG:Bitmap;
//BMP : View
public var m_BitmapData_View:BitmapData = new BitmapData(VIEW_W, VIEW_H, true, 0x00000000);
//テキスト
public var m_Text:TextField = new TextField();
//ゲームオーバーフラグ
public var m_IsGameOver:Boolean = false;
//--移動させるブロックまわり
//ブロックの形状(ブロックがある部分を1にする)
public var m_Data_MoveBlock:BitmapData = new BitmapData(4,4, false, 0);
//ブロックの画像
public var m_Bitmap_MoveBlock:Bitmap = new Bitmap(new BitmapData(4*PANEL_LEN, 4*PANEL_LEN, true, 0x00000000));
//現在のブロックの情報
public var m_BlockIndex:int = 0;
public var m_RotIter:int = 0;
//入力
public var m_Input:Array = new Array(INPUT_NUM);
public var m_Input_Old:Array = new Array(INPUT_NUM);//エッジ検出用
//--データまわり
//BMP : Panel(ブロックの有無や炎の管理用)
public var m_BitmapData_Data:BitmapData = new BitmapData(MAP_NUM_X, MAP_NUM_Y, false, 0x000000);
//BMP : Util(作業用)
public var m_BitmapData_Util:BitmapData = new BitmapData(VIEW_W, VIEW_H, false, 0x000000);
//Filter : 延焼処理
public var m_Filter_FireSpread:ConvolutionFilter;
//Filter : 着火処理
public var m_Filter_FireMask:ColorMatrixFilter;
//--炎描画まわり
//実際の炎の描画
public var m_BitmapData_Fire_View:BitmapData = new BitmapData(VIEW_W, VIEW_H, false, 0x00);
//炎の状態を0x00~0xFFで保持するデータ
public var m_BitmapData_Fire_Data:BitmapData = new BitmapData(VIEW_W, VIEW_H, false, 0x00);
//Fire => Viewの変換のためのパレット(0x00~0xFFの値を、実際の炎の色に置き換える)
public var m_Palette_Data_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);
//--燃え広がる処理
//発火点描画の事前計算用
public var m_BitmapData_Emit:Array = new Array(16);
//燃えた部分を消すマスクの事前計算用
public var m_BitmapData_Mask:Array = new Array(16);
//Timer
public var m_TotalTimer:Number = 0.0;
public var m_FireTimer:Number = 0.0;//Intervalごとにリセット
//Interval : 燃え広がる速度
public var m_Interval:Number = 5.0;//現在の値
public var m_Interval_HalfTime:Number = 60.0;//時間がこれだけかかったらMinとMaxの中間になる
//--Debug
public var m_DebugPalette_R:Array;
public var m_DebugPalette_B:Array;
public var m_DebugPalette_Zero:Array;
//==Function==
public function GameMain(){
//画像ロード開始
{
const LoadFunc:Function = function(in_URL:String, in_OnLoad:Function):void{
var loader:Loader = new Loader();
loader.load(new URLRequest(in_URL), new LoaderContext(true));//画像のロードを開始して
loader.contentLoaderInfo.addEventListener(
Event.COMPLETE,//ロードが完了したら
function(e:Event):void{
in_OnLoad(loader.content);//初期化に入る
}
);
}
LoadFunc(URL_BLOCKS, OnLoadEnd_Blocks);
}
}
public function OnLoadEnd_Blocks(in_Graphic:DisplayObject):void{
//Init ImageManager
{
ImageManager.Init(in_Graphic);//それを保持した後
}
//初期化
{
Init();
}
}
public function Init():void{
var x:int;
var y:int;
//=延焼アルゴリズム=
//m_Filter_FireSpread
{
//#延焼処理
//炎が1、それ以外が0の状態で、上下左右にPANEL_FIRE_Uなどを伝搬させる
m_Filter_FireSpread = new ConvolutionFilter(
3,3,
[
0, PANEL_FIRE_U, 0,
PANEL_FIRE_L, -0xFF, PANEL_FIRE_R,
0, PANEL_FIRE_D, 0
]
);
}
//m_Filter_FireMask
{
//#着火処理
//ブロックのある部分(B=1)だけ炎(Rの値)を残す
m_Filter_FireMask = new ColorMatrixFilter([
1, 0, 0xFF, 0, -0xFF,//R = R + 0xFF*(B-1)
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0
]);
}
//=Param=
//m_Interval
{
m_Interval = INTERVAL_MAX;
}
//=BG=
{
//マップのサイズ
var MapW:int = MAP_NUM_X * PANEL_LEN;
var MapH:int = MAP_NUM_Y * PANEL_LEN;
//マップ以外の部分のサイズ
var RestW:int = VIEW_W - MapW;
var RestH:int = VIEW_H - MapH;
//マップ以外のところに必要なパネルの数
var SideNumX:int = Math.ceil(RestW/2 / PANEL_LEN);
var SideNumY:int = Math.ceil(RestH/2 / PANEL_LEN);
//背景に必要なパネルの数
var TotalNumX:int = MAP_NUM_X + 2 * SideNumX;
var TotalNumY:int = MAP_NUM_Y + 2 * SideNumY;
//背景用ビットマップのサイズ
var BgBitmapW:int = TotalNumX * PANEL_LEN;
var BgBitmapH:int = TotalNumY * PANEL_LEN;
//背景用ビットマップ
var BitmapData_BG:BitmapData = new BitmapData(BgBitmapW, BgBitmapH, false, 0x000000);
//背景のパネル配置
var Map:Array = new Array(TotalNumY);
for(y = 0; y < TotalNumY; y++){
Map[y] = new Array(TotalNumX);
for(x = 0; x < TotalNumX; x++){
var index:int = O;
if(x < SideNumX){index = W;}
if(x >= TotalNumX - SideNumX){index = W;}
if(y >= TotalNumY - SideNumY){index = W;}
Map[y][x] = index;
}
}
//背景の描画
ImageManager.DrawBlocks(Map, BitmapData_BG);
//背景の配置
m_Bitmap_BG = new Bitmap(BitmapData_BG);
m_Bitmap_BG.x = MapW/2 - BgBitmapW/2;
m_Bitmap_BG.y = MapH/2 - BgBitmapH/2;
}
//=炎描画=
//m_BitmapData_PerlinNoise
{
//普通にパーリンノイズを生成して
const PanelLen:int = 24;//火種のおおまかな大きさ(ドット絵に使うので、1マス=32ドットあたりを想定)
const Octave:int = 2;//変化は雑でいい
m_BitmapData_PerlinNoise.perlinNoise(PanelLen,PanelLen, Octave, 1000*Math.random(), true, true);
//それを縦に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_Data_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_Data_to_View[i] = (0xFF<<24)|(r<<16)|(g<<8)|(b<<0);
}
}
//=発火点まわり=
//m_BitmapData_Emit
{
for(i = 0; i < 16; i++){
m_BitmapData_Emit[i] = new BitmapData(PANEL_LEN, PANEL_LEN, false, 0x00);
}
//[0]だけは特殊なのでここで用意
{
//thresholdで境界線が燃え広がる感じに移動するように値を設定
for(y = 0; y < 32; y++){
var Ratio:Number = y/(32-1);
for(x = 0; x < 32; x++){
/*
var GapX:int = 32/2 - Math.abs(32/2 - x);
const HalfGap:Number = 12.0;
var p:Number = HalfGap / (GapX+HalfGap);
m_BitmapData_Emit[0].setPixel(x, y, 0xFF*Math.pow(Ratio, p));
/*/
var GapX:int = Math.abs(32/2 - x);
var GapY:int = 32 - y;
var theta:Number = Math.atan2(GapY, GapX);
var color:uint;
if(Math.atan2(2,1) < theta){
color = 0xFF * Ratio;
}else{
theta = Math.atan2(GapY, 32/2-GapX);
var tan:Number = Math.tan(theta);
var rate:Number;
if(tan > 0.01){
var souji_x:Number = (32 / tan)
rate = souji_x / (32/2 + souji_x);
}else{
rate = 1.0;
}
color = 0xFF * rate;
}
m_BitmapData_Emit[0].setPixel(x, 32-1 - y, color);
//*/
}
}
//test
m_BitmapData_Emit[0].applyFilter(m_BitmapData_Emit[0], PANEL_RECT, POS_ZERO, m_Filter_FireBlur);
}
}
//m_BitmapData_Mask
{
for(i = 0; i < 16; i++){
m_BitmapData_Mask[i] = new BitmapData(PANEL_LEN, PANEL_LEN, false, 0xFF);
}
}
//==
//View
{
m_Root.x = VIEW_W/2 - (PANEL_LEN * MAP_NUM_X)/2;
m_Root.y = VIEW_H/2 - (PANEL_LEN * MAP_NUM_Y)/2;
addChild(m_Root);
{
//背景
m_Root.addChild(m_Bitmap_BG);
//ブロック部分
m_Root.addChild(new Bitmap(m_BitmapData_View));
//炎部分
var bmp:Bitmap = new Bitmap(m_BitmapData_Fire_View);
bmp.blendMode = BlendMode.ADD;//加算で表示
m_Root.addChild(bmp);
}
}
//Data
{
//#最初の状態を設定
//一番下の列にブロックを並べる
m_BitmapData_Data.fillRect(new Rectangle(0,MAP_NUM_Y-1,MAP_NUM_X,1), (PANEL_BLOCK<<0));
//m_BitmapData_Data.fillRect(new Rectangle(0,0,1,MAP_NUM_Y-1), (PANEL_BLOCK<<0));//test:縦ライン
//m_BitmapData_Data.fillRect(m_BitmapData_Data.rect, (PANEL_BLOCK<<0));//test:全体
//右端だけ着火
m_BitmapData_Data.setPixel(MAP_NUM_X-1,MAP_NUM_Y-1, (PANEL_FIRE_R<<16) | (PANEL_BLOCK<<0));
//m_BitmapData_Data.setPixel(MAP_NUM_X/2,MAP_NUM_Y-1, (PANEL_FIRE_D<<16) | (PANEL_BLOCK<<0));//test:中央下
//test
ImageManager.DrawBlock(m_BitmapData_View, m_BitmapData_Data);
}
//Block
{
m_Root.addChild(m_Bitmap_MoveBlock);
RecreateBlock();
}
//Input
{
//Reset
for(i = 0; i < INPUT_NUM; i++){
m_Input[i] = false;
m_Input_Old[i] = false;
}
//Listener
{
//キー入力を見る
stage.addEventListener(KeyboardEvent.KEY_DOWN, OnKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, OnKeyUp);
}
}
//Text
{
m_Text.selectable = false;
m_Text.autoSize = TextFieldAutoSize.LEFT;
m_Text.defaultTextFormat = new TextFormat('Verdana', 60, 0xFFFF00, true);
m_Text.text = '';
m_Text.filters = [new GlowFilter(0xFF0000)];
m_Root.addChild(m_Text);
}
//Call Update
{
addEventListener(Event.ENTER_FRAME, Update);
}
if(IS_DEBUG){
//Debug : Palette
{
m_DebugPalette_R = new Array(256);
m_DebugPalette_B = new Array(256);
m_DebugPalette_Zero = new Array(256);
for(var i:int = 0; i < 256; i++){
//R
switch(i){
case 0://何もなし
m_DebugPalette_R[i] = 0xFF000000;
break;
default://炎(方向の複合もあるので、0以外は全部)
m_DebugPalette_R[i] = 0xFFFF0000;
break;
}
//B
switch(i){
case 0://空間
m_DebugPalette_B[i] = 0xFF000000;
break;
case 1://ブロック
m_DebugPalette_B[i] = 0xFF0000FF;
break;
default://else
m_DebugPalette_B[i] = 0xFF888888;
break;
}
//Zero
m_DebugPalette_Zero[i] = 0x00000000;
}
}
//Debug:最初の可視化
{
Update_Debug();
}
}
}
//#Reset : Block
public function RecreateBlock():void{
//データ
{
//Clear
m_Data_MoveBlock.fillRect(m_Data_MoveBlock.rect, 0);
m_RotIter = 0;
//Create
m_BlockIndex = Math.random() * BLOCK_TYPE_NUM;
for(var y:int = 0; y < 4; y++){
for(var x:int = 0; x < 4; x++){
m_Data_MoveBlock.setPixel(x, y, BLOCK_FORM[m_BlockIndex][m_RotIter][y][x]);
}
}
}
//表示
{
//描画
ImageManager.DrawBlock(m_Bitmap_MoveBlock.bitmapData, m_Data_MoveBlock);
//位置
m_Bitmap_MoveBlock.x = 3 * PANEL_LEN;
m_Bitmap_MoveBlock.y = 0;
}
//生成時点でぶつかっているならゲームオーバー
{
var SrcX:int = m_Bitmap_MoveBlock.x / PANEL_LEN;
var SrcY:int = m_Bitmap_MoveBlock.y / PANEL_LEN;
if(! IsEmpty(SrcX, SrcY, m_RotIter)){
//フラグ
{
m_IsGameOver = true;
}
//表示
{
//Text
m_Text.text = 'GAME OVER';
//Centering
m_Text.x = (stage.stageWidth - m_Text.width) / 2;
m_Text.y = (stage.stageHeight - m_Text.height) / 2;
}
}
}
}
//#Input
private function OnKeyDown(event:KeyboardEvent):void{
if(event.keyCode == Keyboard.LEFT){ m_Input[INPUT_L] = true;}
if(event.keyCode == Keyboard.RIGHT){m_Input[INPUT_R] = true;}
if(event.keyCode == Keyboard.UP){ m_Input[INPUT_U] = true;}
if(event.keyCode == Keyboard.DOWN){ m_Input[INPUT_D] = true;}
if(event.keyCode == Keyboard.SPACE){m_Input[INPUT_ROT] = true;}
}
private function OnKeyUp(event:KeyboardEvent):void{
if(event.keyCode == Keyboard.LEFT){ m_Input[INPUT_L] = false;}
if(event.keyCode == Keyboard.RIGHT){m_Input[INPUT_R] = false;}
if(event.keyCode == Keyboard.UP){ m_Input[INPUT_U] = false;}
if(event.keyCode == Keyboard.DOWN){ m_Input[INPUT_D] = false;}
if(event.keyCode == Keyboard.SPACE){m_Input[INPUT_ROT] = false;}
}
private function UpdateInput_Post():void{
for(var i:int = 0; i < INPUT_NUM; i++){
m_Input_Old[i] = m_Input[i];
}
}
//#Update
public function Update(e:Event):void{
//var DeltaTime:Number = 1.0 / stage.frameRate;
var DeltaTime:Number = 1.0 / 30.0;
//ゲームオーバーならもう何もしない
if(m_IsGameOver){
return;
}
//ブロックを入力で動かす処理
Update_Block(DeltaTime);
//炎を広げたりマスを消したりする処理
Update_Fire(DeltaTime);
//入力更新(エッジとの兼ね合いのため、最後にやる)
UpdateInput_Post();
}
//移動先のチェック用関数
public function IsEmpty(in_OffsetX:int, in_OffsetY:int, in_RotIter:int):Boolean{
//
for(var y:int = 0; y < 4; y++){
for(var x:int = 0; x < 4; x++){
//ブロックがなければスルー
{
if(BLOCK_FORM[m_BlockIndex][in_RotIter][y][x] == 0){
continue;
}
}
//移動先のブロック位置
var BlockX:int = in_OffsetX + x;
var BlockY:int = in_OffsetY + y;
//範囲チェック
{//範囲外は壁とみなす
if(BlockX < 0){return false;}
if(BlockX >= MAP_NUM_X){return false;}
if(BlockY < 0){return false;}
if(BlockY >= MAP_NUM_Y){return false;}
}
//衝突チェック
{//移動先にすでにブロックがあれば移動不可
if(m_BitmapData_Data.getPixel(BlockX, BlockY) != 0){
return false;
}
}
}
}
//ひっかかるところがなかったので、空とみなす
return true;
}
//#Update : Block
public function Update_Block(in_DeltaTime:Number):void{
//現在の位置(Index)
var SrcX:int = m_Bitmap_MoveBlock.x / PANEL_LEN;
var SrcY_U:int = m_Bitmap_MoveBlock.y / PANEL_LEN;//Yは2マスをまたにかけるので両方用意
var SrcY_D:int = m_Bitmap_MoveBlock.y / PANEL_LEN + 1;
//入力による回転
{
if(m_Input[INPUT_ROT] && !m_Input_Old[INPUT_ROT]){//Edge : Rot
var RotIter:int;
{
RotIter = m_RotIter + 1;
if(RotIter >= 4){RotIter -= 4;}
}
if(IsEmpty(SrcX, SrcY_U, RotIter) && IsEmpty(SrcX, SrcY_D, RotIter)){
//回転を実行
m_RotIter = RotIter;
//m_Data_MoveBlock
for(var y:int = 0; y < 4; y++){
for(var x:int = 0; x < 4; x++){
m_Data_MoveBlock.setPixel(x, y, BLOCK_FORM[m_BlockIndex][m_RotIter][y][x]);
}
}
//m_Bitmap_MoveBlock
ImageManager.DrawBlock(m_Bitmap_MoveBlock.bitmapData, m_Data_MoveBlock);
}
}
}
//入力による左右移動
{
if(m_Input[INPUT_L] && !m_Input_Old[INPUT_L]){//Edge : Left
if(IsEmpty(SrcX-1, SrcY_U, m_RotIter) && IsEmpty(SrcX-1, SrcY_D, m_RotIter)){
m_Bitmap_MoveBlock.x = (SrcX-1) * PANEL_LEN;
}
}
if(m_Input[INPUT_R] && !m_Input_Old[INPUT_R]){//Edge : Right
if(IsEmpty(SrcX+1, SrcY_U, m_RotIter) && IsEmpty(SrcX+1, SrcY_D, m_RotIter)){
m_Bitmap_MoveBlock.x = (SrcX+1) * PANEL_LEN;
}
}
//再計算
SrcX = m_Bitmap_MoveBlock.x / PANEL_LEN;
}
//下移動
{
//入力があれば通常より速く落下
var Move:Number;
{
var FallVel:Number = FALL_VEL;
if(m_Input[INPUT_D]){
FallVel *= FALL_ACCEL_RATIO;
}
Move = FallVel * in_DeltaTime;
}
m_Bitmap_MoveBlock.y += Move;//端数が切り捨てられそうだが大丈夫っぽいのでこれで
//再計算
SrcY_U = m_Bitmap_MoveBlock.y / PANEL_LEN;
SrcY_D = m_Bitmap_MoveBlock.y / PANEL_LEN + 1;
}
//接地チェック
{
var mtx:Matrix = new Matrix(1,0,0,1, 0,0);
if(! IsEmpty(SrcX, SrcY_D, m_RotIter)){
//設置位置にセット
//m_Bitmap_MoveBlock.y = SrcY_U * PANEL_LEN;//厳密には、ぶつからないところまで上に移動すべきかもしれないが
//描き写す:データ
mtx.tx = SrcX; mtx.ty = SrcY_U;
m_BitmapData_Data.draw(m_Data_MoveBlock, mtx, null, BlendMode.LIGHTEN);
//描き写す:描画
mtx.tx *= PANEL_LEN; mtx.ty *= PANEL_LEN;
m_BitmapData_View.draw(m_Bitmap_MoveBlock.bitmapData, mtx);
//次のを生成
RecreateBlock();
}
}
}
//#Update : Fire
public function Update_Fire(in_DeltaTime:Number):void{
//Timer
{
m_TotalTimer += in_DeltaTime;
m_FireTimer += in_DeltaTime;
}
//一定周期ごとに炎のマスを進める
if(m_FireTimer >= m_Interval){
//残りの部分を燃やし尽くす
{
Emit(1.0);
}
//m_FireTimer
{
m_FireTimer -= m_Interval;
}
//m_Interval
{
m_Interval = ImageManager.Lerp(INTERVAL_MIN, INTERVAL_MAX, m_Interval_HalfTime / (m_TotalTimer + m_Interval_HalfTime));
}
//Next
{
FireSpread();
}
//Reset
{
for(var i:int = 1; i < 16; i++){
m_BitmapData_Emit[i].fillRect(PANEL_RECT, 0x00);//発火リセット
m_BitmapData_Mask[i].fillRect(PANEL_RECT, 0xFF);//マスク復活
}
}
//Debug
if(IS_DEBUG){
Update_Debug();
}
}
//火種の計算
Emit(m_FireTimer / m_Interval);
//現在の炎の状態を描画
DrawFire();
}
//デバッグ描画用更新
public function Update_Debug():void{
//View
{
const scl:Number = PANEL_LEN;
const mtx:Matrix = new Matrix(scl,0,0,scl, 0,0);
m_BitmapData_View.fillRect(m_BitmapData_View.rect, 0xFF888888);
m_BitmapData_View.draw(m_BitmapData_Data, mtx);
m_BitmapData_View.paletteMap(m_BitmapData_View, m_BitmapData_View.rect, POS_ZERO, m_DebugPalette_R,null,m_DebugPalette_B);
}
}
//発火点の描画
public function Emit(in_Ratio:Number):void{
//燃えているパネルがあれば、方向に応じた形状の発火点にして描く
var mtx:Matrix = new Matrix();
//まずは方向に応じた形状を計算
{
//最初に[1]を描画して、それを[2]以降に回転したりして描いていく
//[0]は特殊([1]以降とは使い方が違う)
//・Emit:閾値判定用の基本画像として使う
//・Mask:前回との差分を保存するための一時バッファとして使う
var SrcBmd_Emit:BitmapData = m_BitmapData_Emit[1];
var SrcBmd_Mask:BitmapData = m_BitmapData_Mask[1];
//[1]
{
var GapBmd_Mask:BitmapData = m_BitmapData_Mask[0];
//マスクの更新&差分取得
{
//まずは前回のマスク(ブロック用α)の値をコピー
GapBmd_Mask.copyPixels(SrcBmd_Mask, PANEL_RECT, POS_ZERO);
//マスクの内容を更新
var thr:uint = 0xFF * (1 - in_Ratio);
SrcBmd_Mask.fillRect(PANEL_RECT, 0xFF);//Clear(必要?)
SrcBmd_Mask.threshold(m_BitmapData_Emit[0], PANEL_RECT, POS_ZERO, ">=", thr, 0xFF000000, 0xFF);//閾値に従って拡大
//今回のマスクと前回のマスクの差分が、すなわち燃えた範囲
GapBmd_Mask.draw(SrcBmd_Mask, null, null, BlendMode.SUBTRACT);
//GapBmd_Mask.fillRect(PANEL_RECT, 0xFF);//test:1マスそのまま
//GapBmd_Mask.draw(SrcBmd_Mask);//test:マスクそのまま
}
//前回のEmitを減少させつつ、マスクの差分を加える
{
//前回の結果を少し残すため、ブラーを使う→うまくいかない
//SrcBmd_Emit.applyFilter(SrcBmd_Emit, PANEL_RECT, POS_ZERO, m_Filter_FireBlur);//m_Filter_FireBlurを流用してみる
const EMIT_DEC_RATIO:Number = 1.0 - Math.pow(0.2, 1+m_Interval*0.15);
const m_CT_EmitDec:ColorTransform = new ColorTransform(EMIT_DEC_RATIO,EMIT_DEC_RATIO,EMIT_DEC_RATIO);
SrcBmd_Emit.colorTransform(PANEL_RECT, m_CT_EmitDec);
//マスクの差分=今回燃えた量なので加える
SrcBmd_Emit.draw(GapBmd_Mask, null, null, BlendMode.ADD);
}
}
//[2]~
{
//発火点:BlendMode.LIGHTENで大きい方の値を採用(=Max)することで合成
//マスク:BlendMode.DARKENで小さい方の値を採用(=Min)することで重ねがけ
for(var i:int = 2; i < 16; i++){
//Clear
{
m_BitmapData_Emit[i].fillRect(PANEL_RECT, 0x00);
m_BitmapData_Mask[i].fillRect(PANEL_RECT, 0xFF);
}
//PANEL_FIRE_U
if((i & PANEL_FIRE_U) != 0){
mtx.identity();
m_BitmapData_Emit[i].draw(SrcBmd_Emit, mtx, null, BlendMode.LIGHTEN);
m_BitmapData_Mask[i].draw(SrcBmd_Mask, mtx, null, BlendMode.DARKEN);
}
//PANEL_FIRE_R
if((i & PANEL_FIRE_R) != 0){
mtx.identity();
mtx.rotate(Math.PI * 1/2);
mtx.tx = PANEL_LEN;
m_BitmapData_Emit[i].draw(SrcBmd_Emit, mtx, null, BlendMode.LIGHTEN);
m_BitmapData_Mask[i].draw(SrcBmd_Mask, mtx, null, BlendMode.DARKEN);
}
//PANEL_FIRE_D
if((i & PANEL_FIRE_D) != 0){
mtx.identity();
mtx.rotate(Math.PI * 2/2);
mtx.tx = PANEL_LEN;
mtx.ty = PANEL_LEN;
m_BitmapData_Emit[i].draw(SrcBmd_Emit, mtx, null, BlendMode.LIGHTEN);
m_BitmapData_Mask[i].draw(SrcBmd_Mask, mtx, null, BlendMode.DARKEN);
}
//PANEL_FIRE_L
if((i & PANEL_FIRE_L) != 0){
mtx.identity();
mtx.rotate(Math.PI * 3/2);
mtx.ty = PANEL_LEN;
m_BitmapData_Emit[i].draw(SrcBmd_Emit, mtx, null, BlendMode.LIGHTEN);
m_BitmapData_Mask[i].draw(SrcBmd_Mask, mtx, null, BlendMode.DARKEN);
}
}
}
}
//実際に描画していく
{
//・ループで調べずにDisplacementMapFilterなどでやりたいけど、それはそれで面倒なのでループでチェック
mtx.identity();
var pos:Point = new Point();
var rect:Rectangle = new Rectangle(0,0,PANEL_LEN,PANEL_LEN);
for(var y:int = 0; y < MAP_NUM_Y; y++){
mtx.ty = y * PANEL_LEN;
rect.y = y * PANEL_LEN;
pos.y = y * PANEL_LEN
for(var x:int = 0; x < MAP_NUM_X; x++){
mtx.tx = x * PANEL_LEN;
rect.x = x * PANEL_LEN;
pos.x = x * PANEL_LEN
var r:uint = ((m_BitmapData_Data.getPixel(x,y)>>16) & 0xFF);
if(r != 0){//燃えていれば
//その部分を発火させ
m_BitmapData_Fire_Data.draw(m_BitmapData_Emit[r], mtx, null, BlendMode.ADD);
//燃やした部分は消す
m_BitmapData_View.copyChannel(m_BitmapData_Mask[r], PANEL_RECT, pos, BitmapDataChannel.BLUE, BitmapDataChannel.ALPHA);
}
}
}
}
}
//炎の描画
public function DrawFire():void{
//発火元を0xFFでm_BitmapData_Fire_Dataに描きこめば、あとはこの関数で自動で炎の描画をする
//描画処理
{
//ブラーで炎を広げる
{
//薄めることで、上の方を細くする効果も兼ねる
m_BitmapData_Fire_Data.applyFilter(m_BitmapData_Fire_Data, VIEW_RECT, POS_ZERO, m_Filter_FireBlur);
}
//全体的に沈静化
{
//パーリンノイズで減衰することで揺らぎを表現
m_BitmapData_Fire_Data.draw(m_BitmapData_PerlinNoise, m_Mtx_PerlinNoise, null, BlendMode.SUBTRACT);
}
//そして0x00~0xFFの値を炎の色に置き換えて表示
{
m_BitmapData_Fire_View.paletteMap(m_BitmapData_Fire_Data, VIEW_RECT, POS_ZERO, null,null,m_Palette_Data_to_View);
}
}
//次回用の更新
{
const ScrollVal:int = 2;
//炎を上にスクロールさせて、燃え上がりを実現
{
//切り捨てて良いスクロールなので、普通にscrollを呼ぶ
m_BitmapData_Fire_Data.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 FireSpread():void{
//m_BitmapData_Dataを次の状態になるように更新
//鎮火
{
//燃えている部分のブロックを消し、ついでに炎の有無を01表現に変換する
m_BitmapData_Data.threshold(
m_BitmapData_Data, DATA_RECT, POS_ZERO,
">", 0x000000, (0xFF<<24)|(0x01<<16)|(PANEL_NONE<<0), 0x00FF0000//Rの値が0以上なら、Rを1に、Bを0にする
);//Colorの指定でαをちゃんと0xFFにする必要がある(0だとαを使わないBitmapでも無視されてしまう)
}
//延焼
{
//炎を上下左右に広げる(結果はUtilに保存)
m_BitmapData_Util.applyFilter(m_BitmapData_Data, DATA_RECT, POS_ZERO, m_Filter_FireSpread);
}
//着火
{
//ブロックのある部分だけ延焼を採用する
//まずは全ての延焼候補をコピー(本当はConvolutionFilterをRにだけ適用できればそれが一番いい)
m_BitmapData_Data.copyChannel(m_BitmapData_Util, DATA_RECT, POS_ZERO, BitmapDataChannel.RED, BitmapDataChannel.RED);
//ブロックのある部分だけを残してそれ以外は0にする
m_BitmapData_Data.applyFilter(m_BitmapData_Data, DATA_RECT, POS_ZERO, m_Filter_FireMask);
}
}
}
}
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import flash.net.*;
import flash.text.*;
import flash.filters.*;
import flash.ui.*;
//#ImageManager
class ImageManager
{
//==Const==
//#Graphic
static public var m_GraphicIndexIter:int = 0;
static public const GRAPHIC_INDEX_BG:int = m_GraphicIndexIter++;
static public const GRAPHIC_INDEX_WALL:int = m_GraphicIndexIter++;
static public const GRAPHIC_INDEX_WALL_X:int = m_GraphicIndexIter++;
static public const GRAPHIC_INDEX_WALL_Y:int = m_GraphicIndexIter++;
static public const GRAPHIC_INDEX_WALL_XorY:int = m_GraphicIndexIter++;
static public const GRAPHIC_INDEX_WALL_XandY:int = m_GraphicIndexIter++;
static public const GRAPHIC_INDEX_NEEDLE:int = m_GraphicIndexIter++;
static public const GRAPHIC_INDEX_NEEDLE_X:int = m_GraphicIndexIter++;
static public const GRAPHIC_INDEX_NEEDLE_Y:int = m_GraphicIndexIter++;
static public const GRAPHIC_INDEX_NEEDLE_XY:int = m_GraphicIndexIter++;
static public const GRAPHIC_INDEX_NUM:int = m_GraphicIndexIter;
//#enum:Quater
static public const LU:int = 0;
static public const RU:int = 1;
static public const LD:int = 2;
static public const RD:int = 3;
//#enum:Pos
static public const POS_X:int = 0;
static public const POS_Y:int = 1;
//#Graphic
static public var m_BitmapData_View:BitmapData;
//#Mapping
static public var GRAPHIC_INDEX_TO_POS:Array;
//#Palette
static public var m_Palette_ForView:Array;
//#Utility
static public const POS_ZERO:Point = new Point(0,0);
//ブロック画像で事前に用意しておくサイズ
static public const W:int = 1000;
static public const H:int = 1000;
//==Var==
//ブロック画像(事前準備分)
static public var m_BitmapData_Block:BitmapData = new BitmapData(W, H, false, 0x000000);
//陰付けフィルター
static public var m_Filter_Shade:GlowFilter = new GlowFilter(
0x000000, 0.9,//色は黒
GameMain.PANEL_LEN/2, GameMain.PANEL_LEN/2,//サイズはパネルより小さく
2,1,//そこそこボカして
true//内側に生成
);
//陰用パレット
static public var m_Palette_Shade:Array = new Array(256);
//陰の作業用ビットマップ
static public var m_BitmapData_Shade:BitmapData = new BitmapData(GameMain.VIEW_W, GameMain.VIEW_H, true, 0x00000000);
//==Function==
//初期化
//#Init
static public function Init(in_Graphic:DisplayObject):void{
var x:int, y:int, i:int;
//m_BitmapData_View
{
m_BitmapData_View = new BitmapData(256, 256, false, 0x000000);
m_BitmapData_View.draw(in_Graphic);
}
//GRAPHIC_INDEX_TO_POS
{//GRAPHIC_INDEX_~から画像の位置へのマッピング(さらにどの隅での処理かも含む)(そして左から何マス目、上から何マス目、という指定)
GRAPHIC_INDEX_TO_POS = new Array(GRAPHIC_INDEX_NUM);
//[LU][RU][LD][RD]
GRAPHIC_INDEX_TO_POS[GRAPHIC_INDEX_BG] = [[3,2], [3,2], [3,2], [3,2]];
GRAPHIC_INDEX_TO_POS[GRAPHIC_INDEX_WALL] = [[1,1], [1,1], [1,1], [1,1]];
GRAPHIC_INDEX_TO_POS[GRAPHIC_INDEX_WALL_X] = [[0,1], [2,1], [0,1], [2,1]];
GRAPHIC_INDEX_TO_POS[GRAPHIC_INDEX_WALL_Y] = [[1,0], [1,0], [1,2], [1,2]];
GRAPHIC_INDEX_TO_POS[GRAPHIC_INDEX_WALL_XorY] = [[0,0], [2,0], [0,2], [2,2]];
GRAPHIC_INDEX_TO_POS[GRAPHIC_INDEX_WALL_XandY] = [[3,0], [4,0], [3,1], [4,1]];
GRAPHIC_INDEX_TO_POS[GRAPHIC_INDEX_NEEDLE] = [[3,3], [4,3], [3,4], [4,4]];
GRAPHIC_INDEX_TO_POS[GRAPHIC_INDEX_NEEDLE_X] = [[0,4], [2,4], [0,4], [2,4]];
GRAPHIC_INDEX_TO_POS[GRAPHIC_INDEX_NEEDLE_Y] = [[1,3], [1,3], [1,5], [1,5]];
GRAPHIC_INDEX_TO_POS[GRAPHIC_INDEX_NEEDLE_XY] = [[0,3], [2,3], [0,5], [2,5]];
}
//m_Palette_ForView
{
m_Palette_ForView = new Array(256);
var index_graphic:int = GRAPHIC_INDEX_BG;
for(i = 0; i < 256; i++){
//区切りごとにindexを変更。次の区切りまではその値をセット
switch(i){
case 0:
case 6:
case 18:
case 24:
index_graphic = GRAPHIC_INDEX_BG; break;
case 3:
case 21:
index_graphic = GRAPHIC_INDEX_NEEDLE_Y; break;
case 9:
case 15:
index_graphic = GRAPHIC_INDEX_NEEDLE_X; break;
case 12:
index_graphic = GRAPHIC_INDEX_NEEDLE_XY; break;
case 27:
index_graphic = GRAPHIC_INDEX_NEEDLE; break;
case 54:
case 63:
index_graphic = GRAPHIC_INDEX_WALL_XorY; break;
case 60:
case 69:
index_graphic = GRAPHIC_INDEX_WALL_X; break;
case 72:
index_graphic = GRAPHIC_INDEX_WALL_Y; break;
case 78:
index_graphic = GRAPHIC_INDEX_WALL_XandY; break;
case 80:
index_graphic = GRAPHIC_INDEX_WALL; break;
}
m_Palette_ForView[i] = index_graphic;
}
}
//m_BitmapData_Block
{
//まずはパーリンノイズを生成
{
const Base:int = GameMain.PANEL_LEN * 2;//小さいほど変化が大きくなる
const NumOctaves:int = 2;//きめ細かさ
m_BitmapData_Block.perlinNoise(Base,Base, NumOctaves, 1000*Math.random(), true, true, BitmapDataChannel.RED);
}
//それを木目っぽく変換
{
//二つの色を周期的に配置して縞々にする
const SrcColor:uint = 0x221100;
const DstColor:uint = 0x8B4513;
//0x00~0xFFを何分割して色を周期的に配置するか
const Cycle:int = 16;
//実際のパレット計算
var ColorPalette:Array = new Array(256);
for(i = 0; i < 256; i++){
var ratio:Number = i/255.0;
ratio = 0.5 - 0.5*Math.cos(Cycle * Math.PI * ratio);
ColorPalette[i] = LerpColor(SrcColor, DstColor, ratio);
}
//適用
m_BitmapData_Block.paletteMap(m_BitmapData_Block, m_BitmapData_Block.rect, POS_ZERO, ColorPalette);
}
}
//m_Palette_Shade
{
for(i = 0; i < 256; i++){
var v:uint = 0xFF * Math.sin(0.5*Math.PI * i/0xFF);
m_Palette_Shade[i] = (v << 16) | (v << 8) | (v << 0);
}
}
}
//ブロック描画
static public function DrawBlock(out_BitmapData:BitmapData, in_MapData:BitmapData):void{
var NumX:int = in_MapData.width;
var NumY:int = in_MapData.height;
var rect:Rectangle = new Rectangle(0,0, GameMain.PANEL_LEN,GameMain.PANEL_LEN);
var pos:Point = new Point(0,0);
//陰用
m_BitmapData_Shade.fillRect(m_BitmapData_Shade.rect, 0x00000000);
//ブロック画像のサンプリング位置はランダムにズラしてみる
var OffsetX:int = Lerp(0, W - NumX*GameMain.PANEL_LEN, Math.random());
var OffsetY:int = Lerp(0, H - NumY*GameMain.PANEL_LEN, Math.random());
var rect_block:Rectangle = new Rectangle(0,0, GameMain.PANEL_LEN,GameMain.PANEL_LEN);
//描画
for(var y:int = 0; y < NumY; y++){
rect.y = y * GameMain.PANEL_LEN;
pos.y = y * GameMain.PANEL_LEN;
rect_block.y = OffsetY + y * GameMain.PANEL_LEN;
for(var x:int = 0; x < NumX; x++){
rect.x = x * GameMain.PANEL_LEN;
pos.x = x * GameMain.PANEL_LEN;
rect_block.x = OffsetX + x * GameMain.PANEL_LEN;
//
switch(in_MapData.getPixel(x,y) & 0x0000FF){
case GameMain.PANEL_NONE:
//空白化
out_BitmapData.fillRect(rect, 0x00000000);
break;
case GameMain.PANEL_BLOCK:
//ブロック描画
out_BitmapData.copyPixels(m_BitmapData_Block, rect_block, pos);
//out_BitmapData.fillRect(rect, 0xFFFFFFFF);//test
m_BitmapData_Shade.fillRect(rect, 0xFFFF0000);//陰をGlowで付けるため、ブロックの部分を白く塗りつぶす
break;
}
}
}
//陰付け(Shading)
{
//GlowFilterのinnerで陰を付ける
m_BitmapData_Shade.applyFilter(m_BitmapData_Shade, m_BitmapData_Shade.rect, POS_ZERO, m_Filter_Shade);
//陰が直線的すぎるので、パレットで曲線に置き換える
m_BitmapData_Shade.paletteMap(m_BitmapData_Shade, m_BitmapData_Shade.rect, POS_ZERO, m_Palette_Shade);
//陰を描画する(色を減衰させる)
out_BitmapData.draw(m_BitmapData_Shade, null, null, BlendMode.MULTIPLY);
//まぁあんまり綺麗にならなかったけどいいや
}
}
//#Draw : Blocks
static public function DrawBlocks(in_Map:Array, out_BitmapData_View:BitmapData):void
{
var x:int, y:int, i:int;
var mtx:Matrix = new Matrix();
var NumX:int = in_Map[0].length;
var NumY:int = in_Map.length;
//Map => Bitmap_Base
//Mapの要素を元に、「0:空白」「1:トゲ」「2:壁」というBitmapを生成
var BitmapData_Base:BitmapData;
{
BitmapData_Base = new BitmapData(NumX, NumY, false, 0x000000);
for(y = 0; y < NumY; y++){
for(x = 0; x < NumX; x++){
var index:int = 0;//default(O, P, G)
{
switch(in_Map[y][x]){
case GameMain.W:
index = 2;
break;
case GameMain.O:
index = GRAPHIC_INDEX_BG;
break;
}
}
BitmapData_Base.setPixel(x, y, index);
}
}
}
//Bitmap_Base => Bitmap_LU,Bitmap_RU,Bitmap_LD,Bitmap_RD
//Bitmap_Baseを元に、四隅の隣接状況をそれぞれ求める(Uniqueな値になるようにする)
var BitmapData_Quater:Array = new Array(4);
{
//Create Filter
const filter:Array = [
new ConvolutionFilter(3,3,
[//LU
1, 3, 0,
9, 27, 0,
0, 0, 0,
]
),
new ConvolutionFilter(3,3,
[//RU
0, 3, 1,
0, 27, 9,
0, 0, 0,
]
),
new ConvolutionFilter(3,3,
[//LD
0, 0, 0,
9, 27, 0,
1, 3, 0,
]
),
new ConvolutionFilter(3,3,
[//RD
0, 0, 0,
0, 27, 9,
0, 3, 1,
]
),
];
for(i = 0; i < 4; i++){
//Init
BitmapData_Quater[i] = new BitmapData(NumX, NumY, false, 0x000000);
//Apply Filter
BitmapData_Quater[i].applyFilter(BitmapData_Base, BitmapData_Base.rect, POS_ZERO, filter[i]);
}
}
//Bitmap_LU,Bitmap_RU,Bitmap_LD,Bitmap_RD => ForView
//Uniqueな値から、対応するIndexへと変換する
var BitmapData_ForView:Array = new Array(4);
{
for(i = 0; i < 4; i++){
//Init
BitmapData_ForView[i] = new BitmapData(NumX, NumY, false, 0x000000);
//Apply Palette
BitmapData_ForView[i].paletteMap(BitmapData_Quater[i], BitmapData_Quater[i].rect, POS_ZERO, null, null, m_Palette_ForView);
}
}
//Draw
//上で求めたIndexに基づき描画
{
var rect:Rectangle = new Rectangle(0,0, 16,16);
for(y = 0; y < NumY; y++){
for(x = 0; x < NumX; x++){
for(i = 0; i < 4; i++){
rect.x = x * 32 + 16 * ((i&1)>>0);
rect.y = y * 32 + 16 * ((i&2)>>1);
index = BitmapData_ForView[i].getPixel(x, y);
//#view
mtx.tx = rect.x - 16 * GRAPHIC_INDEX_TO_POS[index][i][POS_X];
mtx.ty = rect.y - 16 * GRAPHIC_INDEX_TO_POS[index][i][POS_Y];
out_BitmapData_View.draw(m_BitmapData_View, mtx, null, null, rect);
}
}
}
}
}
//Utility
static public function Lerp(in_Src:Number, in_Dst:Number, in_Ratio:Number):Number{
return (in_Src * (1 - in_Ratio)) + (in_Dst * in_Ratio);
}
static public function LerpColor(in_Src:uint, in_Dst:uint, in_Ratio:Number):uint{
var r:uint = Lerp((in_Src>>16)&0xFF, (in_Dst>>16)&0xFF, in_Ratio);
var g:uint = Lerp((in_Src>>8)&0xFF, (in_Dst>>8)&0xFF, in_Ratio);
var b:uint = Lerp((in_Src>>0)&0xFF, (in_Dst>>0)&0xFF, in_Ratio);
return (0xFF<<24) | (r<<16) | (g<<8) | (b<<0);
}
}