はじめまして。こんにちわ。
「ガールフレンド(仮)」でフロントエンドの開発をしている秋山と申します。

「ガールフレンド(仮)」とは、ユーザーが主人公となり、様々な女の子と出会っていく“声が出る♪”学園恋愛カードゲームです。各カードには、人気声優のボイスが付いていて、さまざまな演出に合わせてキャラクターの声を聞くことができます。

今回は、ガールフレンド(仮)に声が入るまでの苦労話とあわせてどう解決(実装)したかなどご紹介できたら思います。

スマホのブラウザ版カードゲーム × 声

ガールフレンド(仮)の開発に入る時点で、スマホブラウザゲームで音声を出しているサービスは他にもありましたが、カードに声が付いているものは少なかったかと思います。
また、スマートフォンのブラウザで音声を再生するには色々と制限や、OS・端末によって実装が異なるため気をつけなくてはいけない点も多く、ガールフレンド(仮)に声が入るまではなかなか苦労する部分も多かったです。

ガールフレンド(仮)は、アニメーション内で声を流すことが多いので、開発初期はAndroidの各OSと無数にある端末をカバーするためにFlashによる実装をしていました。
しかし!昨夏のFlashPlayer騒動後にはFlashからの脱却を迫られ、両OSともにJavaScriptでAudioオブジェクトを制御する実装に変更した経緯があります。

まずはそのAudioオブジェクトについて簡単に

audio要素は、HTML5から新たに追加された音声を再生するための要素です。audioは、HTML5の<audio>タグで直接記述したり、JavaScriptから生成・再生を制御することができます。ガールフレンド(仮)では、すべてJavaScriptで制御しています。

スマートフォンブラウザ × Audio

ガールフレンド(仮)は、スマートフォン専用のサービスなのですが、スマートフォン上でAudioを実装するには、OSはもとより端末ごとに挙動が違ったりします。
ここではその一部を紹介します。

対応OSと音声ファイルについて

ガールフレンド(仮)では、各OSに最適化するために下記のファイル形式を採用しています。
ただ、管理・運用コストを考えるとmp3に統一してしまったほうが良かったかなと感じています。

iPhone/iOS4以降/caf/モノラル ビットレート48kHz
Android/2.3以降/mp3/モノラル ビットレート48kHz

ちょっと話しは逸れますが、私自身開発時に知ったことですが、mp3を商用で利用する際にはライセンス料が発生しますのでご注意ください。

iPhone(iOS)は音声ファイルの先読みができない

通常、下記のように記述すれば音声ファイルの読み込みが期待できます。

// 初期設定
var voice = new Audio();
voice.src = 'sample.mp3';
voice.load();
しかし、iPhoneはタップなどのユーザーアクションをトリガーにしないと音声ファイルの読み込みができないので(ガラケーみたいですね)、下記のように音声ボタンを押したときにreadyStateプロパティから再生可能かを判定する必要があります。

// 初期設定
var voice = new Audio();
voice.src = 'sample.mp3';
voice.load(); // Androidは先読みできるので記述しておく

// 音声ボタンの処理
voiceBtn.addEventListener('touchstart', function(e) {
    // 再生可能状態か判定
    if(voice.readyState === 4) {
        // ボタンを再生中に切り替える
        gf.audio.showPlaying();
        // 再生
        voice.play();
    } else {
        // ボタンをローディングに切り替える
        gf.audio.showLoading();
        // 音声ファイルの読み込みを開始
        voice.load();
        // 再生可能になった時に処理
        voice.addEventListener("canplay", function(e) {
            voice.removeEventListener("canplay", arguments.callee);
            gf.audio.showPlaying();
            voice.play();
        });
    }}, false);
しばしば「ユーザーアクションによってしか再生できない」と書いている方がいますが、実際には読み込みさえ完了していれば、こちらの意図したタイミングで再生することができます。

currentTime設定に注意

同一の音声ファイルを繰り返し再生することも多いと思います。その際は、現在の再生位置を設定・取得するプロパティcurrentTimeを0にすることで、再生位置を音声ファイルの0秒に設定します。

しかし、ここでも注意!

下記のようにplay()直前で設定すると、iPhoneでは再生を開始してくれません。PCやAndroid端末では再生されます。

voice.currentTime = 0;
voice.play();

これを回避するためには、再生終了時のendedイベントが発生時にcurrentTimeを0にする必要があります。

// 再生終了の処理を設定
voice.addEventListener('ended', gf.audio.ended, false);

// 再生終了時の処理
gf.audio.ended = function() {
    // 再生位置を0秒に戻す
    currentTime = 0;
    // 音声ボタンを停止中に戻す
    gf.audio.showStopping();
};

また、Glaxy S3では、再生完了イベント(endedイベント)発生後にcurrentTimeの設定ができなくなり、複数回再生することができません。

うっかり再生は絶対NG

これが一番気を使った点です。スマートフォンにはマナーモードの設定がありますが、スマートフォンブラウザからは端末の音声ミュートボタンの状態を取得することができません。
つまり、ユーザーがマナーモードにしていると思っていても、問答無用に声が出てしまいます。
スマートフォンサービスの特性上、ユーザーは電車の中などの外出先で使用することも多いので、ユーザーが意図せず声が出てしまうのを防ぐ必要があります。

そこでガールフレンド(仮)では、下記の2点を実装しました。

ミュートボタンを設置

Cookieを使用したミュートボタンを各ページに設置し、ユーザーが任意で設定できるようにしています。
このボタンがオフになっている場合は、アラートを表示するようにしています。


アニメーションで再生前にアラートを表示

フルアニメーションでは、上にミュートボタンは設置できないので、声が出る場合はアニメーション開始前に下記のようなアラートを表示しています。
また、このアラートでタップさせることで、前述のiPhoneの読み込み問題も回避しています。


あれ?声でないよ?

うっかり再生対策もし、開発もだいぶ進んだ頃、開発陣以外の人にもテストプレイをしてもらった時に「あれ?声でなくない?」という言う人が現れました。

原因は、端末のメディア音量が0になっていたためでした。

スマートフォンには、ボタン操作音などのシステム音量と音楽を聴いてるときなどのメディア音量など、複数の音量調節があります。

・ブラウザでページを閲覧中はシステム音量
・ブラウザで音声再生中はメディア音量

つまり、いくら声が流れる前に音量を上げても、メディア音量が0になっていたら声が聞けないということです。
こちらの問題には、チュートリアルで声を聞ける部分の再生前に、「音声が聞こえない場合は、再生中に端末の音量UPボタンを押してください。」というアラートを表示するようにしました。

最後に

今回は実装における苦労話などを書かせてもらいましたが、本当に苦労したのは実際の“声”を集めるところだったと思います。
多くの方々にご協力いただいたおかげで、総勢60名以上の豪華声優の方々に参加いただくことができました。

キャラクター(カードイラスト)と声、それらを活かすゲーム性や演出などが上手く混じり合ったときにユーザーに喜んでもらえるサービスになるのかな、とあらためて実感しています。

最後まで読んでいただきありがとうございました!
今回の記事で少しでも興味持たれた方は、ぜひガールフレンド(仮)で遊んでみてください。