Flashゲーム講座 & アクションスクリプトサンプル集



ラスタースクロールについて

 


■ラスタースクロールとは?

 
テレビゲーム機内で、スプライトや背景を設定すると1フレームの映像が完成します。 この完成ビットマップ映像をラスターと呼ぶことにします。
 
このラスターをブラウン管モニタに出力すればユーザーに映像が届けられます。
 
ブラウン管モニタは、電子銃を走査する事で描画されます。
 
電子銃の移動の仕方ですが、まず最左上が開始位置となります。ここから右端まで走査して1行目を描画します。右端まで移動したら次の行の左端まで移動します。ここから右端まで走査して2行目を描画します。
 
これを下端まで繰り返して1画面が描画されます。最右下まで移動したら、最左上に戻ります。
 
電子銃の動き
 
右端から次の行の左端まで移動する時間内を水平帰線区間(Hブランク)といいます。
 
この、ほんの僅かな時間内に、ラスター全体の座標を変更(スクロール)することができます。 ハードウェアによっては、移動だけでなく回転や拡大縮小もできます。
 
横1行描画するごとにラスターの姿勢を変更することができます。
 
このような方法を駆使して実現するエフェクトは、ラスタースクロールと呼ばれます。
 
なお、電子銃が右下から左上まで移動する時間内を垂直帰線区間(Vブランク)といいます。
 
Flash で、ビットマップの姿勢を変更しながら横1行ずつ描画すれば、テレビゲーム機のラスタースクロールを擬似的に再現することができそうです。
 




サインカーブで波打つビットマップ

 
サンプル表示(別窓)
 
サンプルをダウンロード
 


 
考え方

ビットマップを1行ずつ微妙にずらしてそれぞれサインカーブの動きをさせます。
 
 
 
例)ライブラリに登録した "source" が波打つ動きをするように _root に描画

// ビットマップのリンケージ名
SOURCE_NAME = "source";

// 描画するターゲットパス
RENDER_MOVIECLIP = _root;

// スクリーンサイズ
SCREEN_WIDTH = 400;
SCREEN_HEIGHT = 300;

// サインカーブパラメータ
phase = 0;		// 位相
speed = 5;		// 波移動速度
amp   = 30;		// 振幅
cycle = 1;		// 周期



// ビットマップデータ確保
bmpData = flash.display.BitmapData.loadBitmap (SOURCE_NAME);

onEnterFrame = function (){
    // クリーンアップ
    RENDER_MOVIECLIP.clear();

    phase += speed;
    var i;
    var tmp = phase;
    var m = new flash.geom.Matrix(1,0,0,1,0,0);
    for(i=0;i < SCREEN_HEIGHT;i++){
        tmp += cycle;        
        // ビットマップデータから1行ずつずらして描画
        m.tx = Math.sin(tmp * Math.PI / 180) * amp;
        RENDER_MOVIECLIP.beginBitmapFill(bmpData, m);
        RENDER_MOVIECLIP.moveTo(0, i);
        RENDER_MOVIECLIP.lineTo(0, i + 1);
        RENDER_MOVIECLIP.lineTo(SCREEN_WIDTH, i + 1);
        RENDER_MOVIECLIP.lineTo(SCREEN_WIDTH, i);
        RENDER_MOVIECLIP.endFill();
    }
};
 
 

 




サインカーブで伸縮するビットマップ

 
サンプル表示(別窓)
 
サンプルをダウンロード
 


 
考え方

サインカーブから滑らかに変化する数値を生成します。

0.0
0.86
1.71
2.5
3.21
3.83
4.33
4.69
4.92
5.0
4.92
4.69
4.33
3.83
3.21
2.5
1.71
0.86
0.0
 
1行ずつビットマップを描画するときに高さをサインカーブで得られた数値分ずらします。



描画時のライン本来のラインずらす値
1.0 1 + 0.0
2.86 2 + 0.86
4.71 3 + 1.71
6.5 4 + 2.5
8.21 5 + 3.21
9.83 6 + 3.83
11.3 7 + 4.33
12.6 8 + 4.69
13.9 9 + 4.92
15.010 + 5.0
15.911 + 4.92
16.612 + 4.69
17.313 + 4.33
17.814 + 3.83
18.215 + 3.21
18.516 + 2.5
18.717 + 1.71
18.818 + 0.86
19.019 + 0.0
 
例)ライブラリに登録した "source" が伸縮する動きをするように _root に描画


