やったこと
ARマーカーっぽくはないですが、QRコードの位置を取得し、QRコード内の色情報を取得し、その色の弾を撃てるゲームっぽいものができました
ブラウザでいい感じの3Dグラフィックスを表示するライブラリ
使い慣れてるという理由でp5jsを使います
3Dも手軽に利用できます
ブラウザでQRコードを読み込むライブラリ
調べたところ有名なのがzxing.jsでした
ですが、たぶんQRコードの位置は取得できなさそう
p5jsじゃなくてJavaのProcessingなら
jsQRというのを見つけた
実際に利用されている方のところで四角形を描いているので位置取れそう
コードを書いていきます
p5jsとjsQRでQRコードの情報読み取り
htmlは、p5jsのテンプレートにjsQRを追加で読み込んでいるだけです。
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>p5</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.js"></script>
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/addons/p5.sound.min.js"></script> -->
<script src="https://cdn.jsdelivr.net/npm/jsqr@latest/dist/jsQR.min.js"></script>
<script src="sketch.js"></script>
<style>
html,
body {
margin: 0;
padding: 0;
}
</style>
</head>
<body></body>
</html>
sketch.jsを作って
sketch.js
let captureGraphic;
function setup() {
createCanvas(640, 480);
captureGraphic = createGraphics(640, 480);
capture = createCapture(VIDEO);
capture.hide();
}
function draw() {
image(capture, 0, 0);
const code = getCodeFromCapture(capture, captureGraphic);
if (code) {
print(code.data);
}
}
function getCodeFromCapture(cap, g) {
g.image(cap, 0, 0, cap.width, cap.height);
const imgData = g.elt
.getContext('2d')
.getImageData(0, 0, cap.width, cap.height).data;
return jsQR(imgData, cap.width, cap.height);
}
p5jsだとブラウザでカメラを利用するまでのコードがめちゃくちゃ短くなります。
ただ、カメラ映像から画像データまでの変換が(どちらにしろですが)必要になります。
できた!!
QRコードの位置を取得したい
線を描くのもp5jsだと短めにかける感じに
let captureGraphic;
function setup() {
createCanvas(640, 480);
captureGraphic = createGraphics(640, 480);
capture = createCapture(VIDEO);
capture.hide();
}
function draw() {
image(capture, 0, 0);
const code = getCodeFromCapture(capture, captureGraphic);
if (code) {
print(code.data);
// QRコードを囲むように線を引く
const pos = code.location;
noFill();
stroke(255, 0, 0);
strokeWeight(10);
beginShape();
vertex(pos.topLeftCorner.x, pos.topLeftCorner.y);
vertex(pos.topRightCorner.x, pos.topRightCorner.y);
vertex(pos.bottomRightCorner.x, pos.bottomRightCorner.y);
vertex(pos.bottomLeftCorner.x, pos.bottomLeftCorner.y);
endShape(CLOSE);
// ついでにテキスト
stroke(0);
strokeWeight(1);
text(code.data, pos.topLeftCorner.x, pos.topLeftCorner.y);
}
}
function getCodeFromCapture(cap, g) {
g.image(cap, 0, 0, cap.width, cap.height);
const imgData = g.elt
.getContext('2d')
.getImageData(0, 0, cap.width, cap.height).data;
return jsQR(imgData, cap.width, cap.height);
}
3Dグラフィックスを表示させてみる
まずは単純なboxを表示してみた
let captureGraphic;
function setup() {
createCanvas(640, 480, WEBGL);
captureGraphic = createGraphics(640, 480);
capture = createCapture(VIDEO);
capture.hide();
}
function draw() {
// 3Dだと座標の原点が画面中央になるので
translate(-width / 2, -height / 2);
image(capture, 0, 0);
const code = getCodeFromCapture(capture, captureGraphic);
if (code) {
// print(code);
const pos = attendPositionData(code.location);
push();
translate(pos.center.x, pos.center.y);
box(100);
pop();
}
}
function getCodeFromCapture(cap, g) {
g.image(cap, 0, 0, cap.width, cap.height);
const imgData = g.elt
.getContext('2d')
.getImageData(0, 0, cap.width, cap.height).data;
return jsQR(imgData, cap.width, cap.height);
}
function attendPositionData(pos) {
const retPos = pos;
retPos.center = {};
retPos.center.x =
(pos.topLeftCorner.x +
pos.topRightCorner.x +
pos.bottomLeftCorner.x +
pos.bottomRightCorner.x) /
4;
retPos.center.y =
(pos.topLeftCorner.y +
pos.topRightCorner.y +
pos.bottomLeftCorner.y +
pos.bottomRightCorner.y) /
4;
return retPos;
}
弾を撃てるようにしてみる
classも使って動くsphereを描画してみる
QRコードから受け取った値を色として出してみています
let captureGraphic;
let burrets = [];
function setup() {
createCanvas(640, 480, WEBGL);
captureGraphic = createGraphics(640, 480);
capture = createCapture(VIDEO);
capture.hide();
}
function draw() {
// background(255);
// 3Dだと座標の原点が画面中央になるので
translate(-width / 2, -height / 2);
image(capture, 0, 0);
const code = getCodeFromCapture(capture, captureGraphic);
if (code) {
// print(code);
const pos = attendPositionData(code.location);
if (frameCount % 5 == 0) {
burrets.push(new Barrett(pos.center.x, pos.center.y, code.data));
}
}
burrets.forEach((e) => {
e.update();
e.draw();
});
burrets = burrets.filter((e) => e.z < 1000);
}
function getCodeFromCapture(cap, g) {
g.image(cap, 0, 0, cap.width, cap.height);
const imgData = g.elt
.getContext('2d')
.getImageData(0, 0, cap.width, cap.height).data;
return jsQR(imgData, cap.width, cap.height);
}
function attendPositionData(pos) {
const retPos = pos;
retPos.center = {};
retPos.center.x =
(pos.topLeftCorner.x +
pos.topRightCorner.x +
pos.bottomLeftCorner.x +
pos.bottomRightCorner.x) /
4;
retPos.center.y =
(pos.topLeftCorner.y +
pos.topRightCorner.y +
pos.bottomLeftCorner.y +
pos.bottomRightCorner.y) /
4;
return retPos;
}
class Barrett {
constructor(_x, _y, _col) {
this.x = _x;
this.y = _y;
this.z = -100;
this.col = _col;
}
update() {
this.z += 10;
}
draw() {
push();
translate(this.x, this.y, this.z);
noStroke();
fill(this.col);
sphere(20);
pop();
}
}
M5Stackを使って色情報を持ったQRコードを表示
今回は簡単にUIFlowを使いました
使い方などは…
いちおうPythonコードも
from m5stack import *
from m5ui import *
from uiflow import *
setScreenColor(0x222222)
color = None
def buttonA_wasPressed():
global color
color = '#FF0000'
lcd.qrcode(color, x=55, y=10, width=220, version=2)
pass
btnA.wasPressed(buttonA_wasPressed)
def buttonB_wasPressed():
global color
color = '#00FF00'
lcd.qrcode(color, x=55, y=10, width=220, version=2)
pass
btnB.wasPressed(buttonB_wasPressed)
def buttonC_wasPressed():
global color
color = '#0000FF'
lcd.qrcode(color, x=55, y=10, width=220, version=2)
pass
btnC.wasPressed(buttonC_wasPressed)
color = '#FF0000'
lcd.qrcode(color, x=55, y=10, width=220, version=2)
結果。AR銃みたいになった
でも楽しかったし、勉強になった
記事にまとめようとするとやりたいこと増えるやつでした
今後
- もっとQRコード自体に情報を持たせて、銃の性能とか撃ったタイミングとか見れたら面白そう
- こうなると、QRコードを表示している端末とカメラ端末を一切通信せずに色々やってみたい
- マーカーの歪みでQRコード端末の姿勢推定とかやりたい
- けど少し傾くだけで結構検出からもれる…
- せっかくブラウザなのでスマートフォンのカメラでやりたい
- 昔の記事 久々にやったらだめだった…更新したい