スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

--.--.-- | | スポンサー広告

沸き立つ湯気

湯気がたってるような表現をするサンプルです。






やってることはFlash CS3で水面ぽい画像表示と似たようなカンジで、ENTER_FRAMEでフレーム毎にオフセット値を変えながらBitmapData#perinNoiseでパーリンノイズを生成してます。ただしDisplacementMapFilterを使うんじゃなくて、白いスプライトのマスクとして生成したビットマップを設定しています。ソイツをお好み焼きの画像に重ねてマス。

今回はBitmapクラスを継承して湯気を描画するSteamクラス(Steam.as)を作ってみました。

package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BitmapDataChannel;
import flash.geom.Point;
import flash.events.Event;
/**
* 湯気を表現するクラス。
*/
public class Steam extends Bitmap {
/** オクターブ数 */
private var _octave:uint = 0;
/** オフセット位置の配列 */
private var _offsetArray:Array;
/** 変化速度の配列 */
private var _speedArray:Array;
/** x方向の周波数の係数 */
private var _baseXScale:Number = 0.7;
/** y方向の周波数の係数 */
private var _baseYScale:Number = 0.7;
/** 乱数シード */
private var _rndSeed:int = 0;
/** 上昇速度係数 */
private var _speedY:Number = 2;
/** ゆらぎ係数 */
private var _speedX:Number = 4;
/**
* コンストラクタ。
* @param width 描画領域の幅
* @param height 描画領域の高さ
* @param octave オクターブ数
*/
public function Steam(width:uint, height:uint, octave:uint = 3) {
_octave = octave;
_offsetArray = new Array();
_speedArray = new Array();
for (var i:uint = 0; i < octave; i++) {
_offsetArray.push(new Point(0, 0));
_speedArray.push(new Point(Math.random(),
Math.random() * _speedY + _speedY));
}
_rndSeed = Math.floor(Math.random() * 65536);
// ビットマップデータ
var bd:BitmapData = new BitmapData(width, height);
super(bd);
// フレームイベントリスナの登録
addEventListener(Event.ENTER_FRAME, doEnterFrame);
}
/**
* フレームイベントハンドラ
* @param e イベントオブジェクト
*/
private function doEnterFrame(e:Event):void {
// オフセット位置の更新
for (var i:uint = 0; i < _octave; i++) {
var offset:Point = _offsetArray[i];
var speed:Point = _speedArray[i];
// x方向のゆらぎ
if (speed.x < 0.5) {
speed.x += speed.x * speed.x * 2;
} else {
speed.x -= (1 - speed.x) * (1 - speed.x) * 2;
}
offset.x = (speed.x - 0.5) * _speedX;
// 上昇
offset.y += speed.y;
}
// パーリンノイズ生成
bitmapData.perlinNoise(bitmapData.width * _baseXScale,
bitmapData.height * _baseYScale,
_octave,
_rndSeed,
false,
true,
BitmapDataChannel.ALPHA,
true,
_offsetArray);
}
}
}
コンストラクタで指定したサイズのBitmapDataを作ってます。_offsetArrayはオクターブ毎のオフセット値を保存する配列で、_speedArrayはオクターブ毎の変化速度に関する係数を保存する配列デス。また、doEnterFrameメソッドをハンドラとしてENTER_FRAMEイベントリスナの登録もしてます。

doEnterFrameメソッドでは、_speedArray配列にしたがってオクターブ毎のオフセット値を更新してパーリンノイズを再生成しています。y方向のオフセットは単純にコンストラクタ時に決めた乱数ぶんずらしてマス。x方向は間欠カオスというのを使ってゆらぎを表現してるツモリですが、効果のほどはビミョウです。

サンプルのflaファイルのフレームアクションはこんなカンジです。
// サイズ
var sw:Number = 320;
var sh:Number = 240;
// 位置
var sx:Number = (stage.stageWidth - sw) / 2;
var sy:Number = (stage.stageHeight - sh) / 2;
// 湯気の色
var sColor:uint = 0xFFFFFF;
// 湯気
var steam:Sprite = new Sprite();
steam.graphics.beginFill(sColor);
steam.graphics.drawRect(0, 0, sw, sh);
steam.graphics.endFill();
steam.x = sx;
steam.y = sy;
steam.cacheAsBitmap = true;
addChild(steam);
// 湯気マスク
var steamMask:Steam = new Steam(sw, sh);
steamMask.cacheAsBitmap = true;
addChild(steamMask);
steamMask.x = sx;
steamMask.y = sy;
steam.mask = steamMask;
画像と同じサイズの白いスプライトsteamを生成して、お好み焼きの画像に重ねてマス。ソイツと同じ位置/サイズでSteamクラスのインスタンスを生成してsteamのマスクにしてます。