// ビットマップのリンケージ名
SOURCE_NAME = "source";

// 描画するターゲットパス
RENDER_MOVIECLIP = _root;

// スクリーンサイズ
SCREEN_WIDTH = 400;
SCREEN_HEIGHT = 300;



// サインカーブパラメータ
phase = 0;		// 位相
speed = 5;		// 波移動速度
amp   = 45;		// 振幅
cycle = 1;		// 周期




// ビットマップデータ確保
bmpData = flash.display.BitmapData.loadBitmap (SOURCE_NAME);

onEnterFrame = function (){
    // クリーンアップ
    RENDER_MOVIECLIP.clear();

    phase += speed;
    var i;
    var tmp = phase;
    var m = new flash.geom.Matrix(1,0,0,1,0,0);
    for(i=0;i < SCREEN_HEIGHT;i++){
        tmp += cycle;        
        // ビットマップデータから1行ずつずらして描画
        m.ty = Math.sin(tmp * Math.PI / 180) * amp;
        RENDER_MOVIECLIP.beginBitmapFill(bmpData, m);
        RENDER_MOVIECLIP.moveTo(0, i);
        RENDER_MOVIECLIP.lineTo(0, i + 1);
        RENDER_MOVIECLIP.lineTo(SCREEN_WIDTH, i + 1);
        RENDER_MOVIECLIP.lineTo(SCREEN_WIDTH, i);
        RENDER_MOVIECLIP.endFill();
    }
};
 
 

 




ドットを拡大してモザイクをかける

 
サンプル表示(別窓)
 
サンプルをダウンロード
 


 
考え方

10 * 10 マスごとにモザイクをかけるとして、この10 * 10 マスの中にある100個のピクセルのカラーの平均値を求めて、その色で 10 * 10 マスの四角形を描画します。

しかし平均を求めるとかなり重いので左上にくる1ドットのカラーを 10 * 10 まで引き伸ばして四角形を描画します。
 
例)_root.source にモザイクをかけて _root.render に描画


// ソース用ターゲット
SOURCE_MOVIECLIP = _root.source;

// 描画用ターゲット
RENDER_MOVIECLIP = _root.render;

// モザイクの領域
MosaicRect = {
x : -100,
y : -100,
w : 200,
h : 200
};

// 1つのモザイクの大きさ
M_WIDTH = 15;
M_HEIGHT = 15;
M_WIDTH_HALF = M_WIDTH / 2;
M_HEIGH_HALF = M_HEIGHT / 2;


bmpData = new flash.display.BitmapData(
MosaicRect.w, MosaicRect.h,true); onEnterFrame = function (){ // クリーンアップ bmpData.fillRect(
new flash.geom.Rectangle(0,0,MosaicRect.w,MosaicRect.h),0x00000000); m = new flash.geom.Matrix(1,0,0,1,
-MosaicRect.x + SOURCE_MOVIECLIP._x-RENDER_MOVIECLIP._x,
-MosaicRect.y + SOURCE_MOVIECLIP._y-RENDER_MOVIECLIP._y); bmpData.draw(SOURCE_MOVIECLIP, m); var i,j; var m_rect = new flash.geom.Rectangle(0,0,M_WIDTH,M_HEIGHT); for(i=0;i < MosaicRect.h;i+=M_HEIGHT){ for(j=0;j < MosaicRect.w;j+=M_WIDTH){ // ピクセルの色を引き伸ばして描画 m_rect.x = j; m_rect.y = i; bmpData.fillRect(m_rect, bmpData.
getPixel32 (j + M_WIDTH_HALF,i + M_HEIGH_HALF)); } } // 描画 RENDER_MOVIECLIP.clear(); m = new flash.geom.Matrix(1,0,0,1,MosaicRect.x,MosaicRect.y); RENDER_MOVIECLIP.beginBitmapFill(bmpData, m,true,true); RENDER_MOVIECLIP.moveTo(MosaicRect.x, MosaicRect.y); RENDER_MOVIECLIP.lineTo(MosaicRect.x, MosaicRect.y + MosaicRect.h); RENDER_MOVIECLIP.lineTo(MosaicRect.x + MosaicRect.w,
MosaicRect.y + MosaicRect.h); RENDER_MOVIECLIP.lineTo(MosaicRect.x + MosaicRect.w, MosaicRect.y); RENDER_MOVIECLIP.endFill(); };
 
 

 




前の画面を半透明にして重ねるブラー

 
サンプル表示(別窓)
 
