Last active
June 14, 2016 09:02
-
-
Save plugnburn/daba358402f595b349aa0ba14afb6d69 to your computer and use it in GitHub Desktop.
Arc Escape: Super Hexagon for the very poor, see complete compressed game at http://arc-escape.surge.sh
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
function T() { | |
var track = 'w=t>>9,k=32,m=2048,a=1-((t/m)%1),d=(((14*t*t)^t)%m)*a,y=([3,3,4.7,2][p=w/k&3])*(t/4),h=((("IQNNNN!!]]!Q!IW]WQNN??!!W]WQNNN?".charCodeAt(w/2&15|p/3<<4))/33)*t)-t,s=y*.98%80+y%80+(w>>7&&a*((5*t%m*a&128)*(0x53232323>>w/4&1)+(d&127)*(0xa444c444>>w/4&1)*1.5+(d*w&1)+(h%k+h*1.99%k+h*.49%k+h*.97%k-64)*(4-(2*a)))),(s*s>>14?127:s)+128', | |
duration = 120, | |
beat=function(b,e,a,t){return(a="data")+":audio/wav;base64,UklGRl9fX19XQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgA"+btoa(eval("for(t=0;++t<e*8e3;)a+=String.fromCharCode(255&("+b+"))"))} | |
return beat(track, duration) | |
} | |
!function() { | |
var rAF = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || window.mozRequestAnimationFrame, | |
aud, screenHeight = 0, screenWidth = 0, minSide = 0, maxSide = 0, centerX = 0, centerY = 0, realLoop = null, score = 0, baseSpeed = 0, speed = 0, | |
radUnit = Math.PI / 180, | |
gameAction = 0, //gameAction: 0 - nothing, 1 - left, 2 - right | |
gameMode = 1, //gameMode: 0 - gameOver, 1 - normal | |
playerAngle = 0, //arrow angle in deg | |
spinAngle = 0, //currentSpinAngle in deg | |
spinDelta = 1, //spin angle per frame | |
spinDir = 1, //spin direction (1 or -1) | |
bgColor = '#030303', //background color | |
wallColor = 'cyan', //wall color | |
wallWidth = 10, //wall width in px | |
arrowColor = 'yellow', //player arrow color | |
arrowAngleSize = 18, //arrow angle size in deg | |
baseColor = 'lightgreen', //player base color | |
baseWidth = 10, //base width in px | |
wallThreshold = 0, //threshold until the next wall is generated | |
walls = []; //walls (up to 4) | |
function mod3(x) { | |
while(x<0) x+=360; | |
while(x>360) x-=360; | |
return x | |
} | |
function angleBetween(angle, start, end){ | |
return mod3(angle - start) <= mod3(end - start) | |
} | |
// render loop | |
function gameLoop(C) { | |
if(gameAction) playerAngle += 10 * (gameAction - 1.5) | |
//draw bg | |
C.fillStyle = bgColor | |
C.fillRect(0, 0, screenWidth, screenHeight) | |
//define arrow angle | |
var arrowStartAngle = spinAngle + playerAngle, | |
arrowEndAngle = spinAngle + playerAngle + arrowAngleSize, | |
baseRadius = minSide / 9; | |
C.font = (baseRadius/1.5|0)+'px sans' | |
C.textAlign = 'center' | |
C.textBaseline = 'middle' | |
//hangle gameover mode | |
if(!gameMode) { | |
var bestScore = localStorage.getItem('best')|0 | |
if(score > bestScore) { | |
bestScore = score | |
localStorage.setItem('best', score) | |
} | |
C.font = (baseRadius*1.2|0)+'px sans' | |
C.fillStyle = 'cyan' | |
C.fillText('GAME OVER', centerX, centerY-baseRadius*2) | |
C.font = (baseRadius/1.5|0)+'px sans' | |
C.fillStyle = 'yellow' | |
C.fillText('Your score: '+score, centerX, centerY-baseRadius/2) | |
C.fillText('Best score: '+bestScore, centerX, centerY+baseRadius/2) | |
C.font = (baseRadius/2.5|0)+'px sans' | |
C.fillStyle = 'lightgreen' | |
C.fillText('Press SPACE or tap to restart', centerX, centerY+2*baseRadius) | |
if(gameAction) window.location.reload(); | |
else rAF(realLoop); | |
return | |
} | |
//draw player base | |
C.beginPath() | |
C.strokeStyle = baseColor | |
C.lineWidth = baseWidth | |
C.arc(centerX, centerY, baseRadius, 0, Math.PI*2) | |
C.stroke() | |
//draw player arrow | |
C.strokeStyle = C.fillStyle = arrowColor | |
var q = 10, i, k, lw = baseWidth/q; | |
C.lineWidth = lw | |
for(i=0;i<q;i++) { | |
var angleDelta = arrowAngleSize * i/q * 0.5; | |
C.beginPath() | |
C.arc(centerX, centerY, baseRadius + baseWidth * 0.5 + i*lw, radUnit*(arrowStartAngle + angleDelta), radUnit*(arrowEndAngle - angleDelta)) | |
C.stroke() | |
} | |
//draw arc walls | |
if(!walls.length || walls[walls.length - 1].r < maxSide - wallThreshold) | |
walls.push({a:Math.ceil(Math.random()*180)|0, r:maxSide, s:Math.ceil(Math.random()*4)}) | |
for(i=0,q=walls.length;i<q;i++) { | |
if(walls[i].r <= baseRadius + baseWidth) { //collision or not? | |
var coll = true, baseWallAngle = walls[i].a, steadyWallAngle = walls[i].s > 1 ? 180/walls[i].s|0 : 315; | |
for(k=0;k<walls[i].s;k++) { | |
baseWallAngle += steadyWallAngle | |
if(angleBetween(playerAngle, baseWallAngle, (baseWallAngle + (walls[i].s > 1 ? steadyWallAngle : 45)))) { | |
coll = false | |
break | |
} | |
baseWallAngle += steadyWallAngle | |
} | |
if(coll) { //collision | |
gameMode = 0 | |
gameAction = 0 | |
aud.pause() | |
rAF(realLoop) | |
return; | |
} | |
else { //no collision | |
score++ | |
if(score % 20 === 0) spinDir = -spinDir | |
speed = baseSpeed * (1 + score / 64) | |
walls[i].r = null | |
} | |
} | |
else { | |
walls[i].r -= speed | |
C.strokeStyle = wallColor | |
C.lineWidth = wallWidth | |
var baseWallAngle = walls[i].a, steadyWallAngle = walls[i].s > 1 ? 180/walls[i].s|0 : 315 | |
for(k=0;k<walls[i].s;k++) { | |
C.beginPath() | |
C.arc(centerX, centerY, walls[i].r, (baseWallAngle + spinAngle)*radUnit, (baseWallAngle + spinAngle + steadyWallAngle)*radUnit) | |
C.stroke() | |
baseWallAngle += steadyWallAngle*2 | |
} | |
} | |
} | |
var newWalls = [] | |
for(i=0;i<q;i++) | |
if(walls[i].r!=null) | |
newWalls.push(walls[i]) | |
walls = newWalls | |
C.fillText(score, centerX, centerY) | |
spinAngle = mod3(spinAngle + spinDelta * spinDir) | |
rAF(realLoop) | |
} | |
//keyboard events | |
addEventListener('keydown', function(e){ | |
if(gameMode) { | |
if(e.which === 37 || e.which === 65) gameAction = 1 | |
else if(e.which === 39 || e.which === 68) gameAction = 2 | |
} | |
else if(e.which === 32) gameAction = 1 | |
}) | |
addEventListener('keyup', function(e){gameAction = 0}) | |
//touch events | |
addEventListener('touchstart', function(e){ | |
if(e.touches.length === 1) { | |
e.preventDefault() | |
var t = e.touches[0], tX = t.clientX | |
gameAction = (t.clientX > screenWidth >> 1) ? 2 : 1 | |
} | |
else gameAction = 0 | |
}) | |
addEventListener('touchend', function(e){e.preventDefault();gameAction = 0}); | |
//init sequence | |
var screenInit = function() { | |
Z.width = screenWidth = document.documentElement.clientWidth | |
Z.height = screenHeight = document.documentElement.clientHeight | |
minSide = Math.min(screenWidth, screenHeight) | |
maxSide = Math.max(screenWidth, screenHeight) | |
centerX = screenWidth >> 1 | |
centerY = screenHeight >> 1 | |
wallThreshold = maxSide / 6 | |
baseSpeed = Math.ceil(maxSide / 320) | |
if(!speed) speed = baseSpeed | |
} | |
addEventListener('resize', screenInit) | |
addEventListener('orientationchange', screenInit) | |
screenInit(); | |
//draw loading screen | |
var C = Z.getContext('2d') | |
C.fillStyle = bgColor | |
C.fillRect(0, 0, screenWidth, screenHeight) | |
C.font = (minSide/9|0)+'px sans' | |
C.textAlign = 'center' | |
C.textBaseline = 'middle' | |
C.fillStyle = 'yellow' | |
C.fillText('LOADING...', centerX, centerY) | |
setTimeout(function(){ | |
//init the soundtrack | |
var audUrl | |
if(!(audUrl = localStorage.getItem('sound'))) | |
localStorage.setItem('sound', audUrl = T()) | |
aud = new Audio(audUrl) | |
aud.loop = true | |
aud.volume = 0.5 | |
aud.play() | |
// run the loop | |
;(realLoop = gameLoop.bind(null, Z.getContext('2d')))() | |
}, 10) | |
}() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment