アクセシビリティ
デベロッパーリソース
西田 善彦氏

西田 善彦氏

storage.goodmix

本記事で紹介している「Flash Helper Scripts」は、Flash/ActionScript開発のためのオープンソースコミュニティ「Spark project」にて共有・提供されています。Spark projectでは、その他にもさまざまな用途のライブラリやソースコードが公開されています。ぜひ、チェックしてみてください。

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

「ユーザーアクションの記録・再生」機能を手軽に実装できるRecorder Lib

みんなが「ユーザーアクションの記録・再生」を手軽にできるように

これまで、ユーザーのマウスやキーボード操作を記録・再生するコンテンツはいくつもリリースされてきました。例えば、最近のサイトだと「MORISAWA FONTPARK2.0」や「UT LOOP」、数年前だと「flash effects」の作品集の中にもあります。

しかし、「ユーザーアクションの記録・再生」機能を持つコンテンツのニーズは高い反面、そういった機能やライブラリはあまり公開されていなかったため、各デベロッパーがコンテンツに合わせて独自の仕様を決め、実装する必要がありました。

今回、あるコンテンツの作成の際にこの記録・再生する機能が必要になり、実装することになりました。その時、こういう「ちょっとしたニーズを満たす機能をライブラリ化すれば、みんな便利かも」と思い、他の人でも簡単に機能を実装できるように汎用性をもたせたライブラリ「Recorder Lib」を作成しました。そして、Spark Projectの理念である「皆でソースコードやノウハウを共有して、幸せになろうよ!」という考えに共感し、Spark Projectにコミットさせていただきました。Recorder Libは、以下のリポジトリから入手できます。

MEMO:様々なライブラリが公開されていますが、こういう「単機能だけど、アイデア次第でいろいろ使える」系のものほど多くの方に利用されていると思われます(TeraClock、Threadなど)。

Recorder Libの機能

Recorder Libの機能は非常にシンプルで、以下の3つの作業を行うことができます。

【データを記録する】

記録するプロパティを指定すると、毎フレームごとに記録し続けます。また、記録する時間やフレーム数を指定することができます。 現バージョンで記録できるデータは、「オブジェクトのプロパティ」「イベント」「キーコード」「BitmapData」です。特にイベントデータは、dispatchEventのタイミングでEventクラスのインスタンスを保持するので、再生用のメソッドを用意しなくても、再生時にそのまま同じイベントを起こすことができます。

MEMO:BitmapDataの記録は生のデータを保持し続けるため、非常にメモリを消費します。

【データをバインドする】

記録したデータを、別のオブジェクトのプロパティに割り当てることができます。例えば、マウスの動作を記録して、オブジェクトのx・y座標に割り当てることで、マウスの軌跡を辿ることができます。また、x・y座標の割り当てを逆にしたり、rotationに割り当てるなど、アイデアに次第で様々な組み合わせが行えます。

【データを再生する】

フレームごとに、バインドしたプロパティにデータを送り続けます。また、時間やフレーム数を指定して再生したり、記録しながら一定時間後に再生するというタイムシフト再生も可能です。

このように「ユーザーアクションの記録・再生」機能を3段階に分けることで、記録するデータの種類を増やしたり、再生時に対応付けるオブジェクトを変えたりといった作業が行いやすくなっています。

図1
Recorder Libの機能は「記録」「バインド」「再生」の3段階に分かれています

Recorder Libの構成は、プログラミングの「MVC(モデル・ビュー・コントロール)」の考え方をもとにそれぞれを切り分けています。そのため、みなさんが自分の開発するコンテンツに合わせて独自に拡張しやすくなっています。

図2
Recorder Libの構成はMVCの考えのもと3つに切り分けています

Recorder Libを使えば、どのようなコンテンツを作成できるのか。それをお見せするために、マウス操作行動判定コンテンツ「マウスハンテイ」を作ってみました。これは、ユーザーのマウス操作(移動やクリック)を10秒間記録し、その挙動をもとにコメントを出すといコンテンツです。

マウスハンテイ

「マウスハンテイ」
「スタート」を押して、マウスを自由に10秒間操作してください。結果発表画面では、マウス操作の再現、移動距離と速さなどの分析結果、簡単なコメントが表示されます