サンプルをダウンロード
 


 
考え方

PlayStation2 とかでよく使われるエフェクトです。
 
前の画面をコピーしておいて次のフレームで少し半透明にして上に重ねて描画します。 さらにこの画面をコピーしておいて次のフレームで少し半透明にして上に重ねて描画します。 これを毎フレーム繰り返します。少しだけ座標をずらしたり角度をずらしたりすると面白い事になります。
 
例)_root.source にブラーをかける


// ソース用ターゲット
SOURCE_MOVIECLIP = _root.source;

// ブラーの領域
BlurRect = {
    x:0,
    y:0,
    w:400,
    h:300
};

// ブラー量
BlurLength = 1.02;

// ブラー透過度
BlurAlpha = 0.9;


// ブラー用ムービークリップ作成
bmpData = new flash.display.BitmapData(BlurRect.w, BlurRect.h,
true, 0x00FFFFFF); bmpBuffer = new flash.display.BitmapData(BlurRect.w, BlurRect.h,
true, 0x00FFFFFF); render = SOURCE_MOVIECLIP.createEmptyMovieClip("render",
SOURCE_MOVIECLIP.getNextHighestDepth()); render._x = BlurRect.x; render._y = BlurRect.y; var trans = new flash.geom.Transform(render); var color = new flash.geom.ColorTransform(
1, 1, 1, BlurAlpha, 0, 0, 0, 0); trans.colorTransform = color; onEnterFrame = function () { //バッファから描画用ビットマップに転送 bmpData.copyPixels(bmpBuffer, new flash.geom.Rectangle(
0, 0, BlurRect.w, BlurRect.h), new flash.geom.Point(0, 0)); // バッファにキャプチャー var m = new flash.geom.Matrix(
1 , 0, 0, 1,-BlurRect.x, -BlurRect.y); bmpBuffer.fillRect(new flash.geom.Rectangle(
0, 0, BlurRect.w, BlurRect.h), 0x00FFFFFF); bmpBuffer.draw(SOURCE_MOVIECLIP, m); // 描画オフセット用行列 var m = new flash.geom.Matrix(1, 0, 0, 1, 0, 0); m.translate(-_root._xmouse,-_root._ymouse); m.scale(BlurLength,BlurLength); m.translate(_root._xmouse,_root._ymouse); // 上に重ねて描画 render.clear(); render.beginBitmapFill(bmpData, m, true, true); render.moveTo(0, 0); render.lineTo(0, BlurRect.h); render.lineTo(BlurRect.w, BlurRect.h); render.lineTo(BlurRect.w, 0); render.endFill(); };
 
 

 




投影変換をかけたレースゲーム風の地面

 
サンプル表示(別窓)
 
サンプルをダウンロード
 


■考え方

 
スーパーファミコン版のマリオカート風の地面を作ります。

透視投影変換式を使って、Flash のスクリーン座標の中央から下へかけて、1行ずつ奥行き z 座標を求めます。 z 値を参考にして 1 行ずつビットマップをスケーリングして描画していきます。
 

■透視投影変換式

 
透視投影変換式

var angle = 1 ~ 180 くらいまでの好きな値;		// 視野角
var fov = 1 / Math.tan( angle * 0.5 * Math.PI / 180);	// 視点からの距離
var width  = 解像度の幅か高さの大きい方の数値 * 0.5;	// 
var height = width * 1.0;			// アスペクト比

スクリーン上のx座標 = 3次元のx座標 / 3次元のz座標 * fov * width;
スクリーン上のy座標 = 3次元のy座標 / 3次元のz座標 * fov * height;
 
例)ライブラリに登録した "source" に投影変換をかけて _root に描画

// 注.この講座にあるベクトルと行列の関数を使用しています。
#include "vec2d.as"
#include "mtx33.as"



var SOURCE_BITMAP      = "source";	// ソースビットマップのリンケージ名
var RENDER_MOVIECLIP   = _root;		// 描画ターゲット

var CAMERA_HEIGHT      = 10;		// カメラの高さ

var SCREEN_WIDTH       = 640;		// 解像度(幅)
var SCREEN_HEIGHT      = 480;		// 解像度(高さ)
var CAMERA_ANGLE       = 60;		// 視野角

