diff --git a/docs/assets/index-BA4Gq_NK.js b/docs/assets/index-BA4Gq_NK.js new file mode 100644 index 0000000..0fa5901 --- /dev/null +++ b/docs/assets/index-BA4Gq_NK.js @@ -0,0 +1 @@ +var H=Object.defineProperty;var L=(s,e,t)=>e in s?H(s,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):s[e]=t;var o=(s,e,t)=>(L(s,typeof e!="symbol"?e+"":e,t),t);(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))i(r);new MutationObserver(r=>{for(const n of r)if(n.type==="childList")for(const a of n.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&i(a)}).observe(document,{childList:!0,subtree:!0});function t(r){const n={};return r.integrity&&(n.integrity=r.integrity),r.referrerPolicy&&(n.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?n.credentials="include":r.crossOrigin==="anonymous"?n.credentials="omit":n.credentials="same-origin",n}function i(r){if(r.ep)return;r.ep=!0;const n=t(r);fetch(r.href,n)}})();const m=s=>document.querySelector(s),_=(s,e)=>{if(e&&(!Number.isInteger(e)||e<=0))throw new TypeError("fps 应该是一个正整数");let t;function i(...n){const a=(()=>{if(e){const h=1e3/e;let u=0;return d=>{const p=d-u;p>=h&&(s.apply(this,[d,...n]),u=d-p%h)}}return h=>{s.apply(this,[h,...n])}})(),l=h=>{t=requestAnimationFrame(l),a(h)};r(),l(0)}function r(){cancelAnimationFrame(t)}return[i,r]};function T(s){let e;const t=new Proxy(s,{construct(i,r){return e||(e=Reflect.construct(i,r)),e}});return s.prototype.constructor=t,t}const F=m(".game"),{width:I,height:j}=F.getBoundingClientRect(),c={column:10,row:20,FPS:null,speed:2,keySpeed:10,score:0,devicePixelRatio:window.devicePixelRatio,get brickWidth(){return Math.round(I*this.devicePixelRatio/this.column)},get brickHeight(){return Math.round(j*this.devicePixelRatio/this.row)},get windowWidth(){return this.brickWidth*this.column},get windowHeight(){return this.brickHeight*this.row}},D=(s,{x:e,y:t,width:i,height:r,color:n})=>{const a=r/10*c.devicePixelRatio,l=r/25*c.devicePixelRatio;s.fillStyle=n,s.beginPath(),s.moveTo(e+a,t),s.lineTo(e+i-a,t),s.arcTo(e+i,t,e+i,t+a,a),s.lineTo(e+i,t+r-a),s.arcTo(e+i,t+r,e+i-a,t+r,a),s.lineTo(e+a,t+r),s.arcTo(e,t+r,e,t+r-a,a),s.lineTo(e,t+a),s.arcTo(e,t,e+a,t,a),s.fill();const h=e+l/2,u=t+l/2,d=i-l,p=r-l;s.strokeStyle="#000000",s.lineWidth=l,s.beginPath(),s.moveTo(h+a,u),s.lineTo(h+d-a,u),s.arcTo(h+d,u,h+d,u+a,a),s.lineTo(h+d,u+p-a),s.arcTo(h+d,u+p,h+d-a,u+p,a),s.lineTo(h+a,u+p),s.arcTo(h,u+p,h,u+p-a,a),s.lineTo(h,u+a),s.arcTo(h,u,h+a,u,a),s.stroke(),s.closePath()},y=document.createElement("canvas"),K=y.getContext("2d");y.height=c.brickHeight*20;y.width=c.brickWidth;let G=0;const w={},A=(s,{x:e,y:t,width:i,height:r,color:n})=>{if(n in w){s.drawImage(y,0,r*w[n],i,r,e,t,i,r);return}D(K,{x:0,y:G*r,width:i,height:r,color:n}),w[n]=G++,s.drawImage(y,0,r*w[n],i,r,e,t,i,r)},M=(s,{x:e,y:t,width:i,height:r,color:n,structure:a})=>{for(let l=0;lArray.from({length:c.column}))}cleanUpCanvas(){this.clearCanvas(this.ctx),this.clearCanvas(this.bgCtx)}clearCanvas(e){e.clearRect(0,0,e.canvas.width,e.canvas.height)}};o(v,"ctx",E.getContext("2d")),o(v,"bgCtx",W.getContext("2d"));let k=v;const B={o:{color:"#FADADD",struct:["11","11"]},i:{color:"#F7E9D4",struct:["0000","1111","0000","0000"]},s:{color:"#C8E6C9",struct:["011","110","000"]},z:{color:"#B3E5FC",struct:["110","011","000"]},l:{color:"#FFCC80",struct:["001","111","000"]},j:{color:"#FFEE58",struct:["100","111","000"]},t:{color:"#CE93D8",struct:["000","111","010"]},".":{color:"green",struct:["1"]}},q=s=>{const e=s.findLastIndex(t=>+t!=0);return e===-1?-s.length:-e-1};class x{constructor(e,t=performance.now()){o(this,"color");o(this,"width");o(this,"height");o(this,"structure");o(this,"x");o(this,"y");o(this,"isRecycle",!1);this.letter=e,this.lastTime=t,this.color=B[this.letter].color,this.width=c.brickWidth,this.height=c.brickHeight,this.structure=B[this.letter].struct,this.x=c.column/2-1,this.y=q(this.structure)}draw(e){M(e,this)}update(e,t){return e-this.lastTime>=1e3/c.speed?(this.lastTime=e-(e-this.lastTime)%(1e3/c.speed),this.isOverlap(t,this.getBinary(),this.x,this.y+1)?!0:(this.y++,!1)):!1}left(e){this.isAtBorder("left")||this.isOverlap(e,this.getBinary(),this.x-1)||this.x--}right(e){this.isAtBorder("right")||this.isOverlap(e,this.getBinary(),this.x+1)||this.x++}downOne(e){return this.isOverlap(e,this.getBinary(),this.x,this.y+1)?!0:(this.y++,!1)}downBottom(e){for(;!this.isOverlap(e,this.getBinary(),this.x,this.y+1);)this.y++;return!0}rotate(e){const t=this.structure[0].length;let i=Array.from({length:t},()=>new Array(t));for(let n=0;n=c.column||l+this.x<0||h+this.y>=c.row))return;i[h][l]=this.structure[n][a]}i=i.map(n=>n.join(""));const r=this.getBinary(i);this.isOverlap(e,r)||(this.structure=i)}getBinary(e=this.structure,t=this.x){const i=[],r=e[0].length,n=c.column-t-r;for(let a=r-1;a>=0;a--){let l;n>=0?l=parseInt(e[a],2)<>-n,i.unshift(l)}return i}correctLastTime(e){this.lastTime=e}isOverlap(e,t=this.getBinary(),i=this.x,r=this.y){if(i-this.x!==0){const n=i-this.x;n>0?t=t.map(a=>a>>n):t=t.map(a=>a<<-n)}for(let n=t.length-1;n>=0;n--)if(!(r+n<0)&&t[n]&(e[r+n]??2**c.column-1))return!0;return!1}isAtBorder(e){const t=this.getBinary(),i={left:2**(c.column-1),right:1};for(let r=t.length-1;r>=0;r--)if(t[r]&i[e])return!0;return!1}}class Y{getRandomLetter(){const e=Object.keys(B);return e[Math.random()*e.length>>0]}record(e,t,i){if(this.isGameOver(i))return!1;const r=i.getBinary();for(let n=r.length-1;n>=0;n--)if(r[n]!==0){e[i.y+n]|=r[n];for(let a=c.column-1,l=r[n];l!==0;a--,l>>=1)l&1&&(t[i.y+n][a]=i.color)}return!0}eliminate(e,t){let i=0;for(let r=c.row-1;r>=0;r--)e[r]===2**c.column-1&&(i++,e.splice(r,1),e.unshift(0),t.splice(r,1),t.unshift(Array.from({length:c.column})),r++);return i}isGameOver(e){const t=e.structure.length;for(let i=0;i{switch(!0){case g.pause.some(e=>e===s.key):f=s.key;break;case Object.values(g.operate).flat(1).some(e=>e===s.key):f=s.key}};window.onkeyup=s=>{switch(!0){case f===s.key:f=null;default:let e;(e=N(s.key))&&(C[e]=!1)}};function N(s){if(g.pause.some(e=>e===s))return"pause";for(const[e,t]of Object.entries(g.operate))if(t.some(i=>i===s))return e}const X=s=>{let e=1e3/c.keySpeed;return g.speedUpKey.some(t=>t===s)&&(e=1e3/g.speedUpRate/c.keySpeed),e},P=function(){const s={left:"left",right:"right",down:"downOne",bottom:"downBottom",up:"rotate"};return(e,t)=>g.onceKey.some(i=>i===t)&&C[t]?null:e[s[t]].bind(e)}(),$=s=>g.pause.some(e=>e===s);let b=0;const J=function(s,e){if(f===null)return;if($(f)){e.pauseGame(),f=null;return}if(s)return;let t=Date.now();const i=N(f);if(!C[i]){b=t;let n=P(e,i);n==null||n(),C[i]=!0;return}let r=X(i);if(t-b>=r){b=t-(t-b)%r;let n=P(e,i);n==null||n()}};class Q{constructor(e,t,i,r){this.renderer=e,this.canvasWithMapCtx=t,this.brick=i,this.Player=r}takeTurns(e){this.brick=e}left(){this.brick.left(this.canvasWithMapCtx.mapBinary)}right(){this.brick.right(this.canvasWithMapCtx.mapBinary)}downOne(){this.brick.downOne(this.canvasWithMapCtx.mapBinary)&&(this.brick.isRecycle=!0)}downBottom(){this.brick.downBottom(this.canvasWithMapCtx.mapBinary)&&(this.brick.isRecycle=!0)}rotate(){this.brick.rotate(this.canvasWithMapCtx.mapBinary)}pauseGame(){this.renderer.pause?this.Player.playGame():this.Player.pauseGame()}}class Z{constructor(){o(this,"_score",0);o(this,"_eliminateNum",0)}get score(){return this._score}get eliminateNum(){return this._eliminateNum}scoreIncrease(e){this._score+=e}eliminateNumIncrease(e){this._eliminateNum+=e}reset(){this._score=0,this._eliminateNum=0}}const ee=T(Z),O=new ee;class te{constructor(){o(this,"events",{})}on(e,t){this.events[e]||(this.events[e]=[]),this.events[e].push(t)}emit(e,...t){const i=this.events[e];i&&i.forEach(r=>{r(...t)})}off(e,t){const i=this.events[e];i&&(this.events[e]=i.filter(r=>r!==t))}clearAllListeners(){this.events={}}}const re=T(te),S=new re;class R{constructor(e){o(this,"canvasWithMapCtx");o(this,"operation");o(this,"scorer");o(this,"eventEmitter");o(this,"gameHelper");o(this,"brick");o(this,"nextBrick");o(this,"lastTime",0);o(this,"pauseTime",0);o(this,"_over",!1);o(this,"_pause",!1);this.canvasWithMapCtx=e,this.scorer=O,this.eventEmitter=S,this.gameHelper=V,this.brick=new x(this.gameHelper.getRandomLetter()),this.nextBrick=new x(this.gameHelper.getRandomLetter()),this.operation=new Q(this,this.canvasWithMapCtx,this.brick,{playGame:this.playGame.bind(this),pauseGame:this.pauseGame.bind(this)})}get over(){return this._over}get pause(){return this._pause}render(e){if(this.userActions(),this._pause){this.cachePauseTime(e);return}this.clearCanvas(this.canvasWithMapCtx.ctx),this.lastTime=e,this.draw(),this.update(e-this.pauseTime),this.canNextOne(e-this.pauseTime)}clearCanvas(e){e.clearRect(0,0,e.canvas.width,e.canvas.height)}draw(){this.operation.brick.draw(this.canvasWithMapCtx.ctx)}update(e){this.operation.brick.update(e,this.canvasWithMapCtx.mapBinary)&&(this.operation.brick.isRecycle=!0)}canNextOne(e){this.operation.brick.isRecycle&&this.newNextOne(e)}newNextOne(e){if(!this.gameHelper.record(this.canvasWithMapCtx.mapBinary,this.canvasWithMapCtx.bg,this.brick)){this._over=!0;return}const i=this.gameHelper.eliminate(this.canvasWithMapCtx.mapBinary,this.canvasWithMapCtx.bg),r=this.gameHelper.computeScore(i);this.scorer.scoreIncrease(r),this.scorer.eliminateNumIncrease(i),this.eventEmitter.emit("updateScore",this.scorer.score),this.eventEmitter.emit("updateEliminate",this.scorer.eliminateNum),U(this.canvasWithMapCtx.bgCtx,this.canvasWithMapCtx.bg),this.brick=this.nextBrick,this.nextBrick=new x(this.gameHelper.getRandomLetter(),e),this.eventEmitter.emit("updateNextBrick",this.nextBrick),this.brick.correctLastTime(e),this.operation.takeTurns(this.brick)}cachePauseTime(e){this.pauseTime+=e-this.lastTime,this.lastTime=e}playGame(){this._pause=!1}pauseGame(){this._pause=!0}userActions(){J(this._pause,this.operation)}}class se{constructor(){o(this,"canvasWithMapCtx");o(this,"renderer");o(this,"Scorer");o(this,"startWithEnd");this.canvasWithMapCtx=new k,this.Scorer=O,this.renderer=new R(this.canvasWithMapCtx),this.startWithEnd=_((e=performance.now())=>{this.renderer.render(e)},c.FPS)}get score(){return this.Scorer.score}get over(){return this.renderer.over}get pause(){return this.renderer.pause}startGame(){this.startWithEnd[0]()}cancelGame(){this.startWithEnd[1]()}restartGame(){this.canvasWithMapCtx.cleanUpCanvas(),this.canvasWithMapCtx=new k,this.renderer=new R(this.canvasWithMapCtx)}playGame(){this.renderer.playGame()}pauseGame(){this.renderer.pauseGame()}}class ie{constructor(){o(this,"game");o(this,"dom");o(this,"eventEmitter");o(this,"scorer");this.game=new se,this.eventEmitter=S,this.scorer=O,this.dom={brickCanvas:m(".brick"),bgCanvas:m(".bg"),nextBrickCanvas:m(".next_brick"),start:m(".start"),pause:m(".pause"),regame:m(".regame"),restart:m(".restart"),score:m(".score>span"),eliminate:m(".eliminate>span")},this.dom.nextBrickCanvas.width=c.brickWidth*4,this.dom.nextBrickCanvas.height=c.brickHeight*4,this.init(),this.addEvent()}init(){this.eventEmitter.clearAllListeners(),this.eventEmitter.on("updateScore",()=>{this.dom.score.innerText=this.scorer.score+""}),this.eventEmitter.on("updateEliminate",()=>{this.dom.eliminate.innerText=this.scorer.eliminateNum+""}),this.eventEmitter.on("updateNextBrick",e=>{this.dom.nextBrickCanvas.getContext("2d").clearRect(0,0,this.dom.nextBrickCanvas.width,this.dom.nextBrickCanvas.height),M(this.dom.nextBrickCanvas.getContext("2d"),{...e,x:0,y:0})})}addEvent(){this.dom.start.addEventListener("click",()=>{this.game.startGame(),this.dom.start.style.display="none",this.dom.pause.style.display="block"}),this.dom.pause.addEventListener("click",()=>{this.game.pause?(this.game.playGame(),this.dom.pause.innerText="暂停"):(this.game.pauseGame(),this.dom.pause.innerText="继续")}),this.dom.regame.addEventListener("click",()=>{this.scorer.reset(),this.eventEmitter.emit("updateScore",this.scorer.score),this.game.restartGame(),this.init()})}}new ie; diff --git a/docs/assets/index-BSW8sJDB.css b/docs/assets/index-BSW8sJDB.css deleted file mode 100644 index 20c72a8..0000000 --- a/docs/assets/index-BSW8sJDB.css +++ /dev/null @@ -1,95 +0,0 @@ -* { - margin: 0px; - padding: 0px; -} -.canvas { - position: absolute; - top: 0; - left: 0; - height: 100%; - width: 100%; - border-radius: 10px; -} -.bg { - z-index: -1; - background-color: rgb(226, 233, 175); -} -.game { - position: relative; - height: 80vh; - background-size: 100% 100%; - aspect-ratio: 9 / 18; - z-index: 99; - transform: scale(0.9); - border-radius: 10px; - border: 1px solid black; -} -.container{ - position: relative; - margin: auto; - height: min-content; - width: 400px; - background-color: rgb(158,173,134); - /* z-index: ; */ -} -button,.score,.eliminate{ - padding: 8px 16px; - color: white; - border: none; - border-radius: 4px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); - cursor: pointer; - transition: background-color 0.3s; -} -.start{ - position: absolute; - top: 50%; - left: 50%; - height: 50px; - width: 100px; - background-color: #328855; - transform: translate(-50%, -50%); - z-index: 99; -} -.start:hover{ - background-color: #64c88a; -} -.next_brick{ - position: absolute; - background-color:white ; - border: 1px solid black; - top: 0; - right: 0; - transform-origin:right center ; - transform: scale(0.4); -} - -.score{ - position: absolute; - background-color: brown; - top: 20%; - right: 0; -} -.eliminate{ - position: absolute; - background-color: brown; - top: 30%; - right: 0; -} -.regame{ - position: absolute; - background-color: brown; - top: 40%; - right: 0; -} - -.restart{ - display: none; -} -.pause{ - display: none; - position: absolute; - background-color: brown; - top: 50%; - right: 0; -} diff --git a/docs/assets/index-CWFViShy.js b/docs/assets/index-CWFViShy.js deleted file mode 100644 index 0689ac7..0000000 --- a/docs/assets/index-CWFViShy.js +++ /dev/null @@ -1,974 +0,0 @@ -var __defProp = Object.defineProperty; -var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; -var __publicField = (obj, key, value) => { - __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); - return value; -}; -(function polyfill() { - const relList = document.createElement("link").relList; - if (relList && relList.supports && relList.supports("modulepreload")) { - return; - } - for (const link of document.querySelectorAll('link[rel="modulepreload"]')) { - processPreload(link); - } - new MutationObserver((mutations) => { - for (const mutation of mutations) { - if (mutation.type !== "childList") { - continue; - } - for (const node of mutation.addedNodes) { - if (node.tagName === "LINK" && node.rel === "modulepreload") - processPreload(node); - } - } - }).observe(document, { childList: true, subtree: true }); - function getFetchOpts(link) { - const fetchOpts = {}; - if (link.integrity) - fetchOpts.integrity = link.integrity; - if (link.referrerPolicy) - fetchOpts.referrerPolicy = link.referrerPolicy; - if (link.crossOrigin === "use-credentials") - fetchOpts.credentials = "include"; - else if (link.crossOrigin === "anonymous") - fetchOpts.credentials = "omit"; - else - fetchOpts.credentials = "same-origin"; - return fetchOpts; - } - function processPreload(link) { - if (link.ep) - return; - link.ep = true; - const fetchOpts = getFetchOpts(link); - fetch(link.href, fetchOpts); - } -})(); -const $ = (selector) => { - return document.querySelector(selector); -}; -const customRaf = (fn, fps) => { - if (!!fps) { - if (!Number.isInteger(fps) || fps <= 0) { - throw new TypeError("fps 应该是一个正整数"); - } - } - let timer; - function raf(...args) { - const run = (() => { - if (fps) { - const interval = 1e3 / fps; - let lastTime2 = 0; - return (timeStamp) => { - const deltaTime = timeStamp - lastTime2; - if (deltaTime >= interval) { - fn.apply(this, [timeStamp, ...args]); - lastTime2 = timeStamp - deltaTime % interval; - } - }; - } - return (timeStamp) => { - fn.apply(this, [timeStamp, ...args]); - }; - })(); - const update = (timeStamp) => { - timer = requestAnimationFrame(update); - run(timeStamp); - }; - cancel(); - update(0); - } - function cancel() { - cancelAnimationFrame(timer); - } - return [raf, cancel]; -}; -function SinglePattern(Ctor) { - let instance; - const p = new Proxy(Ctor, { - construct(target, args) { - if (!instance) { - instance = Reflect.construct(target, args); - } - return instance; - } - }); - Ctor.prototype.constructor = p; - return p; -} -const container = $(".game"); -const { width, height } = container.getBoundingClientRect(); -const gameParam = { - column: 10, - row: 20, - FPS: null, - speed: 2, - keySpeed: 10, - score: 0, - devicePixelRatio: window.devicePixelRatio, - // 给方块计算出整数值宽高,不然小数情况可能会出现方块间的间隙 - get brickWidth() { - return Math.round(width * this.devicePixelRatio / this.column); - }, - get brickHeight() { - return Math.round(height * this.devicePixelRatio / this.row); - }, - // 以方块的整数值加上行列算出整个画布的宽高 - get windowWidth() { - return this.brickWidth * this.column; - }, - get windowHeight() { - return this.brickHeight * this.row; - } -}; -const drawStyle = (ctx, { x, y, width: width2, height: height2, color }) => { - const radius = height2 / 10 * gameParam.devicePixelRatio; - const borderWidth = height2 / 25 * gameParam.devicePixelRatio; - ctx.fillStyle = color; - ctx.beginPath(); - ctx.moveTo(x + radius, y); - ctx.lineTo(x + width2 - radius, y); - ctx.arcTo(x + width2, y, x + width2, y + radius, radius); - ctx.lineTo(x + width2, y + height2 - radius); - ctx.arcTo(x + width2, y + height2, x + width2 - radius, y + height2, radius); - ctx.lineTo(x + radius, y + height2); - ctx.arcTo(x, y + height2, x, y + height2 - radius, radius); - ctx.lineTo(x, y + radius); - ctx.arcTo(x, y, x + radius, y, radius); - ctx.fill(); - const borderX = x + borderWidth / 2; - const borderY = y + borderWidth / 2; - const borderWidthAdjusted = width2 - borderWidth; - const borderHeightAdjusted = height2 - borderWidth; - ctx.strokeStyle = "#000000"; - ctx.lineWidth = borderWidth; - ctx.beginPath(); - ctx.moveTo(borderX + radius, borderY); - ctx.lineTo(borderX + borderWidthAdjusted - radius, borderY); - ctx.arcTo( - borderX + borderWidthAdjusted, - borderY, - borderX + borderWidthAdjusted, - borderY + radius, - radius - ); - ctx.lineTo( - borderX + borderWidthAdjusted, - borderY + borderHeightAdjusted - radius - ); - ctx.arcTo( - borderX + borderWidthAdjusted, - borderY + borderHeightAdjusted, - borderX + borderWidthAdjusted - radius, - borderY + borderHeightAdjusted, - radius - ); - ctx.lineTo(borderX + radius, borderY + borderHeightAdjusted); - ctx.arcTo( - borderX, - borderY + borderHeightAdjusted, - borderX, - borderY + borderHeightAdjusted - radius, - radius - ); - ctx.lineTo(borderX, borderY + radius); - ctx.arcTo(borderX, borderY, borderX + radius, borderY, radius); - ctx.stroke(); - ctx.closePath(); -}; -const offsetCanvas = document.createElement("canvas"); -const offsetCtx = offsetCanvas.getContext("2d"); -offsetCanvas.height = gameParam.brickHeight * 20; -offsetCanvas.width = gameParam.brickWidth; -let index = 0; -const cache = {}; -const drawBrickPiece = (ctx, { x, y, width: width2, height: height2, color }) => { - if (color in cache) { - ctx.drawImage( - offsetCanvas, - 0, - height2 * cache[color], - width2, - height2, - x, - y, - width2, - height2 - ); - return; - } - drawStyle(offsetCtx, { - x: 0, - y: index * height2, - width: width2, - height: height2, - color - }); - cache[color] = index++; - ctx.drawImage( - offsetCanvas, - 0, - height2 * cache[color], - width2, - height2, - x, - y, - width2, - height2 - ); -}; -const drawBrick = (ctx, { x, y, width: width2, height: height2, color, structure }) => { - for (let i = 0; i < structure.length; i++) { - for (let j = 0; j < structure[i].length; j++) { - if (structure[i][j] == "0") - continue; - drawBrickPiece(ctx, { - x: (x + j) * width2, - y: (y + i) * height2, - width: width2, - height: height2, - color - }); - } - } -}; -const drawBg = function(ctx, colors, brickWidth = gameParam.brickWidth, brickHeight = gameParam.brickHeight) { - ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); - for (let i = 0; i < colors.length; i++) { - for (let j = 0; j < colors[i].length; j++) { - if (colors[i][j] === void 0) - continue; - drawBrickPiece(ctx, { - x: j * brickWidth, - y: i * brickHeight, - width: brickWidth, - height: brickHeight, - color: colors[i][j] - }); - } - } -}; -const canvas = $(".canvas.brick"); -const bgCanvas = $(".canvas.bg"); -canvas.height = bgCanvas.height = gameParam.windowHeight; -canvas.width = bgCanvas.width = gameParam.windowWidth; -const _CanvasWithMapCtx = class _CanvasWithMapCtx { - constructor() { - __publicField(this, "ctx"); - __publicField(this, "bgCtx"); - __publicField(this, "mapBinary"); - __publicField(this, "bg"); - this.ctx = _CanvasWithMapCtx.ctx; - this.bgCtx = _CanvasWithMapCtx.bgCtx; - this.mapBinary = new Array(gameParam.row).fill(0); - this.bg = Array.from( - { length: gameParam.row }, - () => Array.from({ length: gameParam.column }) - ); - } - cleanUpCanvas() { - this.clearCanvas(this.ctx); - this.clearCanvas(this.bgCtx); - } - clearCanvas(ctx) { - ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); - } -}; -__publicField(_CanvasWithMapCtx, "ctx", canvas.getContext("2d")); -__publicField(_CanvasWithMapCtx, "bgCtx", bgCanvas.getContext("2d")); -let CanvasWithMapCtx = _CanvasWithMapCtx; -const bricks = { - o: { - color: "#FADADD", - struct: [ - "11", - "11" - ] - }, - i: { - color: "#F7E9D4", - struct: [ - "0000", - "1111", - "0000", - "0000" - ] - }, - s: { - color: "#C8E6C9", - struct: [ - "011", - "110", - "000" - ] - }, - z: { - color: "#B3E5FC", - struct: [ - "110", - "011", - "000" - ] - }, - l: { - color: "#FFCC80", - struct: [ - "001", - "111", - "000" - ] - }, - j: { - color: "#FFEE58", - struct: [ - "100", - "111", - "000" - ] - }, - t: { - color: "#CE93D8", - struct: [ - "000", - "111", - "010" - ] - }, - ".": { - color: "green", - struct: ["1"] - } -}; -const getY = (structure) => { - const index2 = structure.findLastIndex((s) => +s !== 0); - if (index2 === -1) - return -structure.length; - return -index2 - 1; -}; -class Brick { - constructor(letter, lastTime2 = performance.now()) { - __publicField(this, "color"); - __publicField(this, "width"); - __publicField(this, "height"); - __publicField(this, "structure"); - __publicField(this, "x"); - __publicField(this, "y"); - __publicField(this, "isRecycle", false); - this.letter = letter; - this.lastTime = lastTime2; - this.color = bricks[this.letter].color; - this.width = gameParam.brickWidth; - this.height = gameParam.brickHeight; - this.structure = bricks[this.letter].struct; - this.x = gameParam.column / 2 - 1; - this.y = getY(this.structure); - } - draw(ctx) { - drawBrick(ctx, this); - } - /** - * @param time 每帧调用时间戳 - * @param canDown 能不能继续下落 - * @returns 是否无法继续下落 - */ - update(time, mapBinary) { - if (time - this.lastTime >= 1e3 / gameParam.speed) { - this.lastTime = time - (time - this.lastTime) % (1e3 / gameParam.speed); - if (!this.isOverlap(mapBinary, this.getBinary(), this.x, this.y + 1)) { - this.y++; - return false; - } else { - return true; - } - } - return false; - } - left(mapBinary) { - if (this.isAtBorder("left")) - return; - if (!this.isOverlap(mapBinary, this.getBinary(), this.x - 1)) { - this.x--; - } - } - right(mapBinary) { - if (this.isAtBorder("right")) - return; - if (!this.isOverlap(mapBinary, this.getBinary(), this.x + 1)) { - this.x++; - } - } - /** - * - * @param mapBinary 已记录方块的二进制数据 - * @returns 是否无法继续下落 - */ - downOne(mapBinary) { - if (!this.isOverlap(mapBinary, this.getBinary(), this.x, this.y + 1)) { - this.y++; - return false; - } - return true; - } - downBottom(mapBinary) { - while (!this.isOverlap(mapBinary, this.getBinary(), this.x, this.y + 1)) { - this.y++; - } - return true; - } - rotate(mapBinary) { - const len = this.structure[0].length; - let newStructure = Array.from( - { length: len }, - () => new Array(len) - ); - for (let i = 0; i < this.structure.length; i++) { - for (let j = 0; j < this.structure[i].length; j++) { - let x = i, y = len - 1 - j; - if (this.structure[i][j] === "1" && (x + this.x >= gameParam.column || x + this.x < 0 || y + this.y >= gameParam.row)) - return; - newStructure[y][x] = this.structure[i][j]; - } - } - newStructure = newStructure.map( - (s) => s.join("") - ); - const newBinary = this.getBinary(newStructure); - if (this.isOverlap(mapBinary, newBinary)) - return; - this.structure = newStructure; - } - getBinary(structure = this.structure, x = this.x) { - const binary = []; - const len = structure[0].length; - const carry = gameParam.column - x - len; - for (let i = len - 1; i >= 0; i--) { - let r; - if (carry >= 0) { - r = parseInt(structure[i], 2) << carry; - } else { - r = parseInt(structure[i], 2) >> -carry; - } - binary.unshift(r); - } - return binary; - } - correctLastTime(time) { - this.lastTime = time; - } - /** - * - * @param mapBinary 已记录方块的二进制数据 - * @param binary 方块二进制数据 - * @param x 第几行 - * @param y 第几列 - * @returns 是不是有方块重叠 - */ - isOverlap(mapBinary, binary = this.getBinary(), x = this.x, y = this.y) { - if (x - this.x !== 0) { - const shift = x - this.x; - if (shift > 0) { - binary = binary.map((b) => b >> shift); - } else { - binary = binary.map((b) => b << -shift); - } - } - for (let i = binary.length - 1; i >= 0; i--) { - if (y + i < 0) - continue; - if (binary[i] & (mapBinary[y + i] ?? 2 ** gameParam.column - 1)) { - return true; - } - } - return false; - } - /** - * - * @param direction 方向 - * @returns 是否在左或右边无法移动 - */ - isAtBorder(direction) { - const binary = this.getBinary(); - const maxBorderBinaryValue = { left: 2 ** (gameParam.column - 1), right: 1 }; - for (let i = binary.length - 1; i >= 0; i--) { - if (binary[i] & maxBorderBinaryValue[direction]) { - return true; - } - } - return false; - } -} -class GameHelper { - getRandomLetter() { - const letters = Object.keys(bricks); - return letters[Math.random() * letters.length >> 0]; - } - /** - * @dec 记录方块的落点位置 以及它的颜色 - * @returns 是否成功记录 如果失败就是游戏结束 - */ - record(mapBinary, bg, brick) { - if (this.isGameOver(brick)) { - return false; - } - const binary = brick.getBinary(); - for (let i = binary.length - 1; i >= 0; i--) { - if (binary[i] === 0) - continue; - mapBinary[brick.y + i] |= binary[i]; - for (let j = gameParam.column - 1, r = binary[i]; r !== 0; j--, r >>= 1) { - if (r & 1) { - bg[brick.y + i][j] = brick.color; - } - } - } - return true; - } - /** - * 消除行和颜色 - */ - eliminate(mapBinary, bg) { - let count = 0; - for (let i = gameParam.row - 1; i >= 0; i--) { - if (mapBinary[i] === 2 ** gameParam.column - 1) { - count++; - mapBinary.splice(i, 1); - mapBinary.unshift(0); - bg.splice(i, 1); - bg.unshift(Array.from({ length: gameParam.column })); - i++; - } - } - return count; - } - isGameOver(brick) { - const len = brick.structure.length; - for (let i = 0; i < len; i++) { - if (brick.y + i < 0) - return true; - } - return false; - } - computeScore(row) { - switch (row) { - case 0: - return 20; - case 1: - return 120; - case 2: - return 320; - case 3: - return 720; - case 4: - return 1520; - default: - return 20; - } - } -} -const SingleGameHelper = SinglePattern(GameHelper); -const gameHelper = new SingleGameHelper(); -const control = { - operate: { - left: ["a", "ArrowLeft"], - right: ["d", "ArrowRight"], - up: ["w", "ArrowUp"], - down: ["s", "ArrowDown"], - bottom: [" "] - }, - onceKey: ["up", "bottom"], - speedUpKey: ["down"], - speedUpRate: 2, - pause: ["Enter", "p"] -}; -const isKeyPressed = { - left: false, - right: false, - up: false, - down: false, - bottom: false -}; -let activeKey = null; -window.onkeydown = (e) => { - switch (true) { - case control.pause.some((item) => item === e.key): - activeKey = e.key; - break; - case Object.values(control.operate).flat(1).some((item) => item === e.key): - activeKey = e.key; - } -}; -window.onkeyup = (e) => { - switch (true) { - case activeKey === e.key: - activeKey = null; - default: - let ctrlKey; - if (ctrlKey = findCtrlKey(e.key)) { - isKeyPressed[ctrlKey] = false; - } - } -}; -function findCtrlKey(activeKey2) { - if (control.pause.some((item) => item === activeKey2)) { - return "pause"; - } - for (const [key, value] of Object.entries(control.operate)) { - if (value.some((item) => item === activeKey2)) { - return key; - } - } -} -const getBrickDownInterval = (ctrlKey) => { - let interval = 1e3 / gameParam.keySpeed; - if (control.speedUpKey.some((item) => item === ctrlKey)) { - interval = 1e3 / control.speedUpRate / gameParam.keySpeed; - } - return interval; -}; -const getHandle = /* @__PURE__ */ function() { - const direction = { - left: "left", - right: "right", - down: "downOne", - bottom: "downBottom", - up: "rotate" - }; - return (operation, ctrlKey) => { - if (control.onceKey.some((item) => item === ctrlKey) && isKeyPressed[ctrlKey]) - return null; - return operation[direction[ctrlKey]].bind(operation); - }; -}(); -const isPauseKey = (activeKey2) => { - return control.pause.some((item) => item === activeKey2); -}; -let lastTime = 0; -const userActions = function(pause, operation) { - if (activeKey === null) - return; - if (isPauseKey(activeKey)) { - operation.pauseGame(); - activeKey = null; - return; - } - if (pause) - return; - let now = Date.now(); - const ctrlKey = findCtrlKey(activeKey); - if (!isKeyPressed[ctrlKey]) { - lastTime = now; - let handle = getHandle(operation, ctrlKey); - handle == null ? void 0 : handle(); - isKeyPressed[ctrlKey] = true; - return; - } - let interval = getBrickDownInterval(ctrlKey); - if (now - lastTime >= interval) { - lastTime = now - (now - lastTime) % interval; - let handle = getHandle(operation, ctrlKey); - handle == null ? void 0 : handle(); - } -}; -class Operation { - constructor(renderer, canvasWithMapCtx, brick, Player) { - this.renderer = renderer; - this.canvasWithMapCtx = canvasWithMapCtx; - this.brick = brick; - this.Player = Player; - } - takeTurns(brick) { - this.brick = brick; - } - left() { - this.brick.left(this.canvasWithMapCtx.mapBinary); - } - right() { - this.brick.right(this.canvasWithMapCtx.mapBinary); - } - downOne() { - const shouldNextOne = this.brick.downOne(this.canvasWithMapCtx.mapBinary); - if (shouldNextOne) { - this.brick.isRecycle = true; - } - } - downBottom() { - const shouldNextOne = this.brick.downBottom(this.canvasWithMapCtx.mapBinary); - if (shouldNextOne) { - this.brick.isRecycle = true; - } - } - rotate() { - this.brick.rotate(this.canvasWithMapCtx.mapBinary); - } - pauseGame() { - if (this.renderer.pause) { - this.Player.playGame(); - } else { - this.Player.pauseGame(); - } - } -} -class Scorer { - constructor() { - __publicField(this, "_score", 0); - __publicField(this, "_eliminateNum", 0); - } - get score() { - return this._score; - } - get eliminateNum() { - return this._eliminateNum; - } - scoreIncrease(v) { - this._score += v; - } - eliminateNumIncrease(v) { - this._eliminateNum += v; - } - reset() { - this._score = 0; - this._eliminateNum = 0; - } -} -const SingleScorer = SinglePattern(Scorer); -const scorer = new SingleScorer(); -class EventEmitter { - constructor() { - __publicField(this, "events", {}); - } - on(event, listener) { - if (!this.events[event]) { - this.events[event] = []; - } - this.events[event].push(listener); - } - emit(event, ...args) { - const listeners = this.events[event]; - if (listeners) { - listeners.forEach((listener) => { - listener(...args); - }); - } - } - off(event, listener) { - const listeners = this.events[event]; - if (listeners) { - this.events[event] = listeners.filter((l) => l !== listener); - } - } - clearAllListeners() { - this.events = {}; - } -} -const SingleEventEmitter = SinglePattern(EventEmitter); -const eventEmitter = new SingleEventEmitter(); -class Renderer { - constructor(canvasWithMapCtx) { - __publicField(this, "canvasWithMapCtx"); - __publicField(this, "operation"); - __publicField(this, "scorer"); - __publicField(this, "eventEmitter"); - __publicField(this, "gameHelper"); - __publicField(this, "brick"); - __publicField(this, "nextBrick"); - __publicField(this, "lastTime", 0); - __publicField(this, "pauseTime", 0); - __publicField(this, "_over", false); - __publicField(this, "_pause", false); - this.canvasWithMapCtx = canvasWithMapCtx; - this.scorer = scorer; - this.eventEmitter = eventEmitter; - this.gameHelper = gameHelper; - this.brick = new Brick(this.gameHelper.getRandomLetter()); - this.nextBrick = new Brick(this.gameHelper.getRandomLetter()); - this.operation = new Operation(this, this.canvasWithMapCtx, this.brick, { - playGame: this.playGame.bind(this), - pauseGame: this.pauseGame.bind(this) - }); - } - get over() { - return this._over; - } - get pause() { - return this._pause; - } - render(time) { - this.userActions(); - if (this._pause) { - this.cachePauseTime(time); - return; - } - this.clearCanvas(this.canvasWithMapCtx.ctx); - this.lastTime = time; - this.draw(); - this.update(time - this.pauseTime); - this.canNextOne(time - this.pauseTime); - } - clearCanvas(ctx) { - ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); - } - draw() { - this.operation.brick.draw(this.canvasWithMapCtx.ctx); - } - update(time) { - const shouldNextOne = this.operation.brick.update( - time, - this.canvasWithMapCtx.mapBinary - ); - if (shouldNextOne) { - this.operation.brick.isRecycle = true; - } - } - canNextOne(time) { - if (this.operation.brick.isRecycle) { - this.newNextOne(time); - } - } - newNextOne(time) { - const isSuccess = this.gameHelper.record( - this.canvasWithMapCtx.mapBinary, - this.canvasWithMapCtx.bg, - this.brick - ); - if (!isSuccess) { - this._over = true; - return; - } - const row = this.gameHelper.eliminate( - this.canvasWithMapCtx.mapBinary, - this.canvasWithMapCtx.bg - ); - const score = this.gameHelper.computeScore(row); - this.scorer.scoreIncrease(score); - this.scorer.eliminateNumIncrease(row); - this.eventEmitter.emit("updateScore", this.scorer.score); - this.eventEmitter.emit("updateEliminate", this.scorer.eliminateNum); - drawBg(this.canvasWithMapCtx.bgCtx, this.canvasWithMapCtx.bg); - this.brick = this.nextBrick; - this.nextBrick = new Brick(this.gameHelper.getRandomLetter(), time); - this.eventEmitter.emit("updateNextBrick", this.nextBrick); - this.brick.correctLastTime(time); - this.operation.takeTurns(this.brick); - } - cachePauseTime(time) { - this.pauseTime += time - this.lastTime; - this.lastTime = time; - } - playGame() { - this._pause = false; - } - pauseGame() { - this._pause = true; - } - userActions() { - userActions(this._pause, this.operation); - } -} -class Game { - constructor() { - __publicField(this, "canvasWithMapCtx"); - __publicField(this, "renderer"); - __publicField(this, "Scorer"); - __publicField(this, "startWithEnd"); - this.canvasWithMapCtx = new CanvasWithMapCtx(); - this.Scorer = scorer; - this.renderer = new Renderer(this.canvasWithMapCtx); - this.startWithEnd = customRaf((time = performance.now()) => { - this.renderer.render(time); - }, gameParam.FPS); - } - get score() { - return this.Scorer.score; - } - get over() { - return this.renderer.over; - } - get pause() { - return this.renderer.pause; - } - startGame() { - this.startWithEnd[0](); - } - cancelGame() { - this.startWithEnd[1](); - } - restartGame() { - this.canvasWithMapCtx.cleanUpCanvas(); - this.canvasWithMapCtx = new CanvasWithMapCtx(); - this.renderer = new Renderer(this.canvasWithMapCtx); - } - playGame() { - this.renderer.playGame(); - } - pauseGame() { - this.renderer.pauseGame(); - } -} -class Ui { - constructor() { - __publicField(this, "game"); - __publicField(this, "dom"); - __publicField(this, "eventEmitter"); - __publicField(this, "scorer"); - this.game = new Game(); - this.eventEmitter = eventEmitter; - this.scorer = scorer; - this.dom = { - brickCanvas: $(".brick"), - bgCanvas: $(".bg"), - nextBrickCanvas: $(".next_brick"), - start: $(".start"), - // Cast the result to HTMLElement - pause: $(".pause"), - regame: $(".regame"), - restart: $(".restart"), - score: $(".score>span"), - eliminate: $(".eliminate>span") - }; - this.dom.nextBrickCanvas.width = gameParam.brickWidth * 4; - this.dom.nextBrickCanvas.height = gameParam.brickHeight * 4; - this.init(); - this.addEvent(); - } - init() { - this.eventEmitter.clearAllListeners(); - this.eventEmitter.on("updateScore", () => { - this.dom.score.innerText = this.scorer.score + ""; - }); - this.eventEmitter.on("updateEliminate", () => { - this.dom.eliminate.innerText = this.scorer.eliminateNum + ""; - }); - this.eventEmitter.on("updateNextBrick", (brick) => { - this.dom.nextBrickCanvas.getContext("2d").clearRect(0, 0, this.dom.nextBrickCanvas.width, this.dom.nextBrickCanvas.height); - drawBrick(this.dom.nextBrickCanvas.getContext("2d"), { - ...brick, - x: 0, - y: 0 - }); - }); - } - addEvent() { - this.dom.start.addEventListener("click", () => { - this.game.startGame(); - this.dom.start.style.display = "none"; - this.dom.pause.style.display = "block"; - }); - this.dom.pause.addEventListener("click", () => { - if (this.game.pause) { - this.game.playGame(); - this.dom.pause.innerText = "暂停"; - } else { - this.game.pauseGame(); - this.dom.pause.innerText = "继续"; - } - }); - this.dom.regame.addEventListener("click", () => { - this.scorer.reset(); - this.eventEmitter.emit("updateScore", this.scorer.score); - this.game.restartGame(); - this.init(); - }); - } -} -new Ui(); diff --git a/docs/assets/index-CxuZ0qD4.css b/docs/assets/index-CxuZ0qD4.css new file mode 100644 index 0000000..b04b0ee --- /dev/null +++ b/docs/assets/index-CxuZ0qD4.css @@ -0,0 +1 @@ +*{margin:0;padding:0}.canvas{position:absolute;top:0;left:0;height:100%;width:100%;border-radius:10px}.bg{z-index:-1;background-color:#e2e9af}.game{position:relative;height:80vh;background-size:100% 100%;aspect-ratio:9 / 18;z-index:99;transform:scale(.9);border-radius:10px;border:1px solid black}.container{position:relative;margin:auto;height:min-content;width:400px;background-color:#9ead86}button,.score,.eliminate{padding:8px 16px;color:#fff;border:none;border-radius:4px;box-shadow:0 2px 4px #0003;cursor:pointer;transition:background-color .3s}.start{position:absolute;top:50%;left:50%;height:50px;width:100px;background-color:#328855;transform:translate(-50%,-50%);z-index:99}.start:hover{background-color:#64c88a}.next_brick{position:absolute;background-color:#fff;border:1px solid black;top:0;right:0;transform-origin:right center;transform:scale(.4)}.score{position:absolute;background-color:brown;top:20%;right:0}.eliminate{position:absolute;background-color:brown;top:30%;right:0}.regame{position:absolute;background-color:brown;top:40%;right:0}.restart{display:none}.pause{display:none;position:absolute;background-color:brown;top:50%;right:0} diff --git a/docs/index.html b/docs/index.html index 0f94c94..3b42a45 100644 --- a/docs/index.html +++ b/docs/index.html @@ -4,8 +4,8 @@ Document - - + +
diff --git a/package.json b/package.json index e967fe1..612ed06 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "description": "", "scripts": { "dev": "vite", - "prod": "vite build --minify=false --base=./ --outDir=docs", + "prod": "vite build --base=./ --outDir=docs", "typeCheck": "tsc --noEmit" }, "dependencies": { diff --git a/src/canvasWithMapCtx.ts b/src/canvasWithMapCtx.ts index 10f420f..ceadedb 100644 --- a/src/canvasWithMapCtx.ts +++ b/src/canvasWithMapCtx.ts @@ -14,21 +14,21 @@ export default class CanvasWithMapCtx implements ICanvasWithMapCtx { static bgCtx = bgCanvas.getContext("2d")! ctx: CanvasRenderingContext2D bgCtx: CanvasRenderingContext2D - mapBinary: number[] - bg: BrickColor[][] + mapBinary: ICanvasWithMapCtx["mapBinary"] + bg: ICanvasWithMapCtx["bg"] constructor() { this.ctx = CanvasWithMapCtx.ctx this.bgCtx = CanvasWithMapCtx.bgCtx this.mapBinary = new Array(gameParam.row).fill(0) as number[] this.bg = Array.from({ length: gameParam.row }, () => - Array.from({ length: gameParam.column }) + Array.from({ length: gameParam.column }, () => void 0) ) } cleanUpCanvas() { this.clearCanvas(this.ctx) this.clearCanvas(this.bgCtx) } - clearCanvas (ctx:CanvasRenderingContext2D) { + clearCanvas(ctx: CanvasRenderingContext2D) { ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height) } } diff --git a/src/draw/index.ts b/src/draw/index.ts index 080bed4..201942a 100644 --- a/src/draw/index.ts +++ b/src/draw/index.ts @@ -1,5 +1,4 @@ -import { gameParam } from "../gameConfig" -import { BrickColor, IBrick } from "../types/brick" +import { IBrick } from "../types/brick" import { drawBrickPiece } from "./drawBrickPiece" export const drawBrick = ( @@ -20,23 +19,4 @@ export const drawBrick = ( } } -export const drawBg = function ( - ctx: CanvasRenderingContext2D, - colors: BrickColor[][], - brickWidth: number = gameParam.brickWidth, - brickHeight: number = gameParam.brickHeight -) { - ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height) - for (let i = 0; i < colors.length; i++) { - for (let j = 0; j < colors[i].length; j++) { - if (colors[i][j] === void 0) continue - drawBrickPiece(ctx, { - x: j * brickWidth, - y: i * brickHeight, - width: brickWidth, - height: brickHeight, - color: colors[i][j], - } as IBrick) - } - } -} + diff --git a/src/gameHelper.ts b/src/gameHelper.ts index 9e7ad65..2f67704 100644 --- a/src/gameHelper.ts +++ b/src/gameHelper.ts @@ -1,10 +1,12 @@ import { bricks } from "./brick/brickConfig" +import { drawBrickPiece } from "./draw/drawBrickPiece" import { gameParam } from "./gameConfig" import { ICanvasWithMapCtx } from "./types" import { BrickLetter, IBrick } from "./types/brick" import { SinglePattern } from "./utils" class GameHelper { + private eliminateTheLine = 2 ** gameParam.column - 1 getRandomLetter(): BrickLetter { const letters = Object.keys(bricks) as BrickLetter[] return letters[(Math.random() * letters.length) >> 0] @@ -39,18 +41,21 @@ class GameHelper { */ eliminate( mapBinary: ICanvasWithMapCtx["mapBinary"], - bg: ICanvasWithMapCtx["bg"] + bg: ICanvasWithMapCtx["bg"], + from: number, + to: number ) { let count = 0 - for (let i = gameParam.row - 1; i >= 0; i--) { - if (mapBinary[i] === 2 ** gameParam.column - 1) { - count++ - mapBinary.splice(i, 1) + while (from < to) { + if (mapBinary[from] === this.eliminateTheLine) { + mapBinary.splice(from, 1) mapBinary.unshift(0) - bg.splice(i, 1) - bg.unshift(Array.from({ length: gameParam.column })) - i++ + bg.splice(from, 1) + bg.unshift(new Array(bg[0].length)) + count++ + continue } + from++ } return count } @@ -62,6 +67,26 @@ class GameHelper { } return false } + drawBg( + ctx: CanvasRenderingContext2D, + colors: ICanvasWithMapCtx["bg"], + brickWidth: number = gameParam.brickWidth, + brickHeight: number = gameParam.brickHeight + ) { + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height) + for (let i = 0; i < colors.length; i++) { + for (let j = 0; j < colors[i].length; j++) { + if (colors[i][j] === void 0) continue + drawBrickPiece(ctx, { + x: j * brickWidth, + y: i * brickHeight, + width: brickWidth, + height: brickHeight, + color: colors[i][j], + } as IBrick) + } + } + } computeScore(row: number) { switch (row) { case 0: diff --git a/src/main.ts b/src/main.ts index 6901a2c..b46fc66 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,5 @@ import Ui from "./ui"; -new Ui() \ No newline at end of file +const ui = new Ui() +console.log(ui); diff --git a/src/renderer.ts b/src/renderer.ts index a0e95ad..5c4c1bf 100644 --- a/src/renderer.ts +++ b/src/renderer.ts @@ -1,5 +1,4 @@ import { Brick } from "./brick" -import { drawBg } from "./draw/index" import { GameHelper, gameHelper } from "./gameHelper" import { userActions } from "./inputHandler" import Operation from "./operate" @@ -82,19 +81,27 @@ export default class Renderer implements IRenderer { } const row = this.gameHelper.eliminate( this.canvasWithMapCtx.mapBinary, + this.canvasWithMapCtx.bg, + this.brick.y, + Math.min( + this.brick.y + this.brick.structure.length, + this.canvasWithMapCtx.mapBinary.length + ) + ) + this.gameHelper.drawBg( + this.canvasWithMapCtx.bgCtx, this.canvasWithMapCtx.bg ) const score = this.gameHelper.computeScore(row) this.scorer.scoreIncrease(score) this.scorer.eliminateNumIncrease(row) - this.eventEmitter.emit("updateScore", this.scorer.score) - this.eventEmitter.emit("updateEliminate", this.scorer.eliminateNum) - drawBg(this.canvasWithMapCtx.bgCtx, this.canvasWithMapCtx.bg) this.brick = this.nextBrick this.nextBrick = new Brick(this.gameHelper.getRandomLetter(), time) - this.eventEmitter.emit("updateNextBrick", this.nextBrick) this.brick.correctLastTime(time) this.operation.takeTurns(this.brick) + this.eventEmitter.emit("updateScore", this.scorer.score) + this.eventEmitter.emit("updateEliminate", this.scorer.eliminateNum) + this.eventEmitter.emit("updateNextBrick", this.nextBrick) } private cachePauseTime(time: number) { this.pauseTime += time - this.lastTime diff --git a/src/types/index.ts b/src/types/index.ts index 446b546..e21f52d 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -29,7 +29,7 @@ export interface ICanvasWithMapCtx { bgCtx: CanvasRenderingContext2D cleanUpCanvas: () => void mapBinary: number[] - bg: BrickColor[][] + bg: (BrickColor | undefined)[][] } export interface PlayWithPause { @@ -53,7 +53,7 @@ export interface IGame extends PlayWithPause { export interface EmitterEvents { updateScore?: Function[] - updateEliminate?:Function[] + updateEliminate?: Function[] updateNextBrick?: Function[] gameOver?: Function[] }