このようなコンテンツも簡単に作成することができます。それでは、Recorder Libの実装方法を詳しく解説していきましょう。

Recorder Libの実装方法

記録の準備

記録の準備として、以下の手順を行います。

//インスタンスの作成 
  var recorder:Recorder = new Recorder(stage); 
  recorder.addEventListener(RecordingEvent.STOP_REC, recStopHandler)
//記録するプロパティの追加 
  recorder.addRecProperty("mouseX", this, "mouseX"); 
  recorder.properties["mouseX"].addEventListener(PropertyEvent.CHANGE, 
  changeHandler);
//バインドするプロパティの設定 
  recorder.bind("mouseX", mc, "x");
  1. Recorderクラス(もしくはそのサブクラス)のインスタンスを作成します。その際、引数に記録をモニターするオブジェクトを指定します(通常は、stageがいいでしょう)。また、記録を開始、停止するタイミングで何か動作を行う場合は、RecordingEventを追加します。
  2. 記録するプロパティを追加します。上記の「recorder.addRecProperty("mX", this, "mouseX");」を例に見ると、1番目の引数は記録・再生時にプロパティをバインドするためのidで、一意の文字列を指定します(プロパティ名と異なっても大丈夫です)。2番目、3番目の引数は、記録するオブジェクトとプロパティの文字列です。この例では、"mX"というラベルでthis.mouseXを記録します。
    また、再生時にデータが変更されるタイミングで何か動作を行う場合は、PropertyEventを追加します(マウスの座標を記録した場合、カーソルが動いている時のみブラーを掛けるなど)。
  3. バインドするプロパティを設定します。上記の「recorder.bind("mX", mc, "x");」を例に見ると、addRecPropertyと同じく、id/オブジェクト名/プロパティの順に引数を指定し、再生時に対応付けるプロパティを設定します。この際に「recorder.bind("mX", mc, "y");」というように異なるプロパティに割り当てることもできます(この場合、マウスで横に動かした動きが、再生時には縦に変換されます)。

値は生データで記録・再生されるので、閾値が異なるデータの場合は、一度別のプロパティに割り当て、変換を行う必要があります。(例:x座標をアルファに変える際など)

データの記録

記録はrec()メソッドを実行することで始まります。また、recInTime(ミリ秒)メソッドを使えば指定時間で、recInFrame(フレーム数)メソッドを使えば指定フレーム数で記録をコントロールできます。

//記録の開始 
  private function mouseDownHandler(e:MouseEvent) :void { 
//記録の開始 
  recorder.rec(); 
//時間(ミリ秒)を指定して記録の開始 
  //recorderTest.recInTime(2000); 
//フレーム数を指定して記録の開始 
  //recorderTest.recInFrame(120); 
  }

データの再生・停止

記録・再生の停止はstop()メソッド、再生はplay()メソッドで行うことができます。

//記録の開始 
  private function mouseUpHandler(e:MouseEvent):void { 
//記録の停止 
  recorder.stop(); 
//再生の開始 
  recorder.play(); 
  }

また、playTimeShift(秒)メソッドで遅延する時間を指定してタイムシフト再生を行うことができます。記録時ではミリ秒単位で指定しましたが、このメソッドでは秒単位で指定します。単位が異なるのは、たいていの場合、ミリ秒がフレーム数に追いつかないからです。

//記録の開始 
  private function mouseDownHandler(e:MouseEvent) :void { 
//記録の開始 
  recorder.rec(); 
//遅延する時間(秒)を指定してタイムシフト再生を開始 
  recorder.playTimeShift(1); 
  }

Recorderの基本動作サンプル。右側の赤い録画ボタンを押して、赤い矢印をドラッグしてください。右から2つのストップボタンを押して記録を停止し、再生ボタンを押して下さい

Recorder Lib:サブクラスの機能紹介

イベントの記録

rec()メソッド実行後、イベント発生時にrecEvent(イベント)メソッドを実行することで、タイミングとイベントが記録されます。再生時には、同じイベントインスタンスが実行されます。

//イベント記録の準備
  private function setRecorder() {
//レコーダーの準備
  recorder = new Recorder(stage);
//記録の開始
  recorder.rec();
//マウスイベントの設定
  stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
  }
