2012/04/21

iPhoneのsafariで効果音を鳴らす

差速度センサのビー玉が思いの他気持良く動くのでゲームっぽく手を入れている。

で、効果音が欲しいと思い調べたのだがこれが想像以上に厄介だった。

基本的には HTML5 の audio タグでやるわけだが iPhone には特殊な制限が有る。

  • ユーザのアクションから起動しないと音声データを読み込めない。
    (※参考1サイトではonloadで読込できるとしているがiOS5では出来なかった)
  • 同時に1つの音声データしか保持/再生できない。
  • 参考1:http://d.hatena.ne.jp/favril/20100806/1281093059
  • 参考2:http://www.riaxdnp.jp/?p=1912

結局、解決策としてはこうなった。

  • 複数の効果音を1つの音声データにまとめて一部を再生する。
  • 読み込みは起動時に何らかのユーザアクションをさせるしかない。
  • 参考3:http://www.riaxdnp.jp/?p=1953
効果音が重なると前のが途切れちゃうけど仕方が無い。 これが現状のiPhoneの限界。

整理してライブラリにまとめて見た。
ソース:

/** * iPhone用効果音ライブラリ。 * - iPhone の制限を回避して任意のタイミングで効果音を鳴らすライブラリ。 * - iPhone の safari には audio タグに以下の制限がある。 * - ユーザのアクションから起動しないと音声データを読み込めない。 * - 同時に1つの音声データしか保持できない。 * - 解決方 * - 複数の効果音を1つの音声データにまとめて一部を再生する。 * - 読み込みは起動時に何らかのユーザアクションをさせるしかない。 * 使用例: xxxx.addEventListener("click", function(){ // 何かのタップで iPhoneSound.load("all.mp3", { // 音の名前 開始秒 終了秒 bootup : {s:0.0, e:1.55, }, shotdown : {s:1.9, e:2.48, }, click : {s:3.293, e:3.8, }, }, function(){ onload(); }); ); function onload() { // ロード後は何時でも呼べる。 iPhoneSound.play("bootup"); } * * @author kotemaru@kotemru.org */ function iPhoneSound(){}; (function(Class){ /** * 音声データの読み込み。 * @param url 音声データのURL * @param parts 部分定義 {効果音名:{s:開始秒,e:終了秒},…} * @param callback 読込完了ハンドラ */ Class.load = function(url, parts, callback) { Class.audio = new Audio(url); Class.parts = parts; Class.audio.addEventListener("loadedmetadata", callback); Class.audio.load(); } /** * 効果音の再生。 * @param name 効果音の名前 */ Class.play = function(name){ var part = Class.parts[name]; playPart(part.s, part.e, 1.0); } function playPart(s,e,v){ Class.audio.pause(); Class.audio.currentTime = s; Class.audio.volume = v; Class.endTime = e; Class.audio.play(); setTimeout(autoStop, (e-s)*1000); } function autoStop(){ if (Class.audio.paused) return; if (Class.audio.currentTime >= Class.endTime) { Class.audio.pause(); } else { setTimeout(autoStop, 10); } } })(iPhoneSound);

ちなみに再生の開始位置は指定出来るが終了位置は指定出来ない。 終了位置に来たらJavaScriptから停止する仕掛けを入れている。

それと volume は指定しても効かなかったのであらかじめ音量のバランスを調整しておく必要がある。

iPhoneのブラウザでアクションゲームはなかなか厳しいのー。

P.S. 遅いサーバに音声ファイルが有ると変な場所を再生したりして動作が不安定になった。 ちょっと実用的では無いかも。


2012/04/18

iPhoneのsafariから差速度センサを使う

iOS4以降はsafariのJavaScriptから差速度センサの入力が取れるらしいので試してみた。
  • 参考1:http://d.hatena.ne.jp/nakamura001/20101128/1290942715
  • 参考2:http://blog.asial.co.jp/788

大雑把な使い方はこれだけ。

