M5Stackでスクワットをして、その動きに連動してゲームができるようなプロダクトを作りたい。
以前の記事に引き続き、M5stackでスクワット運動でゲームするようなスクリプトを作成しています。
↓以前の記事(M5Stack側のスクリプト)
作成する
PC上でゲームを作成するやり方はいくつか存在するが、 p5.jsを利用してゲーム作成を目指すことにした。
こちらの記事を参照して、M5stackのセンサー情報を元に図形を動かす方法を把握した。
https://msr-r.net/p5js-m5stickc-plus/
今回はゲームを作成するというところから、
ベースとしては同じで図形ってところからFlappyBirdsができるようにすることを目指すことにした。
ゲームの内容
・M5stackのスクワットをするごとにプレイヤーのBirdが動く
・FlappyBirdsのスクリプトは参照先の元を利用した
フォルダー構成
同一のファルダー内に下記の3つのファイルを配置する。
・ble.js
・p5js.html
→2つに関しては下記のサイトを参照して、そのまま利用している。
・sketch.js
→次に記載します。ファイル名はそのままにする。
M5stackの動きをゲームに反映させる仕組み
①M5Stackから現在のスクワットの回数をBLE通信で送信する
②ChromでBLE接続を行い、スクワット回数を受け取る
③受け取ったスクワット回数が以前に取得した回数よりも大きい場合、ゲーム内のプレイヤーが動く
(つまり、回数が増えるとクリックと同じ関数が呼び出される)
sketch.js(ゲームのメインスクリプト)
// ---- エンティティ関連の関数 ---------------------------------------------
// 全エンティティ共通
function updatePosition(entity) {
entity.x += entity.vx;
entity.y += entity.vy;
}
// プレイヤーエンティティ用
function createPlayer() {
return {
x: 200,
y: 300,
vx: 0,
vy: 0
};
}
function applyGravity(entity) {
entity.vy += 0.15;
}
function applyJump(entity) {
entity.vy = -5;
}
function drawPlayer(entity) {
square(entity.x, entity.y, 40);
}
function playerIsAlive(entity) {
// プレイヤーの位置が生存圏内なら true を返す。
// 600 は画面の下端
return entity.y < 600;
}
// ブロックエンティティ用
function createBlock(y) {
return {
x: 900,
y,
vx: -2,
vy: 0
};
}
function drawBlock(entity) {
rect(entity.x, entity.y, 80, 400);
}
function blockIsAlive(entity) {
// ブロックの位置が生存圏内なら true を返す。
// -100 は適当な値(ブロックが見えなくなる位置であればよい)
return -100 < entity.x;
}
// 複数のエンティティを処理する関数
/**
* 2つのエンティティが衝突しているかどうかをチェックする
*
* @param entityA 衝突しているかどうかを確認したいエンティティ
* @param entityB 同上
* @param collisionXDistance 衝突しないギリギリのx距離
* @param collisionYDistance 衝突しないギリギリのy距離
* @returns 衝突していたら `true` そうでなければ `false` を返す
*/
function entitiesAreColliding(
entityA,
entityB,
collisionXDistance,
collisionYDistance
) {
// xとy、いずれかの距離が十分開いていたら、衝突していないので false を返す
let currentXDistance = abs(entityA.x - entityB.x); // 現在のx距離
if (collisionXDistance <= currentXDistance) return false;
let currentYDistance = abs(entityA.y - entityB.y); // 現在のy距離
if (collisionYDistance <= currentYDistance) return false;
return true; // ここまで来たら、x方向でもy方向でも重なっているので true
}
// ---- ゲーム全体に関わる部分 --------------------------------------------
/** M5Stackの情報 */
let squat_count = 0;
let pre_squat_count =0;
/** プレイヤーエンティティ */
let player;
/** ブロックエンティティの配列 */
let blocks;
/** ゲームの状態。"play" か "gameover" を入れるものとする */
let gameState;
/** ブロックを上下ペアで作成し、`blocks` に追加する */
function addBlockPair() {
let y = random(-100, 100);
blocks.push(createBlock(y)); // 上のブロック
blocks.push(createBlock(y + 600)); // 下のブロック
}
/** ゲームオーバー画面を表示する */
function drawGameoverScreen() {
background(0, 192); // 透明度 192 の黒
fill(255);
textSize(64);
textAlign(CENTER, CENTER); // 横に中央揃え & 縦にも中央揃え
text("GAME OVER", width / 2, height / 2); // 画面中央にテキスト表示
}
/** ゲームのリセット */
function resetGame() {
// 状態をリセット
gameState = "play";
// プレイヤーを作成
player = createPlayer();
// ブロックの配列準備
blocks = [];
}
/** ゲームの更新 */
function updateGame() {
// ゲームオーバーなら更新しない
if (gameState === "gameover") return;
// ブロックの追加と削除
if (frameCount % 120 === 1) addBlockPair(blocks); // 一定間隔で追加
blocks = blocks.filter(blockIsAlive); // 生きているブロックだけ残す
// 全エンティティの位置を更新
updatePosition(player);
for (let block of blocks) updatePosition(block);
// プレイヤーに重力を適用
applyGravity(player);
// プレイヤーが死んでいたらゲームオーバー
if (!playerIsAlive(player)) gameState = "gameover";
// 衝突判定
for (let block of blocks) {
if (entitiesAreColliding(player, block, 20 + 40, 20 + 200)) {
gameState = "gameover";
break;
}
}
}
/** ゲームの描画 */
function drawGame() {
// 全エンティティを描画
background(0);
drawPlayer(player);
for (let block of blocks) drawBlock(block);
// ゲームオーバー状態なら、それ用の画面を表示
if (gameState === "gameover") drawGameoverScreen();
}
/** マウスボタンが押されたときのゲームへの影響 */
function onMousePress() {
switch (gameState) {
case "play":
// プレイ中の状態ならプレイヤーをジャンプさせる
applyJump(player);
break;
case "gameover":
// ゲームオーバー状態ならリセット
resetGame();
// M5stackから取得したカウントをリセット
squat_count = 0;
break;
}
}
// ---- setup/draw 他 --------------------------------------------------
//bleSetup()を追加する
function setup() {
bleSetup();
createCanvas(800, 600);
rectMode(CENTER);
resetGame();
}
function draw() {
updateGame();
drawGame();
}
//M5Stackからの値を読み取り
//スクワットのカウントが増えたらマウスをクリックした動作と同じことが発生する
function getValue(value) {
squat_count = value ;
console.log(squat_count);
if (squat_count > pre_squat_count){
pre_squat_count = squat_count ;
onMousePress();
}
}
//念のため、マウス機能を残す
function mousePressed() {
onMousePress();
}