前回まではこちらを参照してください。
https://qiita.com/tri-comma/items/e9790b420d9786f91149
表示は何で実装しようか考えた結果、canvasにしました。
まだメダル枚数表示とか音とか、実装すべきものは残ってますが、いったん出来たところまでで記事にします。
最後に動くソース一式を掲載しますが、まずはhtmlからどうぞ。
HTML部分
シンプル。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ジャンケンマンフィーバー2024</title>
<style>
body {
margin: 0;
overflow: hidden;
}
#canvas {
margin: auto;
width: 100vh;
height: 100vh;
}
#base {
display: none;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<img id="base" src="rps.png" />
<script>
// ここに処理を書きます
</script>
</body>
</html>
描画領域のcanvasタグと、描画素材のimgタグがあるだけです。
ちなみに画像はこんな感じ。
(DALL-E3で作成したものをレタッチ。何気にこれが一番時間かかったんじゃないか?)
ゲームロジック部分
前回から少し変更があるので再掲します。
class JankenFever {
#status;
constructor(ms = 100) {
this.STS = { STANDBY: 0, PLAYING: 1, SPINING: 2 };
this.RSP = [ 'R', 'S', 'P' ];
this.ROULETTE = [1, 2, 4, 7, 2, 4, 1, 2, 7, 4, 2, 20];
this.RESULT = {
WIN: { val:'WIN', next: this.STS.SPINING, callback: this.onwin },
LOSE: { val:'LOSE', next: this.STS.STANDBY, callback: this.onlose },
DRAW: { val:'DRAW', next: this.STS.PLAYING, callback: this.ondraw },
};
this.JUDGE = {
'RR': this.RESULT.DRAW, 'SS': this.RESULT.DRAW, 'PP': this.RESULT.DRAW,
'RS': this.RESULT.WIN, 'SP': this.RESULT.WIN, 'PR': this.RESULT.WIN,
'RP': this.RESULT.LOSE, 'SR': this.RESULT.LOSE, 'PS': this.RESULT.LOSE,
};
this.status = this.STS.STANDBY;
this.medal = 1000;
this.player = { medal: 10 };
this.onstart = null;
this.onspin = null;
this.onstop = null;
this.interval = ms;
this.ridx = 0;
}
set status(sts) {
this.#status = sts;
if (sts === this.STS.SPINING) this.spin();
}
get status() { return this.#status; }
set onwin(fn) { this.RESULT.WIN.callback = fn; }
set onlose(fn) { this.RESULT.LOSE.callback = fn; }
set ondraw(fn) { this.RESULT.DRAW.callback = fn; }
start() {
if (this.status === this.STS.STANDBY && this.insertMedal(this.player)) {
this.status = this.STS.PLAYING;
this.onstart ? this.onstart() : null;
return { result: true, medal: this.medal, pmedal: this.player.medal };
} else { return { reslut: false, status: this.status, medal: this.medal, pmedal: this.player.medal }; }
}
play(yourHand) {
if (this.status == this.STS.PLAYING && this.RSP.indexOf(yourHand) > -1) {
const myHand = this.RSP[this.rnd(this.rnd(this.RSP.length))];
const result = this.judge(myHand, yourHand);
this.status = result.next;
result.callback ? result.callback() : null;
return { result: result.val, medal: this.medal, pmedal: this.player.medal };
} else { return { result: false, status: this.status, yourHand }; }
}
spin() {
if (this.status == this.STS.SPINING) {
this.ridx = this.rnd(this.ROULETTE.length);
this.scnt = 0;
this.riid = setInterval(()=>{
this.onspin ? this.onspin(this.ridx, this.ROULETTE) : null;
this.ridx = (this.ridx + 1) % this.ROULETTE.length;
if (this.scnt++ > 31) {
clearInterval(this.riid);
this.stop();
}
}, this.interval);
return { result: true, medal: this.medal, pmedal: this.player.medal };
} else { return { result: false, status: this.status }; }
}
stop() {
if (this.status == this.STS.SPINING) {
const medal = this.ROULETTE[this.ridx];
this.medal -= medal;
this.player.medal += medal;
this.status = this.STS.STANDBY;
this.onstop ? this.onstop() : null;
return { result: true, medal: this.medal, pmedal: this.player.medal };
} else { return { result: false, status: this.status }; }
}
insertMedal(player) {
if (player.medal > 0 && this.medal > Math.max.apply(null,this.ROULETTE)) {
player.medal--;
this.medal++;
return true;
}
return false;
}
judge(myHand, yourHand) { return this.JUDGE[myHand+yourHand]; }
rnd(m) { return Math.floor(Math.random()*m); }
}
変更点
- spinメソッドを変更: onspinイベントを一定周期で発生させる、規定回数が来たらstopメソッドを呼び出す
- stopメソッドを追加: メダルの払い出しとonstopイベントの呼び出し、ステータスをスタンバイに変更
- コンストラクタにonspinの発生周期(ミリ秒)を指定できるように変更
表示制御部分
ちょっと長いですけど、まずはソースから。
このあと簡単な解説もあります。
class JFView {
constructor(cid, iid) {
this.game = new JankenFever(100);
this.game.onstart = ()=>this.drawComThinking();
this.game.onwin = ()=>{
clearInterval(this.plyItvID);
this.canInsertMedal = true;
this.blinkResult('win');
};
this.game.onlose = ()=>{
clearInterval(this.plyItvID);
this.canInsertMedal = true;
this.blinkResult('lose');
this.plyItvID = setTimeout(()=>this.drawDemo(), 4000);
};
this.game.ondraw = ()=>{
clearInterval(this.plyItvID);
this.blinkResult('draw');
this.plyItvID = setTimeout(()=>this.drawComThinking(false), 1500);
};
this.game.onspin = (ri, rm) => this.drawRoulette(ri, rm);
this.game.onstop = () => this.plyItvID = setTimeout(()=>this.drawDemo(), 3000);
this.canvas = document.getElementById(cid);
this.ctx = this.canvas.getContext('2d');
this.image = document.getElementById(iid);
this.canvas.width = 1024 * (window.innerWidth / window.innerHeight);
this.canvas.height = 1024;
this.canvas.style.width = window.innerWidth + 'px';
this.canvas.style.height = window.innerHeight + 'px';
this.wmgn = Math.floor((this.canvas.width - 1024) / 2);
this.image.addEventListener('load', e=>this.drawInit());
this.canInsertMedal = true;
this.plyItvID;
this.resItvID;
this.myHand = '';
this.cx = (canvas.width - 1024) / 2 + 536;
this.cy = 411;
this.r = 130;
this.degs = [-76,-47,-15,17,49,77,102,131,163,-165,-133,-101];
this.fs = 28;
this.ctx.font = `bold ${this.fs}px Impact`;
this.ctx.lineWidth = 2.5;
this.canvas.addEventListener('click', (e) => {
const cvsx = Math.floor(1024 * (e.x / window.innerHeight) - this.wmgn);
const cvsy = Math.floor(1024 * (e.y / window.innerHeight));
if (cvsx>440 && cvsx<615 && cvsy>705 && cvsy<925 && this.canInsertMedal) {
clearInterval(this.plyItvID);
this.game.start();
}
if (cvsx>355 && cvsx<441 && cvsy>586 && cvsy<632) this.onpress('R');
if (cvsx>487 && cvsx<573 && cvsy>586 && cvsy<632) this.onpress('S');
if (cvsx>621 && cvsx<707 && cvsy>586 && cvsy<632) this.onpress('P');
});
}
drawInit() {
const { ctx, image, wmgn } = this;
ctx.drawImage(image, 0, 0, 1, 1024, 0, 0, wmgn + 1, 1024);
ctx.drawImage(image, 1022, 0, 1, 1024, wmgn + 1023, 0, wmgn + 1, 1024);
ctx.drawImage(image, 0, 0, 1024, 1024, wmgn, 0, 1024, 1024);
this.drawDemo();
}
drawDemo() {
this.drawComHand(0);
this.plyItvID = setInterval((()=>{
let rsp = 0;
return () => this.drawComHand(rsp = (rsp + 1) % 3);
})(), 1000);
}
drawComHand(rsp) {
const { ctx, image, wmgn, game } = this;
const rsp_xy = [ {x:1024,y:0}, {x:1024,y:170}, {x:1024,y:340} ][rsp];
ctx.drawImage(image, rsp_xy.x, rsp_xy.y, 150, 170, wmgn + 448, 317, 150, 170);
this.drawRoulette(-1, game.ROULETTE);
}
drawRoulette(rt = -1, rtmst) {
const { degs, ctx, cx, cy, r, fs } = this;
let i = 0;
rtmst.forEach((num)=>{
const [numStr, rad] = [num + '', Math.PI / 180 * degs[i]];
ctx.strokeStyle = rt===i ? '#FF0' : ['#C22','#C22'][i%2];
ctx.fillStyle = rt===i ? '#F00' : ['#FFF','#FFC'][i%2];
const dx = Math.floor(cx + r * Math.cos(rad) - (fs * numStr.length) / 2);
const dy = Math.floor(cy + r * Math.sin(rad) * 185/210);
ctx.strokeText(numStr, dx, dy);
ctx.fillText(numStr, dx, dy);
i++;
});
}
onpress(rsp) {
this.drawButton(rsp);
if (!this.canInsertMedal) this.game.play(this.myHand = rsp);
}
drawButton(rsp) {
const { ctx, image, wmgn } = this;
const xy = {
R: { px:1024, py:614, bx:355, by:586 },
S: { px:1024, py:660, bx:487, by:586 },
P: { px:1024, py:706, bx:621, by:586 }
}[rsp];
ctx.drawImage(image, xy.px, xy.py, 86, 46, wmgn + xy.bx, xy.by, 86, 46);
setTimeout( () => ctx.drawImage(image, xy.bx, xy.by, 86, 46, wmgn + xy.bx, xy.by, 86, 46), 150);
}
drawComThinking(flush = true) {
this.canInsertMedal = false;
flush ? this.blinkOff() : null;
let rsp = 0;
this.plyItvID = setInterval(() => {
rsp = ([0,1,2].filter(i=>i!==rsp))[Math.round(Math.random())];
this.drawComHand(rsp);
}, 100);
}
blinkOff() {
const { resItvID, ctx, image, wmgn } = this;
clearInterval(resItvID);
ctx.drawImage(image, 343, 261, 68, 52, wmgn + 343, 261, 68, 52);
ctx.drawImage(image, 646, 260, 68, 52, wmgn + 646, 260, 68, 52);
ctx.drawImage(image, 343, 503, 68, 52, wmgn + 343, 503, 68, 52);
ctx.drawImage(image, 646, 503, 68, 52, wmgn + 646, 503, 68, 52);
}
blinkResult(result) {
const handSels = { win:{'R':1,'S':2,'P':0}, lose:{'R':2,'S':0,'P':1}, draw:{'R':0,'S':1,'P':2} };
this.drawComHand(handSels[result][this.myHand]);
this.blinkOff();
const drawBlink = this.getDrawBlink(result);
drawBlink();
this.resItvID = setInterval(drawBlink, 500);
}
getDrawBlink(result) {
const { wmgn, ctx, image } = this;
const self = this;
const p = [{
winl:[1024, 510, 68, 52, wmgn + 343, 261, 68, 52],
winr:[1092, 510, 68, 52, wmgn + 646, 260, 68, 52],
lose:[1024, 562, 68, 52, wmgn + 343, 503, 68, 52],
draw:[1092, 562, 68, 52, wmgn + 646, 503, 68, 52],
},
{
winl:[343, 261, 68, 52, wmgn + 343, 261, 68, 52],
winr:[646, 260, 68, 52, wmgn + 646, 260, 68, 52],
lose:[343, 503, 68, 52, wmgn + 343, 503, 68, 52],
draw:[646, 503, 68, 52, wmgn + 646, 503, 68, 52],
}];
let i = 0;
return function() {
i++;
let d = p[i % 2][result === 'win' ? 'winl' : result];
ctx.drawImage(image, d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8]);
if (result === 'win') {
d = p[i % 2]['winr'];
ctx.drawImage(image, d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8]);
}
if (i === 7) clearInterval(self.resItvID);
};
}
}
const view = new JFView('canvas', 'base');
解説
概要
- canvasに、あらかじめ用意した画像をペタペタする方針
- ルーレット部分だけはText描画
- ライブラリの類は使いませんでした
コンストラクタ
- View制御クラスの中でゲームロジックの制御クラスをインスタンス化
- ロジックのコールバックを登録
- onstart: グーチョキパーをてけてけする
- onwin: WINをピカピカする
- onlose: LOSEをピカピカする、ピカピカが終わったらデモに戻る
- ondraw: DRAWをピカピカする
- onspin: ルーレット表示
- onstop: デモに戻る
- canvas初期化: 描画領域とウィンドウ領域のアスペクト比あわせ、初期描画
- テキスト描画のための各種初期化
- クリックイベントの登録
- ロジックのコールバックを登録
メソッド
- drawInit: 初期描画、ゲームの有効領域は1:1のため横長の場合はパターンで埋める
- drawDemo: メダルを入れるまでの、ゲームデモ画面。グーチョキパーをゆっくり繰り返す
- drawComHand: 画面中央にグー/チョキ/パーのいずれかを表示する処理
- drawRoulette: 12個の数字を描画する
- onpress: グーチョキパーボタンを押した時の処理
- drawButton: ボタンをポチッとな、の描画処理
- drawComThinking: グーチョキパーを高速描画
- blinkOFF: WIN/LOSE/DRAWのピカピカを止める
- blinkResult: WIN/LOSE/DRAWのピカピカ描画処理
- getDrawBlink: ピカピカ描画処理の本体(functionを返却)
ポイント
- 繰り返しになりますけど「メダル枚数表示」とか、まだ未完成部分がありますよ
- iOS Safariだとwindowのclickイベントは処理できない → canvasでaddEventListenerする
- 画面全体を描画領域にしてみた。余白を埋めたり座標変換したり、まあまあ頭使いました
- ルーレットの数字描画、最終的には数字ごとに角度を決めることで調整(degs定義を参照)
- windowのリサイズには対応していない(そこまで需要ないでしょ)
- 可読性を損なわない程度に短くしたつもり(描画のあたりはもうちょっとどうにかできそう)
- 貴重な人生の時間をジャンケンゲーム制作に費やすアラフィフ
すべてのソース
以下のソースと画像ファイルがあればゲーム試せます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ジャンケンマンフィーバー2024</title>
<style>
body {
margin: 0;
overflow: hidden;
}
#canvas {
margin: auto;
width: 100vh;
height: 100vh;
}
#base {
display: none;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<img id="base" src="rps.png" />
<script>
class JankenFever {
#status;
constructor(ms = 100) {
this.STS = { STANDBY: 0, PLAYING: 1, SPINING: 2 };
this.RSP = [ 'R', 'S', 'P' ];
this.ROULETTE = [1, 2, 4, 7, 2, 4, 1, 2, 7, 4, 2, 20];
this.RESULT = {
WIN: { val:'WIN', next: this.STS.SPINING, callback: this.onwin },
LOSE: { val:'LOSE', next: this.STS.STANDBY, callback: this.onlose },
DRAW: { val:'DRAW', next: this.STS.PLAYING, callback: this.ondraw },
};
this.JUDGE = {
'RR': this.RESULT.DRAW, 'SS': this.RESULT.DRAW, 'PP': this.RESULT.DRAW,
'RS': this.RESULT.WIN, 'SP': this.RESULT.WIN, 'PR': this.RESULT.WIN,
'RP': this.RESULT.LOSE, 'SR': this.RESULT.LOSE, 'PS': this.RESULT.LOSE,
};
this.status = this.STS.STANDBY;
this.medal = 1000;
this.player = { medal: 10 };
this.onstart = null;
this.onspin = null;
this.onstop = null;
this.interval = ms;
this.ridx = 0;
}
set status(sts) {
this.#status = sts;
if (sts === this.STS.SPINING) this.spin();
}
get status() { return this.#status; }
set onwin(fn) { this.RESULT.WIN.callback = fn; }
set onlose(fn) { this.RESULT.LOSE.callback = fn; }
set ondraw(fn) { this.RESULT.DRAW.callback = fn; }
start() {
if (this.status === this.STS.STANDBY && this.insertMedal(this.player)) {
this.status = this.STS.PLAYING;
this.onstart ? this.onstart() : null;
return { result: true, medal: this.medal, pmedal: this.player.medal };
} else { return { reslut: false, status: this.status, medal: this.medal, pmedal: this.player.medal }; }
}
play(yourHand) {
if (this.status == this.STS.PLAYING && this.RSP.indexOf(yourHand) > -1) {
const myHand = this.RSP[this.rnd(this.rnd(this.RSP.length))];
const result = this.judge(myHand, yourHand);
this.status = result.next;
result.callback ? result.callback() : null;
return { result: result.val, medal: this.medal, pmedal: this.player.medal };
} else { return { result: false, status: this.status, yourHand }; }
}
spin() {
if (this.status == this.STS.SPINING) {
this.ridx = this.rnd(this.ROULETTE.length);
this.scnt = 0;
this.riid = setInterval(()=>{
this.onspin ? this.onspin(this.ridx, this.ROULETTE) : null;
this.ridx = (this.ridx + 1) % this.ROULETTE.length;
if (this.scnt++ > 31) {
clearInterval(this.riid);
this.stop();
}
}, this.interval);
return { result: true, medal: this.medal, pmedal: this.player.medal };
} else { return { result: false, status: this.status }; }
}
stop() {
if (this.status == this.STS.SPINING) {
const medal = this.ROULETTE[this.ridx];
this.medal -= medal;
this.player.medal += medal;
this.status = this.STS.STANDBY;
this.onstop ? this.onstop() : null;
return { result: true, medal: this.medal, pmedal: this.player.medal };
} else { return { result: false, status: this.status }; }
}
insertMedal(player) {
if (player.medal > 0 && this.medal > Math.max.apply(null,this.ROULETTE)) {
player.medal--;
this.medal++;
return true;
}
return false;
}
judge(myHand, yourHand) { return this.JUDGE[myHand+yourHand]; }
rnd(m) { return Math.floor(Math.random()*m); }
}
class JFView {
constructor(cid, iid) {
this.game = new JankenFever(100);
this.game.onstart = ()=>this.drawComThinking();
this.game.onwin = ()=>{
clearInterval(this.plyItvID);
this.canInsertMedal = true;
this.blinkResult('win');
};
this.game.onlose = ()=>{
clearInterval(this.plyItvID);
this.canInsertMedal = true;
this.blinkResult('lose');
this.plyItvID = setTimeout(()=>this.drawDemo(), 4000);
};
this.game.ondraw = ()=>{
clearInterval(this.plyItvID);
this.blinkResult('draw');
this.plyItvID = setTimeout(()=>this.drawComThinking(false), 1500);
};
this.game.onspin = (ri, rm) => this.drawRoulette(ri, rm);
this.game.onstop = () => this.plyItvID = setTimeout(()=>this.drawDemo(), 3000);
this.canvas = document.getElementById(cid);
this.ctx = this.canvas.getContext('2d');
this.image = document.getElementById(iid);
this.canvas.width = 1024 * (window.innerWidth / window.innerHeight);
this.canvas.height = 1024;
this.canvas.style.width = window.innerWidth + 'px';
this.canvas.style.height = window.innerHeight + 'px';
this.wmgn = Math.floor((this.canvas.width - 1024) / 2);
this.image.addEventListener('load', e=>this.drawInit());
this.canInsertMedal = true;
this.plyItvID;
this.resItvID;
this.myHand = '';
this.cx = (canvas.width - 1024) / 2 + 536;
this.cy = 411;
this.r = 130;
this.degs = [-76,-47,-15,17,49,77,102,131,163,-165,-133,-101];
this.fs = 28;
this.ctx.font = `bold ${this.fs}px Impact`;
this.ctx.lineWidth = 2.5;
this.canvas.addEventListener('click', (e) => {
const cvsx = Math.floor(1024 * (e.x / window.innerHeight) - this.wmgn);
const cvsy = Math.floor(1024 * (e.y / window.innerHeight));
if (cvsx>440 && cvsx<615 && cvsy>705 && cvsy<925 && this.canInsertMedal) {
clearInterval(this.plyItvID);
this.game.start();
}
if (cvsx>355 && cvsx<441 && cvsy>586 && cvsy<632) this.onpress('R');
if (cvsx>487 && cvsx<573 && cvsy>586 && cvsy<632) this.onpress('S');
if (cvsx>621 && cvsx<707 && cvsy>586 && cvsy<632) this.onpress('P');
});
}
drawInit() {
const { ctx, image, wmgn } = this;
ctx.drawImage(image, 0, 0, 1, 1024, 0, 0, wmgn + 1, 1024);
ctx.drawImage(image, 1022, 0, 1, 1024, wmgn + 1023, 0, wmgn + 1, 1024);
ctx.drawImage(image, 0, 0, 1024, 1024, wmgn, 0, 1024, 1024);
this.drawDemo();
}
drawDemo() {
this.drawComHand(0);
this.plyItvID = setInterval((()=>{
let rsp = 0;
return () => this.drawComHand(rsp = (rsp + 1) % 3);
})(), 1000);
}
drawComHand(rsp) {
const { ctx, image, wmgn, game } = this;
const rsp_xy = [ {x:1024,y:0}, {x:1024,y:170}, {x:1024,y:340} ][rsp];
ctx.drawImage(image, rsp_xy.x, rsp_xy.y, 150, 170, wmgn + 448, 317, 150, 170);
this.drawRoulette(-1, game.ROULETTE);
}
drawRoulette(rt = -1, rtmst) {
const { degs, ctx, cx, cy, r, fs } = this;
let i = 0;
rtmst.forEach((num)=>{
const [numStr, rad] = [num + '', Math.PI / 180 * degs[i]];
ctx.strokeStyle = rt===i ? '#FF0' : ['#C22','#C22'][i%2];
ctx.fillStyle = rt===i ? '#F00' : ['#FFF','#FFC'][i%2];
const dx = Math.floor(cx + r * Math.cos(rad) - (fs * numStr.length) / 2);
const dy = Math.floor(cy + r * Math.sin(rad) * 185/210);
ctx.strokeText(numStr, dx, dy);
ctx.fillText(numStr, dx, dy);
i++;
});
}
onpress(rsp) {
this.drawButton(rsp);
if (!this.canInsertMedal) this.game.play(this.myHand = rsp);
}
drawButton(rsp) {
const { ctx, image, wmgn } = this;
const xy = {
R: { px:1024, py:614, bx:355, by:586 },
S: { px:1024, py:660, bx:487, by:586 },
P: { px:1024, py:706, bx:621, by:586 }
}[rsp];
ctx.drawImage(image, xy.px, xy.py, 86, 46, wmgn + xy.bx, xy.by, 86, 46);
setTimeout( () => ctx.drawImage(image, xy.bx, xy.by, 86, 46, wmgn + xy.bx, xy.by, 86, 46), 150);
}
drawComThinking(flush = true) {
this.canInsertMedal = false;
flush ? this.blinkOff() : null;
let rsp = 0;
this.plyItvID = setInterval(() => {
rsp = ([0,1,2].filter(i=>i!==rsp))[Math.round(Math.random())];
this.drawComHand(rsp);
}, 100);
}
blinkOff() {
const { resItvID, ctx, image, wmgn } = this;
clearInterval(resItvID);
ctx.drawImage(image, 343, 261, 68, 52, wmgn + 343, 261, 68, 52);
ctx.drawImage(image, 646, 260, 68, 52, wmgn + 646, 260, 68, 52);
ctx.drawImage(image, 343, 503, 68, 52, wmgn + 343, 503, 68, 52);
ctx.drawImage(image, 646, 503, 68, 52, wmgn + 646, 503, 68, 52);
}
blinkResult(result) {
const handSels = { win:{'R':1,'S':2,'P':0}, lose:{'R':2,'S':0,'P':1}, draw:{'R':0,'S':1,'P':2} };
this.drawComHand(handSels[result][this.myHand]);
this.blinkOff();
const drawBlink = this.getDrawBlink(result);
drawBlink();
this.resItvID = setInterval(drawBlink, 500);
}
getDrawBlink(result) {
const { wmgn, ctx, image } = this;
const self = this;
const p = [{
winl:[1024, 510, 68, 52, wmgn + 343, 261, 68, 52],
winr:[1092, 510, 68, 52, wmgn + 646, 260, 68, 52],
lose:[1024, 562, 68, 52, wmgn + 343, 503, 68, 52],
draw:[1092, 562, 68, 52, wmgn + 646, 503, 68, 52],
},
{
winl:[343, 261, 68, 52, wmgn + 343, 261, 68, 52],
winr:[646, 260, 68, 52, wmgn + 646, 260, 68, 52],
lose:[343, 503, 68, 52, wmgn + 343, 503, 68, 52],
draw:[646, 503, 68, 52, wmgn + 646, 503, 68, 52],
}];
let i = 0;
return function() {
i++;
let d = p[i % 2][result === 'win' ? 'winl' : result];
ctx.drawImage(image, d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8]);
if (result === 'win') {
d = p[i % 2]['winr'];
ctx.drawImage(image, d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8]);
}
if (i === 7) clearInterval(self.resItvID);
};
}
}
const view = new JFView('canvas', 'base');
</script>
</body>
</html>
動かすとこんな感じ。
次回予告
- メダル表示はどうしようね、考え中
- 音声はVoiceVOXでずんだもんにお願い中
- できあがったら自前のサイトにアップします(もちろんアフィリエイトを添えて笑)
追記:
https://qiita.com/tri-comma/items/53ba6d47e34e1d73d86d
「 javascriptで作るゲーム【ジャンケンマンフィーバー】完結編(音声出力) 」
続きの記事を書きました。