//マウスを押した際の動作 
  private function mouseDownHandler(e:MouseEvent) :void { 
//イベントの記録 
  recorder.recEvent(e); 
//マウスダウン時の処理 
  e.target.x += 10; 
  }

キーコードの記録

キーコードはプロパティではなく関数にのみバインドします。そのため、再生時には「Aキーを押したタイミングでpressA()を実行する」といった対応付けを行うことができます。

//インスタンスの作成 
  var recorder:KeyRecorder = new KeyRecorder(stage); 
  recorder.addEventListener(RecordingEvent.STOP_REC, recStopHandler) 
//バインドするキーコードと、再生時に実行する関数を設定 
  recorder.bindKeyCode(65, keyTest); 
private function keyTest():void{ 
  trace("Press A") 
  }

キーコード記録のサンプル。右側の赤い録画ボタンを押して、矢印キーを使ってオブジェクトを移動させます。右から2つのストップボタンを押して記録を停止し、再生ボタンを押して下さい。

BitmapDataの記録

BitmapDataを記録するには、以下の準備が必要です。

//記録するDisplayObjectと、バインドするBitmapObject 
  var recBitmap:Sprite = new Sprite(); 
  var playBitmap:Bitmap = new Bitmap(); 
//インスタンスの作成 
  var recorder:BitmapRecorder = new BitmapRecorder(recTargetDisplayObject); 
  recorder.addEventListener(RecordingEvent.STOP_REC, recStopHandler) 
  playBitmap = new Bitmap(); 
//バインドするBitmapObjectを設定 
  recorder.bindBitmap("play1", playBitmap);

new BitmapRecorder(記録するDisplayObject)により、記録するBitmapDataをキャプチャするDisplayObjectを指定します。続いて、bindBitmap(id,再生するBitmapオブジェクト)メソッドにより、再生時に描画するDisplayObjectを指定します。後は、同じくrec()、stop()、play()を実行することで、画像を記録することができます。

BitmapData記録のサンプル。右側の赤い録画ボタンを押し、しばらくしてストップボタンを押して記録を停止します。再生ボタンを押すと、左側のオブジェクトの動きを再生します

また、Webカメラの映像を表示するvideoクラスのインスタンスをSpriteの子として登録し、Spriteを記録することで、カメラ映像の記録、再生ができます(ただし、重いです)。

private function setRecorder() {
  //"recContainer"という名前のDisplayObjectを記録
  recorder = new BitmapRecorder(recContainer);
//"playBitmap"という名前のDisplayObjectにバインド("video"は識別用の任意のID)
  recorder.bindBitmap("video", playBitmap);
  }
private function setBitmap():void {
  //Webカメラの準備
  camera = Camera.getCamera();
  if (camera != null) {
  video = new Video(320,240);
  video.attachCamera(camera);
  //記録するDisplayObjectの子に追加
  recContainer.addChild(video);
  }
//再生用BitmapDataの準備
  playBitmap = new Bitmap();
  //再生するDisplayObjectの子に追加
  playContainer.addChild(playBitmap);
  }

Webカメラ映像記録のサンプル。右側の赤い録画ボタンを押し、しばらくしてストップボタンを押して記録を停止します。再生ボタンを押すと、Webカメラの映像が再生します

今後やりたいこと

Recorder Libの今後の予定ですが、以下の機能を追加したいと思っています。

  • 記録できるデータ形式の追加(たとえば音声データなど)
  • 記録データの圧縮、エンコード

差し当たって、テキストデータの圧縮機能を追加すれば、サーバへの記録時の容量削減などが行えて、利用の幅が広がると思いますので、今後進めていきたいと思います。

著者について

西田 善彦氏

株式会社 アイ・エム・ジェイ WI第1事業本部 所属

1981年、大阪生まれ。2007年、和歌山大学大学院修了後、株式会社アイ・エム・ジェイに入社。大手ECサイトのインターフェース設計、開発進行管理、Flashコンテンツの開発等を担当。その後、Flashデベロッパー専任となり、大手企業のブランドサイト、プロモーションサイト、CGMコンテンツなどの開発に携わっている。