window.addEventListener("devicemotion", function(ev){ var g = ev.accelerationIncludingGravity; // == {x,y} 傾き var a = ev.acceleration; // == {x,y,z} 加速度 }, true);

第3パラメータの true はイベント優先度。 リアルタイム性が必要な時は指定した方が良い。

y軸は画面系の座標と±が逆になる。

値を数字で表示して見てもつまらないので画面上でビー玉を転がしてみた。

サンプル:ビー玉が転がるだけ

  • http://wsjs-gae.appspot.com/RollingMarble
    (iOS5のiPod4,iPhone4でしか確認してません)

    (ビー玉の画像は http://www.ritart.net/botan.html からお借りしました。)

結構リアルに転がってますw
反応も思いの他良くこれならちょっとしたゲームくらい作れそうです。

ソース:

<html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <meta content="minimum-scale=1.0, width=device-width, maximum-scale=1.0, name="viewport"> <meta name="format-detection" content="telephone=no"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <script> function onLoad() { RollingMarble.instance = new RollingMarble(); // 加速度センサ イベント登録 window.addEventListener("devicemotion", function(ev){ RollingMarble.instance.marble.accele(ev.accelerationIncludingGravity); }, true); // PCデバック用キー入力 イベント登録 document.onkeydown = function(ev){ var g = {x:0, y:0}; switch(ev.keyCode) { case 37: g={x:-0.5, y:0}; break; //← case 38: g={x:0, y:0.5}; break; //↑ case 39: g={x:0.5, y:0}; break; //→ case 40: g={x:0, y:-0.5}; break; //↓ default: } RollingMarble.instance.marble.accele(g); }; RollingMarble.instance.start(); } function RollingMarble() { this.instance = null; this.marble = new Marble("marble"); this.stage = new Stage("stage"); } (function(Class) { Class.prototype.start = function() { setTimeout(ticker, 25); } function ticker() { Class.instance.marble.action(); setTimeout(ticker, 25); } })(RollingMarble); function Marble(id) { this.img = document.getElementById(id); this.w = this.img.width; this.h = this.img.height; this.img.style.position = "absolute"; this.reset(); } (function(Class) { Class.prototype.reset = function() { this.isDropping = false; this.x = Stage.W/2; this.y = Stage.H/2; this.gx = 0; this.gy = 0; this.img.width = this.w; this.img.style.left = Math.floor(this.x-this.w/2)+"px"; this.img.style.top = Math.floor(this.y-this.h/2)+"px"; } Class.prototype.accele = function(grav) { this.gx += grav.x/2; this.gy -= grav.y/2; // 上下は逆 } Class.prototype.action = function() { if (this.isDropping) return this.dropping(); with (this) { x += gx; y += gy; if (x < 0) drop(-w/2, y); else if (x > Stage.W) drop(Stage.W+w/2, y); if (y < 0) drop(x, -h/2); else if (y > Stage.H) drop(x, Stage.H+h/2); img.style.left = Math.floor(x-w/2)+"px"; img.style.top = Math.floor(y-h/2)+"px"; } } Class.prototype.drop = function(x, y) { this.x = x; this.y = y; this.isDropping = true; } Class.prototype.dropping = function() { with (this) { img.width *= 0.9; img.style.left = Math.floor(x-(img.width/2))+"px"; img.style.top = Math.floor(y-(img.width/2))+"px"; if (img.width < 5) reset(); } } })(Marble); function Stage(id) { this.div = document.getElementById(id); this.div.style.width = Stage.W+"px"; this.div.style.height = Stage.H+"px"; this.div.style.left = "60px"; this.div.style.top = "60px"; this.div.style.position = "absolute"; this.div.style.backgroundColor = "white"; }; (function(Class) { Class.W = 600; Class.H = 600; })(Stage); </script> <style> body{ background: black; } </style> </head> <body onload="onLoad()"> <div id="stage"> <img id="marble" src="blue-ball.png" /> </div> </body> </html>


プロフィール
20年勤めた会社がリーマンショックで消滅、紆余曲折を経て現在はフリーランスのSE。 失業をきっかけにこのブログを始める。

サイト内検索

登録
RSS/2.0

カテゴリ

最近の投稿【iPhone】

リンク

アーカイブ