耐久レースゲーム🏁 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座標がマイナスになったらマップの一番下からコピー
}
}
図にするとこんな感じ
左のマップデータ用配列から、右の画面データ用配列にループを使って転送しています。今回は上から下へスクロールしたいので、
と、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;
});
})();