Home

miniapp

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にリンク差し替え。

ウェブはバカと暇人のもの

ウェブはバカと暇人のもの (光文社新書) ウェブはバカと暇人のもの (光文社新書)
中川淳一郎

光文社 2009-04-17
売り上げランキング : 1101
おすすめ平均

Amazonで詳しく見る by G-Tools

タイトルが煽りすぎ、釣りすぎな感じではありますが、
【Web1.374?】「ウェブはバカと暇人のもの」中川淳一郎:マインドマップ的読書感想文
こちらを拝見して面白そうだと思い、僕も読んでみました。

読んで良かったです。かなり面白い。最近読んだ中では一番。
ほんとに身も蓋も無いところがかなりツボ。声出して何回も笑えました。

著者はこんな人。

中川 淳一郎 (ナカガワ ジュンイチロウ)
1973年東京都生まれ。編集者・PRプランナー。一橋大学商学部卒業。博報堂CC局(コーポレートコミュニケーション局)で企業のPR業務を請け負う。2001年に退社し、しばらく無職となったあと雑誌のライターになり、その後「テレビブロス」編集者になる。企業のPR活動、ライター、雑誌編集などをしながら、2006年からインターネット上のニュースサイトの編集者になる。現在は編集・執筆業務の他、ネットでの情報発信に関するコンサルティング業務、プランニング業務も行っている

要約

著者はニュースサイトの編集者をやっている関係で、ネット漬けの日々を送っているが、とにかくネットが気持ち悪い。そこで他人を「死ね」「ゴミ」「クズ」と罵倒しまくる人も気持ち悪いし、「通報しますた」と揚げ足取りばかりする人も気持ち悪いし、アイドルの他愛もないブログが「絶賛キャーキャーコメント」で埋まるのも気持ち悪いし、ミクシィの「今日のランチはカルボナーラ」みたいなどうでもいい書き込みも気持ち悪い。うんざりだ。—本書では、「頭の良い人」ではなく、「普通の人」「バカ」がインターネットをどう利用しているのか?リアルな現実を、現場の視点から描写する。



下記に良くも悪くも気になった部分をメモしておきます。

もしもナンシー関がブログをやっていたら…
彼女は消しゴム版画家であり、テレビに出てくる芸能人を消しゴム版画と辛口コラムでバッサバッサと斬っていた。週刊文春や週刊朝日、噂の真相での連載が大人気(略)
雑誌の読者にとっては、「そうそう、オレもそう思っていた! よくぞ言ってくれた!」といったたぐいのコラムを連発していたのである。

ナンシー関のブログは多分うまくいかない。
ネットでは過激な意見は書けない。

というのも、雑誌は特定の嗜好を持った人を相手にした媒体であり、その嗜好の人々にあったネタを提供することでわざわざお金を払って買ってもらうものだからだ。
一方、無料でみられるテレビは、不特定多数のどんな嗜好を持っているか分からない人をも満足させる、いや、不快に思わせてはいけない必要がある媒体のため、どこで発信する内容はより無害で、より大衆受けしそうなものとなる。

ネットもテレビと同じ。ネットでは過激な意見は書けない。
炎上するだけ。

ネットはもっとも発言に自由度が無い場所
リアル世界によるネットへの介在は、「不当な書き込み」への抑止力を生むが、同時に「正しい書き込み」に対する抑止力をも生むからだ。

「オープンソースプログラムを作る」などといった「頭の良い人」の世界では、Web2.0というものが非常にしっくりきて、すばらしいプログラムの誕生げ役立つだろう。だが、相手が暇つぶしの道具としてインターネットを使っている「普通の人」か「バカ」の場合、双方向性は運営当事者にとっては無駄である。

ネットで流行るのは結局「テレビネタ」
ブログでもテレビネタは人気
テレビを見ることによって、その日どんなキーワードや話題がネットで関心を持たれるかがすぐに分かる

コピペできない雑誌・新聞はネットにさほど影響ナシ

買わなければ見られない、次の号が出れば話題は流れる。

バカの意見は無視してOK

J-CASTニュース : 松本人志が硫化水素自殺で「放言」 「アホが死んだら別に俺はええねん」

上記URLの事件について。
TOKYO FMはサンケイスポーツの取材に対し、「発言の一部を取り上げ、ねじ曲げられて報道されています。局には発言についての抗議は無い」とコメント。

吉本興業も同様。これにより、この騒動はパタリとやんだ。

常にネットの声に怯え、ネットの悪意ある声でさえも「貴重なお客様のご意見」とする趨勢のなか、「バカの意見は無視してOK」「自分が正しいと思う信念があるのであれば、それを貫くことが大事」という前例をつくっただけに、画期的な出来事であったといえよう。