ちょっと重いですかねー。

追記
最初は生成したSteamインスタンスはお好み焼きのマスクにしてたんですが、やっぱり考え直して白いスプライトを生成してSteamインスタンスはソイツのマスクにしました。

スポンサーサイト

テーマ:Flash - ジャンル:コンピュータ

2008.01.28 | | Comments(0) | Trackback(0) | Flash CS3

しみが広がるような描画

ステージ上でマウスボタンを押すと、押してる間ジワジワっと液体が広がるようなカンジになります。






シミの描画は、塗りつぶしで曲線で閉じた図形を描いてるダケです。マウスボタンを押してる間はその図形がだんだんデカクなるという寸法デス。

曲線の描画方法は、座標の配列を作っといてソイツラをコントロールポイントに、配列上で並びあった座標の中点をアンカーポイントにしてGraphics#curveToを実行しています。Foundation Actionscript 3.0 Animation:Making Things Move!に書いてあったテクニックです。

そんで、じゃあ座標の配列はどうやって作ってんのかってーと、ボタンを押した時点でマウスカーソルの座標を保持するオブジェクトをいくつか作ります(サンプルでは20個)。で、ボタンを押してる間は、ソイツラが放射状に離れてくようにしてます。ソレゾレの座標の離れてく距離は、座標ごと/フレームごとにランダムです。

今回のサンプルでは特にクラスは作ってなくて、全部フレームアクションで書いてます。こんなカンジです。

// 点の数
var pointCnt:uint = 20;
// 染みが広がる速度
var speed:Number = 5;
// 描画中の染み
var stain:Sprite;
// 描画中の染みの色
var stainColor:uint;
// 描画中の染みの情報
var infoArray:Array = new Array();
// マウスボタン
stage.addEventListener(MouseEvent.MOUSE_DOWN, doMouseDown);
function doMouseDown(e:MouseEvent):void {
// 染みを描画するスプライトを生成
stain = new Sprite();
stain.x = mouseX;
stain.y = mouseY;
stain.filters = [new BlurFilter()];
addChild(stain);
// 染み描画情報の生成
infoArray = new Array();
var dAngle:Number = 2 * Math.PI / pointCnt;
for (var i:uint = 0; i < pointCnt; i++) {
var info:Object = {x:0, y:0, r:0, angle:dAngle * i};
infoArray.push(info);
}
// 染みの色設定
stainColor = Math.random() * 0xFFFFFF;
// フレームイベントリスナの登録
stain.addEventListener(Event.ENTER_FRAME, doStainEnterFrame);
}
stage.addEventListener(MouseEvent.MOUSE_UP, doMouseUp);
function doMouseUp(e:MouseEvent): void {
// フレームイベントリスナの解除
stain.removeEventListener(Event.ENTER_FRAME, doStainEnterFrame);
}
// フレームイベント
function doStainEnterFrame(e:Event):void {
// 染みを広げる
for (var i:uint = 0; i < pointCnt; i++) {
var info:Object = infoArray[i];
info.r += Math.random() * speed;
info.x = Math.cos(info.angle) * info.r;
info.y = Math.sin(info.angle) * info.r;
}
// 閉じた曲線の描画
var g:Graphics = stain.graphics;
g.beginFill(stainColor);
var info1:Object = infoArray[infoArray.length - 1];
var info2:Object = infoArray[0];
g.moveTo((info1.x + info2.x) / 2, (info1.y + info2.y) / 2);
for (i = 1; i < pointCnt; i++) {
info1 = info2;
info2 = infoArray[i];
g.curveTo(info1.x, info1.y, (info1.x + info2.x) / 2, (info1.y + info2.y) / 2);
}
info1 = info2;
info2 = infoArray[0];
g.curveTo(info1.x, info1.y, (info1.x + info2.x) / 2, (info1.y + info2.y) / 2);
}
最初に定義してるpointCntが、曲線のコントロールポイントの数デス。閉じた曲線を描くのでアンカーポイントも同数デス。

イベントハンドラは、MouseEvent#MOUSE_DOWNのdoMouseDownメソッド/MouseEvent#MOUSE_UPのdoMouseUpメソッド/Event#ENTER_FRAMEのdoStainEnterFrameメソッドの3つデス。

