アクセシビリティ
デベロッパーリソース

Flash記事

 

こくばん.in:リアルな書き味と消し味を実現するテクニック


宗原 吉則氏

宗原 吉則氏

void element

作成日:
2008年5月20日
ユーザレベル:
中級, 上級
製品:
Flash CS3

「こくばん.in」とは

こくばん.in」は、“黒板に落書き”という学生の頃に誰もが体験したことをWeb上で味わうことのできるお絵かきサービスです。絵に自信がない人でも気軽に落書きを楽しめる場所として、2008年2月末にサービスインしました。

使い方は、まさに黒板と同じです。画面下部に並ぶ6色のチョークのいずれかを選択し、黒板上をクリック&ドラッグするだけで自由に線を書くことができます。線を書く際に、上下カーソルキーあるいはマウスホイールを使えば、線の太さが変わります。また、画面右下には黒板消しがあり、クリック&ドラッグすることで書いた線を消すことができます。

こくばん.in

図1:チョークを勢いよく動かすと線がかすれた感じになったり、黒板消しでこすると線がぼやけた感じになったりと、実際のチョークの書き味と消し味を再現しています

落書きが完成したら、画面右下の投稿ボタン使って「みんなのらくがき」コーナーに投稿することができます。また、落書き中のマウスの動きも全て記録されており、黒板消しで消した軌跡も含め、落書きの過程をリプレイすることができます。

Flash CS3 Professional

こくばん.inが生まれたきっかけ

今年1月、ActionScriptのエキスパートであるコリン・ムック氏が「今から始める ActionScript 3.0 - WORLD WIDETOUR 」のために来日しました。その際、日本人のActionScriptテクニックを彼に見せつけるという名目でゲリライベントが開かれ、私はスピーカーとして参加しました。私のテーマは「BitmapDataで面白エフェクト」で、スクリーンに映し出される発表資料に対してその場で強調や注釈を書き込めるようにFlashで制作しました。テーマがテーマだけに、いっそのことその書き込み機能自体を面白エフェクト化しようと思い、チョークと黒板消しでやってみたところ、大きな反響が得られました。

通常のお絵かきサービスでは、「好きなように描け」と言わんばかりに、いきなり真っ白いキャンバスと自由に選べるパレットが与えられるだけです。それでは、絵に自信のない人はすぐにつまづいてしまうことが多いでしょう。ですが、黒板なら誰もが何かしら絵を描いたことがあると思いますし、チョークも限られた色数しかないので学校で描いた落書きそのままに描くことができるうえ、なにより適当でも許される空気感があります。これなら絵心のないライトユーザーにも受け入れられるのではないかと思い、「こくばん.in」を開発して公開することにしました。

開発時のポイント

こくばん.inの開発では、とにかく気持ちよさを追求しました。「チョークの書き味」「チョークを黒板に当てた時の音」「黒板消しの消し具合」、それら全てがユーザーに違和感なく伝わるようにしました。黒板らしさを前面に押し出すために、わざわざチョークの粉まで発生させています。

粉が降り積もる処理は、2年前に趣味で作ったFlashゲーム「桜吹雪」で開発した堆積処理を再利用しています。チョークのかすれ具合、黒板消しのぼかしはBitmapDataを駆使しています。とにかく何から何まで BitmapData 尽くしです。また、バックエンドにかかる負荷をできるだけ抑えるために、完成した作品の画像化は全てFlash側で行っています。サーバへ送信する際には、いったんBase64Encodeで文字列に変換しています。ActionScript 3.0になってバイナリデータを扱うことができるようになったため、このような手法がこれから常道となっていくのではないでしょうか。

それでは、「チョークのかすれ具合」と「黒板消しのぼかし具合」をどのように実現しているのか、その基本的な考えとスクリプトコードを解説しましょう。

チョークのかすれ具合

チョークのかすれ具合は、ベタ塗りの中にところどころ塗り漏らしを作ることで再現しています。塗り漏らしを作る手法は、ベタ塗りした矩形にわざわざひとつひとつ穴を開けていくと手間がかかるので、BitmapDataのpixelDissolveメソッドを使用することにしました。このメソッドを使うと、パラメータで指定した個数だけランダムに別の色で塗りつぶすことができます。しかし、そのままではくっきりと穴が開きすぎて、かすれ具合が不自然な感じになるので、さらに薄めのブラーフィルタをかけています。この処理過程をドラッグ中にマウスが移動するごとに繰り返すことで、マウスの軌跡に沿ったチョークのラインを引くことができます。

