generated at
GifBird
> オッコードが綺麗だtakker
>   コメントまでついてる!贅沢!
>   古のJSって感じですね・・(もはや何も覚えてない)inajob
>  これをrewriteして描画周りの処理の書き方を学ぶのよさそう
>   教科書の数式を自分の記号体系に翻訳して理解するパターン
>  ゲームというUIで動画を作る動画編集アプリと言えなくもないねinajob
というわけでここでcode readingしつつ書き換えてみる

2022-05-22
19:09:03 TSに書き換え中
下手にclassを消さないほうがいいかな
2022-05-20
08:53:30 方針
とりあえずすべて一つのhtmlファイルに収めて、ローカルでも動くようにしようかな
そのあとScrapboxでも動くようにする
Gyazoにuploadする機能までつけるとおもしろそう

code
deno fmtしたあとdeno lspの警告を全部消した
不要な変数の削除
let const に置き換え
classに変換
これを自動で行えたのは助かった
JQueryとgif.jsに依存している
script.ts
const loadImg = (name: string): Image => { const img = new Image(); img.src = name; return img; }; const gif = new GIF({ workers: 2, quality: 10, width: 120, height: 160, workerScript: "js/gif.worker.js", }); const addFrame = (ctx: CanvasRenderingContext2D): void => gif.addFrame(ctx, { copy: true, delay: 10 }); const render = async (): Promise<Blob> => new Promise((resolve) => { gif.once("finished", (blob) => resolve(blob)); gif.render(); }); // グローバルにある特別なやつ const canv: HTMLCanvasElement = document.getElementById("canv")!; const ctx = canv.getContext("2d"); let time = 0; let mousef = false; let overTime = 0; let animCount = 0; //let daikonImg = loadImg('img/daikon.gif'); //let etsukoImg = loadImg('img/etsuko.gif'); const birdImgs = [loadImg("img/bird1.gif"), loadImg("img/bird2.gif")]; interface Point { x: number; y: number; } interface Character { position: Point; velocity: Point; acceleration: Point; width: number; height: number; type: "me" | ""; } const tick = (chr: Character): Character => { const newChr = { position: { x: chr.position.x + chr.velocity.x, y: chr.position.y + chr.velocity.y, }, velocity: { x: chr.velocity.x + chr.acceleration.x, y: chr.velocity.y + chr.acceleration.y, }, acceleration: { x: chr.acceleration.x, y: chr.acceleration.y, }, width: chr.width, height: chr.height, }; newChr.position.x = Math.max(Math.min(-3, newChr.position.x), 3); newChr.position.y = Math.max(Math.min(-3, newChr.position.y), 3); return newChr; }; const isOutOfArea = (chr: Character): boolean => chr.position.x + chr.width < 0 || chr.position.x > 240 || chr.position.y + chr.height < 0 || chr.position.y > 320; const isHit = (chr1: Character, chr2: Character): boolean => { const lx = Math.min(chr1.position.x + chr1.width, chr2.position.x + chr2.width); // 最も左にある右側 const rx = Math.max(chr1.position.x, chr2.position.x); // 最も右にある左側 const ty = Math.min(chr1.position.y + chr1.height, chr2.position.y + chr2.height); // 最も上にある下側 const by = Math.max(chr1.position.y, chr2.position.y); // 最も下にある上側 return rx < lx && by < ty; }; const draw = (chr: Character): void => { switch (this.type) { case "my": ctx.fillStyle = "yellow"; ctx.fillRect(this.position.x, this.position.y, this.width, this.height); ctx.drawImage( birdImgs[animCount % 2], this.position.x - 2, this.position.y - 2, this.width + 4, this.height + 4, ); break; default: ctx.fillStyle = "green"; ctx.fillRect(this.position.x, this.position.y, this.width, this.height); break; } }; class MainScene { constructor() { // this.bullets = []; this.my = new Chr(30, 50, 13 * 1.5, 13 * 1.5); this.my.ay = 0.08; this.my.type = "my"; this.bullets.push( new Chr(0, 0, 120, 10), new Chr(0, 160 - 10, 120, 10), ); } hitCheck() { return this.bullets.some((bullet) => isHit(bullet, this.my)); } tick() { let result = true; if (mousef) this.my.vy -= 0.16; if (time % 40 == 0) animCount++; if (time % 5 == 0 && mousef) animCount++; // gen if (time % 120 == 0) { const SIZE = 50; const r = Math.random() * (160 - 20 - SIZE) + 20; const c = new Chr(120, 0, 5 * 1.5, r) c.vx = -1; this.bullets.push(c); const d = new Chr(120, r + SIZE, 5 * 1.5, 160 - (r + SIZE)) d.vx = -1; this.bullets.push(d); } // draw if (overTime > 0) { overTime--; ctx.fillStyle = "red"; ctx.fillRect(0, 0, 120, 160); if (overTime == 0) result = false; } if (overTime == 0) this.my.tick(); this.my.draw(); if (overTime == 0) { for (const v of this.bullets) { v.tick(); if (this.hitCheck()) { overTime = 20; } } } this.bullets.forEach((v, i, arr) => { v.draw(); if (v.del) { arr.splice(i, 1); } }); ctx.fillStyle = "white"; ctx.font = "12px 'sans-serif'"; ctx.fillText("score: " + (time / 120 | 0), 10, 20); if (time < 100) { ctx.fillStyle = "white"; ctx.font = "30px 'sans-serif'"; ctx.fillText("GifBird", 10, 100); } if (time % 5 == 0 || overTime != 0) { addFrame(ctx); } return result; } } const blobToDataURL = async (blob): Promise<string> => { const reader = new FileReader(); const promise = new Promise<string>((resolve, reject) => { reader.addEventListener("load", (e) => { reader.removeEventListener("erorr", reject); resolve(e.target.result as string); }, { once: true }); reader.addEventListener("error", reject); }); reader.readAsDataURL(blob); return await promise; }; $(function () { //$('.over .title').hide(); $("#loading").hide(); $(".over .gameover").hide(); $(".js-retry").bind("pointerdown", () => { $("#screenshot").empty(); start(); }); $(".js-title").bind("pointerdown", () => { $(".over .gameover").hide(); $(".over .title").show(); }); $(document.body).bind("pointerdown", () => { mousef = true; }); $(document.body).bind("pointerup", () => { mousef = false; }); $(document.body).bind("touchstart touchmove mousemove", (e) => { if (e.type == "touchmove") { e.preventDefault(); } }); function start() { initGif(); $(".over .title").hide(); $(".over .gameover").hide(); time = 0; overTime = 0; const scene = new MainScene(); await new Promise((resolve) => setTimeout(resolve, 10)); // 初期化 ctx.fillStyle = "#444"; ctx.fillRect(0, 0, 240, 320); if (overTime == 0) time++; while (scene.tick()) { await new Promise((resolve) => setTimeout(resolve, 10)); } ctx.fillStyle = "#444"; ctx.fillRect(0, 0, 240, 320); const score = (time / 120 | 0); $(".js-over-logo").text("score:" + score); $(".over .gameover").fadeIn(); $(".retry").hide(); $("#screenshot").text("Generating GIF Animation.."); const blob = await render(); $(".retry").show(); $("#screenshot").empty(); const img = $("<img>"); img.attr("src", URL.createObjectURL(blob)); $("#screenshot").append(img); const link = $("<a>").text("download"); link.attr("href", URL.createObjectURL(blob)); link.attr("download", "gifbird.gif"); $("#screenshot").append(link); //window.open(URL.createObjectURL(blob)); const url = await blobToDataURL(blob); $("#bin").val(url.replace(/^data:image\/gif;base64,/, "")); } });
CSSとHTML
元ページを参照

このゲーム難しいtakker
マウス操作のタイミングがシビア
壁の穴に来るよう高度を制御するのが困難
すぐ天井や壁にぶつかってしまう

#2022-05-22 19:08:13
#2022-05-20 08:55:48
#2022-05-19 07:21:35