原点に戻る
Michael Wallent
Microsoft Corporation
June 5, 2000
日本語版最終更新日 2000 年 11 月 2 日
この記事は、もともと MSDN Online Voices のコラム "DHTML Dude" に掲載されたものです。
先月はコラムをお休みして申し訳ありませんでした。先月初め、予定より早く息子が生まれ、おわかりいただけると思いますが、予定がすべて狂ってしまいました。ともあれ、今月のコラムを始めましょう。
1996 年に、のちに「DHTML Dude」となるコラムを書き始めたとき、私は古典的なアーケード ゲーム Asteroids の DHTML バージョンを書きました。これは、Internet Explorer 4.0 が HTML 3.2 の Web との違いを示すクールな DHTML アプリケーションの 1 つでした。ある日、ハード ディスクをかき回しているときに、Asteroids の最初のバージョンがあったので、Internet Explorer 5.5 で動かしてみたところ、何と動いたのです! (バージョン間の互換性のおかげです。)
私はコードを見返して、Asteroids の機能とパフォーマンスの両方を改善する一連の変更を加えました。変更の一部は Internet Explorer 5 で追加された新機能に基づき、その他の変更は、システムの動作についての新しい知識による単なる変更でした。変更点を見てください。そして、岩石を破壊してみてください。
岩石の起源
Asteroids サンプルを表示する。
オリジナル バージョンで私が直面した難問の 1 つは、岩石の作成と破壊を管理することでした。Internet Explorer 4.0 では、ページ上に新しいコンテンツを作成するには、innerHTML または insertAdjacentHTML() を使用するしかありませんでした。これらのメソッドを試してみましたが、遅すぎました。代わりに、14 個の岩石の配列を作成することによって、1 つのメソッドを作成しました。これらの岩石のそれぞれには、サイズを示す特殊な ID が与えられます。岩石の表示と非表示を切り替えたい場合は、該当する岩石の visiblityを切り替えます。この方法には、一度に 15 個以上の岩石を画面に表示できないという大きな制約がありました。パフォーマンス上の問題もありました。ページを横切るように岩石を動かすためには、全てのチェックマークに対して配列全体をループする必要があったのです。
バージョン 5 では、もっと簡単に新しい要素を作成できる手段が導入されました。それは W3C DOM の cloneNode() メソッドです。このメソッドのおかげで、新しいコンテンツの作成がはるかに容易になり、15 個以上の岩石をページに表示できるようになったので、パフォーマンス問題も解決しました。結果として、ラウンド 1 で岩石の画面をクリアしてラウンド 2 に進むと、より多くの岩石がより速く動きます。配列を反復する必要はなく、すべての岩石を既知のコンテナ "RockContainer" に作成して、この要素の子集合を反復します。これは岩石の画像だけを含んでいることがわかっているからです。
岩石を作成するための新旧のコードを以下に示します。まず、元のコードは次のとおりです。
function splitRock(aRock) {
var newR1, newR2, f, f2;
f = aRock.factor;
f2 = f + 1;
if (f == 3) {
aRock.src = aRock.srcBase +"1.gif";
aRock.className = "rocke";
aRock.animcount = 1;
aRock.style.color = "#000001";
aRock.dx = 0;
aRock.dy = 0;
}
else {
newR1 = document.all(aRock.id + "a");
newR2 = document.all(aRock.id + "b");
aRock.src = aRock.srcBase +"1.gif";
aRock.className = "rocke";
aRock.animcount = 1;
aRock.style.color = "#000001";
aRock.dx = 0;
aRock.dy = 0;
newR1.style.posTop = aRock.style.posTop;
newR1.style.posLeft = aRock.style.posLeft;
newR2.style.posTop = aRock.style.posTop;
newR2.style.posLeft = aRock.style.posLeft;
initializeRock(newR1, f2);
initializeRock(newR2, f2);
}
soundExplode();
}
これは、岩石が爆発するときに呼び出されるメソッドです。この岩石が小さな岩石かどうかを確かめる必要があります。これが、最初の f=3 チェックです。f=3 の場合は、分裂させる代わりに、3 コマの爆発アニメーションを表示します。岩石を分裂させる必要がある場合は、ID によって正しい岩石を見つけて、初期化する必要があります。ご覧のように、元のコードは非常に不細工で、スケーラビリティもありません。次に、新しいコードを見てください。
function splitRock(aRock) {
var newR1, newR2, f, f2;
f = aRock.factor;
f2 = f+1;
if (aRock.nextRock == "NONE") {
aRock.src = aRock.srcBase + "1.gif";
aRock.className = "rocke";
aRock.animcount = 1;
aRock.dx = 0;
aRock.dy = 0;
}
else {
newR1 = createRock(theDoc.all(aRock.nextRock));
newR2 = createRock(theDoc.all(aRock.nextRock));
aRock.src = aRock.srcBase+"1.gif";
aRock.className = "rocke";
aRock.animcount = 1;
aRock.dx = 0;
aRock.dy = 0;
newR1.style.posTop = aRock.style.posTop;
newR1.style.posLeft = aRock.style.posLeft;
newR2.style.posTop = aRock.style.posTop;
newR2.style.posLeft = aRock.style.posLeft;
initializeRock(newR1, f2);
initializeRock(newR2, f2);
}
soundExplode();
}
function createRock(rockTemplate) {
var r;
r = rockTemplate.cloneNode(false);
r.id = "ActiveRock";
eRockContainer.insertBefore(r, null);
return r;
}
今度は、それぞれの岩石が新しいプロパティ "nextRock" を持っています。これは岩石テンプレートの ID であり、このテンプレートを複製することによって、岩石の子を作成します。始めに 14 個の画像をページに置く代わりに、3 個を置きます。これが、作成されるそれぞれの岩石の "テンプレート" です。私は常にこの方法を使用して、動的コンテンツを作成しています。説明的なコンテンツと手続き型コンテンツのちょうど中間に当たります。テンプレートの HTML を以下に示します。
<img class=rock id=TR1 nextRock=TR2 src="rock1.gif" orgSrc="rock1.gif" srcBase="rock1e" points=20
style="position: absolute; top: 0; left: 0; visibility: hidden; z-index: 10">
<img class=rock id=TR2 nextRock=TR3 src="rock2.gif" orgSrc="rock2.gif" srcBase="rock2e" points=50
style="position: absolute; top: 0; left: 0; visibility: hidden; z-index: 11">
<img class=rock id=TR3 nextRock=NONE src="rock3.gif" orgSrc="rock3.gif" srcBase="rock3e" points=100
style="position: absolute; top: 0; left: 0; visibility: hidden; z-index: 13">
岩石を作成するには、cloneNode() メソッドを呼び出した後、新しい岩石を RockContainer オブジェクトに挿入します。このメカニズムで注意しなければならないことが 1 つあります。それは、ID を毎回忘れずにリセットすることです。さもないと 2 つの要素が同じ ID を持つことになり、2 番目の複製呼び出しは失敗します。
岩石が最終的に削除されるときには、visibility を hidden に戻す代わりに、removeNode() メソッドを使用してツリーから削除します。
岩石を飛ばす
Internet Explorer のような開発プラットフォームを 3 年間も使用すると、コードをより高速にするコツがわかってきます。私は以前に比較的抽象的かつ簡単にパフォーマンスについて書いたことがありますが、このような大きなアプリケーションでは、文脈に即してパフォーマンスに関するテクニックを簡単に見ていくことができます。また、このアプリケーションはアニメーション中心なので、ループのパフォーマンスが重要です。1 つでも遅いものがあると、すべてが台無しになってしまいます。
私はパフォーマンスを高めるために 2 つのことをしました。すなわち、コードの手直しと変数のキャッシングです。コードの手直しについては、特別なことは何もしていません。コードを客観的に見直して、無駄を省こうとしただけです。DHTML ページを速くするのは、Win32 ページを速くする場合と同じです。つまり、コードを小さく、スマートにすればよいのです (当然のことです)。主な DHTML の改良は、ドキュメントならびにコレクションと要素の共通参照のすべてを、要素への直接アクセスからグローバル変数に変えたことです。このようにした方がよいのは、"document" を参照するたびに、スクリプト エンジン内のコードはその名前が何を意味するのかを調べるために、COM IDispatch を使用して MSHTML に呼び出しを行うからです。無数のプロセッサ サイクルの後、MSHTML はインスタンスを見つけて、それを返します。しかし、変数名を参照した場合は、完全にスクリプト エンジンのコンテキスト内にとどまり、はるかに少ない呼び出ししか行われません。
キャッシュしたのは、以下のものです。
// パフォーマンス アイテム
var theDoc = document;
var eLaser1 = Laser1;
var eLaser2 = Laser2;
var eLaser3 = Laser3;
var eRockContainer = RockContainer;
var eRockChildren = eRockContainer.children;
これらのキャッシュ アイテムは、moveRocks() などの主要なメソッドでアクセスしたアイテムです。キャッシングによってアニメーションがよりスムーズになりました。どのようにスムーズになったかを言葉で正確に表現するのは難しいですが、30 個以上の岩石のアニメーションをスムーズに機能させるのに十分でした。
その他のこと
Internet Explorer の新機能をフルに利用するために、ほかにもいろいろな変更を加えることができます。たとえば、expando プロパティを持つ画像を使用する代わりに、それらを DHTML ビヘイビアにすれば、さらにカプセル化を進めることができます。あるいは、まったく画像を使用しないという方法もあります。代わりに、VML PolyLines を使用して、リアルなベクター グラフィックの宇宙船と岩石を作成するのです。そうすれば、宇宙船を1 回転させるのに 16 枚の画像を使用する必要はなくなります。
バージョン 4.0 の時代にプラットフォームがどのようであったかを覚えているのはクールですが、Asteroids だけでなく、いろいろ便利なものを作成するための、さらにパワフルなプラットフォームへと Internet Explorer が成長したことを理解する方が、もっとクールです。
|