見出し画像

耐久レースゲーム🏁 kintone

毎月楽しみにしているimoniCampで、LT大会があるとのこと。わーい!
テーマ:「自分的kintoneカスタマイズアップデート」
「自分的」といえばイロモノ路線ということで、前回に引き続きゲームつくりました。😆

耐久レースゲーム🏁

今回は、縦スクロールのアクションゲーム「耐久レースゲーム」です。ステキなチェッカーフラッグアイコンはDOTOWNさん。ありがとうございます。

限られた時間で、プレイヤー(車)を操作しフラッグを集めるというもの。
[←][→]左右矢印キーでコースを外れないよう操作し[↑][↓]上下矢印キーでスピードをコントロール。

え?なんか前回のハロウィンゲームの焼き直しでは?と思われた方

そのとーりです。😅

でも、でもですね。今回はアクションゲームに必要な2つの要素を実装しています。それは

キー入力処理
スクロール処理

の2つです。解説していきますね。

キー入力処理

アクションゲームを作るとき、マウスで操作する方法とキーボードで操作する方法が思いつきます。

今回は、前回のハロウィンゲームでは分からなかったキーボードで操作するやり方が分かったので実装しました。

いつもように、kintone JSカスタマイズ の検索でたどり着くさきはーー
@juri_donさん!
ありがとうございます。

//スペースキー押しても読み上げる
document.onkeydown = (e) => {
  if (e.code === "Space") {
    readEng(event.record.英文.value);
  }
};

これだーーー!!
糸口がみつかれば、あとはここから芋づる式に検索したらいいわけで

W3C (WORLD WIDE WEB CONSORTIUM)さん。
ありがとうございます。

ふむふむ。こんな感じ。

    /////////////////////////////////////
    //キーボード入力
    /////////////////////////////////////
    document.onkeydown = (e) => {
      //左[←]キークリック
      if (e.code === "ArrowLeft") { 
        xp = xp - 1;
        if (xp < 0) { //左からはみ出さない
          xp = 0;
        }
      }

やった!これでキーボードを使ったアクションゲームがつくれるぞ。

スクロール処理

見ためは前回のモンスター🎃👻🦇が降ってくるハロウィンゲームとよく似てますが、今回はちゃんと裏にマップをもってて、それをスクロールしています。

これができるとですね。縦スクロールのシューティングゲームや、広大なマップを持ったロールプレイングゲームなんかもつくれちゃうというわけです!技術的には!!

実装方法は以下

      /////////////////////////////////////
      //スクロール処理1
      /////////////////////////////////////
      for (let yy = 15; yy > -1; yy--) {  //y列
        for (let xx = 0; xx < 8; xx++) {  //x行
          scrData[yy][xx] = mapData[ym][xx]; //マップから画面データへ転送
        }
        ym = ym - 1;  //マップy座標減算(下から順番にコピーするため)
        if (ym < 0) { 
          ym = 31;    //マップy座標がマイナスになったらマップの一番下からコピー
        }
      }

図にするとこんな感じ

左のマップデータ用配列から、右の画面データ用配列にループを使って転送しています。今回は上から下へスクロールしたいので、

for (let yy = 15; yy > -1; yy--) { //y列

と、yy--とした方が分かりやすいと思いました。
で、次のスクロールを開始するy座標を更新してるのが以下です。

      /////////////////////////////////////
      //スクロール処理2
      /////////////////////////////////////
      wait = wait - 1;  //ウエイト減算
      if (wait < 0) {   //ウエイト終了
        wait = gear;    //次のウエイト設定(=ギア)
        ys = ys - 1;    //次のマップスタートy座標を減算
      }
      if (ys < 0) {     //スタートy座標最上段になったら
        ys = 31;        //スタートy座標最下段に設定
        mapData[0][xf] = '🟦'; //前回フラッグのクリア(マップx最上段)
        xf = Math.floor(Math.random() * 3 ) + 2;
        mapData[0][xf] = '🚩'; //今回フラッグ設定(マップx最上段)
      }
      ym = ys;  //マップスタートy座標設定

ysというのがマップの転送開始を管理している変数で、ここをマイナスしていくことで、マップから画面データの転送を下からいっこずつ上にずらしなら転送します。

で、マップの一番上にきたら、マップの一番下から同じように順番に転送することでマップデータをループさせて利用しています。

ちなみにスクロール処理を1と2に分けてる理由は、プレイヤー(車)🚘の書き換え予定座標に、フラッグ🚩や木🌳があるかどうかを判定するためです。

最後に

ほかには、上下の矢印キーで車のスピードを変化させたりするところ(任意の難易度へ変更できる機能)ですが、こちらも前回ハロウィンゲームではできてなかったところです。

スピードを落とすには、メインルーチンにウエイトを挟めばよいのですが、単純に挟むと各キャラクター(特にプレイヤー)の動きも遅くなってしまうので、スクロール処理だけ遅くするにはちょっと考えないといけません。正直めんどうな処理ですが対応しました。😅

以下にテンプレートとJSソースを貼っておきます。
ではでは楽しんでみてください。

(() => {
  'use strict';

  kintone.events.on(['app.record.create.show',
                     'app.record.edit.show'
                    ],(event) => {

    //マップ
    const mapData = [
      ['🟩','🌳','🟦','🟦','🟦','🟦','🌳','🟩'],
      ['🟩','🌳','🟦','🟦','🟦','🟦','🌳','🟩'],
      ['🌳','🟦','🟦','🟦','🟦','🌳','🟩','🟩'],
      ['🌳','🟦','🟦','🟦','🟦','🌳','🟩','🟩'],
      ['🌳','🟦','🟦','🟦','🌳','🟩','🟩','🟩'],
      ['🟩','🌳','🟦','🟦','🟦','🌳','🟩','🟩'],
      ['🟩','🟩','🌳','🟦','🟦','🟦','🌳','🟩'],
      ['🟩','🟩','🌳','🟦','🟦','🟦','🟦','🌳'],
      ['🟩','🟩','🟩','🌳','🟦','🟦','🟦','🌳'],
      ['🟩','🟩','🟩','🟩','🌳','🟦','🟦','🌳'],
      ['🟩','🟩','🟩','🌳','🟦','🟦','🟦','🌳'],
      ['🟩','🟩','🟩','🌳','🟦','🟦','🌳','🟩'],
      ['🟩','🟩','🌳','🟦','🟦','🟦','🌳','🟩'],
      ['🟩','🟩','🌳','🟦','🟦','🟦','🌳','🟩'],
      ['🟩','🌳','🟦','🟦','🟦','🌳','🟩','🟩'],
      ['🌳','🟦','🟦','🟦','🟦','🌳','🟩','🟩'],
      ['🌳','🟦','🟦','🟦','🌳','🟩','🟩','🟩'],
      ['🌳','🟦','🟦','🌳','🟩','🟩','🟩','🟩'],
      ['🌳','🟦','🟦','🟦','🌳','🟩','🟩','🟩'],
      ['🌳','🟦','🟦','🟦','🟦','🌳','🟩','🟩'],
      ['🌳','🟦','🟦','🟦','🟦','🌳','🟩','🟩'],
      ['🌳','🟦','🟦','🟦','🟦','🌳','🟩','🟩'],
      ['🟩','🌳','🟦','🟦','🟦','🟦','🌳','🟩'],
      ['🟩','🌳','🟦','🟦','🟦','🟦','🌳','🟩'],
      ['🟩','🟩','🌳','🟦','🟦','🟦','🌳','🟩'],
      ['🟩','🟩','🌳','🟦','🟦','🟦','🟦','🌳'],
      ['🟩','🟩','🟩','🌳','🟦','🟦','🟦','🌳'],
      ['🟩','🟩','🟩','🌳','🟦','🟦','🟦','🌳'],
      ['🟩','🟩','🌳','🟦','🟦','🟦','🟦','🌳'],
      ['🟩','🟩','🌳','🟦','🟦','🟦','🟦','🌳'],
      ['🟩','🌳','🟦','🟦','🟦','🟦','🌳','🟩'],
      ['🟩','🌳','🟦','🟦','🟦','🟦','🌳','🟩'],
    ];

    //画面データ 動的配列で初期化
    let scrData = [
      ['🟦','🟦','🟦','🟦','🟦','🟦','🟦','🟦'],
      ['🟦','🟦','🟦','🟦','🟦','🟦','🟦','🟦'],
      ['🟦','🟦','🟦','🟦','🟦','🟦','🟦','🟦'],
      ['🟦','🟦','🟦','🟦','🟦','🟦','🟦','🟦'],
      ['🟦','🟦','🟦','🟦','🟦','🟦','🟦','🟦'],
      ['🟦','🟦','🟦','🟦','🟦','🟦','🟦','🟦'],
      ['🟦','🟦','🟦','🟦','🟦','🟦','🟦','🟦'],
      ['🟦','🟦','🟦','🟦','🟦','🟦','🟦','🟦'],
      ['🟦','🟦','🟦','🟦','🟦','🟦','🟦','🟦'],
      ['🟦','🟦','🟦','🟦','🟦','🟦','🟦','🟦'],
      ['🟦','🟦','🟦','🟦','🟦','🟦','🟦','🟦'],
      ['🟦','🟦','🟦','🟦','🟦','🟦','🟦','🟦'],
      ['🟦','🟦','🟦','🟦','🟦','🟦','🟦','🟦'],
      ['🟦','🟦','🟦','🟦','🟦','🟦','🟦','🟦'],
      ['🟦','🟦','🟦','🟦','🟦','🟦','🟦','🟦'],
      ['🟦','🟦','🟦','🟦','🟦','🟦','🟦','🟦'],
    ];

    let screen = '';  //画面
    let scrX = '';    //x列画面設定用

    const spcG = kintone.app.record.getSpaceElement('spcG');  //ゲームスペース

    let initFlag = 1; //初期化フラグ

    let ym = 31;      //マップy座標
    let ys = ym;      //マップスタートy座標

    let xf = Math.floor(Math.random() * 3 ) + 2; //フラッグx座標           

    let wait = 0;     //ウエイト
    let gear = 10;    //ギア
    let count = 0;    //カウント
    let score = 0;    //スコア
    let hiscore = 0;  //ハイスコア

    let xp = 4;       //プレイヤーx座標
    let yp = 15;      //プレイヤーy座標

    const timer = window.setInterval(() => {

      const rec = kintone.app.record.get();
      count = parseInt(rec.record['カウント'].value,10);
      count = count + 1;
      rec.record['カウント'].value = count;
      kintone.app.record.set(rec);

      /////////////////////////////////////
      //ゲーム終了処理
      /////////////////////////////////////
      if (rec.record['カウント'].value > 300) {
        //スコア・ハイスコア取得
        const rec = kintone.app.record.get();
        score = parseInt(rec.record['スコア'].value, 10);
        hiscore = parseInt(rec.record['ハイスコア'].value, 10);
 
        //ゲーム終了メッセージ処理
        if (score > hiscore) {
          rec.record['ハイスコア'].value = rec.record['スコア'].value;
          window.alert('おめでとう!ハイスコア ' + rec.record['ハイスコア'].value);
        } else {
          window.alert('ゲームオーバー ' + rec.record['スコア'].value);
        }
        kintone.app.record.set(rec);

        initFlag = 1; //初期化フラグセット
      }

      /////////////////////////////////////
      //初期化
      /////////////////////////////////////
      if (initFlag !== 0) {
        initFlag = 0;  //初期化フラグリセット

        ym = 31;      //マップy座標
        ys = ym;      //マップスタートy座標

        wait = 0;     //ウエイト
        gear = 10;    //ギア
        count = 0;    //カウント
        score = 0;    //スコア

        const rec = kintone.app.record.get();
        rec.record['スコア'].value = score;
        rec.record['ギア'].value = gear;
        rec.record['カウント'].value = count;
        kintone.app.record.set(rec);

        mapData[0][xf] = '🟦'; //前回フラッグクリア
        xf = Math.floor(Math.random() * 3 ) + 2;
        mapData[0][xf] = '🚩'; //フラッグ

        xp = 4;       //プレイヤーx座標
        yp = 15;      //プレイヤーy座標
      }

      /////////////////////////////////////
      //スクロール処理1
      /////////////////////////////////////
      for (let yy = 15; yy > -1; yy--) {  //y列
        for (let xx = 0; xx < 8; xx++) {  //x行
          scrData[yy][xx] = mapData[ym][xx]; //マップから画面データへ転送
        }
        ym = ym - 1;  //マップy座標減算(下から順番にコピーするため)
        if (ym < 0) { 
          ym = 31;    //マップy座標がマイナスになったらマップの一番下からコピー
        }
      }  

      /////////////////////////////////////
      //当たり判定
      /////////////////////////////////////
      switch (scrData[yp][xp]) {  //絵文字で判定するためプレイヤー絵文字セット前に処理
        case '🌳':    //木にぶつかった
          scrData[yp][xp] = '💥';
          gear = 10;  //ギア最遅
          rec.record['ギア'].value = gear;
          kintone.app.record.set(rec);
          break;
        case '🚩':    //フラッグ取った
          scrData[yp][xp] = '🚖'; //フラッグを取得したら黄タクシーに変身^^;
          if (wait === gear) {    //ループで余分に加算しないように
            score = score + 1;
          }  
          rec.record['スコア'].value = score;
          kintone.app.record.set(rec);
          break;
        default:
          scrData[yp][xp] = '🚘'; //プレイヤーの車(赤)セット
      }  

      /////////////////////////////////////
      //スクロール処理2
      /////////////////////////////////////
      wait = wait - 1;  //ウエイト減算
      if (wait < 0) {   //ウエイト終了
        wait = gear;    //次のウエイト設定(=ギア)
        ys = ys - 1;    //次のマップスタートy座標を減算
      }
      if (ys < 0) {     //スタートy座標最上段になったら
        ys = 31;        //スタートy座標最下段に設定
        mapData[0][xf] = '🟦'; //前回フラッグのクリア(マップx最上段)
        xf = Math.floor(Math.random() * 3 ) + 2;
        mapData[0][xf] = '🚩'; //今回フラッグ設定(マップx最上段)
      }
      ym = ys;  //マップスタートy座標設定

      /////////////////////////////////////
      //画面書き換え
      /////////////////////////////////////
      screen = '';  //画面初期化
      for (let y = 0; y < 16; y++) {    //y列
        scrX = '';
        for (let x = 0; x < 8; x++) {   //x行
          scrX = scrX + scrData[y][x];
        }
        screen = screen + scrX + '\n';  //改行
      }
      spcG.innerText = screen;
    },100);

    /////////////////////////////////////
    //キーボード入力
    /////////////////////////////////////
    document.onkeydown = (e) => {
      //左[←]キークリック
      if (e.code === "ArrowLeft") { 
        xp = xp - 1;
        if (xp < 0) { //左からはみ出さない
          xp = 0;
        }
      }
      //右[→]キークリック
      if (e.code === "ArrowRight") { 
        xp = xp + 1;
        if (xp > 7) { //右からはみ出さない
          xp = 7;
        }
      }
      //上[↑]キークリック(スピードアップ)
      if (e.code === "ArrowUp") { 
        const rec = kintone.app.record.get();
        gear = parseInt(rec.record['ギア'].value,10);
        gear = gear - 1;
        if (gear < 0) { //最小0
            gear = 0;
        }
        rec.record['ギア'].value = gear;
        kintone.app.record.set(rec);
      }
      //下[↓]キークリック(スピードダウン)
      if (e.code === "ArrowDown") { 
        const rec = kintone.app.record.get();
        gear = parseInt(rec.record['ギア'].value,10);
        gear = gear + 1;
        if (gear > 10) { //最大10
          gear = 10;
        }
        rec.record['ギア'].value = gear;
        kintone.app.record.set(rec);
      }
    };

    return event;
  });

})();

この記事が気に入ったらサポートをしてみませんか?