このページの本文へ

CSS3 AnimationsとJSの連携でキャラクターを制御 (3/3)

2011年08月18日 13時00分更新

文●ハン☆スタ制作委員会/マインドフリー株式会社

  • この記事をはてなブックマークに追加
本文印刷

キューの利用

 今回はキューで走塁状況を管理しているので、キャラクターの走塁はCSSで書いたkeyframesをclassで切り替えて制御しています。classを切り替えるタイミングをanimation-durationの値分キューに入れて待ち、切り替える方法です。

 キューの処理は以下のようになっています。


// キューコンストラクタ
function Queue() {
  // キューの管理
  this.queue = [];
  // 実行中監視フラグ
  this.running = false;
}
Queue.prototype = {
  // キューに登録
  // funcは省略可
  add: function() { // (func, wait) or (wait)
    var _a = arguments;
    // waitの指定がない場合は即時(10ms)実行
    if ( typeof _a[0] !== "function" ) {
      this.queue.push(_a[0] || 10);
    } else {
      this.queue.push([_a[0], _a[1] || 10]);
    }
    return this;
  },
  // 登録されたキューを全て削除
  clear: function() {
    this.running = false;
    this.queue = [];
    return this;
  },
  // 登録されたキューを先頭から実行する
  flush: function() {
    var _this = this,
      _thisQueue;
    // flushの実行は一度だけ
    if ( this.running ) return false;
    this.running = true;
    // キューの先頭にある関数を実行し(ない場合は何もしない)
    // waitミリセカンド後に次のキューへ移る
    // 実行したら次のキューの処理へ
    (function() {
      var _thisWait;
      // キューが残っていなければ処理を中断する
      if ( _this.queue.length === 0 ) {
        // there is no queue
        _this.running = false;
        return false;
      }
      _thisQueue = _this.queue[0];
      // 先頭のキューを削除して詰める
      _this.queue.splice(0, 1);
      // 現在のキューに登録されている関数を実行
      if ( typeof _thisQueue === "object" ) {
        _thisQueue[0]();
        _thisWait = _thisQueue[1];
      } else {
        _thisWait = _thisQueue;
      }
      // 関数とペアで登録されたwaitタイム後、次のキューへ
      setTimeout(arguments.callee, _thisWait);
    }());
    return this;
  }
}

 キューは、コンストラクタ関数でキューオブジェクトを作成し、実行したい処理と待ちたい時間をadd()メソッドで追加して使います。関数であれば次に実行したい処理、数値であれば数値分待機した後に、次にaddした関数を実行します。


var queue = new Queue();
queue
  .add(function() { /* something to do */ })
  .add(500) // 500ms後に次の処理が実行される
  .add(function() { /* something to do */ })

ランナーの制御

 ランナーの追加と走塁は前のキューを利用したプレーヤーマネージャオブジェクトで制御しています。ランナーはそれぞれアニメーションを管理するためのキューを持ち、走塁はキューを利用しています。


// 攻撃側のプレーヤーマネージャ
function Player(id) {
  this.player = mQ(d.createElement("div"));
  this.queue = new MBBM.Queue(); // キューの初期化
  field.append(
      this.player
          .attr("id", id)
          .addKlass("chara runner")
      );
  // waitTimeはCSSで指定されたものを使う
  this.waitTime = this.player.css()["-webkit-animation-duration"];
  this.waitTime = parseFloat(this.waitTime) * 1000;
  return this;
}
Player.prototype = {
  // 省略
  wait: function(time) { // animation-duration分waitするメソッド
    this.queue.add(time || this.waitTime);
    return this;
  },
  // 省略
  toPoint: function(from, to, callback) { // 指定の塁まで走塁するメソッド
    var _this = this,
      _arr = ["home", "first", "second", "third", "home"];
    if ( from === to - 1 ) { // 次の塁へ進むだけなら・・・
      this.queue
          .add(function() {
            _this.player
                .removeKlass("batter wait")
                .removeKlass(_arr[from])
                .addKlass(_arr[to]);
          });
    } else
    if ( from === to - 2 ) { // 2塁先へ進む場合は・・・
      this.queue
          .add(function() {
            _this.player
                .removeKlass("batter wait")
                .removeKlass(_arr[from])
                .addKlass(_arr[to-1])
          })
          .add(_this.waitTime)
          .add(function() {
            _this.player
                .removeKlass(_arr[to-1])
                .addKlass(_arr[to])
            });
    } else
    // 中略
    if ( _arr[to] !== "home" ) {
      this.queue
          .add(_this.waitTime)
          .add(function() {
            _this.player.addKlass("wait");
          });
    } else {
      this.queue
          .add(_this.waitTime)
          .add(function() {
            _this.player.addKlass("batter");
          });
    }
  }
}

 走塁はPlayer.toPoint()メソッドで実行します。走塁の制御にはWebSocketからプッシュされるmessageイベントから取得したデータを利用します。