この処理を実装するには工夫が必要です。なぜなら、pixelDissolveやブラーフィルタは対象範囲を矩形で指定するため、斜めの線に対して直接かけることはできないからです。さらに、斜めの線をいきなり黒板上に書いてしまうと、エフェクトをかける際に余分な領域にまでかかってしまいます。そこで、いったん別のビットマップデータを用意し、そこにマウス操作に合わせた線を真横に書いてからエフェクトをかけた上で、それを適切な位置に配置するように座標変換をかけて転写するようにしました。この転写の課程を、以下に図解しておきます。なお、転写時に ColorTransformでアルファ調整することで、重ね塗り感を出しています。

マウスの動きについて始点Aと終点Bを定める

マウスの動きについて始点Aと終点Bを定める

ビットマップデータを最終的に転写させる位置

ビットマップデータを最終的に転写させる位置

チョークのビットマップデータをそのまま転写した場合の位置

チョークのビットマップデータをそのまま転写した場合の位置

図4

マウスの位置を中心にチョークを動かしたいので、チョークの太さ分だけ平行移動。そうしないと、マウスがチョークの左上角のところにくるので、マウスの動きと描画範囲に不自然さが生じてしまうのです。

線分ABの傾きと等しくなるようにビットマップデータ回転させる

線分ABの傾きと等しくなるようにビットマップデータ回転させる

始点Aの座標に向けて平行移動させる

始点Aの座標に向けて平行移動させる

以下は、この転写の課程を実現するためのサンプルコードです。

// x: 現在のマウスx座標
// y: 現在のマウスy座標
// px: 直前のマウスx座標
// py: 直前のマウスy座標
// h: チョークの太さ
public function draw( x:int, y:int ):void {
var dx:int = x - px;
var dy:int = y - py;
var w:Number = Math.sqrt( dx*dx + dy*dy );
var rad:Number = Math.atan2( dy, dx );
var rect:Rectangle = new Rectangle( 0, 0, w, h );
var num:int = w*h*0.7;

var draw_bf:BlurFilter = new BlurFilter( 2, 2, 1 );
var draw_ctf:ColorTransform = new ColorTransform( 1, 1, 1, 0.8 );
var draw_seed:int = int( 0xff * Math.random() );

// かすれ処理
var draw_bd:BitmapData = new BitmapData( w, h, true, 0xffffffff );
draw_bd.pixelDissolve( draw_bd, rect, rect.topLeft, draw_seed, num, 0x00ffffff );
draw_bd.applyFilter( draw_bd, rect, rect.topLeft, draw_bf );

// 転写用変換マトリックス
var draw_mtx:Matrix = new Matrix();
draw_mtx.translate( -h/2, -h/2 );
draw_mtx.rotate( rad );
draw_mtx.translate( px, py );

// 転写
canvas_bd.draw( draw_bd, draw_mtx, draw_ctf );

px = x;
py = y;
}

サンプルコードでは分かりやすくするために、ビットマップの回転具合および転写先の座標を指定するために使用する Matrixなど新規生成していますが、実際のこくばん.inでは、あらかじめ生成しておき、都度パラメータを初期化することで余計な負荷を抑えています。また、マウスの動かし方によって繋がりに不自然が生じないように幾つか工夫をしていますが、ここでは理解しやすくるために割愛しています。

このチョークの処理で変動する値はマウスの座標値です。お絵描きモードでは MouseEvent.MOUSE_MOVE が発生するたびにマウスの座標を渡し、リプレイモードではリプレイデータに記録されたマウスの座標を渡します。こうして描画処理を共通化させておけば、微調整も楽に行うことができます。

黒板消しのぼかし具合

黒板消しで消した時のぼやけた感じは、ブラーフィルタを使って実現しています。描画は全てビットマップデータ上で行っているため、特定の領域にブラーフィルタをかけるための処理が必要になります。ここでは黒板消しを向きに対して水平に動かした場合の移動範囲をぼかしているので、矩形を任意の角度に傾けた領域を対象とします。

ブラーフィルタは水平あるいは垂直方向にしか効果がないため、そのままでは任意の方向にかけることができません。そこでビットマップデータを用意して、移動&回転によって水平方向に転写した上で、ブラーをかけて元の位置に再度転写転写するようにしました。この転写の課程を、以下に図解しておきます。基本的な考えは、チョークのぼかし具合と同じです。

