スポンサーサイト

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

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

水槽を横から見たような描画

TOTOのトップページにあるフラッシュが面白いのでさっそくマネしてみました。だいぶ貧相なカンジになりましたケド。






水面を横切るようにマウスカーソルを上下させると波形を変えられマス(ステージを一回クリックする必要があるかもしれません)。

空気を通して見た画像に水を通して見た画像をちょっとずらして重ねておいて、水を通して見た画像にマスクをかけて、フレーム毎にマスクを変形(再描画)することでそれっぽく見せています。

水面の揺れは、バネで連結した(っぽい)点を16個並べてソイツラを曲線でつないで(実際は各点を制御点に、各点の中点を端点とする二次ベジェ曲線)います。各点は時間がたつと元の位置に戻るようにもとの位置ともバネ(っぽいナニカ)で接続しています。ただし、各点はy方向にのみ移動します。

今回のサンプルでは、上記の水面をあらわす点をWaterPointクラスとして定義しました。WaterPointクラスのソースコードはこんなカンジです。

package {
/**
* 水面上の点クラス。
*/
public class WaterPoint {
/** 接続オブジェクトの配列 */
private var _jointToArray:Array;
/** ばね定数 */
private var _k:Number;
/** 減衰定数 */
private var _a:Number;
/** x座標 */
private var _x:Number = 0;
/** y座標 */
private var _y:Number = 0;
/** y方向の速度 */
private var _vy:Number = 0;
/**
* コンストラクタ。
* @param k ばね定数
* @param a 減衰定数
* @param ix 初期x座標
* @param iy 初期y座標
*/
public function WaterPoint(k:Number, a:Number, ix:Number, iy:Number) {
_jointToArray = new Array();
_k = k;
_a = a;
_x = ix;
_y = iy;
}
/**
* 指定した物体とバネで接続します。
* @param target 接続する対象
*/
public function joint(target:Object):void {
_jointToArray.push(target);
}
/**
* 状態を更新します。
*/
public function update():void {
// かかっている力
var tensionY:Number = 0;
for each (var t:Object in _jointToArray) {
tensionY += _k * (t.y - _y);
}
// 減衰
_vy *= _a;
// 速度
_vy += tensionY;
}
/**
* 現在の状態に従って移動します。
*/
public function move():void {
// 速度ベクトルにしたがって移動
_y += _vy;
}
/**
* x座標を戻します。
* @return x座標
*/
public function get x():Number {
return _x;
}
/**
* y座標を戻します。
* @return y座標
*/
public function get y():Number {
return _y;
}
/**
* y座標を設定します。
* @param value y座標
*/
public function set y(value:Number):void {
_y = value;
}
}
}
WaterPointクラスは、コンストラクタでばね定数/減衰定数/初期座標をうけとります。またjointメソッドでばね接続する対象を追加します。jointメソッドに渡すパラメータはObject型で定義してますが、実際はx/yプロパティを持っている必要があります。

updateメソッドを実行すると、接続されたオブジェクトの距離やばね定数からその点の速度ベクトルを算出します。その後、moveメソッドを実行すると速度ベクトルにしたがって位置が更新されます。

で、サンプルのflaファイルのほーはというと、フレームアクションはこんなカンジです。
// 水面ポイント数
var pointCnt:uint = 16;
// 水槽
var wt:Sprite = in_water_mc;
// ポイント間の距離
var pointDist:Number = in_water_mc.width / (pointCnt - 1);
// ばね定数
var k:Number = 0.04;
// 減衰定数
var a:Number = 0.95;
// 水面ポイントの配列
var pointArray:Array = new Array();
// マスク
var msk:Sprite = new Sprite();
msk.x = wt.x;
msk.y = wt.y;
addChild(msk);
wt.mask = msk;
// 初期座標
var ix:Number = 0;
var iy:Number = wt.height / 2;
// 初期処理
init();
function init():void {
// ポイントの生成
for (var i:uint = 0; i < pointCnt; i++) {
var p:WaterPoint = new WaterPoint(k, a, ix + i * pointDist, iy);
if (i > 0) {
// ひとつ前の水面ポイントと連結
pointArray[i - 1].joint(p);
p.joint(pointArray[i - 1]);
}
// 初期位置と連結
p.joint({x:p.x, y:p.y});
// ポイントを保存
pointArray.push(p);
}
// 真ん中付近のポイントを動かす
var idx:uint = Math.floor(pointCnt / 2);
pointArray[idx].y = 0;
}
// フレームイベント
wt.addEventListener(Event.ENTER_FRAME, doEnterFrame);
function doEnterFrame(e:Event):void {
var nearest:WaterPoint = null;
if (mouseX > wt.x && mouseX < wt.x + wt.width &&
mouseY > wt.y && mouseY < wt.y + wt.height) {
nearest = getNearest(wt.mouseX, wt.mouseY);
}
var p:WaterPoint;
// ポイントの状態を更新します。
for each (p in pointArray) {
if (!nearest || p != nearest) {
p.update();
}
}
// ポイントを移動します。
for each (p in pointArray) {
if (nearest && p == nearest) {
p.y = wt.mouseY;
} else {
p.move();
}
}
// マスクを描画
drawMask();
}
// マスクを描画します。
function drawMask():void {
var g:Graphics = msk.graphics;
g.clear();
g.beginFill(0x0000FF);
g.moveTo(ix, wt.height);
var p:WaterPoint = pointArray[0];
var np:WaterPoint = pointArray[1];
g.lineTo(p.x, p.y);
for (var i:uint = 1; i < pointCnt - 1; i++) {
p = np;
np = pointArray[i + 1];
g.curveTo(p.x, p.y, (p.x + np.x) / 2, (p.y + np.y) / 2);
}
g.lineTo(np.x, np.y);
g.lineTo(np.x, wt.height);
g.lineTo(ix, wt.height);
g.endFill();
}
// 指定した座標に最も近い水面ポイントを戻します。
function getNearest(x:Number, y:Number):WaterPoint {
var minD2:Number = getDist2(pointArray[0], x, y);
var nearest:WaterPoint = pointArray[0];
for (var i:uint = 1; i < pointCnt; i++) {
var p:WaterPoint = pointArray[i];
var d2:Number = getDist2(p, x, y);
if (d2 < minD2) {
minD2 = d2;
nearest = p;
}
}
if (minD2 > pointDist * pointDist) {
nearest = null;
}
return nearest;
}
// 指定したポイントと座標との距離の二乗を戻します。
function getDist2(p:WaterPoint, x:Number, y:Number):Number {
var dx:Number = p.x - x;
var dy:Number = p.y - y;
return dx * dx + dy * dy;
}

in_water_mcは、水を通して見た画像をムービークリップにしたモノです。mskは、ソイツにかけるマスクを描画するためのSpriteオブジェクトです。

最初に、画像の半分の高さに等間隔にWaterPointオブジェクトを配置します。この際、ひとつ前に作ったオブジェクト(左隣のオブジェクト)と接続します。初期位置とも接続します。マスクを描画する際にこのオブジェクトのx/yプロパティを使用するので、生成したオブジェクトは配列に入れてます。

ENTER_FRAMEイベントでは、配列に保存したWaterPointオブジェクトの状態を更新(速度ベクトルを算出)して、移動します。そして、移動後の座標を使ってdrawMaskメソッドでマスクを再描画します。

ただし、マウスカーソルがいずれかのWaterPointオブジェクトに近くにあるときは、そのWaterPointオブジェクトはマウスカーソルにくっついて移動するようにしています。
スポンサーサイト

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

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

コメント

コメントの投稿


秘密にする

«  | HOME |  »

プロフィール

HundredthMonkey

Author:HundredthMonkey
プログラマ。

ブロとも申請フォーム

この人とブロともになる

メールフォーム

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

ブログ内検索


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