캐논볼과슬링샷 류관희
배우는내용 포탄의탄도시뮬레이션 포탄 포물선운동 수평변위 수직변위 ( 중력의영향 ) 포탄이지면혹은목표물에닿으면정지 2
주요내용 포탄의기본탄도시뮬레이션 포탄발사 ( 호를그리며이동, 입력 : 수평속도, 수직속도 폼입력필드 ) 포탄이지면혹은목표물에닿으면정지 캐논볼 (cannon ball) 사각형대포 ( 특정각도로기울어짐 ) 매개변수 : 대포에서발사하는속도, 각도 슬링샷 (slingshot) 새총막대에묶인공 공의속도 : 새총에있는공에서부터한지점까지의거리 각도 : 새총의그지점까지의수평선에서부터형성된각도 3
대포가없는기본탄도프로그램 4
대포가있는캐논볼프로그램 5
대포가있는캐논볼프로그램 6
슬링샷 7
공통구현사항 일정시간마다이벤트설정후공의애니메이션작동하고공을재배치 공의포물선운동시뮬레이션 수평변위 (dx): dx=horvelocity( 변하지않음 ) 수직변위 (dy): 가속량 ( 중력 ): gravity 구간처음의수직속도 : verticalvel1 구간끝의수직속도 : verticalvel2=verticalvel1+gravity dy=(verticalvel1+verticalvel2)/2 목표물에닿았을때목표물대신다른그림으로대체하는기능 8
캐논볼 초기값 대포발사속도 대포발사각도 대포의회전 캐논볼구현 9
슬링샷구현 슬링샷 마우스버튼을누른채새총고무줄에붙어있는공을끌어당긴후마우스버튼을놓으면공을발사 새총머리부터공까지의거리와각도를통한공의움직임계산 10
Form 관리 <form name="f" id="f" onsubmit="return fire();"> 속도, 각도를지정하고대포를발사 <br/> 대포에서발사되는속도 <input name="vo" id="vo" value="10" type="number" min="-100" max="100" /> <br> 각도 <input name="ang" id="ang" value="0" type="number" min="0" max="80"/> <input type="submit" value=" 발사 "/> </form> 11
객체관리 캔버스에그려질객체 ( 항목의모음이나세트와같은개념 ) 객체 속성과메서드 Ball, Picture, Myrectangle, Sling 속성 : draw 메서드정의와위치, 크기를설정하는속성 메서드 : 회전 12
everything 객체 everything (var everything = [];) var target = new Myrectangle(300,100,80,200,"rgb(0,5,90)") var ground = new Myrectangle(0,300,600,30,"rgb(10,250,0) everything.push(target); everything.push(ground); everything.push(cball); 13
Ball 객체 function Ball(sx,sy,rad,stylestring) { this.sx = sx; this.sy = sy; this.rad = rad; this.draw = drawball; this.moveit = moveball; this.fillstyle = stylestring; function moveball(dx,dy) { this.sx +=dx; this.sy +=dy; function drawball() { ctx.fillstyle=this.fillstyle; var cball = new ctx.beginpath(); Ball(iballx,ibally,10,"rgb(250,0,0)"); // ctx.fillstyle= rgb(0,0,0); ctx.arc(this.sx,this.sy,this.rad,0,math.pi*2,true); ctx.fill(); 14
Myrectangle 객체 function Myrectangle(sx,sy,swidth,sheight,stylestring) { this.sx = sx; this.sy = sy; this.swidth = swidth; this.sheight = sheight; this.fillstyle = stylestring; this.draw = drawrects; this.moveit = moveball; function drawrects() { ctx.fillstyle = this.fillstyle; ctx.fillrect(this.sx,this.sy,this.swidth,this.sheight); var target = new Myrectangle(300,100,80,200,"rgb(0,5,90)"); var ground = new Myrectangle(0,300,600,30,"rgb(10,250,0)"); 15
Picture 객체 everything (var everything = [];) function drawanimage() { ctx.drawimage(this.img,this.sx, this.sy,this.swidth,this.sheight); function Picture (sx,sy,swidth,sheight,filen) { var imga = new Image(); imga.src=filen; this.sx = sx; this.sy = sy; this.img = imga; this.swidth = swidth; this.sheight = sheight; this.draw = drawanimage; this.moveit = moveball; 16
everything 객체 (1) everything (var everything = [];) var cball = new Ball(iballx,ibally,10,"rgb(250,0,0)"); var target = new Picture(targetx,targety,targetw,targeth,"hill.j pg"); var htarget = new Picture(htargetx, htargety, htargetw, htargeth, "plateau.jpg"); var ground = new Myrectangle(0,300,600,30,"rgb(10,250,0)"); var cannon = new Myrectangle(cannonx,cannony,cannonleng th,cannonht,"rgb(40,40,0)"); 17
everything 객체 (2) var targetindex = everything.length; everything.push([target,false]); everything.push([ground,false]); var ballindex = everything.length; everything.push([cball,false]); var cannonindex = everything.length; // 나중사용을위해저장 everything.push([cannon,true,0,cannonx,cannony+cannonht*.5]); // 나중에회전을지정 18
대포각도에따른대포의 위치변화 19
사각형그리기 (1) <html> <head> <title> 사각형 </title> <script type="text/javascript"> var ctx; function init(){ ctx = document.getelementbyid('canvas').getcontext('2d'); ctx.fillstyle = "rgb(250,0,0)"; ctx.fillrect(50,50,100,200); ctx.fillstyle = "rgb(0,0,250)"; ctx.fillrect(50,50,5,5); </script> </head> <body onload="init();"> <canvas id="canvas" width="400" height="300"> 이브라우저에서는 HTML5 의 canvas 요소가지원되지않습니다. </canvas> </body> </html> 20
사각형그리기 (2) <html> <head> <title> 사각형 </title> <script type="text/javascript"> var ctx; function init(){ ctx = document.getelementbyid('canvas').getcontext('2d'); ctx.fillstyle = "rgb(250,0,0)"; ctx.rotate(-math.pi/6); ctx.fillrect(50,50,100,200); ctx.rotate(math.pi/6); ctx.fillstyle = "rgb(0,0,250)"; ctx.fillrect(50,50,5,5); </script> </head> rotate 회전의기준점 :0,0 라디안단위의시계방향 <body onload="init();"> <canvas id="canvas" width="400" height="300"> 이브라우저에서는 HTML5 의 canvas 요소가지원되지않습니다. </canvas> </body> </html> 21
사각형그리기 (3) <html> <head> <title> 사각형 </title> <script type="text/javascript"> var ctx; function init(){ ctx = document.getelementbyid('canvas').getcontext('2d'); ctx.fillstyle = "rgb(250,0,0) ; ctx.save(); // 현재의좌표계 ( 위치와축방향저장 ) ctx.translate(50,50); ctx.rotate(-math.pi/6); ctx.translate(-50,-50); ctx.fillrect(50,50,100,200); ctx.restore(); // 저장한좌표계의복원 ctx.fillstyle = "rgb(0,0,250)"; ctx.fillrect(50,50,5,5); </script> </head> <body onload="init();"> <canvas id="canvas" width="400" height="300"> 이브라우저에서는 HTML5 의 canvas 요소가지원되지않습니다. </canvas> </body> Translate 평행이동 22
공의움직임 (1) <body onload="init();"> <canvas id="canvas" width="600" height="400"> 이브라우저에서는 HTML5 의 canvas 요소가지원되지않습니다. </canvas> <br/> <form name="f" id="f" onsubmit="return fire();"> 속도, 각도를지정하고대포를발사 <br/> 대포에서발사되는속도 <input name="vo" id="vo" value="10" type="number" min="-100" max="100" /> <br> 각도 <input name="ang" id="ang" value="0" type="number" min="0" max="80"/> <input type="submit" value=" 발사 "/> </form> </body> function init(){ ctx = document.getelementbyid('canvas').getco ntext('2d'); drawall(); 23
공의움직임 (2) function drawall() { ctx.clearrect(0,0,cwidth,cheight); var i; for (i=0;i<everything.length;i++) { var ob = everything[i]; if (ob[1]) { // 평행이동과회전에필요 ctx.save(); ctx.translate(ob[3],ob[4]); ctx.rotate(ob[2]); ctx.translate(-ob[3],-ob[4]); ob[0].draw(); ctx.restore(); else { ob[0].draw(); 24
공의움직임 (3) function fire() { var angle = Number(document.f.ang.value); var outofcannon = Number(document.f.vo.value); var angleradians = angle*math.pi/180; horvelocity = outofcannon*math.cos(angleradians); verticalvel1 = - outofcannon*math.sin(angleradians); everything[cannonindex][2]= - angleradians; cball.sx = cannonx + cannonlength*math.cos(angleradians); cball.sy = cannony+cannonht*.5 - cannonlength*math.sin(angleradians); drawall(); tid = setinterval(change,100); return false; 25
공의움직임 (4) function change() { var dx = horvelocity; verticalvel2 = verticalvel1 + gravity; var dy = (verticalvel1 + verticalvel2)*.5; verticalvel1 = verticalvel2; cball.moveit(dx,dy); // 목표물에닿는지검사 var bx = cball.sx; var by = cball.sy; if ((bx>=target.sx)&&(bx<=(target.sx+target.swidth))&& (by>=target.sy)&&(by<=(target.sy+target.sheight))) { clearinterval(tid); // target 을제거하고 htarget 을삽입 everything.splice(targetindex,1,[htarget,false]); everything.splice(ballindex,1); drawall(); // 공이지면영역을벗어났는지검사 if (by>=ground.sy) { clearinterval(tid); drawall(); 26
대포발사결과 27
슬링샷 28
var everything = []; 슬링샷 var target = new Picture(700,210,209,179,chicken); var ground = new myrectangle(0,370,1200,30,"rgb(10,250,0)"); var cball = new Ball(startrockx,startrocky,ballrad,"rgb(250,0,0)"); var mysling= new Sling(startrockx,startrocky,startrockx+80,startrocky- 10,startrockx+80,startrocky+10,startrockx+70,startrocky+180,"rgb(120,20,10)"); everything.push(target); everything.push(ground); everything.push(mysling); everything.push(cball); 29
var everything = []; 슬링샷 function drawall() { // drawall 함수는캔버스전체를지운후, everything 배열의모든원소를그림 ctx.clearrect(0,0,cwidth,cheight); var i; for (i=0;i<everything.length;i++) { everything[i].draw(); 30
새총그리기 (1) (bx,by) (s1x,s1y) (s3x,s3y) (s2x,s2y) function Sling(bx,by,s1x,s1y,s2x,s2y,s3x,s3y,stylestring) { this.bx = bx; this.by = by; this.s1x = s1x; this.s1y = s1y; this.s2x = s2x; this.s2y = s2y; this.s3x = s3x; this.s3y = s3y; this.strokestyle = stylestring; this.draw = drawsling; this.moveit = movesling; 31
새총그리기 (2) (bx,by) (s1x,s1y) (s2x,s2y) function drawsling() { ctx.strokestyle = this.strokestyle; ctx.linewidth = 4; ctx.beginpath(); ctx.moveto(this.bx,this.by); ctx.lineto(this.s1x,this.s1y); ctx.moveto(this.bx,this.by); ctx.lineto(this.s2x,this.s2y); (s3x,s3y) ctx.moveto(this.s1x,this.s1y); ctx.lineto(this.s2x,this.s2y); ctx.lineto(this.s3x,this.s3y); ctx.stroke(); 32
마우스의작동 function init(){ ctx = document.getelementbyid('canvas').getcontext('2d'); canvas1 = document.getelementbyid('canvas'); canvas1.addeventlistener('mousedown',findball,false); canvas1.addeventlistener('mousemove',moveit,false); canvas1.addeventlistener('mouseup',finish,false); // 처음그리기 drawall(); <body onload="init();"> <canvas id="canvas" width="1200" height="600"> 이브라우저에서는 HTML5 의 canvas 요소가지원되지않습니다. </canvas> <br/> 마우스를누른채탄알을드래그하세요. 마우스버튼을놓으면새총이발사됩니다. 새총은최종위치에그대로있게됩니다. 게임을다시하려면페이지를새로고침하세요. </body> 33
마우스에의한볼의선택 function findball(ev) { var mx; var my; if ( ev.layerx ev.layerx == 0) { // 파이어폭스, 크롬 mx= ev.layerx; my = ev.layery; else if (ev.offsetx ev.offsetx == 0) { // 오페라 mx = ev.offsetx; my = ev.offsety; if (distsq(mx,my, cball.sx,cball.sy)<ballradsq) { inmotion = true; drawall(); // 거리의세제곱을계산에이용 function distsq(x1,y1,x2,y2) { return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2); 34
마우스에의한선택한볼의 움직임 (bx,by) (s1x,s1y) (s3x,s3y) (s2x,s2y) // 탄알드래그와 mysling 변경을위한함수 function moveit(ev) { var mx; var my; if (inmotion) { if ( ev.layerx ev.layerx == 0) { // 파이어폭스 mx= ev.layerx; my = ev.layery; else if (ev.offsetx ev.offsetx == 0) { // 오페라 mx = ev.offsetx; my = ev.offsety; cball.sx = mx; cball.sy = my; mysling.bx = mx; mysling.by = my; drawall(); 35
마우스에의해결정된볼의속도 function finish(ev) { // 마우스에서손을뗄때, 탄알과 mysling 이드래그돼있다면, // 탄알이포물선을그리며운동하게끔지정 if (inmotion) { inmotion = false; // 최초속도를길이만큼증가시켜야함, 편의상정사각형으로만듬 // 700 은그럴싸한호가형성되도록임의로정한값입니다. var outofcannon = distsq(mysling.bx,mysling.by,mysling.s1x,mysling.s1y)/700; // 선간격 bx, by, s1x, s1y, 새총의상박을토대로구한각도를이용 var angleradians = -Math.atan2(mysling.s1y-mysling.by,mysling.s1x-mysling.bx); horvelocity = outofcannon*math.cos(angleradians); verticalvel1 = - outofcannon*math.sin(angleradians); drawall(); tid = setinterval(change,100); 36
공의애니메이션 function change() { // 이함수는새총으로부터목표물이나지면까지의탄알의움직임을형성함 var dx = horvelocity; verticalvel2 = verticalvel1 + gravity; var dy = (verticalvel1 + verticalvel2)*.5; verticalvel1 = verticalvel2; cball.moveit(dx,dy); // 목표물에닿았는지검사 var bx = cball.sx; var by = cball.sy; // 목표물의내부를검사 // 타격영역을좁히기위해경계를 40 씩조정 if ((bx>=target.sx+40)&&(bx<=(target.sx+target.swidth-40))&& (by>=target.sy+40)&&(by<=(target.sy+target.sheight-40))) { // clearinterval(tid); // 기존이미지를깃털이미지로대체 target.img = feathers; // 지면영역을벗어났는지검사지면영역을벗어났는지검사 if (by>=ground.sy) { clearinterval(tid); drawall(); 37
슬링샷결과 38
표시항목변경을위한배열의 splice 함수 원소를몇개든제거할수도있고제거한후삽입할수있음 everything.splice(targetindex, 1, [htarget, false]); everything.splice(ballindex, 1); 39