doMouseDownメソッドでは、マウスカーソルの位置にシミを描画する用のSpriteインスタンス(stain)を生成して、addChildしてます。それっぽくするためにstainにはBlurFilterをかけてます。そして、曲線を描画するための座標配列を作ってます。配列の要素はObjectインスタンスで、x座標/y座標/原点からの距離/進む角度をプロパティに持ってます。また、染みの色もここでMath#randomを使って決めてます。

ボタンを押してる間だけシミが広がるようにするためにdoMouseDownメソッドでdoStainEnterFrameメソッドをイベントリスナに登録してます。doMouseUpメソッドでコイツを解除してるので、ボタンを離すと広がるのが止まるという寸法デス。

doStainEnterFrameメソッドでは、角度プロパティと乱数を使って座標配列の座標を原点から遠ざけてます。そうやって更新した座標を使って、doMouseDownメソッドで作ったSpriteインスタンスのgraphicsに図形を描画してます。

テーマ:Flash - ジャンル:コンピュータ

2008.01.19 | | Comments(0) | Trackback(0) | Flash CS3

多脚歩行する

多脚歩行。といっても、こんな歩き方をする生き物はいないだろうと思いますケド。のサンプルです。胴体の部分をマウスでつまんで動かせマス。





各脚は3つの楕円スプライトを含むスプライトです。コイツラを根元を基点とするフォワードキネマティクスで動かしています。AS3.0でのフォワードキネマティクスの実装についてはFoundation Actionscript 3.0 Animation:Making Things Move!(和訳:ActionScript 3.0 アニメーション)に詳しい解説が載っています。

このサンプルでは脚一本につき横長の楕円形のインスタンスを3個配置して、インスタンスの基準点(左より)を支点、別に指定する長さ分だけ右側の座標をピボットとしています。根元側のピボット座標に先端側の支点を重ねて、根元側の角度を基準とした角度を先端側に設定することで、節が接続されているように見えるという寸法デス。

で、今回はこの脚部分をクラスにしてみました。節となるインスタンスや節の長さ等はこのクラスのコンストラクタのパラメータとして指定しています。

package {
import flash.display.Sprite;
/**
* 脚クラス。
*/
public class Leg extends Sprite {
/** 節の配列 */
private var _segArray:Array;
/** x方向の速度ベクトル */
private var _vx:Number = 0;
/** y方向の速度ベクトル */
private var _vy:Number = 0;
/**
* コンストラクタ。
* @param propArray 節の配列
*/
public function Leg(segArray:Array) {
_segArray = segArray;
for (var i:uint = 0, iEnd:uint = _segArray.length; i < iEnd; i++) {
var seg:Object = _segArray[i];
var sprite:Sprite = seg.sprite;
addChild(sprite);
}
// 節を接続
join(0);
}
/**
* 節を接続します。
* @param cycle 歩行サイクル
*/
private function join(cycle:Number):void {
var jx:Number = 0;
var jy:Number = 0;
var rot:Number = 0;
for (var i:uint = 0, iEnd:uint = _segArray.length; i < iEnd; i++) {
var seg:Object = _segArray[i];
// 一つ前のピボットに支点をジョイント
seg.sprite.x = jx;
seg.sprite.y = jy;
// 節を回転
seg.sprite.rotation = seg.base
+ Math.sin(cycle + seg.offset) * seg.range
+ rot;
// 次の節用にピボット座標と曲げ角度を記憶
var pivot:Object = getPivot(seg);
jx += pivot.x;
jy += pivot.y;
rot += seg.sprite.rotation;
}
}
/**
* 節のピボット座標を戻します。
* @param seg 節オブジェクト
* @return ピボット座標をプロパティに持つオブジェクト
*/
private function getPivot(seg:Object):Object {
var sprite:Sprite = seg.sprite;
var angle:Number = sprite.rotation * Math.PI / 180;
return {x:Math.cos(angle) * seg.length, y:Math.sin(angle) * seg.length};
}
/**
* 脚の歩行サイクルを設定します。
* @param cycle 歩行サイクル
*/
public function setCycle(cycle:Number):void {
var old:Object = getTip();
join(cycle);
var tip:Object = getTip();
// 速度ベクトルを更新
_vx = tip.x - old.x;
_vy = tip.y - old.y;
}
/**
* 脚の先端の座標を戻します。
* @return 先端座標をプロパティに持つオブジェクト
*/
private function getTip():Object {
return getPivot(_segArray[_segArray.length - 1]);
}
/**
* x方向の速度ベクトルを戻します。
* @return x方向の速度ベクトル
*/
public function get vx():Number {
return _vx;
}
/**
* y方向の速度ベクトルを戻します。
* @return y方向の速度ベクトル
*/
public function get vy():Number {
return _vy;
}
}
}
コンストラクタのパラメータは、節の情報を持つオブジェクトの配列デス。なので、このサンプルでは、配列の要素数は3デス。渡される節の情報はこんなカンジです。
  • length:節の長さ
  • sprite:節をあらわす表示オブジェクト(サンプルでは青い楕円)
  • base:基準角度。ココで指定した角度を中心に往復運動します
  • range:可動範囲。脚が動く角度の範囲デス
  • offset:角度のオフセット値。指定角度に対するずれ
