【20 | APIテスト】 << 【ホーム】 >> 【22 | フォーム作成】
↓↓クリックして頂けると励みになります。
データベースからスコアボードのデータをゲームで取得できるようにします。
frontend側で作業します。
「frontend/game.js」ファイルを以下のように編集します。
記述編集 【Desktop/RailsGame/frontend/game.js】
// initilize context kaboom({ scale: 3, width: 240, height: 160, background: [0, 0, 0], canvas: document.getElementById("screen"), }); const PLAYER_SPEED = 80; const OGRE_SPEED = 30; const WIZARD_SPEED = 20; const FIRE_SPEED = 100; const BASE_X = width() / 2; const BASE_Y = 50; const BASE_URL = "http://127.0.0.1:3000/api/v1"; loadSprite("floor", "/sprites/floor.png", { sliceX: 8 }); loadSprite("wall_left", "/sprites/wall_left.png"); loadSprite("wall_mid", "/sprites/wall_mid.png"); loadSprite("wall_right", "/sprites/wall_right.png"); loadSprite("wall_fountain", "/sprites/wall_fountain.png", { sliceX: 3, anims: { idle: { from: 0, to: 2, speed: 5, loop: true }, }, }); // プレイヤー loadSprite("knight", "/sprites/knight.png", { sliceX: 8, anims: { idle: { from: 0, to: 3, speed: 5, loop: true }, run: { from: 4, to: 7, speed: 10, loop: true }, }, }); // 敵 loadSprite("ogre", "/sprites/ogre.png", { sliceX: 8, anims: { run: { from: 0, to: 7, speed: 5, loop: true }, }, }); // 罠 loadSprite("spikes", "/sprites/spikes.png", { sliceX: 4, anims: { idle: { from: 0, to: 3, speed: 3, loop: true }, }, }); // 落とし穴 loadSprite("hole", "/sprites/hole.png", { sliceX: 2, anims: { open: { from: 0, to: 1, speed: 5, loop: false }, }, }); // 宝箱 loadSprite("chest", "/sprites/chest.png", { sliceX: 3, anims: { open: { from: 0, to: 2, speed: 20, loop: false }, close: { from: 2, to: 0, speed: 20, loop: false }, }, }); // 魔法使い loadSprite("wizard", "/sprites/wizard.png", { sliceX: 8, anims: { idle: { from: 0, to: 3, speed: 5, loop: true }, run: { from: 4, to: 7, speed: 10, loop: true }, }, }); scene("play", ({level}) => { // 10x10 半角スペース10 addLevel( [ " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", ], { width: 16, height: 16, " ": () => [sprite("floor", { frame: ~~rand(0, 8) })], } ) const mapConfig = { width: 16, height: 16, l: () => [sprite("wall_left"), area(), solid(), "wall"], r: () => [sprite("wall_right"), area(), solid(), "wall"], w: () => [sprite("wall_mid"), area(), solid(), "wall"], f: () => [ sprite("wall_fountain", { anim: "idle" }), area(), solid(), "wall", ], // 敵 "&": () => [ sprite("ogre", { anim: "run" }), scale(0.75), area(), solid(), origin("center"), { dir: choose([-1, 1]), timer: 0 }, "ogre", "danger", ], // 罠 "^": () => [ sprite("spikes", { anim: "idle" }), area(), "spikes", "danger" ], // 落とし穴 h: () => [ sprite("hole"), area(), { opened: false }, "hole" ], }; // マップリスト const matrix = [ [ "lwwwffwwwr", "l r", "l & r", "l ^ r", "l & r", "l^ r", "l & r", "l h ^ r", "l r", "lwwwwwwwwr", ], [ "lffffffffr", "l r", "l r", "l r", "l r", "l r", "l r", "l r", "l r", "lwwwwwwwwr", ], ]; // マップレベル map = addLevel(matrix[level], mapConfig); // ----- スコア表示 ----- add([pos(200, 20), sprite("chest", { frame: 2 }), origin("center")]); add([pos(182, 30), text("Treasure"),scale(0.1),]); const scoreLabel = add([ text("0"), pos(200, 45), { value: 0 }, scale(0.3), origin("center"), ]); add([text("Player"), pos(200, 62), origin("center"), scale(0.08)]); add([text("Brave"), pos(200, 70), origin("center"), scale(0.15)]); // ----- プレイヤー ----- const player = add([ pos(map.getPos(2, 2)), sprite("knight", { anim: "idle" }), solid(), // 他のオブジェクトが移動できないようにします。 //area(), // 形状からコライダーエリアを生成し、衝突検出を可能にします origin("center"), area({ width: 16, height: 16, offset: vec2(0, 8) }), ]); // 敵やトラップに触るとプレイヤーが死ぬ player.onCollide("danger", async (d) => { shake(10); burp(); addKaboom(player.pos); destroy(player); destroy(d); await wait(2); go("over", { score: scoreLabel.value }); }); onKeyDown("left", () => { player.flipX(true); player.move(-PLAYER_SPEED, 0); }); onKeyDown("right", () => { player.flipX(false); player.move(PLAYER_SPEED, 0); }); onKeyDown("up", () => { player.move(0, -PLAYER_SPEED); }); onKeyDown("down", () => { player.move(0, PLAYER_SPEED); }); onKeyPress(["left", "right", "up", "down"], () => { player.play("run"); }); // 止まっている時と動いている時のアニメーションを分ける onKeyRelease(["left", "right", "up", "down"], () => { if ( !isKeyDown("left") && !isKeyDown("right") && !isKeyDown("up") && !isKeyDown("down") ) { player.play("idle"); } }); // 穴の上でスペースを押すとハシゴが出てLevel2マップに移動する onKeyPress("space", () => { //穴の動作 every("hole", async (h) => { if (player.isTouching(h)) { if (!h.opened) { h.play("open"); h.opened = true; await wait(1); go("play", { level: 1 }); } } }) //宝箱の動作 every("chest", async (c) => { if (player.isTouching(c)) { if (!c.opened) { c.play("open"); c.opened = true; scoreLabel.value++; scoreLabel.text = scoreLabel.value; } } }); }) // ----- 敵 ----- // フレームごとに実行されるイベント (1 秒あたり 60 回) // 対象:敵タグを持つ全てのゲームオブジェクト onUpdate("ogre", (o) => { o.move(o.dir * OGRE_SPEED, 0); o.timer -= dt(); // カウントダウン if (o.timer <= 0) { o.dir = -o.dir; o.timer = rand(5); } }); // -------------- ここから最終マップ(MAP LEVEL 2) --------------- if (level == 0) return; // ----- 宝箱 ----- // 2秒ごとにランダムな位置に新しい宝箱を追加します。 // そして4秒以内に消えます。 loop(2, () => { const x = rand(1, 8); const y = rand(1, 8); add([ sprite("chest"), pos(map.getPos(x, y)), area(), solid(), { opened: false }, lifespan(4, { fade: 0.5 }), // 4 秒後に自動で破壊され、0.5 秒後にフェードアウトします。 "chest", ]); }); // ----- 魔法使い ----- const wizard = add([ sprite("wizard"), pos(map.getPos(8, 7)), origin("center"), area(), "danger", state("move", ["idle", "attack", "move"]), ]); // 「idle」状態に入るたびにコールバックを 1 回実行します wizard.onStateEnter("idle", async () => { wizard.play("idle"); // ここでは 0.5 秒間「idle」状態を維持し、その後「移動」状態に入ります。 await wait(0.5); wizard.enterState("attack"); }); // 「attack」状態に入るたびにコールバックを 1 回実行します wizard.onStateEnter("attack", async () => { if (player.exists()) { const dir = player.pos.sub(wizard.pos).unit(); // ファイヤー add([ pos(wizard.pos), move(dir, FIRE_SPEED), rect(2, 2), area(), origin("center"), color(RED), cleanup(), // ファイヤーが画面外に出た場合はクリーンアップ "fire", "danger", ]); } await wait(1); wizard.enterState("move"); }); // 「move」状態に入るたびにコールバックを 1 回実行します wizard.onStateEnter("move", async () => { wizard.play("run"); await wait(2); wizard.enterState("idle"); }); wizard.onStateUpdate("move", () => { if (!player.exists()) return; const dir = player.pos.sub(wizard.pos).unit(); wizard.flipX(dir.x < 0); wizard.move(dir.scale(WIZARD_SPEED)); }); wizard.enterState("move"); }); /** * -------------------- * シーン: ゲームオーバー * -------------------- */ scene("over", ({ score }) => { add([pos(120, 20), sprite("chest", { frame: 2 }), origin("center"),scale(1.3)]); add([pos(94, 30), text("Result"),scale(0.2),]); add([pos(50, 45), text("Number of treasures obtained"),scale(0.1),]); add([text(score, 26), origin("center"), pos(width() / 2, height() / 2)]); onMousePress(() => { // go("play", { level: 0 }); go("intro") }); }); /** * --------------- * SCENE - スコアボード * --------------- */ const fetchTop5 = async () => { const response = await fetch(BASE_URL + "/games"); const games = await response.json(); return games; }; scene("intro", async () => { // Step 1 -APIS からトップ5を取得する const games = await fetchTop5(); console.log(games); // Step 2 - スコアボードのレンダリング add([ pos(BASE_X, BASE_Y - 30), sprite("knight", { anim: "idle" }), origin("center"), ]); add([pos(99, 28), text("Ranking"),scale(0.14),]); games.forEach((game, index) => { add([ text(`${game.player.username.toUpperCase()}\u00A0${game.score}`, { size: 10, width: 180, // font: "sink", // 4 built-in fonts: "apl386", "apl386o", "sinko" }), pos(BASE_X, BASE_Y + 20 * index), origin("center"), ]); }); onMousePress(() => { go("play", { level: 0 }); }); }); // go("play", { level: 0 }); go("intro"); // debug.inspect = true;
backend側で作業します。
「Gemfile」37行目のコメントアウトを外します。
gem "rack-cors"
bundleします。
コマンド
bundle
「config/initializers/cors.rb」ファイルを編集します。
記述編集 【config/initializers/cors.rb】
# Be sure to restart your server when you modify this file. # Avoid CORS issues when API is called from the frontend app. # Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests. # Read more: https://github.com/cyu/rack-cors Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do # origins "example.com" origins '127.0.0.1:5500' resource "*", headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head] end end
ブラウザを確認します。
データベースに保存されたスコアが表示されるようになりました。
http://127.0.0.1:5500/
↓↓クリックして頂けると励みになります。
【20 | APIテスト】 << 【ホーム】 >> 【22 | フォーム作成】