// WebSocketにイベントbind
qws.bind({ // qwsはWebSocket
  // 省略
  "message": function(e) {
    var data = JSON.parse(e.data),
      type = data.type;
    switch ( type ) {
    // 中略
    // ランナー
    case "game.moveRunner":
      // 対象のランナーを送られてきた塁までアニメーションで移動する
      MBBM.go(parseInt(data.uniformNo)-1, data.from, data.to);
      break;
    }
  }
});

今後の展望

 今回は、iPhone専用というかなり限定した環境向けのWebアプリを、HTML5/CSS3と関連する技術で作ってみましたが、実際にやってみてとても作りやすいと感じました。

 もちろん、iPhoneのスペック、処理能力ではPCにかなわない点が多く、パフォーマンス最適化の苦労もあります。しかし、モバイル端末にはPCにはない魅力や長所もありますから、モバイルのよさを活かした使い方をすればよいだけです。Handy StadiumではiPhoneの加速度センサーを利用しましたが、ほかにもGeolocation APIによる位置情報を組み合わせたりしてもおもしろいでしょう。

 また、マルチプラットフォーム対応への試みとして、Handy StadiumをChrome ウェブストアのアプリとしても公開しました。マルチプラットフォーム対応はWebアプリのメリットとしてよく挙げられますが、実際には単純に動くようにするだけでなく、PCではPCの特性を考慮してチューニングする必要があると分かりました。

 現状ではWebSocketをサポートしているブラウザーがまだまだ少なく、AndroidブラウザーやInternet Explorer、Firefox、Operaなどでは残念ながらHandy Stadiumをプレイできません。WebSocketをサポートされていないブラウザーでも別の技術を使用して通信をしてくれるnode.jsのモジュールのSocket.IOを使用することによって、それらのブラウザーでも遊べるようにできそうです。

 HandyStadiumは現在ベータ版の冠が付いていますが、今後も継続して新しい機能を実装していき、モバイルWebアプリの可能性を探っていきたいと考えています。

 連載は今回で終わりですが、Webアプリの可能性を感じてもらえましたか? これまでお読みいただき、ありがとうざいいました!

 マインドフリーのシステムチームでは、ブログでも情報を発信しています。フロントエンドの技術ばかりではありませんが、ぜひ見に来てください。

テクヤン - マインドフリー .NET カフェ
http://d.hatena.ne.jp/mftech/

著者:ハン☆スタ制作委員会/マインドフリー株式会社

著者写真

日々切磋琢磨する大阪のイノベーター集団マインドフリー。先端Techとフリーなマインドを武器に「Fun!」なものづくりを目指してます。今回も新しい技術や開発言語が大好き!なSuperフレッシュエンジニア“やんぎー”(Facebookアプリ「Profile Photo Mosaic」を開発)が「世界を驚かせる!?計画」のひとつとして、Webアプリ開発の極みに迫るため「ハン☆スタ」をつくってみました。Let's play baseball♪
フロントエンドエンジニア 5509のnori も技術協力しています。

前へ 1 2 3 次へ

この連載の記事

一覧へ

この記事の編集者は以下の記事をオススメしています