var SCREEN_SIZE        = (SCREEN_WIDTH > SCREEN_HEIGHT) ? SCREEN_WIDTH : SCREEN_HEIGHT;	// スクリーンの解像度の大きい方
var SCREEN_SIZE_HALF   = Math.floor(SCREEN_SIZE * 0.5);				// スクリーンの解像度の大きい方(半分)
var SCREEN_FOV         = 1 / Math.tan(CAMERA_ANGLE * 0.5 * Math.PI / 180);			// カメラから投影スクリーンまでの距離

var SCREEN_WIDTH_HALF  = Math.floor(SCREEN_WIDTH / 2.0);
var SCREEN_HEIGHT_HALF = Math.floor(SCREEN_HEIGHT / 2.0);


// ビットマップデータ確保
var bmpData = flash.display.BitmapData.loadBitmap (SOURCE_BITMAP);

// 1行ごとの行列をあらかじめ作成
var mtxData = new Array();
for(var i=0;i < SCREEN_HEIGHT_HALF;i++){
	// スクリーン系 y 座標に位置する 3次元座標系の奥行き z 座標
	var z = CAMERA_HEIGHT * SCREEN_FOV * SCREEN_SIZE_HALF / (i + 1);
	
	// 投影変換で施されるスケール値
	var scale = 1.0 / z * SCREEN_FOV * SCREEN_SIZE_HALF;

	mtxData[i] = Mtx33Translate(-SCREEN_WIDTH_HALF, i - z);
	mtxData[i] = Mtx33ScalePost(-scale,1.0,mtxData[i]);
	mtxData[i] = Mtx33TranslatePost(SCREEN_WIDTH_HALF,0,mtxData[i]);
}

// カメラ行列用パラメータ
CameraPos = Vec2dCreate(SCREEN_WIDTH_HALF,SCREEN_HEIGHT_HALF);	// カメラの座標 (ビットマップの2D座標上と同じ)
CameraSpd = Vec2dCreate(0,0);				// カメラの移動量(ビットマップの座標方向と同じ)
CameraRot = 0;					// カメラの向き  (ビットマップのx方向を 0 度として時計回りに 360 度回転で指定)
CameraMatrix = Mtx33Create();				// カメラ行列


onEnterFrame = function (){
	var i

	// カメラ操作 ---------------------------------------------------------
	if(Key.isDown(Key.UP)){
		// 前方向に移動量を加算
		var spd = Vec2dTransformVector(1,0,Mtx33Rotate(CameraRot));
		spd = Vec2dScale(spd,0.5);
		CameraSpd = Vec2dAdd(CameraSpd,spd);
	}
	if(Key.isDown(Key.DOWN)){
		// 後方向に移動量を加算
		var spd = Vec2dTransformVector(-1,0,Mtx33Rotate(CameraRot));
		spd = Vec2dScale(spd,0.5);
		CameraSpd = Vec2dAdd(CameraSpd,spd);
	}
	if(Key.isDown(Key.LEFT))		CameraRot -= 2.0;	// 左回転
	if(Key.isDown(Key.RIGHT))		CameraRot += 2.0;	// 右回転

	CameraSpd = Vec2dScale(CameraSpd,0.90);	// 摩擦
	CameraPos = Vec2dAdd(CameraPos,CameraSpd);	// 移動量を座標に加算


	// カメラ行列 ---------------------------------------------------------
	CameraMatrix = Mtx33Translate(-CameraPos.x,-CameraPos.y);
	CameraMatrix = Mtx33RotatePost(-CameraRot + 90,CameraMatrix);
	CameraMatrix = Mtx33TranslatePost(SCREEN_WIDTH_HALF,SCREEN_HEIGHT_HALF,CameraMatrix);

	// クリーンアップ
	RENDER_MOVIECLIP.clear();
	
	// ビットマップデータから1行ずつカメラ変換をかけて描画 ---------------
	for(i=0;i < SCREEN_HEIGHT_HALF;i++){
		var d = (i) / SCREEN_HEIGHT_HALF * 255;
		RENDER_MOVIECLIP.beginBitmapFill(bmpData, Mtx33Transform(CameraMatrix,mtxData[i]),false,true);
		RENDER_MOVIECLIP.moveTo(0, SCREEN_HEIGHT_HALF + i);
		RENDER_MOVIECLIP.lineTo(0, SCREEN_HEIGHT_HALF + i + 1);
		RENDER_MOVIECLIP.lineTo(SCREEN_WIDTH, SCREEN_HEIGHT_HALF + i + 1);
		RENDER_MOVIECLIP.lineTo(SCREEN_WIDTH, SCREEN_HEIGHT_HALF + i);
		RENDER_MOVIECLIP.endFill();
	}
};