HTML5のCanvasでつくるダイナミックな表現―CreateJSを使う

第36回 たくさんのオブジェクトを連結リストで扱う

この記事を読むのに必要な時間:およそ 5.5 分

前回の第35回たくさんのパーティクルに弾けるようなアニメーションをさせることで表現は仕上がった。今回は本連載の最終回として,たくさんのオブジェクトの扱いについてひとつ知識を深めたい。今回のお題にかぎらず,多くのオブジェクトは配列に入れるのがお約束だ。ただ,そうした処理を自らクラスでつくろうとするとき,「連結リスト」という仕組みがある。それを紹介しよう。

連結リストの使い道

「連結リスト」はオブジェクトを順番にまとめる仕組みだ。配列と同じように,オブジェクトをいくつもエレメントとして加えてから,それらすべてを取り出して扱える。配列と異なるのは,エレメントがインデックスをもたないことだ。そのため,連結リストではエレメントをいきなり指定することはできず,頭から順に取り出しながら目指すオブジェクトを探さなければならない。

では,連結リストはどのような場合に使えて,エレメントをどういうコードで扱うのか。第30回Box2Dでたくさんのボールを床に落とし続けるで示したシミュレーションが雰囲気を教えてくれる。第30回コード1ランダムな位置と大きさのボールをひたすらつくって床に自由落下させるは,3次元空間の物体(剛体)のオブジェクトを以下のようなコードで取り出して,シミュレーションした。

シミュレーションを進める関数(update())は,b2World.GetBodyList()メソッドで物理空間に加えられた初めの剛体(body)を取り出す。そのシミュレーションが済んだら,b2Body.GetNext()メソッドでつぎの剛体が得られる。whileループで,取り出す剛体がなくなるまで物理演算を繰り返した(Box2Dの処理については第29回「Box2Dで落としたボールを床に弾ませる」の「すべての剛体を物理シミュレーションする参照)。

function update(delta) {

  var body = world.GetBodyList();
  while (body) {

    body = body.GetNext();
  }
}

今回のお題にも,すべてのパーティクルをアニメーションさせる以下のような関数(updateAnimation())がある(第35回「たくさんのパーティクルに弾けるようなアニメーションをさせる」すべてのパーティクルを弾けるように動かす参照)。ここでは配列(particles)を使って,オブジェクトはforループで取り出した。もちろん,この処理にまったく問題はない。

function updateAnimation(eventObject) {
  var count = particles.length;

  for (var i = 0; i < count; i++) {
    var particle = particles[i];

  }

}

今回は練習として,この配列を連結リストのオブジェクトに置き換えてみる。連結リストは簡単なクラスで定める。すると,前掲のコードは,新たに連結リストのオブジェクト(particles)を使ってつぎのように書き替えることになる。連結リストから初めのオブジェクトを得たり,そのつぎのオブジェクトを取り出すには,Box2Dと違ってメソッドでなくプロパティ(firstとnext)を用いた。これは,ライブラリをつくつろうというのではなく,あくまで連結リストの学習なので,少し手間を省いたためだ。

function updateAnimation(eventObject) {

  var particle = particles.first;
  while (particle) {

    particle = particle.next;
  }

}

連結リストのクラスを実装する

連結リストのクラスは,どのような機能をもてばよいか。大きくふたつある。

第1に,連結リストは初めと終わりのエレメントを知らなければならない。すべてのエレメントを扱おうとするとき,その初めのオブジェクトから取り出す。また,連結リストに新たなオブジェクトを加えるとき,終わりのエレメントにつなげてゆくことになる。

第2に,連結リストに加えたエレメントには,それぞれの前と後のオブジェクトを教えてあげる。ムカデ競走の隊列と同じで,自分の前の人と後の人さえ覚えていれば,他にメンバーが加わろうが入れ替わろうが知らなくても順番は組める図1)。

図1 ムカデ競走の隊列は自分の前後さえ知っていれば組める

図1 ムカデ競走の隊列は自分の前後さえ知っていれば組める

ふたつの機能は,連結リストにエレメントを加えるメソッドで担うことにする。配列(Arrayクラス)に合わせて,メソッド名はpush()としよう。最初に加えたオブジェクトはfirst,最後のエレメントはlastというプロパティで参照をもつ。

連結リスト.push(オブジェクト)

そして,オブジェクトを連結リストに加えるといっても,順番はエレメント任せだ。前後のオブジェクトの参照を,それぞれprevとnextというプロパティで与えるに過ぎない。いってしまえば,連結リスト自身は初めと終わりを覚えているだけで,その間の順番はまったく知らない。

連結リストのクラスLinkedListとそのpush()メソッドは,以下のコード1のように実装した。コンストラクタの関数本体は空なので,初めプロパティは何ももたない。

push()メソッドの引数(element)に初めてオブジェクトを渡すと,lastプロパティがまだないので,if条件の評価がfalseとなって,else文によりその参照がfirstとlastプロパティに与えられる。その後加えるオブジェクトについてはif条件がtrueとされるので,それまで最後だったオブジェクト(_last)のnextプロパティに自らの参照,自分のprevプロパティには最後だったオブジェクトの参照を与える。そして,LinkedListオブジェクトのlastプロパティを,新たに加えたオブジェクトで差し替えた。

コード1 連結リストのクラスとエレメントを加えるメソッド

function LinkedList() {}
LinkedList.prototype.push = function (element) {
  var _last = this.last;
  if (_last) {
    _last.next = element;
    element.prev = _last;
    this.last = element;
  } else {
    this.first = this.last = element;
  }
};

著者プロフィール

野中文雄(のなかふみお)

ソフトウェアトレーナー,テクニカルライター,オーサリングエンジニア。上智大学法学部卒,慶応義塾大学大学院経営管理研究科修士課程修了(MBA)。独立系パソコン販売会社で,総務・人事,企画,外資系企業担当営業などに携わる。その後,マルチメディアコンテンツ制作会社に転職。ソフトウェアトレーニング,コンテンツ制作などの業務を担当する。2001年11月に独立。Web制作者に向けた情報発信プロジェクトF-siteにも参加する。株式会社ロクナナ取締役(非常勤)。

URLhttp://www.FumioNonaka.com/

著書

コメント

コメントの記入