打ち出したボールが何かに衝突して跳ね返るみたいなコードを探す。
究極かもしれないと思えるサンプルコードを発見!
endo blog: HTML5 – Canvas 円同士の衝突アニメーション
しかし例のごとく setInterval で clearInterval していない。
無限に増殖する円が延々衝突し続けるという究極の CPU 殺しでもあった。
一生懸命自己解析していたら CPU ファンが凄い勢いで回り初めて焦った。
もしファンの無いスマホなんかで見ていたら…
しかたがないので停止や初期化のボタンを付けてコピペ。
TimerClass をインスタンス化したら start(func), stop() メソッドで簡単に使えるようにしてみた。
それをスマートフォン用にレイアウト、のつもり。
肝心のアルゴリズムはコピペです、すんません。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>球の衝突(負荷強)</title> <!-- for Smart Phone --> <meta name="viewport" content=" width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <style> body { margin: 0; overflow: hidden; -webkit-text-size-adjust: 100%; } img { margin: 0; padding: 0; overflow: hidden; } #ID_CANVAS { background-color: #FFFFFF; } </style> <script type="text/javascript"><!-- var canvas = null; var context = null; var timer = null; var cw = 0; var ch = 0; var g = 0.1; const INTERVAL = 10; var circles = []; var colorList = ['0','1','2','3','4','5','6','7', '8','9','a','b','c','d','e','f']; var TimerClass = function() { this.count = 0; this.id = -1; this.start = function(func) { if (this.id == -1) { this.id = setInterval(func, INTERVAL); } } this.stop = function() { if (this.id > -1) { clearInterval(this.id); this.id = -1; } } } var onTimer = function() { context.clearRect(0, 0, cw, ch); if (timer.count>100 || timer.count==0) { timer.count = 0; circles.push(new Circle()); circles[circles.length-1].init(circles.length % 2); } for (var i=0; i<circles.length; i++) { for(var j=i+1; j<circles.length; j++) { collisionCircleCircle(circles[i], circles[j]); } } for (var i=0; i<circles.length; i++) { circles[i].move(); circles[i].collisionWall(); circles[i].view(); } timer.count++; } function Circle(){ this.h = 0.9; this.x = 0; this.y = 100; this.vx = 0; this.vy = 0; this.r = 0; this.color = '#'; this.init = function(vec) { this.vx = Math.random() * 9 + 1; this.r = Math.random() * 25 + 5; if (vec) { this.x = cw + 100; this.vx *= -1; } else { this.x = -100; } this.color += colorList[Math.floor(Math.random() * 3) + 3]; this.color += colorList[Math.floor(Math.random() * 5) + 5]; this.color += colorList[Math.floor(Math.random() * 7) + 9]; } this.move = function() { this.x += this.vx; this.y += this.vy; this.vy += g; } this.view = function() { context.beginPath(); context.fillStyle = this.color; context.arc(this.x, this.y, this.r, 0, 360, false); context.fill(); } this.collisionWall = function() { if (this.x+this.r > cw && this.vx > 0 || this.x-this.r < 0 && this.vx < 0) { this.vx *= -this.h; } else if (this.y+this.r > ch && this.vy > 0 || this.y-this.r < 0 && this.vy < 0) { this.vy *= -this.h; } } } function collisionCircleCircle(a,b){ if( (b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y) < (a.r+b.r)*(a.r+b.r) ){ var vx = a.x-b.x; var vy = a.y-b.y; var len = Math.sqrt(vx*vx+vy*vy); var d = a.r+b.r-len; if (len>0) len =1/len; vx *= len; vy *= len; d /= 2.0; a.x += vx*d; a.y += vy*d; b.x -= vx*d; b.y -= vy*d; var t; t = -(vx*a.vx+vy*a.vy)/(vx*vx+vy*vy); var arx = a.vx+vx*t; var ary = a.vy+vy*t; t = -(-vy*a.vx+vx*a.vy)/(vy*vy+vx*vx); var amx = a.vx-vy*t; var amy = a.vy+vx*t; t = -(vx*b.vx+vy*b.vy)/(vx*vx+vy*vy); var brx = b.vx+vx*t; var bry = b.vy+vy*t; t = -(-vy*b.vx+vx*b.vy)/(vy*vy+vx*vx); var bmx = b.vx-vy*t; var bmy = b.vy+vx*t; var e = 0.8; var adx = (a.r*amx+b.r*bmx+bmx*e*b.r-amx*e*b.r)/(a.r+b.r); var bdx = -e*(bmx-amx)+adx; var ady = (a.r*amy+b.r*bmy+bmy*e*b.r-amy*e*b.r)/(a.r+b.r); var bdy = -e*(bmy-amy)+ady; a.vx = adx+arx; a.vy = ady+ary; b.vx = bdx+brx; b.vy = bdy+bry; } } var init = function() { if (window.TouchEvent) { cw = window.innerWidth; ch = window.innerHeight - 60; timer = new TimerClass(); // Create Canvas and Context canvas = document.getElementById("ID_CANVAS"); canvas.width = cw; canvas.height = ch; context = canvas.getContext("2d"); // Create ToolBar var back = document.getElementById("ID_BACK"); back.style.top = ch + "px"; back.style.left = 0 + "px"; back.addEventListener("touchend", function() { document.location = "."; }); var start = document.getElementById("ID_START"); start.style.top = ch + "px"; start.style.left = 80 + "px"; start.addEventListener("touchend", function() { timer.start(onTimer); }); var stop = document.getElementById("ID_STOP"); stop.style.top = ch + "px"; stop.style.left = 160 + "px"; stop.addEventListener("touchend", function() { timer.stop(); }); var clear = document.getElementById("ID_CLEAR"); clear.style.top = ch + "px"; clear.style.left = 240 + "px"; clear.addEventListener("touchend", function() { timer.stop(); circles = []; context.clearRect(0, 0, cw, ch); }); } } //--> </script> </head> <body onLoad="init()"> <canvas id="ID_CANVAS"></canvas> <img id="ID_BACK" src="button1/back.png" style="position:absolute"> <img id="ID_START" src="button1/start.png" style="position:absolute"> <img id="ID_STOP" src="button1/stop.png" style="position:absolute"> <img id="ID_CLEAR" src="button1/clear.png" style="position:absolute"> </body> </html>
はっきりいって何がどうなってこうなるかよくワカンネェ!
重力で落下するみたいな処理も多分入っているよな、どこだか解らないけど。
何日かチマチマ弄っていればそのうち少しは理解できる、といいな。
今回は覚書とタイマーの簡単な利用方法、ということで。
それにしても神様、私にもっと絵心というものを下さい。
ボタンが我ながらダサすぎる、今後の課題はイラストかも。