クリックされなきゃ意味がない
ネットではB級企画が受ける
(中略)ネットユーザーが本当にクリックしてくれそうなB級企画をつぶし、おもしろくも何ともない、ただおしゃれで「Loading」の時間だけがやたらと長く、フラッシュ使いまくりで、どこをクリックしていいのかわからないうえに余計な音がでるサイトを作って、ひとりご満悦なのである。

とても責められている気がしました。。これは気をつけないといかんですね。

褒めてるサイトも。

だが大企業や役所でも、ネットユーザーの嗜好に合わせた秀逸な企画はいくつかある。モテモテになる(?)フレグランススプレーの「AXE」(ユニーリーバ)のサイト「THE AXE EFFECT」、「暴君ハバネロ」(東ハト)のサイト「暴君ハバネロ特区」、そして2009年度の佐賀県庁の人材採用ページはなかなかバカらしく、これはおとずれる価値がある。ネットのことをよく分かっている人が作っている。

結局は、リアルの世界で活躍している人が、多額の報酬を得たり、スポットライトを浴びているのである。



本の最後はかなり絶望的な締めで終わり。。そりゃあんまりだ!と正直思いましたw
読み物としてもかなり面白いですし、この業界に働く者としても参考になりましたよ。

追記 2009/05/12
著者の方のインタビューがありました。
『ウェブはバカと暇人のもの』著者に聞く 一般ビジネスパーソンのためのウェブリテラシー(前編):IT&ウェブ業界の転職をサポートする「CAREERzine」(キャリアジン)

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。

マウスで遊ぶ本格ゲーム

4Gamer.net — MMORPG「Puppeteers」の名称が「Tears Saga」に。最新プレイムービーが公開(Tears Saga)

ムービー観ても操作方法が分からなかったのですが、かなり多彩な動きをしていました。
これがマウス操作だけ(?)で遊べるってのは凄い。

5人だけで開発が進められているMMORPG

これも凄いですよね。超エース5人が集まったんでしょうか。
韓国のFlashも凄い印象があるんですけどね。最近チェックしなくなったのですが、どうなっているんでしょう。

前々から、マウスで遊ぶストリートファイター、【daletto】ストリートファイター オンラインも個人的に超注目なんですが、遊んでません。やってみようかな。

マウス操作だけってのがFlashに通じすぎるものがありますよね。

ペプシネックスのベアブリック・キャンペーン ワーナーブラザース編

またやってる! 今回はワーナー映画のキャラ。

_img_campaign_bearbrick.jpg

チャーリーとチョコレート工場だけ10個くらい欲しい。金ぴかだから。
ダークナイトも凄く面白かったのでジョーカーも欲しい。
明日からペプシを買いまくります!!

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

以前作ったのの続き。
前回のは位置が固定されていて動かせなかったけど、どうにかして動かせるようにしました。
まだ未完ですが、勢いで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;
		}
	}
}

emacsキーバインド & Happy Hacking Keyboard

以前からemacsキーバインドって慣れると便利なんだろなー、って思ってたので、
macだとKeyRemap4MacBook、WindowsだとXKeymacs使って、他のアプリケーションでemacsキーバインドを使ってます。
特にXKeymacsはかなり色々できて凄いです。はてなの伊藤直也さんも愛用しているようですね。
ですが肝心のFlashDevelopではいくつかの機能が動いてくれませんでした。残念。

やっぱり慣れると楽ちんです。矢印キーまで手を移動させなくて済むのは快適。
遠いエンターキーも押さなくて済んでます。

キーボードはMac純正 US配列のWirelessKeyboardを使っていたんだけど、日本語配列のHHKB Lite2 for Macにしました。
理由はスペースキーが小さめで、その周りのボタンが多い&押しやすい位置にあったから。
大きいスペースキーが苦手なのです。

追記
またUS配列に戻しました…。

さらに、KeyRemap4MacBookのソースは公開されているので、英数キーとかなキーの設定を他のキーとのコンビネーションで押したときはCmmandにする機能を弄り、Optionにする機能に変更。
Windowsの方も変換、無変換キーをAltに変更。こちらはコンビネーションの時だけってのは見つからなかった。

それとSandSは便利です。スペースキー単体で押すとそのままのスペース、他のキーとのコンビネーションでShiftになる機能。KeyRemap4MacBookとXKeymacsの両方で出来ます。

親指でいろんな修飾キーが操作できて快適。

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

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

別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

Return to page top