LegクラスはSpriteを継承したクラスで、パラメータとして渡された節の表示オブジェクトをaddChildして内包するようになってます。

コンストラクタの最後で実行しているjoinメソッドが各節を接続し、角度を調整する部分デス。メソッドパラメータは、角度デス。コイツを元に各節のrotationプロパティを設定します。

先頭の表示オブジェクトから順に、
  • 手前の表示オブジェクトのピボット座標に支点をあわせる
  • 手前の表示オブジェクトの角度を基準に自身の角度を決定する
というカンジです。節の角度は、基準角度+(指定角度+オフセットの正弦)×可動範囲+手前の節の角度となっています。なので、指定角度はどんどん加算してけば正弦波を基準とした動きになります。

ピボット座標はgetPivotメソッドで、表示オブジェクトの角度とコンストラクタで指定された長さから計算しています。

_vx, _vyは脚の先端部分の速度ベクトルです。setCycleメソッドが実行された際の、先端の表示オブジェクトのピボット座標の変化を設定しています。内部的には特に使用しないのですが、フレームアクションで脚が地面を蹴ったときの処理に使ってマス。

で、サンプルのフレームアクションはこんなカンジです。
// 歩行速度
var speed:Number = 0.05;
// 重力
var gravity:Number = 0.1;
// 初期位置
var sx:Number = 100;
var sy:Number = 100;
// 脚の設定値
var rLeg1PropArray:Array
= [{length:40, sprite:r1seg1_mc, base:-30, range:20, offset:0},
{length:40, sprite:r1seg2_mc, base:70, range:20, offset:-Math.PI / 3},
{length:20, sprite:r1seg3_mc, base:75, range:20, offset:-Math.PI * 2 / 3}];
var rLeg2PropArray:Array
= [{length:40, sprite:r2seg1_mc, base:-30, range:20, offset:Math.PI},
{length:40, sprite:r2seg2_mc, base:70, range:20, offset:Math.PI * 2 / 3},
{length:20, sprite:r2seg3_mc, base:75, range:20, offset:Math.PI / 3}];
var lLeg1PropArray:Array
= [{length:40, sprite:l1seg1_mc, base:210, range:20, offset:Math.PI / 2},
{length:40, sprite:l1seg2_mc, base:-80, range:20, offset:Math.PI * 5 / 6},
{length:20, sprite:l1seg3_mc, base:90, range:20, offset:Math.PI * 7 / 6}];
var lLeg2PropArray:Array
= [{length:40, sprite:l2seg1_mc, base:210, range:20, offset:Math.PI * 3 / 2},
{length:40, sprite:l2seg2_mc, base:-80, range:20, offset:Math.PI * 11 / 6},
{length:20, sprite:l2seg3_mc, base:90, range:20, offset:Math.PI / 6}];
// 歩行体
var crab:Sprite = new Sprite();
crab.x = sx;
crab.y = sy;
addChild(crab);
// 右脚1
var rLeg1:Leg = new Leg(rLeg1PropArray);
crab.addChild(rLeg1);
// 右脚2
var rLeg2:Leg = new Leg(rLeg2PropArray);
crab.addChild(rLeg2);
// 左脚1
var lLeg1:Leg = new Leg(lLeg1PropArray);
crab.addChild(lLeg1);
// 左脚2
var lLeg2:Leg = new Leg(lLeg2PropArray);
crab.addChild(lLeg2);
// 脚の配列
var legArray:Array = [rLeg1, rLeg2, lLeg1, lLeg2];
// 全体の速度ベクトル
var vx:Number = 0;
var vy:Number = 0;
// 胴体
var body:MovieClip = body_mc;
body.x = 0;
body.y = 0;
crab.addChild(body);
// ドラッグ中かどうか
var dragging:Boolean = false;
body.addEventListener(MouseEvent.MOUSE_DOWN,
function (e:Event):void {
vx = 0;
vy = 0;
dragging = true;
stage.addEventListener(MouseEvent.MOUSE_MOVE, doStageMouseMove);
});
stage.addEventListener(MouseEvent.MOUSE_UP,
function (e:Event):void {
dragging = false;
stage.removeEventListener(MouseEvent.MOUSE_MOVE, doStageMouseMove);
});
// マウスカーソル位置への移動をします。
function doStageMouseMove(e:Event):void {
vx = (mouseX - crab.x) * 0.5;
vy = (mouseY - crab.y) * 0.5;
crab.x = mouseX;
crab.y = mouseY;
}
// 歩行サイクル
var cycle:Number = 0;
addEventListener(Event.ENTER_FRAME, doEnterFrame);
function doEnterFrame(e:Event):void {
if (!dragging) {
// 重力
vy += gravity;
// 速度ベクトルによる移動
doVelocity();
}
// 脚角度更新
doCycle();
cycle += speed;
if (!dragging) {
// 床/壁衝突処理
doCollision();
}
}

