概要
micro:bitとPCをBluetoothで接続し、取得したセンシングデータを用いて、JavaScript上のテトリス風ゲームをコントロールします.
使用するセンシングデータと役割
- 加速度センサー
- x軸
左右に傾けると、ブロックが左右に移動. - y軸
奥に傾けると、ブロックが90°回転.
- x軸
- 温度センサー
温度に応じて、ブロックの落下速度を調節. - 照度センサー
周囲の明るさに応じて、ゲーム画面の明るさを調節. - (Bボタン)
Bボタンを押すと、ブロックを落下.
micro:bit(MakeCode)のプログラム
micro:bitのプログラムは、センシングデータをBletoothで送信するだけですので、非常にシンプルです.
micro:bitプログラムのJavaScript版はこちら
bit-makecode.js
bluetooth.onBluetoothConnected(function () {
basic.showIcon(IconNames.Yes)
})
bluetooth.onBluetoothDisconnected(function () {
basic.showIcon(IconNames.No)
})
// Bボタンの状態
let bButtonState = 0
let temperature = 0
let lightLevel = 0
let accZ = 0
let accY = 0
let accX = 0
bluetooth.startUartService()
bluetooth.startAccelerometerService()
bluetooth.startTemperatureService()
bluetooth.startLEDService()
basic.forever(function () {
// 加速度センサーのデータ
accX = input.acceleration(Dimension.X)
accY = input.acceleration(Dimension.Y)
accZ = input.acceleration(Dimension.Z)
// 光センサーのデータ
lightLevel = input.lightLevel()
// 温度センサーのデータ
temperature = input.temperature()
// Bボタンの状態
if (input.buttonIsPressed(Button.B)) {
// Bボタンが押されたと認識
bButtonState = 1
} else {
// Bボタンが離された状態
bButtonState = 0
}
// センサーのデータを送信
bluetooth.uartWriteString(`a:${accX},${accY},${accZ}`)
basic.pause(100)
bluetooth.uartWriteString(`t:${temperature};l:${lightLevel};b:${bButtonState}`)
basic.pause(100)
})
micro:bitとPCをBluetoothで接続
通常のWeb Bluetooth APIではセンシングデータが取得できなかったため、ラッパーライブラリ "BlueJelly" を使用しました.
3年前にBlueJellyをmicro:bit用に改良していましたので、今回はそちらを使用しました.
BlueJelly.jsのコード
bluejelly.js
/*
============================================================
BlueJelly.js
============================================================
Web Bluetooth API Wrapper Library
Copyright 2017-2020 JellyWare Inc.
https://monomonotech.jp/
GitHub
https://github.com/electricbaka/bluejelly
This software is released under the MIT License.
Web Bluetooth API
https://webbluetoothcg.github.io/web-bluetooth/
*/
//--------------------------------------------------
//BlueJelly constructor
//--------------------------------------------------
var BlueJelly = function(){
this.bluetoothDevice = null;
this.dataCharacteristic = null;
this.hashUUID ={};
this.hashUUID_lastConnected;
//callBack
this.onScan = function(deviceName){console.log("onScan");};
this.onConnectGATT = function(uuid){console.log("onConnectGATT");};
this.onRead = function(data, uuid){console.log("onRead");};
this.onWrite = function(uuid){console.log("onWrite");};
this.onStartNotify = function(uuid){console.log("onStartNotify");};
this.onStopNotify = function(uuid){console.log("onStopNotify");};
this.onDisconnect = function(){console.log("onDisconnect");};
this.onClear = function(){console.log("onClear");};
this.onReset = function(){console.log("onReset");};
this.onError = function(error){console.log("onError");};
}
//--------------------------------------------------
//setUUID
//--------------------------------------------------
BlueJelly.prototype.setUUID = function(name, serviceUUID, characteristicUUID){
console.log('Execute : setUUID');
console.log(this.hashUUID);
this.hashUUID[name] = {'serviceUUID':serviceUUID, 'characteristicUUID':characteristicUUID};
}
//--------------------------------------------------
//scan
//--------------------------------------------------
BlueJelly.prototype.scan = function(uuid){
return (this.bluetoothDevice ? Promise.resolve() : this.requestDevice(uuid))
.catch(error => {
console.log('Error : ' + error);
this.onError(error);
});
}
//--------------------------------------------------
//requestDevice
//--------------------------------------------------
BlueJelly.prototype.requestDevice = function(uuid) {
console.log('Execute : requestDevice');
return navigator.bluetooth.requestDevice({
acceptAllDevices: true,
optionalServices: [this.hashUUID[uuid].serviceUUID]})
.then(device => {
this.bluetoothDevice = device;
this.bluetoothDevice.addEventListener('gattserverdisconnected', this.onDisconnect);
this.onScan(this.bluetoothDevice.name);
});
}
//--------------------------------------------------
//connectGATT
//--------------------------------------------------
BlueJelly.prototype.connectGATT = function(uuid) {
if(!this.bluetoothDevice)
{
var error = "No Bluetooth Device";
console.log('Error : ' + error);
this.onError(error);
return;
}
if (this.bluetoothDevice.gatt.connected && this.dataCharacteristic) {
if(this.hashUUID_lastConnected == uuid)
return Promise.resolve();
}
this.hashUUID_lastConnected = uuid;
console.log('Execute : connect');
return this.bluetoothDevice.gatt.connect()
.then(server => {
console.log('Execute : getPrimaryService');
return server.getPrimaryService(this.hashUUID[uuid].serviceUUID);
})
.then(service => {
console.log('Execute : getCharacteristic');
return service.getCharacteristic(this.hashUUID[uuid].characteristicUUID);
})
.then(characteristic => {
this.dataCharacteristic = characteristic;
this.dataCharacteristic.addEventListener('characteristicvaluechanged',this.dataChanged(this, uuid));
this.onConnectGATT(uuid);
})
.catch(error => {
console.log('Error : ' + error);
this.onError(error);
});
}
//--------------------------------------------------
//dataChanged
//--------------------------------------------------
BlueJelly.prototype.dataChanged = function(self, uuid) {
return function(event) {
self.onRead(event.target.value, uuid);
}
}
//--------------------------------------------------
//read
//--------------------------------------------------
BlueJelly.prototype.read= function(uuid) {
return (this.scan(uuid))
.then( () => {
return this.connectGATT(uuid);
})
.then( () => {
console.log('Execute : readValue');
return this.dataCharacteristic.readValue();
})
.catch(error => {
console.log('Error : ' + error);
this.onError(error);
});
}
//--------------------------------------------------
//write
//--------------------------------------------------
BlueJelly.prototype.write = function(uuid, array_value) {
return (this.scan(uuid))
.then( () => {
return this.connectGATT(uuid);
})
.then( () => {
console.log('Execute : writeValue');
data = Uint8Array.from(array_value);
return this.dataCharacteristic.writeValue(data);
})
.then( () => {
this.onWrite(uuid);
})
.catch(error => {
console.log('Error : ' + error);
this.onError(error);
});
}
//--------------------------------------------------
//startNotify
//--------------------------------------------------
BlueJelly.prototype.startNotify = function(uuid) {
return (this.scan(uuid))
.then( () => {
return this.connectGATT(uuid);
})
.then( () => {
console.log('Execute : startNotifications');
this.dataCharacteristic.startNotifications()
})
.then( () => {
this.onStartNotify(uuid);
})
.catch(error => {
console.log('Error : ' + error);
this.onError(error);
});
}
//--------------------------------------------------
//stopNotify
//--------------------------------------------------
BlueJelly.prototype.stopNotify = function(uuid){
return (this.scan(uuid))
.then( () => {
return this.connectGATT(uuid);
})
.then( () => {
console.log('Execute : stopNotifications');
this.dataCharacteristic.stopNotifications()
})
.then( () => {
this.onStopNotify(uuid);
})
.catch(error => {
console.log('Error : ' + error);
this.onError(error);
});
}
//--------------------------------------------------
//disconnect
//--------------------------------------------------
BlueJelly.prototype.disconnect= function() {
if (!this.bluetoothDevice) {
var error = "No Bluetooth Device";
console.log('Error : ' + error);
this.onError(error);
return;
}
if (this.bluetoothDevice.gatt.connected) {
console.log('Execute : disconnect');
this.bluetoothDevice.gatt.disconnect();
} else {
var error = "Bluetooth Device is already disconnected";
console.log('Error : ' + error);
this.onError(error);
return;
}
}
//--------------------------------------------------
//clear
//--------------------------------------------------
BlueJelly.prototype.clear= function() {
console.log('Excute : Clear Device and Characteristic');
this.bluetoothDevice = null;
this.dataCharacteristic = null;
this.onClear();
}
//--------------------------------------------------
//reset(disconnect & clear)
//--------------------------------------------------
BlueJelly.prototype.reset= function() {
console.log('Excute : reset');
this.disconnect(); //disconnect() is not Promise Object
this.clear();
this.onReset();
}
//--------------------------------------------------
//micro:bit UUID(class constant)
//--------------------------------------------------
Object.defineProperty(BlueJelly, 'MICROBIT_BASE_UUID', {value: "e95d0000-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_SERVICESGENERIC_ACCESS', {value: "00001800-0000-1000-8000-00805f9b34fb", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_DEVICE_NAME', {value: "00002a00-0000-1000-8000-00805f9b34fb", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_APPEARANCE', {value: "00002a01-0000-1000-8000-00805f9b34fb", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS', {value: "00002a04-0000-1000-8000-00805f9b34fb", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_GENERIC_ATTRIBUTE', {value: "00001801-0000-1000-8000-00805f9b34fb", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_SERVICE_CHANGED', {value: "2a05", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_DEVICE_INFORMATION', {value: "0000180a-0000-1000-8000-00805f9b34fb", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_MODEL_NUMBER_STRING', {value: "00002a24-0000-1000-8000-00805f9b34fb", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_SERIAL_NUMBER_STRING', {value: "00002a25-0000-1000-8000-00805f9b34fb", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_HARDWARE_REVISION_STRING', {value: "00002a27-0000-1000-8000-00805f9b34fb", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_FIRMWARE_REVISION_STRING', {value: "00002a26-0000-1000-8000-00805f9b34fb", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_MANUFACTURER_NAME_STRING', {value: "00002a29-0000-1000-8000-00805f9b34fb", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_ACCELEROMETER_SERVICE', {value: "e95d0753-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_ACCELEROMETER_DATA', {value: "e95dca4b-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_ACCELEROMETER_PERIOD', {value: "e95dfb24-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_MAGNETOMETER_SERVICE', {value: "e95df2d8-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_MAGNETOMETER_DATA', {value: "e95dfb11-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_MAGNETOMETER_PERIOD', {value: "e95d386c-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_MAGNETOMETER_BEARING', {value: "e95d9715-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_MAGNETOMETER_CALIBRATION', {value: "e95db358-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_BUTTON_SERVICE', {value: "e95d9882-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_BUTTON_A_STATE', {value: "e95dda90-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_BUTTON_B_STATE', {value: "e95dda91-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_IO_PIN_SERVICE', {value: "e95d127b-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_PIN_DATA', {value: "e95d8d00-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_PIN_AD_CONFIGURATION', {value: "e95d5899-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_PIN_IO_CONFIGURATION', {value: "e95db9fe-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_PWM_CONTROL', {value: "e95dd822-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_LED_SERVICE', {value: "e95dd91d-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_LED_MATRIX_STATE', {value: "e95d7b77-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_LED_TEXT', {value: "e95d93ee-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_SCROLLING_DELAY', {value: "e95d0d2d-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_EVENT_SERVICE', {value: "e95d93af-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_MICROBIT_REQUIREMENTS', {value: "e95db84c-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_MICROBIT_EVENT', {value: "e95d9775-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_CLIENT_REQUIREMENTS', {value: "e95d23c4-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_CLIENT_EVENT', {value: "e95d5404-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_DFU_CONTROL_SERVICE', {value: "e95d93b0-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_DFU_CONTROL', {value: "e95d93b1-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_TEMPERATURE_SERVICE', {value: "e95d6100-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_TEMPERATURE', {value: "e95d9250-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_TEMPERATURE_PERIOD', {value: "e95d1b25-251d-470a-a062-fa1922dfa9a8", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_UART_SERVICE', {value: "6e400001-b5a3-f393-e0a9-e50e24dcca9e", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_TX_CHARACTERISTIC', {value: "6e400002-b5a3-f393-e0a9-e50e24dcca9e", writable: false});
Object.defineProperty(BlueJelly, 'MICROBIT_RX_CHARACTERISTIC', {value: "6e400003-b5a3-f393-e0a9-e50e24dcca9e", writable: false});
センシングデータ 受信確認プログラム (HTML/JavaScript)
加速度・照度・温度センサーの値が正しく取得できているかを確認します.
BlueJelly.jsを同じディレクトリに置く必要があります.
受信確認用のHTML/JavaScriptのコード
data-recive-test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>micro:bit Sensor Data</title>
<script src="bluejelly.js"></script> <!-- BlueJelly ライブラリの読み込み -->
<style>
#sensorData {
font-size: 20px;
margin-top: 20px;
}
#dataList {
font-size: 16px;
margin-top: 20px;
max-height: 200px;
overflow-y: scroll;
}
</style>
</head>
<body>
<h1>micro:bit 取得データ表示</h1>
<button id="startButton">micro:bit 接続</button>
<div id="sensorData">
<p><strong>加速度 (X, Y, Z): </strong><span id="accData">0, 0, 0</span></p>
<p><strong>温度: </strong><span id="tempData">0</span></p>
<p><strong>明るさ: </strong><span id="lightData">0</span></p>
</div>
<div id="dataList">
<ul id="sensorDataList"></ul>
</div>
<script>
const blueJelly = new BlueJelly();
// UUIDの設定(UARTサービスを利用)
const serviceUUID = '6e400001-b5a3-f393-e0a9-e50e24dcca9e'; // UART Service
const characteristicUUID = '6e400002-b5a3-f393-e0a9-e50e24dcca9e'; // TX Characteristic
let characteristic;
// ボタンが押されたときにBluetoothデバイスを接続
document.getElementById('startButton').addEventListener('click', async () => {
try {
// デバイスをスキャンして接続
const device = await navigator.bluetooth.requestDevice({
filters: [{ namePrefix: 'BBC micro:bit' }], // 名前が "BBC micro:bit" を含むデバイスに限定
optionalServices: [serviceUUID]
});
console.log('Device selected:', device);
const server = await device.gatt.connect();
console.log('Connected to GATT server');
const service = await server.getPrimaryService(serviceUUID);
console.log('Service found');
characteristic = await service.getCharacteristic(characteristicUUID);
console.log('Characteristic found');
// 通知の開始
await characteristic.startNotifications();
console.log('Notifications started');
// データ受信時のイベントリスナーを設定
characteristic.addEventListener('characteristicvaluechanged', event => {
const value = event.target.value; // DataView
const decoder = new TextDecoder('utf-8');
const decodedString = decoder.decode(value); // UTF-8デコード
console.log(decodedString);
// リストに追加
//const dataList = document.getElementById('sensorDataList');
//const newListItem = document.createElement('li');
//newListItem.textContent = `${decodedString}`;
//dataList.appendChild(newListItem);
// データ解析
const accMatch = decodedString.match(/a:([\d\-,]+)/);
const tempMatch = decodedString.match(/t:(\d+)/);
const lightMatch = decodedString.match(/l:(\d+)/);
if (accMatch) {
const accData = accMatch[1].split(',');
document.getElementById('accData').textContent = `X: ${accData[0]}, Y: ${accData[1]}, Z: ${accData[2]}`;
}
if (tempMatch) {
document.getElementById('tempData').textContent = tempMatch[1];
}
if (lightMatch) {
document.getElementById('lightData').textContent = lightMatch[1];
}
});
} catch (error) {
console.error('Connection Error:', error);
}
});
</script>
</body>
</html>
センシングデータを用いたテトリス風ゲーム
センシングデータを用いたテトリス風ゲームのHTML/JavaScriptのコード
sensor-data-tetris.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>micro:bit Tetris</title>
<script src="bluejelly.js"></script>
<style>
body {
margin: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 1); /* 初期の背景色 */
transition: background-color 0.5s;
}
canvas {
display: block;
margin: 20px auto;
border: 5px solid white; /* 枠線を追加 */
border-radius: 10px;
}
button {
font-size: 18px;
padding: 10px 20px;
margin-top: 20px;
}
</style>
</head>
<body>
<button id="connectButton">micro:bit 接続</button>
<canvas id="gameCanvas" width="300" height="600"></canvas>
<script>
const blueJelly = new BlueJelly();
const serviceUUID = '6e400001-b5a3-f393-e0a9-e50e24dcca9e'; // UART Service
const characteristicUUID = '6e400002-b5a3-f393-e0a9-e50e24dcca9e'; // TX Characteristic
let characteristic;
let gameStarted = false;
let currentBlock;
let board = Array.from({ length: 20 }, () => Array(10).fill(0)); // 横10列
let fallSpeed = 1000; // ms
let lastFallTime = Date.now();
let lastRotationTime = 0; // 回転のクールダウン管理
const rotationCooldown = 1000; // 回転のクールダウン時間 (ms)
let brightness = 1; // 背景の明るさ
const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");
// ブロックの形状パターン(7種類)
const blockShapes = [
[[1, 1], [1, 1]], // 正方形
[[0, 1, 0], [1, 1, 1]], // T字
[[1, 1, 0], [0, 1, 1]], // Z字
[[0, 1, 1], [1, 1, 0]], // 逆Z字
[[1, 0], [1, 0], [1, 1]], // L字
[[0, 0, 1], [1, 1, 1]], // 逆L字
[[1, 1, 1, 1]], // 長方形
];
let bButtonPressed = false; // Bボタンの状態
let lastBPressTime = 0; // 最後にBボタンが押された時間
document.getElementById('connectButton').addEventListener('click', async () => {
try {
const device = await navigator.bluetooth.requestDevice({
filters: [{ namePrefix: 'BBC micro:bit' }],
optionalServices: [serviceUUID]
});
const server = await device.gatt.connect();
const service = await server.getPrimaryService(serviceUUID);
characteristic = await service.getCharacteristic(characteristicUUID);
await characteristic.startNotifications();
characteristic.addEventListener('characteristicvaluechanged', handleSensorData);
document.getElementById('connectButton').style.display = 'none'; // 接続ボタンを非表示
gameStarted = true; // ゲームを開始
startGame();
} catch (error) {
console.error('Connection Error:', error);
}
});
function handleSensorData(event) {
const value = event.target.value;
const decoder = new TextDecoder('utf-8');
const decodedString = decoder.decode(value);
const accMatch = decodedString.match(/a:([\d\-,]+)/);
const tempMatch = decodedString.match(/t:(\d+)/);
const lightMatch = decodedString.match(/l:(\d+)/);
const buttonBMatch = decodedString.match(/b:(\d+)/); // Bボタンの入力
// 加速度センサーのデータ
if (accMatch) {
const accData = accMatch[1].split(',');
const tiltX = parseInt(accData[0]) / 2; // 感度調整
const tiltY = parseInt(accData[1]) / 2; // 感度調整
if (tiltX > 100 && canMove(currentBlock, 1, 0)) currentBlock.x++; // 右移動
if (tiltX < -100 && canMove(currentBlock, -1, 0)) currentBlock.x--; // 左移動
// 手前に傾けた際の回転
const now = Date.now();
if (tiltY > 200 && now - lastRotationTime > rotationCooldown) {
rotateBlock(currentBlock);
lastRotationTime = now; // 回転時間を更新
}
}
// 温度センサーで落下速度調整
if (tempMatch) {
const temperature = parseInt(tempMatch[1]);
fallSpeed = Math.max(200, 1000 - (temperature - 20) * 250); // 温度で落下速度調整
}
// 明るさ調整
if (lightMatch) {
const lightLevel = parseInt(lightMatch[1]);
brightness = Math.min(1, lightLevel / 255); // 明るさを計算
document.body.style.backgroundColor = `rgba(0, 0, 0, ${1 - brightness})`; // 背景色に適用
}
// Bボタンが押された時の処理
if (buttonBMatch && buttonBMatch[1] === "1") {
const now = Date.now();
if (!bButtonPressed || now - lastBPressTime > 1000) { // 1秒間のクールダウン
bButtonPressed = true;
lastBPressTime = now;
dropBlockImmediately();
}
} else {
bButtonPressed = false; // ボタンが離された場合はリセット
}
}
function rotateBlock(block) {
const newShape = block.shape[0].map((_, index) =>
block.shape.map(row => row[index]).reverse()
);
if (canMove({ ...block, shape: newShape }, 0, 0)) block.shape = newShape; // 衝突がない場合のみ回転
}
function dropBlockImmediately() {
while (canMove(currentBlock, 0, 1)) {
currentBlock.y++;
}
fixBlockToBoard(currentBlock);
clearLines();
currentBlock = generateBlock();
}
function canMove(block, dx, dy) {
return block.shape.every((row, y) =>
row.every((value, x) => {
if (!value) return true; // 空白部分は無視
const newX = block.x + x + dx;
const newY = block.y + y + dy;
return newX >= 0 && newX < 10 && newY < 20 && board[newY]?.[newX] === 0;
})
);
}
function fixBlockToBoard(block) {
block.shape.forEach((row, y) => {
row.forEach((value, x) => {
if (value) board[block.y + y][block.x + x] = value;
});
});
}
function clearLines() {
board = board.filter(row => row.some(cell => cell === 0)); // 空白の行のみ残す
while (board.length < 20) board.unshift(Array(10).fill(0)); // 上に空白行を追加
}
function generateBlock() {
const shape = blockShapes[Math.floor(Math.random() * blockShapes.length)];
return { x: Math.floor((10 - shape[0].length) / 2), y: 0, shape }; // 初期位置調整
}
function drawBoard() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let y = 0; y < 20; y++) {
for (let x = 0; x < 10; x++) {
if (board[y][x]) {
ctx.fillStyle = "blue";
ctx.fillRect(x * 30, y * 30, 30, 30);
}
}
}
}
function drawBlock(block) {
ctx.fillStyle = "red";
block.shape.forEach((row, dy) => {
row.forEach((value, dx) => {
if (value) {
ctx.fillRect((block.x + dx) * 30, (block.y + dy) * 30, 30, 30);
}
});
});
}
function updateGame() {
if (!gameStarted) return;
// ゲームオーバーチェック
if (board[0].some(cell => cell !== 0)) {
alert("Game Over!");
gameStarted = false;
return;
}
const now = Date.now();
if (now - lastFallTime > fallSpeed) {
if (canMove(currentBlock, 0, 1)) {
currentBlock.y++;
} else {
fixBlockToBoard(currentBlock);
clearLines();
currentBlock = generateBlock();
}
lastFallTime = now;
}
drawBoard();
drawBlock(currentBlock);
requestAnimationFrame(updateGame);
}
function startGame() {
currentBlock = generateBlock();
updateGame();
}
</script>
</body>
</html>
こちらからプレイできます.↓
工夫した点
- ブロックの回転
micro:bitを手前に傾けるとブロックが90°回転しますが、1度傾けると2回連続で回転してしまったため、1度傾けられたら1秒間は反応しないようにしました. - 文字列でのセンシングデータ送信
micro:bitがBluetoothで送信できる文字列には上限があるようで、全てのセンシングデータを一度に送信することはできなかったため、2度に分けて送信するようにしました.
改善したかった点
温度によってブロックの落下スピードが変化するようにしておりますが、ゲームスタート時の温度を基準にして、micro:bitで操作しきれる範囲での調整にすれば良かったです.
(micro:bitは大学に返却してしまい、実機でのテストができないため、どうにもできず...)