CLOSE OPEN

ENT TECH LAB.

TAGSTAGS

CountDownerAS3


Timerクラスはインターバルなイベント発行にはよいけどタイマーとしては使えないよねという方に。ActionScript3.0の(できるだけ)ずれないタイマーライブラリです。

Timer()

Timerクラスでタイマー(まぎらわしい)を作る…

久々のASネタです。
ActionScriptにはTimerクラスがあり、任意の一定時間毎にイベントを発行してくれます。

上記のようにすれば1秒間隔でfunc()関数が呼び出されます。func()内で初期値180 の変数を1ずつ値を減らせば3分タイマーが作れそうです。ところが…

Timerクラスは遅れる

リファレンスによると、

Timer オブジェクトを作成して、1 回実行または指定した間隔で繰り返して定時にコードを実行できます。SWF ファイルのフレームレートまたはランタイムの環境(使用可能なメモリやその他の要素) に応じて、ランタイムではややオフセットの間隔でイベントを送出できます。例えば、SWF ファイルを 10 fps(1 秒あたりのフレーム数)、つまり 100 ミリ秒間隔で再生するように設定し、80 ミリ秒でイベントが発生するようにタイマーを設定すると、100 ミリ秒に近い間隔でイベントが送出されます。また、メモリに負荷のかかるスクリプトでも、イベントの送出がずれる場合があります。

おかしな日本語でごまかされそうですが、要するに「処理によっては遅れます(オフセット)よ」と言っています。10ミリや20ミリ遅れたってたいしたことないわっていうFlashであればお話はここで終わります。しかし、何月の何日何時までのタイマーを作りたいって時は困ってしまう訳です。

なぜ遅れるか

fig01.png
上図はFlashの処理のタイミングを表しています。スクリプトの処理は表示を更新する前に行われます(Walker使用時除く)。もしFlashのフレームレートが10fpsに設定された場合、表示更新と表示更新の間、100ミリ秒が処理に与えられた時間です。もし、処理に時間がかかってしまって100ミリ秒を過ぎてしまったらどうなるのか?定刻を遅れたにも関わらず、Flashは知らない顔をして表示を更新していきます。これが塵積もって大きな遅れとなっていきます。

CountDownerAS3

遅れないタイマー

EnterFrameもTimerも遅れる中、処理に関わらず遅れないものがあります。それはコンピュータの時計です(たいして正確ではありませんが…)。
fig02.jpg
上図のようにEnterFrame毎に時計(Date().getTime())を見にいき、前回との差を足して(引いて)いけば遅れないタイマーになります。そこで作ってみたのがCountDownerAS3です。大層な名前がついていますが、仕組みはシンプルです。

ソース一式はGitHubにアップしてあります。

/src/にパスを通し、下記のように使用します。

作ったカウントダウン_countDownでCountDownerEvent.COUNT_DOWN_READYCountDownerEvent.COUNT_DOWN_COMPLETEイベントをListenします。
COUNT_DOWN_READYはカウントダウンの準備が出来た時、COUNT_DOWN_COMPLETEは指定した日時に達したとき発行されます。指定日時は、

_countDown.targetDate = new Date(2013, 0, 1).getTime();

上記のように、UNIX時間で指定します。例では2013年のお正月にセットしています。
あとは_countDown.initialize();で初期化・開始しています。

タイマーの初期化をサーバー時計に合わせる

ご存知の通り、Flashの時計Date()は自分のPCの時計に因ります。上記のサンプルではPCの時計を2013/1/1に合わせただけで終了してしまうでしょう。
これを回避する為に、CountDownerAS3にはサーバー時間を基準とするオプションを付けました。初期化するときに、

_countDown.initialize(CountDowner.NOW_GET_METHOD_SERVER, “UNIX時間を返すサーバーサイドスクリプト”);

このように引数を渡します。「UNIX時間を返すサーバーサイドスクリプト」の所にはサーバーに置いたスクリプトへのパスを指定してください。サーバーサイドのスクリプトは、PHP5であれば

これでいいでしょう。
NOW_GET_METHOD_SERVERで初期化することで、最初の時間設定をサーバー時間で行うことができます。
タイムゾーンは、FlashPlayerがローカルPCのタイムゾーンに合わせてくれます。
タイマーを更新するたびにサーバーに時間を問い合わせたらサーバーがもちませんので、更新はローカルPCの時計で処理します。
「それだと結局ローカルPCの時計によってはずれるのでは」って事になりますが、CountDownerAS3では1秒(デフォルト)以上の差を検知した時にタイマーの更新を見送る事にしています。この時Errorがthrowされるのでグローバルエラーをハンドリングして処理を切り分けるか、ソースから削除する等しちゃってください。1秒以上時間がかかる処理を実行したい時などはdiffThresholdプロパティで調整できます。
また、PCの時計はマザーボードに搭載される時計の精度に因るので、結局サーバー時間とずれてしまいます。これを回避するために、108000フレーム毎(60fpsで約30分)にサーバーの時計と同期するようになっています。
何らかの理由でタイマー更新が出来なくなった時、remainプロパティなど残り時間を表すプロパティがNaNになります。if (_countDown.remain != 0 && !_countDown.remain)などして切り分けましょう。

おまけ

おまけとして、BitmapやMovieClipで数値表示をするクラスを同梱しました。moduleパッケージに入っています。使い方は、ソースコード内にあるSample.asを参考にしてください。

最後に

ライセンスは修正BSDです。同梱してある数字のフォント(画像)は自作で、こちらも同じ修正BSDライセンスです。ご自由にお使いください。

フィードバック、不具合報告などいただけたら嬉しいです!

追記2013/2/6/ : 若干改良いたしました

2014年まで

関連記事
BACK TO TOP