A Pen by Mark Cidade on CodePen.
Last active
April 18, 2017 00:34
-
-
Save markrockcity/12b066b0b00b592bc31e2d79a74742e7 to your computer and use it in GitHub Desktop.
"Worms"
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <button id='playpause' title='play/pause'>❚❚</button> | |
| <button id='clear' title='clear'>X</button> | |
| <br/> | |
| <canvas id="canvas"></canvas> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| var canvas = document.getElementById("canvas"); | |
| var ctx = canvas.getContext("2d"); | |
| var playPauseButton = document.getElementById("playpause"); | |
| var clearButton = document.getElementById("clear"); | |
| canvas.height = window.innerHeight * 0.94; | |
| canvas.width = window.innerWidth * 0.95; | |
| var ww = 100; //grid width | |
| var hh = 100; //grid height | |
| var w = (canvas.width / ww); //grid square width | |
| var h = (canvas.height / hh); //grid square height | |
| ///////////////////////////////////// | |
| var text = "X"; | |
| //when clicking on canvas: | |
| canvas.addEventListener("click", event => | |
| { | |
| if (t) | |
| return; | |
| //the last worm in the list: | |
| var cworms = worms[worms.length-1]; | |
| cworms[0].x = (event.pageX-canvas.offsetLeft) / w; | |
| cworms[0].y = (event.pageY-canvas.offsetTop) / h; | |
| cworms[0].color.a = 1; | |
| cworms[0].color.r = 255; | |
| cworms[0].color.g=0; | |
| cworms[0].color.b=0; | |
| cworms[0].vx = 0; | |
| cworms[0].vy = 0; | |
| cworms[0].width = w*2; | |
| }, | |
| false); | |
| //middle | |
| var mh = hh/2, mw = ww/2; | |
| //W[orm]() | |
| function W(x,y,r,g,b) | |
| { | |
| var a = []; | |
| //segment protoype | |
| var w = { | |
| x:x, | |
| y:y, | |
| color:{r:r, g:g, b:b, a:0.01}, | |
| vx: 0, | |
| vy:0, | |
| width: 10 | |
| }; | |
| //worm segments | |
| for(var i=0;i<randi(3,15);i++) | |
| { | |
| a.push | |
| ({ | |
| x:w.x, | |
| y:w.y, | |
| color: i==0 ? {r:150,g:150,b:150,a:1} //head | |
| : {r:w.color.r, g:w.color.g, b:w.color.b, a:0.8}, | |
| vx: 0, | |
| vy:0, | |
| width: w.width | |
| }); | |
| } | |
| return a; | |
| } | |
| //"worm" | |
| var worms = []; | |
| //= [W(0,0,255,0,0), | |
| // W(mw,mh,0,255,0), | |
| // W(75,75,0,0,255)]; | |
| //initial worms | |
| var initialWormCount = 15; | |
| for(var i=0; i < initialWormCount; i++) | |
| { | |
| worms.push(W | |
| ( | |
| rand(0,ww), //x | |
| rand(0,hh), //y | |
| randi(0,100), // R | |
| randi(0,100), // G | |
| randi(0,100) // B | |
| )); | |
| } | |
| //random-integer() | |
| function randi(min,max) | |
| { | |
| min = Math.ceil(min); | |
| max = Math.floor(max); | |
| return Math.floor(Math.random() * (max - min + 1)) + min; | |
| } | |
| //random-float() | |
| function rand(min,max) | |
| { | |
| return Math.random() * (max - min ) + min; | |
| } | |
| function clamp255(x) | |
| { | |
| return Math.max(0,Math.min(255,x)); | |
| } | |
| function color(x) | |
| { | |
| var r = clamp255(randi(x-5,x+5)), | |
| g = clamp255(randi(x-5,x+5)), | |
| b = clamp255(randi(x-5,x+5)); | |
| return "rgb("+r+","+g+","+b+")"; | |
| } | |
| //t = paused | |
| var t = false; | |
| //when clicking on playPauseButton | |
| playPauseButton.onclick = function toggleBetweenPlayAndPause() | |
| { | |
| t = !t; | |
| if (!t) | |
| { | |
| playPauseButton.innerHTML = "❚❚"; | |
| main(); | |
| } | |
| else | |
| playPauseButton.innerHTML = "▶"; | |
| } | |
| //CLEAR BUTTON | |
| clearButton.onclick = () => | |
| { | |
| worms = []; | |
| }; | |
| ///////////////////////////////////// | |
| var lastRate = 0; | |
| var avgRate = 0; | |
| var rateColor = "black"; | |
| var textColor = "black"; | |
| var bgAlpha = {v:0.125, min: 0.125, max: 0.25, co1: 1, co2: 0.00125}; | |
| // UPDATE | |
| function update(framerate) | |
| { | |
| if (t) | |
| { | |
| text ="paused"; | |
| return; | |
| } | |
| var targetRate = 35; | |
| //bgAlpha (for controlling motion blur and removing artifacts) | |
| if (bgAlpha.v <= bgAlpha.min) | |
| bgAlpha.co1 = 1; //1 = increase alpha | |
| if (bgAlpha.v >= bgAlpha.max) | |
| bgAlpha.co1 = -1; //-1 = decrease alpha | |
| bgAlpha.v = bgAlpha.v + bgAlpha.co1 * bgAlpha.co2; | |
| //removes worm segments and worms if framerate is too low | |
| if (framerate < targetRate && worms.length > 2) | |
| { | |
| var w = worms[0]; | |
| var choppedHead = w.shift(); | |
| var newHead = worms[0]; | |
| if (w.length < 2) | |
| worms.shift(); | |
| else | |
| { | |
| newHead.width = choppedHead.width * 1.1; | |
| newHead.color = {r:200,g:200,b:200,a:1}; | |
| } | |
| if (w.length == 2) | |
| { | |
| newHead.width *= 1.5; | |
| } | |
| textColor = rateColor = "red"; | |
| } | |
| //adds worm if framerate is too high or nor worms are left | |
| else if (framerate > targetRate+30 || worms.length == 0) | |
| { | |
| worms.push(W | |
| ( | |
| rand(0,ww), rand(0,hh), //position (x,y) | |
| randi(0,255), randi(0,255), randi(0,255) //color (rgb) | |
| )); | |
| textColor = rateColor = "green"; | |
| } | |
| else | |
| { | |
| //rateColor = "black"; | |
| textColor = "grey"; | |
| } | |
| //for each worm... | |
| for(var i=0; i < worms.length; ++i) | |
| { | |
| //worms[i].push (worms[i].shift()); | |
| var w = worms[i]; //actual worm | |
| var head = w[0]; | |
| //if (w.length > 2) | |
| // head.color = {r:0,g:0,b:0,a:1}; | |
| //for each worm segment... | |
| for(var j=0; j < w.length; ++j) | |
| { | |
| var worm = w[j]; //worm segment | |
| var prev = w[j>0?j-1:j]; //previous segment | |
| if (rand(0,3)<1) | |
| { | |
| var dx = Math.abs(worm.x-prev.x); | |
| var dy = Math.abs(worm.y-prev.y); | |
| var rx = rand(-0.5,0.5)/10; | |
| var ry = rand(-0.5,0.5)/10; | |
| var arx = Math.abs(rx) * Math.max(1,dx); | |
| var ary = Math.abs(ry) * Math.max(1,dy); | |
| if (j ==0) //head | |
| { | |
| worm.vx += rx * 1.1; | |
| worm.vy += ry * 1.1; | |
| } | |
| else //tail segment | |
| { | |
| //x | |
| if (worm.x > prev.x) | |
| worm.vx -= arx; | |
| if (worm.x < prev.x) | |
| worm.vx += arx; | |
| if (Math.abs(worm.x - head.x-head.width)==0) | |
| worm.vx = 0; | |
| if (dx > 1) | |
| { | |
| worm.vx /= 2; | |
| prev.vx /= 2; | |
| head.vx *= 1.05; | |
| } | |
| //y | |
| if (worm.y > prev.y) | |
| worm.vy -= ary; | |
| if (worm.y < prev.y) | |
| worm.vy += ary; | |
| if (Math.abs(worm.y - head.y-head.width)==0) | |
| worm.vy = 0; | |
| if (dy > 1) | |
| { | |
| worm.vy /= 2; | |
| prev.vy /= 2; | |
| head.vy *= 1.05; | |
| } | |
| } | |
| //[c]olor(e[x]istingColor, a, b)) | |
| function _c (x,a,b) | |
| { | |
| return Math.min | |
| ( | |
| 255, | |
| Math.max(0,(x+randi(a,b))) | |
| ); | |
| } | |
| //color cycling | |
| if (rand(0,10)<1) | |
| { | |
| var c = worm.color; | |
| //... | |
| var threshold = 10; | |
| var limit = 0; | |
| if ( c.r < threshold && c.g==limit && c.b==limit | |
| || c.r== limit && c.g < threshold && c.b==limit | |
| || c.r==limit && c.g==limit && c.b < threshold) | |
| { | |
| let min = 0; | |
| let max = 25; | |
| c.r = _c(c.r,min,max); | |
| c.g = _c(c.g,min,max); | |
| c.b = _c(c.b,min,max); | |
| } | |
| let min = -2; | |
| let max = +1; | |
| c.r = _c(c.r,min,max); | |
| c.g = _c(c.g,min,max); | |
| c.b = _c(c.b,min,max); | |
| } | |
| } | |
| //move horizontal + vertical wall bounce | |
| worm.x = Math.min(Math.max(0,worm.x+worm.vx),ww-1); | |
| if (worm.x == 0 || worm.x == ww-1) | |
| worm.vx = -worm.vx/2; | |
| //move vertical + horizontal wall bounce | |
| worm.y = Math.min(Math.max(0,worm.y+worm.vy),hh-1); | |
| if (worm.y == 0 || worm.y == hh-1) | |
| worm.vy = -worm.vy/2; | |
| } | |
| } | |
| //text for framerate | |
| avgRate = (framerate + lastRate)/2; | |
| lastRate = framerate; | |
| if (Math.abs(avgRate-framerate)>10) | |
| { | |
| text = Math.round(avgRate); | |
| } | |
| } | |
| // RENDER() | |
| function render() | |
| { | |
| //this clears the canvas between frames: | |
| //ctx.clearRect(0, 0, canvas.width,canvas.height); | |
| //render bg | |
| /* | |
| for(var i=0; i < ww; ++i) | |
| for(var j=0; j < hh; ++j) | |
| { | |
| var alt = i % 2 == 0 && j % 2 > 0 || i % 2 > 0 && j % 2 == 0; | |
| //ctx.fillStyle = color(parseInt((i+j)*1.5)); | |
| //ctx.fillStyle = alt ? "#eee" : "#fff"; | |
| ctx.fillStyle = alt ? "rgb(220,190,91)" : "rgb(255,220,190)"; | |
| //ctx.fillStyle = "rgb(50,10,1)" | |
| ctx.fillRect(w * i , h * j, w+1, h+1); | |
| }*/ | |
| var bgColor = `rgba(30,20,15,${bgAlpha.v})`; | |
| ctx.fillStyle = bgColor; | |
| ctx.fillRect(0,0,canvas.width,canvas.height); | |
| //render worms | |
| for(var i=0; i < worms.length; ++i) | |
| { | |
| var worm = worms[i]; | |
| //render segments (including head, j=0) | |
| for(var j=0; j < worm.length; ++j) | |
| { | |
| var cs = worm[j]; //current segment | |
| //ctx.strokeStyle = "#000"; | |
| ctx.lineWidth = rand(4,5); | |
| ctx.fillStyle = "rgba("+ cs.color.r+","+ cs.color.g+","+ cs.color.b+","+ cs.color.a+")"; | |
| var prev = worm[j>0 ? j-1 : j]; | |
| //renders line between segments | |
| var renderLines = true; | |
| if (renderLines && worm.indexOf(cs) > 0) | |
| { | |
| /* | |
| text = cs == prev; | |
| ctx.font = "bold 20px sans-serif"; | |
| ctx.clearRect(0, 0, 70, 45); | |
| //ctx.fillStyle = 'black'; | |
| ctx.fillText(text,1, 20); | |
| */ | |
| ctx.strokeStyle = `rgba(${ cs.color.r},${ cs.color.g},${cs.color.b},${ cs.color.a})`; | |
| ctx.lineWidth = cs.width*2; | |
| ctx.beginPath(); | |
| ctx.moveTo(w*prev.x, h*prev.y); | |
| ctx.lineTo(w* cs.x, h* cs.y); | |
| //ctx.closePath(); | |
| ctx.stroke(); | |
| } | |
| /* | |
| ctx.fillRect | |
| ( | |
| w*prev.x, | |
| h*prev.y, | |
| w*(cworm.x - prev.x), | |
| h*(cworm.y - prev.y) | |
| );*/ | |
| //circle | |
| //if(j==0 || j==worm.length-1) | |
| { | |
| ctx.lineWidth = 1; | |
| ctx.beginPath(); | |
| ctx.arc(w*cs.x,h*cs.y, cs.width, 0, Math.PI * 2, false); | |
| ctx.closePath(); | |
| //ctx.stroke(); | |
| //let gradient = ctx.createRadialGradient(w*cs.x,h*cs.y,cs.width,w*cs.x,h*cs.y,0); | |
| //gradient.addColorStop(0,bgColor); | |
| // gradient.addColorStop(1,"white"); | |
| // ctx.fillStyle = gradient; | |
| ctx.fill(); | |
| } | |
| }//j (segments) | |
| } | |
| //text | |
| ctx.font = "bold 20px sans-serif"; | |
| var wormCountText = worms.length.toString(); | |
| var textWidth = Math.max(ctx.measureText(text).width, | |
| ctx.measureText(wormCountText).width); | |
| ctx.fillStyle = bgColor; | |
| ctx.fillRect(0, 0, textWidth+4, 45); | |
| //ctx.textAlign = "right"; | |
| ctx.fillStyle = rateColor; | |
| ctx.fillText(text,1, 20); | |
| ctx.fillStyle = textColor; | |
| ctx.fillText(wormCountText,1,40); | |
| //dot | |
| ctx.fillStyle="red"; | |
| ctx.fillRect(canvas.width-3,canvas.height-3,2,2); | |
| } | |
| var then = Date.now(); | |
| function main() | |
| { | |
| // Request to do this again ASAP | |
| if (!t) | |
| requestAnimationFrame(main); | |
| var now = Date.now(); | |
| var delta = now - then; | |
| update(1/(delta/1000)); | |
| render(); | |
| then = now; | |
| }; | |
| main(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| button | |
| { | |
| font-family: Arial; | |
| margin-bottom: 2px; | |
| height: 2em; | |
| } | |
| #clear | |
| { | |
| position: relative; | |
| top: -1px; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment