ホーム > タグ > flash

flash

マウスで操作出来るスリットスキャン

スリットスキャンに、
マウスのインタラクションを付けるとどうなるかな、と思い、作ってみました。

最初は意識していなかったんだけど、以前教えてもらったこちらに似てしまった。

BitmapData::drawで毎フレーム動画をキャプチャし、配列に保存しています。
今回は過去400枚を保存しています。かなり多いと思うんですが、いけるもんですねぇ。

映像を縦1pxづつに区切って管理し、それぞれに何フレーム早めるか、遅らせるかを管理しています。
デフォルトでは保存したキャプチャの真ん中を表示。(今回は400枚保存してるから、200枚目を表示。)

BetweenAs3のイージングを変えるとまた違った感じになります。

ちょっとハマったのが、NetStreamインスタンスをローカル変数にすると、drawする時に下のセキュリティエラーが出る事。

SecurityError: Error #2123: セキュリティサンドボックス侵害 : BitmapData.draw: file:///hoge.swf は unknown URL にアクセスできません。ポリシーファイルへのアクセスも許可されていません。

drawする時にNetStreamインスタンスを参照して何かチェックしてるんですかね?
すぐにエラーが出ずに、しばらくすると出るのも謎でした。

一番良かったのは、絶好の映像ネタが見つかった事w
背景が固定で人が動く映像が見つかって良かった。
背景まで動いているとグチャグチャになりすぎてしまい、良く分からない映像になってしまいます。
ムーンウォークしてる人は、もちろん僕じゃないですよ!w

Fluid

前回asに書き直しただけのFluid \ Learning \ Processing 1.0のソースを解体して整理。クラス構造も大幅に変えてます。
それから、オリジナルはマス目に色を塗って動きを出していますが、パーティクルを表示して動かす形に変更しました。

オリジナルは作製途中なのかな、って感じがしました。。

気泡っぽい感じが出た。

流体パーティクル

Processingの流体サンプルをasに

[Flash]流体っぽいのを作ろうと思った | blog ViolentCoding
を見てずっと気になっていたんですが、同記事で紹介されている

Fluid \ Learning \ Processing 1.0
をそのままas3に持ってきてみました。

Processingの流体サンプルをそのまま持ってきたもの – wonderfl build flash online

変数宣言が微妙に違うぐらいで、そこを直せば割とあっさり動きました。
最適化してないせいもあるけど、やっぱFlashは重いですね。
変数名なんかはそのままです。
スパイス的な謎の定数が結構あって完全に読み解くのは大変そう。

しかし紹介して下さったe_s_jpさんが作ってるDisplacementMapFilterのバージョンですが、
よくあそこまで膨らませたなぁ、と思います。
凄いす。

僕も、processing.orgから持ってきたコードを、もうちょっと分かりやすく分解して理解してみようと思います。

変数名なんかを自分用に置き換えただけなのはコレ。

package {
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.display.StageQuality;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Rectangle;

	[SWF(backgroundColor="0x0", width="465", height="465", frameRate="60")]
	public class Main2 extends Sprite {

		public function Main2():void {
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}

		private var pmouseX:Number;
		private var pmouseY:Number;

		private function init(e:Event = null):void {
			removeEventListener(Event.ADDED_TO_STAGE, init);
			stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
			stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
			stage.quality = StageQuality.MEDIUM;

			addChild(new Bitmap(canvas));

			numCols = width / resolution;
			numRows = height / resolution;

			vsquaresVectors = new Vector.>(numRows + 1);
			vbuffersVectors = new Vector.>(numRows + 1);

			for (var i:int = 0; i < numParticles; i++) {
				particles[i] = new Particle(Math.random() * width, Math.random() * height);
			}

			for (var col:int = 0; col <= numCols; col++) {
				vsquaresVectors[col] = new Vector.(numCols + 1);
				vbuffersVectors[col] = new Vector.(numCols + 1);
				for (var row:int = 0; row <= numRows; row++) {
					vsquaresVectors[col][row] = new Vsquare(col * resolution, row * resolution);
					vbuffersVectors[col][row] = new Vbuffer(col * resolution, row * resolution);
				}

				vsquaresVectors[col].fixed = true;//付け足し
				vbuffersVectors[col].fixed = true;//付け足し
			}

			vsquaresVectors.fixed = true;//付け足し
			vbuffersVectors.fixed = true;//付け足し

			addEventListener(Event.ENTER_FRAME, draw);
		}

		private function draw(e:Event):void {
			mouseX = this.mouseX;
			mouseY = this.mouseY;

			var axvel:int = mouseX - pmouseX;
			var ayvel:int = mouseY - pmouseY;

			mouseXvel = (axvel != mouseXvel) ? axvel : 0;
			mouseYvel = (ayvel != mouseYvel) ? ayvel : 0;

			for (var col:int = 0; col < numCols; col++) {
				for (var row:int = 0; row < numRows; row++) {
					vbuffersVectors[col][row].updatebuf(col, row);
					//何にも無い色の設定
					vsquaresVectors[col][row].color = 32;
				}
			}

			for each(var p:Particle in particles) {
				p.updatepos();
			}

			canvas.lock();//付け足し
			for (col = 0; col < numCols; col++) {
				for (row = 0; row < numRows; row++) {
					vsquaresVectors[col][row].addbuffer(col, row);
					vsquaresVectors[col][row].updatevels(axvel, ayvel);
					vsquaresVectors[col][row].display(col, row);
				}
			}
			canvas.unlock();//付け足し

			pmouseX = mouseX;
			pmouseY = mouseY;
		}

		private function mouseDownHandler(e:Event):void {
			mousePressed = true;
		}

		private function mouseUpHandler(e:Event):void {
			mousePressed = false;
		}
	}

}

import flash.display.BitmapData;
var width:int = 200;
var height:int = 200;
var mousePressed:Boolean;
var mouseX:Number = 0;
var mouseY:Number = 0;
var resolution:int = 5;
var penSize:int = 30;
var numCols:int;
var numRows:int;
var numParticles:int = 7000;
var vsquaresVectors:Vector.>;
var vbuffersVectors:Vector.>;
var particles:Vector. = new Vector.(numParticles, true);
var pcount:int = 0;
var mouseXvel:int = 0;
var mouseYvel:int = 0;
var canvas:BitmapData = new BitmapData(width, height, false, 0x0);

class Particle{
	public function Particle(xIn:Number, yIn:Number) {
		x = xIn;
		y = yIn;
	}

	public var x:Number;
	public var y:Number;
	public var xvel:Number = 0;
	public var yvel:Number = 0;
	public var pos:int = 0;

	public function updatepos():void {
		//var col1:Number;//使われていない
		if (x > 0 && x < width && y > 0 && y < height) {
			var col:int = int(x / resolution);//自身が属しているvsquareを見つける
			var row:int = int(y / resolution);
			var vsquare:Vsquare = vsquaresVectors[col][row];

			var ax:Number = (x % resolution) / resolution;
			var ay:Number = (y % resolution) / resolution;

			xvel += (1 - ax) * vsquaresVectors[col][row].xvel * 0.05;
			yvel += (1 - ay) * vsquaresVectors[col][row].yvel * 0.05;

			xvel += ax * vsquaresVectors[col + 1][row].xvel * 0.05;
			yvel += ax * vsquaresVectors[col + 1][row].yvel * 0.05;

			xvel += ay * vsquaresVectors[col][row + 1].xvel * 0.05;
			yvel += ay * vsquaresVectors[col][row + 1].yvel * 0.05;

			//自身がいるマスの色を濃くする
			vsquare.color += 4;

			x += xvel;
			y += yvel;
		}
		else {
			x = Math.random() * width;
			y = Math.random() * height;
			xvel = 0;
			yvel = 0;
		}

		xvel *= 0.5;
		yvel *= 0.5;
	}
}

class Vbuffer {
	public function Vbuffer(xIn:int, yIn:int) {
		x = xIn;
		y = yIn;
		pressureX = 0;
		pressureY = 0;
	}

	public var x:int;
	public var y:int;
	public var xvel:Number = 0;
	public var yvel:Number = 0;
	public var pressureX:Number = 0;
	public var pressureY:Number = 0;
	public var pressure:Number = 0;

	public function updatebuf(col:int, row:int):void {
		if (col > 0 && col < numCols && row > 0 && row < numRows) {
			pressureX = (
				  vsquaresVectors[col - 1][row - 1].xvel * 0.5
				+ vsquaresVectors[col - 1][row].xvel
				+ vsquaresVectors[col - 1][row + 1].xvel * 0.5
				- vsquaresVectors[col + 1][row - 1].xvel * 0.5
				- vsquaresVectors[col + 1][row].xvel
				- vsquaresVectors[col + 1][row + 1].xvel * 0.5
			);

			pressureY = (
				  vsquaresVectors[col - 1][row - 1].yvel * 0.5
				+ vsquaresVectors[col][row - 1].yvel
				+ vsquaresVectors[col + 1][row - 1].yvel * 0.5
				- vsquaresVectors[col - 1][row + 1].yvel * 0.5
				- vsquaresVectors[col][row + 1].yvel
				- vsquaresVectors[col + 1][row + 1].yvel * 0.5
			);

			pressure = (pressureX + pressureY) * 0.25;
		}
	}
}

import flash.geom.Rectangle;
class Vsquare{

	public function Vsquare(xIn:int, yIn:int) {
		x = xIn;
		y = yIn;
	}

	public var x:int;
	public var y:int;
	public var xvel:Number = 0;
	public var yvel:Number = 0;
	public var color:Number = 0;

	public function addbuffer(col:int, row:int):void {
		if (col > 0 && col < numCols && row > 0 && row < numRows) {
			xvel += (
				  vbuffersVectors[col - 1][row - 1].pressure * 0.5
				+ vbuffersVectors[col - 1][row].pressure
				+ vbuffersVectors[col - 1][row + 1].pressure * 0.5
				- vbuffersVectors[col + 1][row - 1].pressure * 0.5
				- vbuffersVectors[col + 1][row].pressure
				- vbuffersVectors[col + 1][row + 1].pressure * 0.5
			) * 0.25;

			yvel += (
				  vbuffersVectors[col - 1][row - 1].pressure * 0.5
				+ vbuffersVectors[col][row - 1].pressure
				+ vbuffersVectors[col + 1][row - 1].pressure * 0.5
				- vbuffersVectors[col - 1][row + 1].pressure * 0.5
				- vbuffersVectors[col][row + 1].pressure
				- vbuffersVectors[col + 1][row + 1].pressure * 0.5
			) * 0.25;
		}
	}

	public function updatevels(mvelX:int, mvelY:int):void {
		if (mousePressed) {
			var dx:Number = x - mouseX;
			var dy:Number = y - mouseY;

			var dist:Number = Math.sqrt(dy * dy + dx * dx);
			if (dist < penSize) {
				if (dist < 4) dist = penSize;
				var mod:Number = penSize / dist;
				xvel += mvelX * mod;
				yvel += mvelY * mod;
			}
		}

		xvel *= 0.99;
		yvel *= 0.99;
	}

	public function display(i:int, u:int):void {
		var tempColor:uint = 0;

		if (color > 255) color = 255;
		if (i > 0 && i < numCols - 1 && u > 0 && u < numRows - 1) {
			tempColor = (
				  vsquaresVectors[i][u + 1].color
				+ vsquaresVectors[i + 1][u].color
				+ vsquaresVectors[i + 1][u + 1].color * 0.5
			) * 0.4;
			tempColor = int(tempColor + color * 0.5);
		}
		else {
			tempColor = int(color);
		}

		var rgb:uint = tempColor << 16 | tempColor << 8 | tempColor;
		canvas.fillRect(new Rectangle(x, y, resolution, resolution), rgb);
	}
}

うずまきエフェクト DisplacementMapFilterで

うずまき。

うずまき – wonderfl build flash online

球もつくりました。けど、これは偶然うまくいってる。
mapBitmap作る部分が間違ってると思う。createDMF関数の中がおかしい。
なので参考にしない方がいいです。すみません。
ひょっとすると渦巻きも…?

DisplacementMapFilter 球 – wonderfl build flash online

DisplacementMapFilterのメモ

追記 2009/05/11
間違ってるとこも多いです。ちょこちょこ直していきます。

かなりくどく書きました。だらだらです。helpが意味不明すぎる。

なんでも自由に変形できる万能フィルタではない。誤解してた。そういう変形は今だとpixel Bender使うのかな…?
そうするとDisplacementMapFilterの使い道ってすごく限られてくるのかと疑問に思ってきた。

おおまかな考え方

通常だと、現在地点にどの位の値を加えれば目的地に着くか? で考えるけど、DisplacementMapFilterだと逆。
目的地に引き寄せる力は何か?を考える。 その力を色に変換する。

例えば

現在地 x = 10の地点を、目的地 x = 50に移したいとする。

《通常の考え方》
普通にxに40足す。

《DisplacementMapFilterの考え方》
フィルタによって x = 50に移る点は、x = 10の地点だとしたい。 そうするためにはx = -40の力を生む色を塗る。

色は、目的地に塗る。(この例ではx = 50の地点)

DisplacementMapFilterでの色の決め方

ヘルプには

このフィルタでは次の式を使用します。
dstPixel[x, y] = srcPixel[x + ((componentX(x, y) – 128) * scaleX) / 256, y + ((componentY(x, y) – 128) *scaleY) / 256)

と書いてある。
flashPlayerはこの式を使って変形する、っていう事かな?

どういう事か?

色の強さは0~255の256段階。 このままだとマイナス方向が指定出来ないため、 真ん中の128を中間地点とする。

このhelpには中間値は127と書いてある。間違い??

真ん中からの離れ具合から強さを決める。 そこから128離れるとき、つまり256の時、 50パーセントの力になる(でも色の最大値は255なので僅かな誤差が出る)。
同様に、-128離れるとき、-50%の力になる。(100パーセントではないので注意。)

色の決め方の式

上の考え方から、

移動させる割合 = 移動させる距離 / 最大移動距離
色の強さ = (0x80 * 移動させる割合) + 0x80

このままだと最大値が0x100になってしまい、色の最大値である0xFFを超えてしまう。そのため、中間値を0x7Fとする。127と書いてあるのはこのこと?

色の強さ = (0x80 * 移動させる割合) + 0x7F

※ 0x7Fは10進数だと127、0x80は10進数だと128

x方向の移動のみ考える。
現在の座標は(0, 0)で、x方向に100px移動させたいとする。 まず目的地である(100, 0)の地点に色を塗ることにする。 この色を塗った地点に吸い寄せる力の色に青を使うとする。

目的地に引き寄せられるような色を決める

問題は何色にするか。 DisplacementMapFilterを作製する際、scaleX, scaleYという値を決める。
普通、100%の際にmaxの値を取るものだが、 DisplacementMapFilterはmaxが50%という変な設定なので、 scaleX, scaleYには取り得る最大値の2倍を設定。
ここではscaleXの値を100 * 2 = 200とする。
(0,0)から(100、0)に移動させたい。 ここは逆に考えます。 (100,0)の地点に吸い寄せられる地点を(0,0)にしたい、と

そのように設定をするためには、何色で塗ればいいのか?
x方向の距離は、目的地を基準として考えるので、0 – 100 = -100 (目的地から-100すると現在地になるという事)
移動させる距離を、最大値との比率で考える。 すると、-100 / 200 = 0.5
さっきの色の話を見直すと、-50%の力を作る色は、 中間地点の128から-50%の値である0×0。

フィルタを作るときは、大体これをfor文でmapBitmapの全ピクセルに実行する。
各ピクセルで、適用先のピクセルを求めていく。

色を決める式を、ヘルプに書いてある式から求めると

先に挙げた式でも、こちらでもやってることは一緒。
先に出たヘルプの式をxの値だけに絞って整理してみる。

フィルタが適用されるx値 = 移動先のx値 + ((色の強さ - 0x80) * dmf最大値) / 0x100

(dmf最大値はDisplacementMapFilterのscaleX, scaleYの値。変形する際の最大移動距離の2倍にする。)

↓変形

フィルタが適用されるx値 - 移動先のx値 = ((色の強さ - 0x80) * dmf最大値) / 0x100

↓変形(フィルタが適用されるx値 - 移動先のx値 を 移動させる距離 に置き換え。)

移動させる距離 = ((色の強さ - 0x80) * dmf最大値) / 0x100

↓変形

移動させる距離 * 0x100 = (色の強さ - 0x80) * dmf最大値

↓変形

( 移動させる距離 * 0x100 ) / dmf最大値 = 色の強さ - 0x80

↓変形

( 移動させる距離 * 0x100 ) / dmf最大値 + 0x80 = 色の強さ

入れ替えて、

色の強さ = ( 移動させる距離 * 0x100 ) / dmf最大値 + 0x80

このままだと最大値が0x100になってしまい、色の最大値である0xFFを超えてしまう。そのため、中間値を0x7Fとする。

色の強さ = ( 移動させる距離 * 0x100 ) / dmf最大値 + 0x7F

これらを使ったテスト。マウス座標に移す変形をする。
ステージサイズ横100 * 縦100で作ってるので拡大して表示されます。

次回はよくある球へのマッピングをやってみます。

追記 2009/05/11
色の決め方の式を追加。

その式つかったwonderflにリンク差し替え。

Clothシミュレーションもどき

2009/04/29 追記
2Dだけど、出来たっぽいです。回転の問題もクリアできました。

何回か布っぽいのを作るシリーズをやってましたが、
今までの物は、ちゃんと点どうしが連結されたものではありませんでした。

今回はちゃんと連結して布のシミュレーションぽい物ができたのでUPします。
しかし、あくまで『それらしい物』であって、z軸も考慮してませんし、怪しい部分が多々あります。

最近Clothシミュレーションよく見ますね。js実装のとか。
2009-02-11 – 最速チュパカブラ研究会
解説も丁寧で凄いなぁ。ソース読んで勉強しよう。

初wonderFlです。

ドラッグで移動、ctrlキー押しながらドラッグで固定。
固定した点をダブルクリックで解除です。

最初に格子状に並べているので布のようですが、
最初に何らかの形に並べれば、その形を維持するように動くはずです。

jointメソッドで隣り合った点どうしを連結します。
ここがどうしても冗長でわかりにくくなってしまってます。
コメントアウトで色々書いておきました。

2009/04/13 追記
ずいぶん改善できました。
布 : 改善 | wonderfl build flash online
最初は8方向に対して連結していましたが、上下左右の4方向でOKでした。
ソースも短くなり、ずいぶん分かりやすくなりました。
上の2009-02-11 – 最速チュパカブラ研究会様のソースを参考にしてます。やっぱ凄いです。

一番最初に布っぽい物を作った時も、それぞれを連結するって事は考えたんですが、やり方が思いつかなった。
今回は実直に総当たりな感じで試しにやってみたら上手くいきました。
上手いやり方を考えるのもいいけど、泥臭いやりかたで試すの大事ですね。

Player10から加わったGraphicsPathCommandを使ってラインを描画するバージョンも作ってみました。

追記
wonderflの使い方が良く分かっておらず、forkしないで新しく作ってました。そちら削除させてもらいました。favoriteに入れてくれた方がいらっしゃったのですが、ごめんなさい!

でも少し重くなりますね。今回はこの方法は適してないんでしょうね。
drawLine関数だけ違います。他の部分は全く同じ。

あとはテクスチャですが、テクスチャを貼るのは結構簡単なようです。
AdvancED ActionScript 3.0 Animationに丁寧に書かれてました。
球体のテクスチャ付き3D描画についても書かれてますよ!

それから、回転させるって以前言ったものの、まだ回転させる方法が思い浮かびません。
ためしに思いつきで作ったのですが、上手く動きませんでした。
なにをもって回転とするのかが良くわかってません。

回転の失敗作も近いうちにUPしようと思います。

AdvancED ActionScript 3.0 Animation

名著 ActionScript3.0アニメーションの続編(洋書)。

_img_cover_9781430216087.jpg

アマゾンで買っちゃったんですが、
AdvancED ActionScript 3.0 Animation | Book Details
上のリンク先で電子書籍(ebookって書いてある。pdfかな?pdfでした。)としても買えるようですね。
コピペが出来るわけです。ということは分からない単語が出てきても、すぐに辞書ひける。
まるまる1ページ翻訳とかもイケますよね。

…しまった!! 早まったかも!?

上記リンクからは、試し読みが出来るだけでなく、書籍内で使われているサンプルファイルもダウンロードできますね。ふとっぱら!!

(追記)
買ってしまいました。paypalとカードの支払いに対応してました。
1 日本円 = 0.0104302 米ドルで、$27.99 → ¥2,684でした。
形式はpdf。

グニャグニャするネットを作る

以前作ったのの続き。
前回のは位置が固定されていて動かせなかったけど、どうにかして動かせるようにしました。
まだ未完ですが、勢いでUPします。


別windowでサンプルを開きます
1.png 2.png

今回はパラメーターを弄れるようにスライダーを付けていて、左からfriction(摩擦 : 0.1~0.98)、spring(バネの係数 : 0.1~1)、_POWER_COEFFICIENT(各点にかかる力を求めるための定数。自分でやっといて良く理解していない : 5~300)です。
スライダーのソース、使い方は書籍『ActionScriptアニメーション』のものを使わせて貰いました。色だけ変えてあります。

Ballクラスにtensionってパラメーターを追加し、マウスとの距離によって変え、バネ運動の際にその値を反映させています。

以下は問題点。

  • 未だにネット部分だけにあたり判定を付けるのが出来てない。
  • なのでスライダーを弄ってもネットが動く。
  • 前回も書いたけど、多分この方法は間違ってる気がする。ちゃんと点どうしが連結されていない。
  • 各値をかなり低くすると、動作がおかしい点がある。

要改善。次は回転させようと思います。

んで、僕は何がやりたいのかと言うと、
The Magic Carpet  —  Manuel’s Coding Blogとか、THE ECO ZOO | ECODA!DOBUTSUENの引っ張れる旗みたいなのを作りたいのです。
といっても3Dにしたい訳ではなく、どういう風に座標を管理しているのか、等の仕組みが知りたい。
同じように、確かFlash8が出たあたり、fladdictさんがグネグネするコーラの缶のデモを作られていた。そこのURLはもう無い(ドメインが違う)し、はっきりとは覚えていないんだけど、テクスチャも貼られていた。そういったのを作りたいのです。

今回のソース一式 net2.zip

メインとなるソースだけ載せてみます。

package
{
	import flash.display.Sprite;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import org.miniapp.customFigure.Ball;

	[SWF(backgroundColor="0x000000", width="800", height="800", frameRate="60")]
	public class Net extends Sprite
	{
		private static const _SPRING:Number = 0.5;
		private static const _FRICTION:Number = 0.5;
		private static const _COLUM:uint = 20
		private static const _ROW:uint = 20;
		private static const _NUM_BALLS:uint = _COLUM * _ROW;
		private static const _DIFF_X:uint = 20;
		private static const _DIFF_Y:uint = 20;
		private static const _START_X:uint = 200;
		private static const _START_Y:uint = 200;
		private static const _BALL_RADIUS:uint = 4;

		//TODO 名前も数値も適当。ちゃんとしたい。
		private static const _POWER_COEFFICIENT:uint = 50;

		/**
		 * コンストラクタ
		 */
		public function Net()
		{
			if (stage)
				init();
			else
				addEventListener(Event.ADDED_TO_STAGE, init);
		}

		private var _mousePressX:Number;
		private var _mousePressY:Number;
		private var _isDragging:Boolean = false;
		private var _lastMouseX:Number;
		private var _lastMouseY:Number;
		private var _balls:Array/* of Ball */
		private var _horizontalBallArray:Array/* of Array */
		private var _verticalBallsArray:Array/* of Array */
		private var _frictionSlider:SimpleSlider;
		private var _springSlider:SimpleSlider;
		private var _powerCoefficientSlider:SimpleSlider;

		private function init():void
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
			stage.scaleMode = StageScaleMode.NO_SCALE;

			_frictionSlider = new SimpleSlider(0.1, 0.98, _FRICTION);
			_frictionSlider.x = 10;
			_frictionSlider.y = 10;
			addChild(_frictionSlider);

			_springSlider = new SimpleSlider(0.1, 1, _SPRING);
			_springSlider.x = 30;
			_springSlider.y = 10;
			addChild(_springSlider);

			_powerCoefficientSlider = new SimpleSlider(5, 300, _POWER_COEFFICIENT);
			_powerCoefficientSlider.x = 50;
			_powerCoefficientSlider.y = 10;
			addChild(_powerCoefficientSlider);

			_balls = [];

			//ボールを繋ぐ線を描くため、縦、横に分けて配列に入れて纏める。
			//縦に並ぶボールを入れた配列をまとめる配列
			_horizontalBallArray = [];
			//横に並ぶボールを入れた配列をまとめる配列
			_verticalBallsArray = [];

			for (var i:uint = 0; i < _COLUM; i++)
			{
				//縦に並ぶボールを管理する配列を作る
				var horizontalBalls:Array/* of Ball */ = [];
				_horizontalBallArray.push(horizontalBalls);
			}

			//ボール生成、配置
			for (i = 0; i < _ROW; i++)
			{
				//横に並ぶボールを管理する配列を作る
				var verticalBalls:Array/* of Ball */ = [];
				for (var j:uint = 0; j < _COLUM; j++)
				{
					var ball:Ball = new Ball(_BALL_RADIUS, 0xFFFFFF);
					ball.x = ball.defaultX = _START_X + _DIFF_X * j;
					ball.y = ball.defaultY = _START_Y + _DIFF_Y * i;
					_balls.push(ball);

					//縦に並ぶボールを入れる
					(_horizontalBallArray[j] as Array).push(ball);
					//横に並ぶボールを入れる
					verticalBalls.push(ball);
					//ball.cacheAsBitmap = true;
					//addChild(ball);
				}
				_verticalBallsArray.push(verticalBalls);
			}

			drawLine();
			stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
			stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
			stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
		}

		/**
		 * ボール同士を線で繋ぐ
		 */
		private function drawLine():void
		{
			graphics.clear();
			graphics.lineStyle(1, 0xFFFFFF);

			//縦のラインを引く
			for (var i:uint = 0; i < _COLUM; i++)
			{
				var horizontalBalls:Array = _horizontalBallArray[i] as Array;
				var firstBall:Ball = Ball(horizontalBalls[0]);
				graphics.moveTo(firstBall.x, firstBall.y);
				for (var j:uint = 1; j < _COLUM; j++)
				{
					var ball:Ball = Ball(horizontalBalls[j]);
					graphics.lineTo(ball.x, ball.y);
				}
			}

			//横のラインを引く
			for (i = 0; i < _ROW; i++)
			{
				var verticalBalls:Array = _verticalBallsArray[i] as Array;
				firstBall = Ball(verticalBalls[0]);
				graphics.moveTo(firstBall.x, firstBall.y);
				for (j = 1; j < _ROW; j++)
				{
					ball = Ball(verticalBalls[j]);
					graphics.lineTo(ball.x, ball.y);
				}
			}
		}

		/**
		 * バネ運動させる
		 * @param	ball バネ運動させるボール
		 * @param	targetPoint 目標位置
		 */
		private function springTo(ball:Ball, targetX:Number, targetY:Number):void
		{
			ball.vx += (targetX - ball.x) * _springSlider.value * ball.tension;
			ball.vy += (targetY - ball.y) * _springSlider.value * ball.tension;

			ball.vx *= _frictionSlider.value;
			ball.vy *= _frictionSlider.value;

			ball.x  += ball.vx;
			ball.y  += ball.vy;
		}

		private function enterFrameHandler(e:Event):void
		{
			if (_isDragging)
			{
				//マウスが移動した距離を調べる
				var dxMouse:Number = mouseX - _lastMouseX;
				var dyMouse:Number = mouseY - _lastMouseY;
			}

			for (var i:uint = 0; i < _NUM_BALLS; i++)
			{
				var ball:Ball = Ball(_balls[i]);
				if (_isDragging)
				{
					//マウスが押された地点とボールの距離を調べる
					var dx:Number = mouseX - ball.x;
					var dy:Number = mouseY - ball.y;
					var distance:Number = Math.sqrt(dx * dx + dy * dy);

					//マウスとボールの位置からボールにかかる力を算出
					//マウスから近いボールほど引っ張られる力が大きくなる
					var tension:Number = _powerCoefficientSlider.value / ( _powerCoefficientSlider.value + distance);
					ball.tension = tension;

					//目的地を算出
					var toX:Number = ball.defaultX + dxMouse;
					var toY:Number = ball.defaultY + dyMouse;
				}
				else
				{
					toX = ball.defaultX;
					toY = ball.defaultY;
				}

				springTo(ball, toX, toY);
				ball.defaultX = toX;
				ball.defaultY = toY;
			}

			drawLine();

			//マウス位置を記憶
			_lastMouseX = mouseX;
			_lastMouseY = mouseY;
		}

		private function mouseDownHandler(e:MouseEvent):void
		{
			_isDragging = true;
			_mousePressX = mouseX;
			_mousePressY = mouseY;
		}

		private function mouseUpHandler(e:MouseEvent):void
		{
			_isDragging = false;
		}
	}
}

球が通り抜ける布みたいなのを作る

前回(バネ運動するネットみたいなのを作る)からの続き。
ドラッグすると球が大きくなります。

別windowでサンプルを開きます
ピクチャ 3.png 42.jpg

今回のソース一式 net2.zip

前回(バネ運動するネットみたいなのを作る)は引っ張るネットを作りましたが、やっぱりProcreoFlashDesign Laboratory ボールと布や、52sec.orgさんのデモのようなのを作りたいので、挑戦しました。

まずは一個のボールをマウスに反応させるところから。
マウスとボールの位置を計算して、その距離に応じてボールが反応するようなのを作ってみた。

次は数を増やしていきます。
今回は線を連結しているボールは表示せず、ただ位置のプロパティを持っているだけのPlotPointってクラスを作って、マス目だけを引く事にしました。

球の座標の求め方が最初は分からなくて試行錯誤してしまったのですが、
ググったらすぐ出てきました。
<参考URL>
座標変換公式
1から始める数学 ベクトル解析 座標系 (ここの球座標系のところ。)

最初から調べればよかった…。
球の座標を求めた後、更に透視投影で座標変換してみたですが、変換しなかった際と比べ、あまり差がなかったので、結局、透視投影での変換はしてません。なのでZ値は無視しています。

一応今回のメインコード。
前回と変わったのはenterFrameHandler内ぐらいで、あとは大体やってる事は一緒です。

package
{
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import org.miniapp.point.PlotPoint;

	[SWF(backgroundColor="0xFFFFFF", width="700", height="700", frameRate="60")]
	public class SphereNet extends Sprite
	{
		private static const _THETA_LIMIT:Number = Math.PI / 2; //90度
		private static const _MAX_RADIUS:uint = 200;
		private static const _MIN_RADIUS:uint = 100;
		private static const _SPRING:Number = 0.2;
		private static const _FRICTION:Number = 0.9;
		private static const _COLUM:uint = 20;
		private static const _ROW:uint = 20;
		private static const _NUM_POINTS:uint = _COLUM * _ROW;
		private static const _DIFF_X:uint = 20;
		private static const _DIFF_Y:uint = 20;
		private static const _START_X:uint = 160;
		private static const _START_Y:uint = 160;

		/**
		 * コンストラクタ
		 */
		public function SphereNet()
		{
			init();
		}

		private var _sphereRadius:uint = SphereNet._MIN_RADIUS;
		private var _points:Array/* of PlotPoint */
		private var _horizontalPointsArray:Array/* of Array */
		private var _verticalPointsArray:Array/* of Array */

		public function init():void
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			_points = [];

			//ポイントを繋ぐ線を描くため、縦、横に分けて配列に入れて纏める。
			//縦に並ぶポイントを入れた配列をまとめる配列
			_horizontalPointsArray = [];
			//横に並ぶポイントを入れた配列をまとめる配列
			_verticalPointsArray = [];

			for (var i:uint = 0; i < SphereNet._COLUM; i++)
			{
				//縦に並ぶボールを管理する配列を作る
				var horizontalPoints:Array/* of PlotPoint */ = [];
				_horizontalPointsArray.push(horizontalPoints);
			}

			//ポイント生成
			for (i = 0; i < SphereNet._ROW; i++)
			{
				//横に並ぶボールを管理する配列を作る
				var verticalPoints:Array/* of PlotPoint */ = [];
				for (var j:uint = 0; j < SphereNet._COLUM; j++)
				{
					var point:PlotPoint = new PlotPoint();
					point.x = point.defaultX = _START_X + _DIFF_X * j;
					point.y = point.defaultY = _START_Y + _DIFF_Y * i;
					_points.push(point);

					//縦に並ぶポイントを入れる
					(_horizontalPointsArray[j] as Array).push(point);
					//横に並ぶポイントを入れる
					verticalPoints.push(point);
				}
				_verticalPointsArray.push(verticalPoints);
			}

			addEventListener(Event.ENTER_FRAME, enterFrameHandler);
			stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
			stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
		}

		/**
		 * ボールを元の大きさにする
		 * @param	e
		 */
		private function mouseUpHandler(e:MouseEvent):void
		{
			_sphereRadius = SphereNet._MIN_RADIUS;
		}

		/**
		 * ボールを大きくする
		 * @param	e
		 */
		private function mouseDownHandler(e:MouseEvent):void
		{
			_sphereRadius = SphereNet._MAX_RADIUS;
		}

		private function enterFrameHandler(e:Event):void
		{
			for (var i:uint = 0; i < SphereNet._NUM_POINTS; i++)
			{
				var point:PlotPoint = PlotPoint(_points[i]);

				var dx:Number = point.defaultX - mouseX;
				var dy:Number = point.defaultY - mouseY;
				var distance:Number = Math.sqrt(dx * dx + dy * dy);

				//球の半径内にいる点に対して球の上に乗った時の座標計算
				//参考 http://kenpei-web.hp.infoseek.co.jp/math/coordinates/index.html
				if (distance <= _sphereRadius)
				{
					//距離と球の半径の関係から力がかかる倍率を決める
					var powerRate:Number = distance / _sphereRadius;

					//Z軸からXY平面への角度
					var theta:Number = SphereNet._THETA_LIMIT * powerRate;

					//真上から見た角度
					var angle:Number = Math.atan2(dy, dx);

					var sin:Number = Math.sin(theta);
					var toX:Number = mouseX + _sphereRadius * sin * Math.cos(angle);
					var toY:Number = mouseY + _sphereRadius * sin * Math.sin(angle);

					springTo(point, toX, toY);
				}
				else
					springTo(point, point.defaultX, point.defaultY);
			}
			drawLine();
		}

		/**
		 * ポイントをバネ運動させる
		 * @param	point
		 * @param	targetX
		 * @param	targetY
		 */
		private function springTo(point:PlotPoint, targetX:Number, targetY:Number):void
		{
			point.vx += (targetX - point.x) * SphereNet._SPRING;
			point.vy += (targetY - point.y) * SphereNet._SPRING;

			point.vx *= SphereNet._FRICTION;
			point.vy *= SphereNet._FRICTION;

			point.x  += point.vx;
			point.y  += point.vy;
		}

		/**
		 * ポイント同士を線で繋ぐ
		 */
		private function drawLine():void
		{
			graphics.clear();
			graphics.lineStyle(1, 0x000000);

			//縦のラインを引く
			for (var i:uint = 0; i < SphereNet._COLUM; i++)
			{
				var horizontalPoints:Array = _horizontalPointsArray[i] as Array;
				var firstPoint:PlotPoint = PlotPoint(horizontalPoints[0]);
				graphics.moveTo(firstPoint.x, firstPoint.y);
				for (var j:uint = 1; j < SphereNet._COLUM; j++)
				{
					var point:PlotPoint = PlotPoint(horizontalPoints[j]);
					graphics.lineTo(point.x, point.y);
				}
			}

			//横のラインを引く
			for (i = 0; i < SphereNet._ROW; i++)
			{
				var verticalPoints:Array = _verticalPointsArray[i] as Array;
				firstPoint = PlotPoint(verticalPoints[0]);
				graphics.moveTo(firstPoint.x, firstPoint.y);
				for (j = 1; j < SphereNet._ROW; j++)
				{
					point = PlotPoint(verticalPoints[j]);
					graphics.lineTo(point.x, point.y);
				}
			}
		}
	}
}

バネ運動するネットみたいなのを作る

/*
<修正>2008/08/08
一部コードを修正しました。
また、負荷が高い処理なので、swfがアクティブな際のみ処理を行うよう変更しました。
でもMacのSafariだとEvent.DEACTIVATEが起こらないんですね…。どうしよう。
*/

52sec.orgさんのデモを見て、僕も同じようなのを作りたくなった。
ActionScript3.0アニメーションも読んだし、勉強の為に。
でも52sec.orgさんのデモは多分、3次元の座標変換をしているので、僕はもうちょっと簡単な物から作ろうと思う。ブラーも無しで。
で、マウスドラッグできるネットを作ります。

試しに一個のボールを引っ張るデモを作った。

ボールそのものをドラッグするんじゃなくて、ドラッグした距離とか方向に応じてボールが反応する。
それで、ボールの数を増やして線で繋ぐと、ネットみたいになる。

別windowでサンプルを開きます
ピクチャ 1.png ピクチャ 3.png

でもこれ、ちゃんとしたネットのシミュレーションしてないんだよな。きっと。
本当は、ボールの一つが引っ張られると、連結されたボールが引っ張られて、その引っ張られたボールに連結されてるボールが…
の繰り返しでなくちゃいけないんじゃなかろうか。
最初はその方向で作ってたんだけど、うまくいかなくて挫折したのでした。
今回はFlashDevelop使って書いた。慣れてるからなのかもしれないけど快適だな〜。

今回のソース一式 NetSample.zip

一応メインとなるソースだけ載せてみます。
Ballクラスってのを使ってるけど、円を描いて、速度のプロパティvx, vyと、デフォルト位置を記憶させるプロパティを持ってます。

package
{
	import flash.display.Sprite;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import org.miniapp.customFigure.Ball;

	[SWF(backgroundColor="0x000000", width="800", height="800", frameRate="60")]
	public class Net extends Sprite
	{
		private static const _SPRING:Number = 0.3;
		private static const _FRICTION:Number = 0.9;
		private static const _COLUM:uint = 20
		private static const _ROW:uint = 20;
		private static const _NUM_BALLS:uint = _COLUM * _ROW;
		private static const _DIFF_X:uint = 30;
		private static const _DIFF_Y:uint = 30;
		private static const _START_X:uint = 100;
		private static const _START_Y:uint = 100;
		private static const _BALL_RADIUS:uint = 4;

		//TODO 名前も数値も適当。ちゃんとしたい。
		private static const _POWER_COEFFICIENT:uint = 100;

		/**
		 * コンストラクタ
		 */
		public function Net()
		{
			init();
		}

		private var _isDragging:Boolean = false;
		private var _mousePressPointX:Number;
		private var _mousePressPointY:Number;
		private var _balls:Array/* of Ball */
		private var _horizontalBallArray:Array/* of Array */
		private var _verticalBallsArray:Array/* of Array */
		private var _canvas:Sprite;

		private function init():void
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;

			_balls = [];

			//マウスドラッグ用の線を描画するためのキャンバス
			_canvas = new Sprite();
			addChild(_canvas);

			//ボールを繋ぐ線を描くため、縦、横に分けて配列に入れて纏める。
			//縦に並ぶボールを入れた配列をまとめる配列
			_horizontalBallArray = [];
			//横に並ぶボールを入れた配列をまとめる配列
			_verticalBallsArray = [];

			for (var i:uint = 0; i < _COLUM; i++)
			{
				//縦に並ぶボールを管理する配列を作る
				var horizontalBalls:Array/* of Ball */ = [];
				_horizontalBallArray.push(horizontalBalls);
			}

			//ボール生成、配置
			for (i = 0; i < _ROW; i++)
			{
				//横に並ぶボールを管理する配列を作る
				var verticalBalls:Array/* of Ball */ = [];
				for (var j:uint = 0; j < _COLUM; j++)
				{
					var ball:Ball = new Ball(_BALL_RADIUS, 0xFFFFFF);
					ball.x = ball.defaultX = _START_X + _DIFF_X * j;
					ball.y = ball.defaultY = _START_Y + _DIFF_Y * i;
					_balls.push(ball);

					//縦に並ぶボールを入れる
					(_horizontalBallArray[j] as Array).push(ball);
					//横に並ぶボールを入れる
					verticalBalls.push(ball);
					ball.cacheAsBitmap = true;
					addChild(ball);
				}
				_verticalBallsArray.push(verticalBalls);
			}

			drawLine();
			addEventListener(Event.DEACTIVATE, deadActiveHandler);
			addEventListener(Event.ACTIVATE, activateHandler);
			stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
			stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
		}

		/**
		 * バネ運動させる
		 * @param	ball バネ運動させるボール
		 * @param	targetPoint 目標位置
		 */
		private function springTo(ball:Ball, targetX:Number, targetY:Number):void
		{
			ball.vx += (targetX - ball.x) * _SPRING;
			ball.vy += (targetY - ball.y) * _SPRING;

			ball.vx *= _FRICTION;
			ball.vy *= _FRICTION;

			ball.x  += ball.vx;
			ball.y  += ball.vy;
		}

		/**
		 * ボール同士を線で繋ぐ
		 */
		private function drawLine():void
		{
			graphics.clear();
			graphics.lineStyle(1, 0xFFFFFF);

			//縦のラインを引く
			for (var i:uint = 0; i < _COLUM; i++)
			{
				var horizontalBalls:Array = _horizontalBallArray[i] as Array;
				var firstBall:Ball = Ball(horizontalBalls[0]);
				graphics.moveTo(firstBall.x, firstBall.y);
				for (var j:uint = 1; j < _COLUM; j++)
				{
					var ball:Ball = Ball(horizontalBalls[j]);
					graphics.lineTo(ball.x, ball.y);
				}
			}

			//横のラインを引く
			for (i = 0; i < _ROW; i++)
			{
				var verticalBalls:Array = _verticalBallsArray[i] as Array;
				firstBall = Ball(verticalBalls[0]);
				graphics.moveTo(firstBall.x, firstBall.y);
				for (j = 1; j < _ROW; j++)
				{
					ball = Ball(verticalBalls[j]);
					graphics.lineTo(ball.x, ball.y);
				}
			}
		}

		private function enterFrameHandler(e:Event):void
		{
			if (_isDragging)
			{
				//マウスがドラッグされた距離を調べる
				var dxMouse:Number = mouseX - _mousePressPointX;
				var dyMouse:Number = mouseY - _mousePressPointY;
				var lengthMouthDrag:Number = Math.sqrt(dxMouse * dxMouse + dyMouse * dyMouse);
				//マウスがドラッグされた角度を求める
				var angleMouseDrag:Number = Math.atan2(dyMouse, dxMouse);

				drawMouseDragLine();
			}

			for (var i:uint = 0; i < _NUM_BALLS; i++)
			{
				var ball:Ball = Ball(_balls[i]);
				if (_isDragging)
				{

					//マウスが押された地点とボールのデフォルト地点の距離を調べる
					var dx:Number = _mousePressPointX - ball.defaultX;
					var dy:Number = _mousePressPointY - ball.defaultY;
					var lengthBetweenMousePressPostionAndBallPosition:Number = Math.sqrt(dx * dx + dy * dy);

					//マウスとボールの位置からボールにかかる力を算出
					//マウスから近いボールほど引っ張られる力が大きくなる
					var powerRate:Number =
					_POWER_COEFFICIENT / ( _POWER_COEFFICIENT + lengthBetweenMousePressPostionAndBallPosition);

					var power:Number = lengthMouthDrag * powerRate;

					//位置関係から移動する目的地を算出
					var toX:Number = ball.defaultX + power * Math.cos(angleMouseDrag);
					var toY:Number = ball.defaultY + power * Math.sin(angleMouseDrag);

					springTo(ball, toX, toY);
				}
				else
					springTo(ball, ball.defaultX, ball.defaultY);
			}
			drawLine();
		}

		/**
		 * マウスドラッグ時の線を引く
		 * @param	e
		 */
		private function drawMouseDragLine():void
		{
			_canvas.graphics.clear();
			_canvas.graphics.lineStyle(1, 0xFFFFFF);
			_canvas.graphics.moveTo(_mousePressPointX, _mousePressPointY);
			_canvas.graphics.lineTo(mouseX, mouseY);
		}

		/**
		 * swfがactiveになったら処理開始
		 * @param	e
		 */
		private function activateHandler(e:Event):void
		{
			//レンダリング開始
			addEventListener(Event.ENTER_FRAME, enterFrameHandler);
		}

		/**
		 * swfがアクティブでなくなったら処理中断
		 * @param	e
		 */
		private function deadActiveHandler(e:Event):void
		{
			removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
		}

		private function mouseDownHandler(e:MouseEvent):void
		{
			_isDragging = true;
			_mousePressPointX = mouseX;
			_mousePressPointY = mouseY;
		}

		private function mouseUpHandler(e:MouseEvent):void
		{
			_isDragging = false;

			//マウスドラッグの線の描画をやめる
			_canvas.graphics.clear();
		}
	}
}

Home > Tags > flash

Return to page top