最短以外は不正解!8パズルでゲームをつくる(2)の続きです。今回はスコアランキングを実装します。
Contents
HTML部分
スコアランキングでは各ステージで1位になったプレイのデータと各ユーザーのもっとも優秀なプレイのデータを表示させます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>鳩でもわかる8パズル ランキング</title> <meta name = "viewport" content = "width=device-width, initial-scale = 1.0"> <link rel="stylesheet" href="./ranking.css"> </head> <body> <div id = "container"> <h1>鳩でもわかる8パズル ランキング</h1> <p><a href="./">ゲームのページへ</a></p> <h2>各ステージ優勝者</h2> <div id = "firsts"></div> <h2>ユーザーランキング</h2> <div id = "users"></div> </div> <script src = "./ranking.js"></script> </body> </html> |
ranking.css
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
body { color: white; background-color: black; } #container { width: 560px; margin-left: auto; margin-right: auto; margin-bottom: 80px; } h1 { font-size: 20px; color: magenta; } h2 { font-size: 18px; color: yellow; margin-top: 40px; } a { color: aqua; font-weight: bold; } a:hover { color: red; } table { border-collapse: collapse; } td { border: 1px #fff solid; padding: 10px 10px 10px 10px; text-align: center; } .players { width: 250px; } .times { width: 110px; } .dates { width: 250px; } |
PHPの処理
ステージクリア時にデータがpostされたときにPHP側でおこなわれる処理を示します。postされたらjson文字列としてファイルに保存してgetのときはファイルに保存されている文字列を返します。
ranking.php
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
<?php if($_SERVER["REQUEST_METHOD"] == "POST") saveData(); if($_SERVER["REQUEST_METHOD"] == "GET") echo getData(); function GetFileName(){ return "../puzzle8-highscore.json"; } function getData(){ $file_path = GetFileName(); if(file_exists($file_path)) return file_get_contents($file_path); //JSON形式 } function saveData(){ // POSTされたJSON文字列を取り出し $json = file_get_contents("php://input"); // JSON文字列をobjectに変換(第2引数をtrueにしないとハマるので注意) $contents = json_decode($json, true); // あるべきキーが存在しないときはなにもしない if(!isset($contents['name'])) return; if(!isset($contents['stage'])) return; if(!isset($contents['time'])) return; $name = $contents['name']; $stage = $contents['stage']; $time = $contents['time']; $name = htmlspecialchars($name, ENT_QUOTES); // プレイヤー名に'<'とか'>'が含まれていたらエスケープする $now = date('Y-m-d H:i:s'); if($name == '' || mb_strlen($name) > 32 || !is_numeric($stage) || !is_numeric($time)) return; $time = (int)$time; $stage = (int)$stage; $file_path = GetFileName(); if(file_exists($file_path)){ $ranking = json_decode(file_get_contents($file_path), true); //JSON形式を元に戻す } else { $ranking = array(); $ranking = [ 'users' => array(), // 各ユーザーの最優秀成績 'stages' => array(), // ステージごとの1位 ]; } $data = [ 'name' => $name, 'stage' => $stage, 'time' => $time, 'date' => $now, ]; // ユーザーランキング // 同じプレイヤーの成績が登録されている場合、新しいデータのほうが優秀であるなら更新する $done1 = false; foreach ($ranking['users'] as &$value) { if($value['name'] == $name){ if($value['stage'] < $stage){ $value['stage'] = $stage; $value['time'] = $time; $value['date'] = $now; } else if($value['stage'] == $stage && $value['time'] > $time){ $value['time'] = $time; $value['date'] = $now; } $done1 = true; break; } } unset($value); // 最後の要素への参照を解除 if(!$done1) $ranking['users'][] = $data; // 更新なき場合は新規登録 $ranking['users'] = sortUsers($ranking['users']); // ステージ → 時間 の優先順でソート // 各ステージの1位に関する情報 $done2 = false; foreach ($ranking['stages'] as &$value) { if($value['stage'] == $stage){ if($value['stage'] == $stage && $value['time'] > $time){ // 最短時間が更新されるなら更新 $value['name'] = $name; $value['time'] = $time; $value['date'] = $now; } $done2 = true; break; } } unset($value); // 最後の要素への参照を解除します if(!$done2) $ranking['stages'][] = $data; // 更新されないなら新規登録 $ranking['stages'] = sortStages($ranking['stages']); // ソート $users_max_count = 100; // 上位◯位までランキングデータとして残す $ranking['users'] = array_slice($ranking['users'], 0, $users_max_count); // これらのデータをjsonで保存する $json = json_encode($ranking, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT); file_put_contents($file_path, $json, LOCK_EX); } function sortUsers($array) { $stages_array = createArrayForSort('stage', $array); $times_array = createArrayForSort('time', $array); $name_array = createArrayForSort('name', $array); array_multisort($stages_array, SORT_DESC, $times_array, SORT_ASC, $name_array, SORT_ASC, $array); return $array; } function sortStages($array) { $stages_array = createArrayForSort('stage', $array); array_multisort($stages_array, SORT_ASC); return $array; } function createArrayForSort($key_name, $array) { foreach ($array as $key => $value) { $key_array[$key] = $value[$key_name]; } return $key_array; } |
JavaScriptの処理
ページが読み込まれたらサーバー側からスコアランキングに関する文字列を取得して、ここからランキング表示用のHTMLタグを生成してこれを表示します。
ranking.js
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
window.addEventListener('load', () => { fetch('./ranking.php', { method: 'GET', }).then(async(res) => { const obj = await res.json(); const aa = createTableHTML(obj); console.log(aa); document.getElementById('firsts').innerHTML = aa.html1; document.getElementById('users').innerHTML = aa.html2; }); }); function createTableHTML(obj){ const stages = obj.stages; const users = obj.users; let html1 = ''; html1 += '<table>'; html1 += `<tr><td>Stage</td><td class = "players">Player Name</td><td class = "times">Time</td><td class = "dates">Date</td></tr>`; for(let i=0; i<stages.length; i++){ let time = Number(stages[i].time); time = Math.round(time / 1000); const sec = time % 60; const min = Math.floor(time / 60); const text = `${min} 分 ${sec} 秒`; html1 += `<tr><td>${stages[i].stage}</td><td>${stages[i].name}</td><td>${text}</td><td>${stages[i].date}</td></tr>`; } html1 += '</table>'; let html2 = ''; html2 += '<table>'; html2 += `<tr><td></td><td class = "players">Player Name</td><td>Stage</td><td class = "times">Time</td><td class = "dates">Date</td></tr>`; for(let i=0; i<users.length; i++){ let time = Number(users[i].time); time = Math.round(time / 1000); const sec = time % 60; const min = Math.floor(time / 60); const text = `${min} 分 ${sec} 秒`; html2 += `<tr><td>${i + 1}</td><td>${users[i].name}</td><td>${users[i].stage}</td><td>${text}</td><td>${users[i].date}</td></tr>`; } html2 += '</table>'; return {html1, html2}; } |
