Drop the Ball: 두 판 사이의 차이
imported>Bd3076 (새 문서: {{pluginX|script=사용자:Bd3076/Drop the Ball/plugin|name=물리 엔진 실험|creator=Bd3076}} {{Bd3076의 게임}} 분류:백괴게임) |
imported>Bd3076 편집 요약 없음 |
||
1번째 줄: | 1번째 줄: | ||
{{pluginX|script=사용자:Bd3076/Drop the Ball/plugin|name= | {{pluginX|script=사용자:Bd3076/Drop the Ball/plugin|name=Drop the Ball|creator=Bd3076}} | ||
{{Bd3076의 게임}} | {{Bd3076의 게임}} | ||
[[분류:백괴게임]] | [[분류:백괴게임]] |
2020년 5월 9일 (토) 13:03 판
var DB= window.localStorage; document.write('<div id="canvasArea"> </div> 단계를 선택할 때에는, 방향키 및 엔터만 사용하세요. (위쪽 방향키로 월드 선택 취소가 가능합니다.)<br /> 게임 중에는, 점선 위의 원하는 지역을 클릭해 공을 추가하세요. <br /> R키를 눌러 단계를 다시 시작할 수 있습니다. Q키로 단계에서 나갈 수 있습니다.<br />저장은 자동입니다.<br /><br /><br />'); var canvasArea = document.getElementById('canvasArea'); canvasArea.innerHTML = '<canvas id="myCanvas" width="720" height="480" style="border:1px solid #000000;"> </canvas>'; var canvas = document.getElementById("myCanvas"); var ctx = canvas.getContext("2d"); const restitution = 0.9; var abs = function(x) { if (x < 0) return -x; return x; }; var SQR = function(x) { return x * x; } var max = function(x, y){ if(x<y) return y; return x; } class Point { constructor(x, y) { this.x = x; this.y = y; } } var changeCoordinates = function(p, deg) { return new Point(p.x * Math.cos(deg) + p.y * Math.sin(deg), p.y * Math.cos(deg) - p.x * Math.sin(deg)); } class Sphere { constructor(x, y, r, movable) { this.type = 'Sphere'; this.center = new Point(x, y); this.r = r; this.movable = movable; this.vx = 0; this.vy = 0; this.exist = 1; if (movable) { this.mass = Math.PI * r * r; } else this.mass = 1e20; console.log(this.mass); } updateLocation() { if (!this.movable) return; if (this.center.x - this.r < 0) { this.vx = abs(this.vx) * restitution; this.center.x = this.r; } if (this.center.x + this.r > 720) { this.vx = -abs(this.vx) * restitution; this.center.x = 720 - this.r; } if (this.center.y - this.r < 0) { this.vy = abs(this.vy) * restitution; this.center.y = this.r; } if (this.center.y + this.r > 480) { this.vy = -abs(this.vy) * restitution; this.center.y = 480 - this.r; } this.vy += 0.98 * 1.7; // g = 9.8m/s^2 this.center.x += 0.01 * this.vx; this.center.y += 0.01 * this.vy; } drawInCanvas() { ctx.strokeStyle = "black"; ctx.setLineDash([]); ctx.beginPath(); ctx.arc(this.center.x, this.center.y, this.r, 0, 2 * Math.PI); if (!this.movable) { ctx.fillStyle = 'black'; ctx.fill(); } ctx.stroke(); } } class fixedRectangle { constructor(x, y, a, b, deg, func) { // a: 가로 길이의 반, b: 세로 길이의 반 this.type = "fixedRectangle"; this.center = new Point(x, y); this.size = new Point(a, b); this.deg = deg; this.mass = 1e20; this.movable = 0; this.exist = 1; this.func = func; // 0: normal, 1: die, 2: goal } drawInCanvas() { ctx.strokeStyle = "black"; ctx.setLineDash([]); var P1 = changeCoordinates(new Point(this.size.x, this.size.y), this.deg); var P2 = changeCoordinates(new Point(-this.size.x, this.size.y), this.deg); var P3 = changeCoordinates(new Point(-this.size.x, -this.size.y), this.deg); var P4 = changeCoordinates(new Point(this.size.x, -this.size.y), this.deg); ctx.beginPath(); ctx.moveTo(this.center.x + P1.x, this.center.y + P1.y); ctx.lineTo(this.center.x + P2.x, this.center.y + P2.y); ctx.lineTo(this.center.x + P3.x, this.center.y + P3.y); ctx.lineTo(this.center.x + P4.x, this.center.y + P4.y); ctx.lineTo(this.center.x + P1.x, this.center.y + P1.y); if(this.func == 0){ ctx.fillStyle = 'black'; } else if(this.func == 1){ ctx.fillStyle = 'red'; } else{ ctx.fillStyle = 'lime'; } ctx.fill(); ctx.stroke(); } } var stageData = []; var stageDataCnt = []; var stageLimit = []; var setStageData = function(){ for(var i=0; i<11; i++){ stageData.push([]); stageLimit.push(0); } // Stage 1-1 stageData.push([ new fixedRectangle(360, 400, 360, 80, 0, 2) ]); stageLimit.push(1); // Stage 1-2 stageData.push([ new fixedRectangle(360, 400, 80, 80, 0, 2) ]); stageLimit.push(1); // Stage 1-3 stageData.push([ new fixedRectangle(360, 400, 10, 10, 0, 2) ]); stageLimit.push(1); // Stage 1-4 stageData.push([ new fixedRectangle(100, 300, 50, 50, 0, 0), new fixedRectangle(300, 300, 50, 50, 0, 2) ]); stageLimit.push(2); // Stage 1-5 stageData.push([ new fixedRectangle(100, 300, 50, 50, 0, 1), new fixedRectangle(300, 300, 50, 50, 0, 2), new fixedRectangle(500, 300, 50, 50, 0, 0) ]); stageLimit.push(3); // stage 1-6 stageData.push([ new fixedRectangle(300, 200, 300, 30, 0, 0), new fixedRectangle(100, 380, 100, 100, 0, 2) ]); stageLimit.push(3); // stage 1-7 stageData.push([ new fixedRectangle(300, 200, 300, 30, 0, 0), new fixedRectangle(100, 380, 100, 100, 0, 2), new fixedRectangle(300, 430, 50, 50, 0, 1) ]); stageLimit.push(3); // stage 1-8 stageData.push([ new fixedRectangle(175, 200, 175, 30, 0, 0), new fixedRectangle(545, 200, 175, 30, 0, 0), new fixedRectangle(360, 400, 360, 80, 0, 2) ]); stageLimit.push(1); stageData.push([]); stageLimit.push(0); stageData.push([]); stageLimit.push(0); // stage 2-1 stageData.push([ new fixedRectangle(420, 120, 300, 10, 0, 0), new fixedRectangle(300, 300, 500, 10, -0.4, 0), new fixedRectangle(700, 440, 20, 20, 0, 2) ]); stageLimit.push(1); // stage 2-2 stageData.push([ new fixedRectangle(360, 300, 100, 100, Math.PI/4, 0), new fixedRectangle(-50, 420, 280, 10, 0, 0), new fixedRectangle(790, 420, 280, 10, 0, 0), new fixedRectangle(360, 480, 260, 10, 0, 2) ]); stageLimit.push(1); // stage 2-3 stageData.push([ new fixedRectangle(720, 270, 690, 170, 0, 0), new fixedRectangle(700, 460, 20, 20, 0, 2) ]); stageLimit.push(2); // stage 2-4 stageData.push([ new fixedRectangle(360, 300, 40, 40, 0, 2), new Sphere(280, 300, 40, 0), new Sphere(440, 300, 40, 0), new Sphere(360, 220, 40, 0), new fixedRectangle(360, 470, 360, 10, 0, 0) ]); stageLimit.push(1); // stage 2-5 stageData.push([ new Sphere(50, 400, 50, 0), new fixedRectangle(300, 130, 180, 10, 0, 1), new fixedRectangle(360, 470, 360, 10, 0, 1), new fixedRectangle(720, 130, 200, 10, 0, 1), new fixedRectangle(710, 220, 10, 70, 0, 2) ]); stageLimit.push(2); // stage 2-6 stageData.push([ new fixedRectangle(720, 130, 200, 10, 0, 1), new fixedRectangle(240, 130, 240, 10, 0, 1), new fixedRectangle(720, 470, 200, 10, 0, 1), new fixedRectangle(240, 470, 240, 10, 0, 1), new fixedRectangle(10, 220, 10, 70, 0, 2) ]); stageLimit.push(2); // stage 2-7 stageData.push([ new Sphere(40, 400, 40, 0), new fixedRectangle(360, 470, 360, 10, 0, 1), new fixedRectangle(720, 130, 630, 10, 0, 1), new fixedRectangle(500, 300, 5, 5, 0, 2) ]); stageLimit.push(1); // stage 2-5 stageData.push([ new Sphere(50, 400, 50, 0), new fixedRectangle(300, 130, 180, 10, 0, 1), new fixedRectangle(360, 470, 360, 10, 0, 1), new fixedRectangle(720, 130, 200, 10, 0, 1), new fixedRectangle(710, 220, 10, 60, 0, 2) ]); stageLimit.push(2); for(var i=0; i<stageData.length; i++){ stageDataCnt.push(stageData[i].length); } } setStageData(); var polygonList = []; var polygonCount = 0; var stageCleared = 0; var stageInterrupt = 0; var action = 0; // 0: selecting world, 1: selecting stage, 2: play var state = 0; // 0: main screen, xxy: world x - stage y const worldCount = 2; const stageCount = 8; var sphereCollision = function(a, b) { if (SQR(a.center.x - b.center.x) + SQR(a.center.y - b.center.y) > SQR(a.r + b.r)) return; //console.log('collide!'); var deg = Math.atan2(b.center.y - a.center.y, b.center.x - a.center.x); var colDist = (a.r + b.r) - Math.sqrt(SQR(a.center.x - b.center.x) + SQR(a.center.y - b.center.y)); if (a.movable) { a.center.x -= colDist / 2 * Math.cos(deg); a.center.y -= colDist / 2 * Math.sin(deg); } if (b.movable) { b.center.x += colDist / 2 * Math.cos(deg); b.center.y += colDist / 2 * Math.sin(deg); } var avt = a.vx * -Math.sin(deg) + a.vy * Math.cos(deg); var avc = a.vx * Math.cos(deg) + a.vy * Math.sin(deg); var bvt = b.vx * Math.sin(deg) + b.vy * -Math.cos(deg); var bvc = b.vx * -Math.cos(deg) + b.vy * -Math.sin(deg); var swapTmp; //console.log(avt + ' ' + avc + ' ' + bvt + ' ' + bvc); bvc = -bvc; var newAvc = (a.mass - b.mass) / (a.mass + b.mass) * avc; var newBvc = (b.mass - a.mass) / (a.mass + b.mass) * bvc; newAvc += (2 * b.mass) / (a.mass + b.mass) * bvc; newBvc += (2 * a.mass) / (a.mass + b.mass) * avc; newBvc = -newBvc; avc = newAvc; bvc = newBvc; //console.log(avt + ' ' + avc + ' ' + bvt + ' ' + bvc); var newAvx = avt * -Math.sin(deg) + avc * Math.cos(deg); var newAvy = avt * Math.cos(deg) + avc * Math.sin(deg); var newBvx = bvt * Math.sin(deg) + bvc * -Math.cos(deg); var newBvy = bvt * -Math.cos(deg) + bvc * -Math.sin(deg); a.vx = newAvx; a.vy = newAvy; b.vx = newBvx; b.vy = newBvy; } var sphereRectCollision = function(a, b) { // a: sphere, b: rectangle a.center.x -= b.center.x; a.center.y -= b.center.y; a.center = changeCoordinates(a.center, -b.deg); var aSpeed = changeCoordinates(new Point(a.vx, a.vy), -b.deg); a.vx = aSpeed.x; a.vy = aSpeed.y; if(b.func == 2){ if(-b.size.x <= a.center.x && a.center.x <= b.size.x && -b.size.y <= a.center.y && a.center.y <= b.size.y){ stageCleared = 1; } a.center = changeCoordinates(a.center, b.deg); a.center.x += b.center.x; a.center.y += b.center.y;aSpeed = changeCoordinates(new Point(a.vx, a.vy), b.deg); a.vx = aSpeed.x; a.vy = aSpeed.y; return; } if(b.func == 1){ if(-b.size.x <= a.center.x && a.center.x <= b.size.x && -b.size.y <= a.center.y && a.center.y <= b.size.y){ a.exist = 0; } else{ var _xabs = abs(a.center.x); var _yabs = abs(a.center.y); if(_xabs<=b.size.x && _yabs <= b.size.y+a.r) a.exist=0; else if(_xabs<=b.size.x+a.r&&_yabs<=b.size.y)a.exist=0; else if(!(_xabs > b.size.x+a.r && _yabs > b.size.y+a.r)){ var _xDiff = _xabs - b.size.x; var _yDiff = _yabs - b.size.y; if(SQR(_xDiff) + SQR(_yDiff) <= SQR(a.r)) a.exist=0; } } a.center = changeCoordinates(a.center, b.deg); a.center.x += b.center.x; a.center.y += b.center.y;aSpeed = changeCoordinates(new Point(a.vx, a.vy), b.deg); a.vx = aSpeed.x; a.vy = aSpeed.y; return; } //console.log(a.center.x + ' ' + a.center.y); //console.log(-b.size.x-a.r); if(a.center.x < -b.size.x-a.r || a.center.x > b.size.x+a.r || a.center.y < -b.size.y-a.r || a.center.y > b.size.y+a.r){ a.center = changeCoordinates(a.center, b.deg); a.center.x += b.center.x; a.center.y += b.center.y;aSpeed = changeCoordinates(new Point(a.vx, a.vy), b.deg); a.vx = aSpeed.x; a.vy = aSpeed.y; return; } if(a.center.x < -b.size.x){ if(a.center.y < -b.size.y){ var xDiff = -b.size.x - a.center.x; var yDiff = -b.size.y - a.center.y; if(SQR(xDiff) + SQR(yDiff) >= SQR(a.r)){ a.center = changeCoordinates(a.center, b.deg); a.center.x += b.center.x; a.center.y += b.center.y; aSpeed = changeCoordinates(new Point(a.vx, a.vy), b.deg); a.vx = aSpeed.x; a.vy = aSpeed.y; return; } sphereCollision(a, new Sphere(-b.size.x, -b.size.y, 1e-2, 0)); } else if(a.center.y > b.size.y){ var xDiff = -b.size.x - a.center.x; var yDiff = a.center.y - b.size.y; if(SQR(xDiff) + SQR(yDiff) >= SQR(a.r)){ a.center = changeCoordinates(a.center, b.deg); a.center.x += b.center.x; a.center.y += b.center.y; aSpeed = changeCoordinates(new Point(a.vx, a.vy), b.deg); a.vx = aSpeed.x; a.vy = aSpeed.y; return; } sphereCollision(a, new Sphere(-b.size.x, b.size.y, 1e-2, 0)); } else{ a.x = -b.size.x - a.r; a.vx = -abs(a.vx); } } else if(a.center.x > b.size.x){ if(a.center.y < -b.size.y){ var xDiff = a.center.x - b.size.x; var yDiff = -b.size.y - a.center.y; if(SQR(xDiff) + SQR(yDiff) >= SQR(a.r)){ a.center = changeCoordinates(a.center, b.deg); a.center.x += b.center.x; a.center.y += b.center.y; aSpeed = changeCoordinates(new Point(a.vx, a.vy), b.deg); a.vx = aSpeed.x; a.vy = aSpeed.y; return; } sphereCollision(a, new Sphere(b.size.x, -b.size.y, 1e-2, 0)); } else if(a.center.y > b.size.y){ var xDiff = a.center.x - b.size.x; var yDiff = a.center.y - b.size.y; if(SQR(xDiff) + SQR(yDiff) >= SQR(a.r)){ a.center = changeCoordinates(a.center, b.deg); a.center.x += b.center.x; a.center.y += b.center.y; aSpeed = changeCoordinates(new Point(a.vx, a.vy), b.deg); a.vx = aSpeed.x; a.vy = aSpeed.y; return; } sphereCollision(a, new Sphere(b.size.x, b.size.y, 1e-2, 0)); } else{ a.x = b.size.x + a.r; a.vx = abs(a.vx); } } else{ if(a.center.y > 0){ a.y = b.size.y + a.r; a.vy = abs(a.vy); a.center.y += a.vy * 0.01; } else{ a.y = -b.size.y - a.r; a.vy = -abs(a.vy); a.center.y += a.vy * 0.01; } } aSpeed = changeCoordinates(new Point(a.vx, a.vy), b.deg); a.vx = aSpeed.x; a.vy = aSpeed.y; a.center = changeCoordinates(a.center, b.deg); a.center.x += b.center.x; a.center.y += b.center.y; } canvas.addEventListener("click", function(e) { if(action != 2){ return; } var _st = (cursorX*8+cursorY+1)*10+cursorStage+1; var x = e.layerX; var y = e.layerY; if (y > 100 || polygonCount >= stageLimit[_st]) return; for (var i = 0; i < polygonList.length; i++) { if (polygonList[i].type == 'Sphere' && SQR(polygonList[i].center.x - x) + SQR(polygonList[i].center.y - y) < SQR(polygonList[i].r + 10)) return; } polygonCount++; polygonList.push(new Sphere(x, y, 10, 1)); }); var makeField = function() { ctx.clearRect(0, 0, 720, 480); ctx.setLineDash([5, 3]); ctx.strokeStyle = 'gray'; ctx.beginPath(); ctx.moveTo(0, 100); ctx.lineTo(720, 100); ctx.stroke(); ctx.setLineDash([]); var _st = (cursorX*8+cursorY+1)*10+cursorStage+1; ctx.fillStyle = "black"; ctx.textAlign = "end"; ctx.textBaseline = "top"; ctx.fillText(polygonCount + ' / ' + stageLimit[_st], 720, 0); ctx.textAlign = "center"; ctx.textBaseline = "middle"; } var cursorX = 0; var cursorY = 0; var cursorStage = 0; function updateLocations() { makeField(); for (var i = 0; i < polygonList.length; i++) { if (polygonList[i].movable) polygonList[i].updateLocation(); polygonList[i].drawInCanvas(); } for (var i = 0; i < polygonList.length; i++) { for (var j = i + 1; j < polygonList.length; j++) { if (polygonList[i].movable || polygonList[j].movable) { if (polygonList[i].type == 'Sphere' && polygonList[j].type == 'Sphere') { sphereCollision(polygonList[i], polygonList[j]); } else if (polygonList[i].type == 'fixedRectangle' && polygonList[j].type == 'Sphere') { sphereRectCollision(polygonList[j], polygonList[i]); } else if (polygonList[i].type == 'Sphere' && polygonList[j].type == 'fixedRectangle') { sphereRectCollision(polygonList[j], polygonList[j]); } } } } for(var i=0; i<polygonList.length; i++){ if(polygonList[i].exist == 0){ polygonList.splice(i, 1); i--; } } if(stageInterrupt) console.log(stageInterrupt); if(stageInterrupt == 1){ console.log('INTERRUPT!'); return; } if(!stageCleared) setTimeout(updateLocations, 10); else{ var _worldCnt = cursorX*8+cursorY+1; var _tmp = 1; if(cursorStage == 7) _tmp = 3; DB.setItem('stage', max(DB.getItem('stage'), _worldCnt * 10 + cursorStage + _tmp)); stageCleared = 0; console.log(stageData[11]); action = 1; setTimeout(drawMainScreen, 1000); } } var drawMainScreen = function(){ ctx.clearRect(0, 0, 720, 480); stageInterrupt = 0; ctx.fillStyle = "black"; ctx.font = "40px sans-serif"; ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.fillText("DROP THE BALL", 360, 50); for(var i=0; i<4; i++){ for(var j=0; j<8; j++){ var _worldCnt = i*8+j+1; if(_worldCnt > worldCount) break; if(cursorX == i && cursorY == j) ctx.strokeStyle = "lime"; else ctx.strokeStyle = "black"; ctx.strokeRect(30+86.25*j, 120+86.25*i, 56.25, 56.25); ctx.font = "12px sans-serif"; ctx.fillStyle = "black"; ctx.fillText("World " + _worldCnt, 59+86.25*j, 160+86.25*i); var _st = DB.getItem('stage'); if(_st >= _worldCnt * 10 + 8){ ctx.fillStyle = "blue"; ctx.fillText("CLEAR", 59+86.25*j, 135+86.25*i); } else if(_st >= (_worldCnt-1) * 10 + 8){ ctx.fillStyle = "green"; ctx.fillText("NOW", 59+86.25*j, 135+86.25*i); } else{ ctx.fillStyle = "red"; ctx.fillText("LOCKED", 59+86.25*j, 135+86.25*i); } } } if(action == 1){ var curWorld = cursorX*8+cursorY+1; for(var j=0; j<8; j++){ if(cursorStage == j) ctx.strokeStyle = "lime"; else ctx.strokeStyle = "black"; ctx.strokeRect(30+86.25*j, 400, 56.25, 56.25); ctx.font = "12px sans-serif"; ctx.fillStyle = "black"; ctx.fillText("Stage " + (j+1), 59+86.25*j, 440); var _st = DB.getItem('stage'); if(_st >= curWorld * 10 + j + 1){ ctx.fillStyle = "blue"; ctx.fillText("CLEAR", 59+86.25*j, 415); } else if(_st == curWorld * 10 + j){ ctx.fillStyle = "green"; ctx.fillText("NOW", 59+86.25*j, 415); } else{ ctx.fillStyle = "red"; ctx.fillText("LOCKED", 59+86.25*j, 415); } } } } window.onkeydown = function() { var _key = event.keyCode; //console.log(_key); if(action == 2){ if(_key == 82){ var _worldCnt = cursorX*8+cursorY+1; stageInterrupt = 1; setTimeout(stageStart, 30, _worldCnt * 10 + cursorStage + 1); } else if(_key == 81){ polygonList = []; action = 1; stageInterrupt = 1; setTimeout(drawMainScreen, 30); } } else if(action == 0){ if(_key == 37){ if(cursorY){ cursorY--; drawMainScreen(); } } else if(_key == 38){ if(cursorX){ cursorX--; drawMainScreen(); } } else if(_key == 39){ var _cnt = cursorX*8+cursorY+2; if(_cnt > worldCount || cursorY==7) return; cursorY++; drawMainScreen(); } else if(_key == 40){ var _worldCnt = cursorX*8+cursorY+9; if(_worldCnt > worldCount) return; cursorX++; drawMainScreen(); } else if(_key == 13){ var _worldCnt = cursorX*8+cursorY+1; if(DB.getItem('stage') >= (_worldCnt-1) * 10 + 8){ action = 1; cursorStage = 0; drawMainScreen(); } } } else if(action == 1){ if(_key == 37){ if(cursorStage){ cursorStage--; drawMainScreen(); } } else if(_key == 39){ if(cursorStage < 7){ cursorStage++; drawMainScreen(); } } else if(_key == 13){ var _worldCnt = cursorX*8+cursorY+1; if(DB.getItem('stage') < _worldCnt * 10 + cursorStage){ return; } action = 2; stageStart(_worldCnt * 10 + cursorStage + 1); } else if(_key == 38){ action = 0; drawMainScreen(); } } } var gameStart = function(){ state = 0; canAddObjects = 0; action = 0; cursorX = cursorY = 0; if(DB.getItem("stage") == null){ DB.setItem("stage", 10); } else if(DB.getItem("stage")%10 >= 8){ DB.setItem("stage", DB.getItem("stage") + (10-(DB.getItem("stage")%10))); } drawMainScreen(); } var stageStart = function(stage){ makeField(); action = 2; polygonList = stageData[stage]; while(polygonList.length > stageDataCnt[stage]){ polygonList.pop(); } polygonCount = 0; stageCleared = 0; stageInterrupt = 0; updateLocations(); } gameStart();