HTML
CSS
JavaScript
jQuery
初心者
NIFTYDay 15

非エンジニアがフロントエンドの研修を受けてガチャを作れるようになるまで

NIFTY Advent Calendar 2017 15日目の記事です。
昨日は@harimmasさんの「Google Home で家電をコントロールしてみる(RM mini + BlackBeanControl + raspberry pi)」でした。
今日は、「非エンジニアがフロントエンドの研修を受けてガチャを作れるようになるまで」です。
研修の話とガチャの話半々くらいで書いています。よろしくお願いします。

フロントエンドの研修を受けた

前談

niftyアルバイトというサービスの企画担当をしています、@nokiyu_oOです。文系出身の非エンジニアです。
普段はサイト版のディレクションやグロースハック、アプリ版のプロモーションなどをお仕事としています。
サイト版のグロースをもっと積極的に、活発に行っていきたいと思っていたところ、
企画チームのリーダー(開発出身)が「スキルアップや運用のスピードアップのためにフロントエンドエンジニアリングの勉強会をやる」と言ってくださったので参加しました。

研修の内容

先述のリーダーに講師になっていただき、以下の内容を各回1〜2時間程度で履修しました。

  1. デバッグ方法レクチャー(Firebugの使い方・diffの取り方など)
  2. HTML/CSSの基礎知識(インライン要素とブロック要素の違いなど)
  3. JS基礎(アルゴリズム・変数・関数)
  4. JS/jQuery基礎(セレクタ)
  5. JS/jQuery基礎(イベント・DOM操作)
  6. JS/jQuery基礎(非同期処理・JSONパース)

上記の講義の後には、各回の履修内容に応じた宿題が出ました(例えば「HTML/CSSの基礎知識」の回では3カラムのWebページを作ってみるなど)
宿題をやっていく上でどうしても詰まる箇所が出てきたので、
補助として個人的にドットインストールを利用したり、『確かな力が身につくJavaScript「超」入門』を買って読んだりもしました。

研修の成果

今まで何が起きているのか検討もつかなかった、自社サイトのフロント部分の挙動が少し分かるように。
HTML/CSSの静的な部分は軽い手直し程度ならできるようになりました。
また、なんとなく読める・分かるだけでなく、最終的には「キーワードを入れて検索ボタンを押すとAmazonの商品検索結果が表示される」というような、Amazon APIやJSONパースを利用したページを作れるようにもなりました。

ガチャを作った

せっかくイベントの取り方やDOM操作を覚えたので、自分でも何か作ってみることにしました。
いつも「今日の夕飯を何にするか」で迷うため、ちょうど某ソシャゲにはまっていたこともあり、夕飯のメニューをガチャで提案してくれるWebページを作ることに決めました。

デモ

夕飯ピックアップガチャ
※スマホでの利用を想定して作ったので、PCからの閲覧はあまりおすすめしません…

ezgif.com-video-to-gif.gif

「10回召喚」ボタンをタップすると、夕飯候補となる料理50件の中から10件が出てきます。
10回召喚に必要な石は100個で手持ちの石は300個あるので、最初に出た10件の中に食べたいものがなければ3回まで引き直すことができます。ページをリロードすれば無限に引けます

料理の画像はぱくたそ写真AC、Google検索で「ライセンス:自由に使用、共有、または使用できる」となっている画像を、
某ソシャゲ風の加工にはServant Frame -英霊召喚カメラ-を使わせていただきました。

コード

こちらに置いてあります。

苦労したところ

ランダムな数を作る

研修では配列や乱数を扱う課題があまり出なかったので、全50枚のカードからランダムに10枚分のデータを持ってくる仕組みに苦労しました。エンジニアの先輩にアドバイスをいただきつつ書いたのが以下のコードです。

dinnergacha.js
//結果生成
function createResult(){
    // ランダムな数を作る関数を定義
    function randomChoice(array, num){
        const arr = array.concat();
        const choiced = []
        let l = array.length;
        let n = Math.min(num, array.length);
        while(n-- > 0){
        let i = Math.floor(Math.random() * l--);
        choiced.push(arr[i]);
        arr.splice(i, 1);
        }
        return choiced;
    }
    // 全データからランダムに9枚持ってくる
    result = randomChoice(data, 9);

    // 星5だけのデータからランダムに1枚持ってきて9枚に足す
    r5data = data.filter((datum) => {
        return datum.rarelity === 5
    })
    result.push(randomChoice(r5data, 1)[0]);
}

まずarray.concatでdata配列をコピーし、選ばれたカードの番号が入る空の配列choicedを作りました。
while文の中ではランダムな整数iを生成し、choicedにdata配列のi個目の要素を追加。
追加した後、その後の重複を避けるためspliceでarrのi個目の位置から要素を1つ削除しています。
これを9回繰り返します。

普通にやるなら上記の繰り返し回数を10回にすればよかったのですが、今回は★5のカードを1枚以上確定させたかったので、まず全50枚の中から選んだ9枚分のデータが入った配列resultを作成。
その後filterで★5だけの配列を作り、その中から選んだ1枚をresultの最後にpushしています。
当初filterを知らず、★5だけの配列を作って上記とほぼ同じ記述をもう一度繰り返すしかないかな?と思っていたため、filterはすごく便利だなと思いました。

アプリ的な見せ方と通信速度の両立

今回元ネタのアプリ的な見せ方をしたかったので、最初の画面で「10回召喚」ボタンをタップするとすぐに結果表示に切り替わるのではなく、
間にいわゆるガチャ演出っぽいアニメーションを挟みたいと考えていました。
「10回召喚」ボタンをタップすると、結果が出る前に「結果発表」と書かれた画像がフェードインし、そのあと結果表示に切り替わります。

dinnergacha.js
function showResult(){
    //召喚中画面再生
    $('<div class="image"><img src="result.jpg"></div>').appendTo('#loadarea').hide().fadeIn(2000);
    //召喚中画面が再生され終わったら消え、結果表示用の画面が出る
    setTimeout(function(){
        $('#loadarea').html("");
        $('<p>ガチャ結果</p>').appendTo('#result');
        //結果・もう一度引くボタンを表示する
        $.each(result, function(key, value){
        $('<div class="title">' + value.title + ' ★' + value.rarelity + '</div>').appendTo('#result');
        $('<div class="image"><img src="' + value.img + '"></div><br>').appendTo('#result');
        })
        $('<p>もっと引く?<p>').appendTo('#result');
        $('#try').show();
        },
        3000)
}

当初ここで読み込む画像はjpgではなくgifにして、setTimeoutでgifの再生が終わる秒数を指定しようと考えていました。
しかし動画から生成したgifだったためサイズが大きくなってしまい、既に読み込んだことのあるブラウザでは想定通りに動いても、初回アクセスの際にはgifの読み込みが終わらないうちに画面切り替えが起こってしまうといった問題が起きてしまいました。
屋外でスマホで利用することを想定していたため演出部分のgifを諦め、更に出てくるカードもjpgで画質を落として極力サイズを小さくしたので、最初よりは大分さくさく動くようになったと思っています。

作ってみた感想と学び

  • 環境構築が必要なくローカルでも動くものを実装するのはモチベーションが保ちやすかった
  • 正直画像編集とアニメーションだけでも「なんとなくアプリっぽい」感じは大分出る
  • 特にスマホでの利用を想定したページの場合、画像の重さには気をつけておくべき
  • 今回のfilterなど、便利なメソッドがたくさんあるので自力で冗長な書き方をする前に一度調べてみることが大切

これから改善したいところ

召喚の回数を選べるようにしたい

本家のソシャゲでは「1回召喚」「10回召喚」が選べるようになっており、「10回召喚で★4確定」といった仕様になっているので、
夕飯ガチャも「1回召喚」「10回召喚」どちらを選ぶかによって消費される石の数や出てくる料理のレアリティなどを変えられるようにしたいです。

クロスドメイン制約を突破したい

元々以下のようにカードのデータをJSONで別に持っておき、$.getJSONで呼び出して使用しようと考えていました。

data.json
data = [
    {"title":"チキンのトマト煮",
     "img":"cards/1.jpg",
     "rarelity":3},
    {"title":"肉じゃが",
     "img":"cards/2.jpg",
     "rarelity":3},}
//以下略
]
dinnergacha.js
$(function(){
    $.getJSON("data.json",function(data){
    //以下、ガチャに必要な処理
    }
})

が、Chromeで試してみたところ以下のエラーが出てしまい動かず。

XMLHttpRequest cannot load file:///Users/test/Documents/dinner/data.json: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https, chrome-extension-resource.

調べてみたらChromeではセキュリティ上の理由でローカルファイルへのAjaxリクエストが禁止されていました。
JSONだけ外部サーバーに上げておく、--allow-file-access-from-filesをつけてChromeを起動するなど解決方法は色々出てきましたが、
PHPやターミナルにまだ苦手意識があったため一番楽な方法を考えた結果、今回はjsのファイル内に記述してゴリ押す形となってしまいました。
結果としてモチベーションを失うことなく完成にこぎつけられたのですが、あまり賢い方法ではないよな…という気もするので、次はここを解決したいと思います。


明日は@megane42さんの「コードレビュー時のガイドライン」です!よろしくお願いします。