// 速度ベクトルによる移動処理を行います。
function doVelocity():void {
crab.x += vx;
crab.y += vy;
}

// 脚角度更新
function doCycle():void {
for each (var leg:Leg in legArray) {
leg.setCycle(cycle);
}
}

// 床/壁衝突処理
function doCollision():void {
// 床/右壁衝突処理
var dx:Number = 0;
var dy:Number = 0;
for each (var leg:Leg in legArray) {
var rect:Rectangle = leg.getBounds(this);
if (rect.left < 0) {
dx = Math.min(dx, rect.left);
vx = 0;
} else if (rect.right > stage.stageWidth) {
dx = Math.max(dx, rect.right - stage.stageWidth);
vx = 0;
}
if (rect.bottom > stage.stageHeight) {
dy = Math.max(dy, rect.bottom - stage.stageHeight);
// 床を蹴ったことによる速度ベクトルの変更
vx = -leg.vx;
vy = -leg.vy;
} else if (rect.top < 0) {
dy = Math.min(dy, rect.top);
vy = 0;
}
}
// 位置調整
crab.x -= dx;
crab.y -= dy;
}
最初のほーで定義してる4つの配列がソレゾレの脚のコンストラクタのパラメータです。length,sprite,base,range,offsetというプロパティを持つオブジェクトの配列になってます。rLeg1PropArray,rLeg2PropArrayが右側の脚用、lLeg1PropArray,lLeg2PropArrayが左側の脚用デス。サンプルではいきなりObjectを作ってますが、節クラスを別途定義しても良かったカモ。

crabスプライトが4本の脚と胴体を内包していて、歩行体全体をあらわしています。脚は先ほどのLegクラスのインスタンスです。胴体はオーサリング環境で作ったインスタンスです。

胴体をあらわすbodyクラスはMOUSE_DOWNイベントリスナを登録していて、コイツとステージに登録したMOUSE_UPイベントリスナでマウスでつまんで動かせるようにしています。

ENTER_FRAMEイベントハンドラでは、
  1. 速度ベクトルによる移動
  2. 脚の角度を更新
  3. 床/壁との衝突処理
をしています。速度ベクトルによる移動はdoVelocityメソッドで処理しています。vx, vyが歩行体の速度ベクトルで、コイツにしたがってcrabの座標を変更しているダケです。

doCycleメソッドが脚の角度の更新デス。cycleが指定角度になります。コレをすべての脚のsetCycleメソッドで指定しています。

doCollisionメソッドが、床/天井/壁との衝突処理デス。天井と壁はぶつかったら突き抜けないようにしてるだけデス。床との衝突は、脚の先端の速度ベクトルの逆ベクトルを歩行体の速度ベクトルとすることで、脚が床を蹴って進むようなカンジにしています。

テーマ:Flash - ジャンル:コンピュータ

2008.01.05 | | Comments(2) | Trackback(0) | Flash CS3

«  | HOME |  »

プロフィール

HundredthMonkey

Author:HundredthMonkey
プログラマ。

ブロとも申請フォーム

この人とブロともになる

メールフォーム

名前:
メール:
件名:
本文:

ブログ内検索


上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。