マウスの動きについて始点Aと終点Bを定める

マウスの動きについて始点Aと終点Bを定める

黒板消しのぼかし効果をかけたい範囲

黒板消しのぼかし効果をかけたい範囲。終点は次のフレームでは始点になるため、始点から終点直前までの描画を繰り返すことで一続きの線(消し跡)になります

そのままぼかし効果をかけると余分な範囲にも効果がかかってしまう

そのままぼかし効果をかけると余分な範囲にも効果がかかってしまう

始点Aの座標の負をとって、原点へ平行移動させる

始点Aの座標の負をとって、原点へ平行移動させる

線分ABの傾きの負をとって、水平方向になるように回転させる

線分ABの傾きの負をとって、水平方向になるように回転させる

黒板消しのサイズの半分だけ平行移動る

マウスの位置を中心に黒板消しを動かすために、黒板消しのサイズの半分だけ平行移動。そうしないと、マウスが黒板消しの左上角のところにくるので、マウスの動きと描画範囲に不自然さが生じてしまうのです。

生成したマトリックスで転写した「消したい範囲」

生成したマトリックスで転写した「消したい範囲」

ぼかし効果をつけたビットマップデータを元の位置に戻す

ぼかし効果をつけたビットマップデータを元の位置に戻す

以下は、この転写の課程を実現するためのサンプルコードです。チョークのかすれ具合のコードと同様に、サンプルコードでは分かりやすくするために Matrix など新規生成しています。

// x: 現在のマウスx座標
// y: 現在のマウスy座標
// px: 直前のマウスx座標
// py: 直前のマウスy座標
// h: 黒板消しの縦幅
public function erase( x:int, y:int ):void {
var dx:int = x - px;
var dy:int = y - py;
var w:Number = Math.sqrt( dx*dx + dy*dy );
var rad:Number = Math.atan2( dy, dx );
var rect:Rectangle = new Rectangle( 0, 0, w, h );

var erase_bf:BlurFilter = new BlurFilter( 2, 2, 1 );
erase_bf.blurX = Math.min( Math.max( w, 2 ), 32 );

var erase_ctf:ColorTransform = new ColorTransform( 1, 1, 1, 0.9 );

// 転写用変換マトリックス
var erase_mtx:Matrix = new Matrix();
erase_mtx.translate( -px, -py );
erase_mtx.rotate( -rad );
erase_mtx.translate( w/2, h/2 );

// 転写
var erase_bd:BitmapData = new BitmapData( w, h, true, 0xff000000 );
erase_bd.draw( canvas_bd, erase_mtx, erase_ctf );
erase_bd.applyFilter( erase_bd, rect, rect.topLeft, erase_bf );

// 戻す
erase_mtx.invert();
canvas_bd.draw( erase_bd, erase_mtx );

px = x;
py = y;
}

黒板消しでは、ぼかし処理を行うビットマップへの転写時にColorTransform でアルファ値を減らすことで薄くしています。この時にただ薄くしたものを転写するだけでは、戻した時にアルファブレンドされ、逆に濃くなってしまいます。これを防ぐため、表示用・ぼかし処理用ビットマップのベースを共に黒(#000000)にしておき、転写によってアルファブレンドが行われないようにしています。ただし、黒板のベースは黒ではなく深緑色(ここでは#003300)です。そのため一番下のレイヤーに深緑色で塗りつぶした矩形を敷いておき、その上に表示用ビットマップを BlendMode.ADDで重ねることで正しい色を出すことができます。これはチョークの色が黒板のベース色よりも明るいから使えるテクニックですが、ノートやホワイトボードなど白色ベースの場合は別のアプローチが必要になります。

以上です。「チョークのかすれ具合」と「黒板消しのぼかし具合」は、パラメータのさじ加減ひとつで書く時の気持ちよさが大きく変わるので、サンプルコードでためしながら自分なりのお絵かきツールを作ってみてはいかがでしょうか。たとえば色鉛筆やクレヨンの質感などは簡単に作ることができます。

著者について

宗原 吉則氏

フリーランスのFlashエンジニア。ゲーム制作をはじめ、見て楽しい触って楽しいコンテンツ作りに余念がない。株式会社アストロインパクト在籍時に、Flash Media Serverによるオンラインコンテンツを数多く手がけている。
void element