├── .gitattributes ├── .gitignore ├── flappyBox.zip ├── index.html └── index.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # ========================= 18 | # Operating System Files 19 | # ========================= 20 | 21 | # OSX 22 | # ========================= 23 | 24 | .DS_Store 25 | .AppleDouble 26 | .LSOverride 27 | 28 | # Icon must end with two \r 29 | Icon 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | -------------------------------------------------------------------------------- /flappyBox.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/towc/flappyBox/d0b618dde768e055f68698dca8071e4c49d3dd0b/flappyBox.zip -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | flappyBox by towc 5 | 6 | 7 | 50 | 51 | 52 | 53 |

Please don't use landscape mode

54 | 55 | 56 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var game = {}; 2 | 3 | game.init = function(){ 4 | 5 | // unchanging variables 6 | 7 | game.player = {}; 8 | game.screen = {}; 9 | game.controls = {}; 10 | game.logics = {}; 11 | game.time = {}; 12 | 13 | game.controls.interactKey = 32; //space 14 | 15 | game.screen.canvas = document.getElementById( 'c' ); 16 | game.screen.ctx = game.screen.canvas.getContext( '2d' ); 17 | 18 | var w = 320, 19 | h = 480, 20 | 21 | ww = window.innerWidth, 22 | wh = window.innerHeight, 23 | 24 | i = 1; 25 | 26 | //while( w * i <= ww && h * i <= wh ) ++i; 27 | 28 | game.screen.canvas.width = game.logics.width = game.screen.width = w * i; 29 | game.screen.canvas.height = game.logics.height = game.screen.height = h * i; 30 | game.screen.halfw = game.logics.halfw = game.screen.width / 2; 31 | game.screen.halfh = game.screen.height / 2; 32 | 33 | game.screen.colors = [ '#fff', '#000', '#f80808', 'rgba(120,120,120,.6)', '#eee' ]; 34 | game.screen.fonts = [ '40px Verdana', '30px Verdana', '20px Verdana', '35px Verdana' ]; 35 | game.screen.texts = [ 'jumps', 'Squashed at ', 'spacebar or touch to begin', 'highscore of ' ]; 36 | 37 | game.screen.lengths = []; 38 | 39 | game.screen.ctx.font = game.screen.fonts[ 2 ]; 40 | game.screen.lengths[ 0 ] = game.screen.ctx.measureText( game.screen.texts[ 0 ] ).width; 41 | 42 | game.screen.ctx.font = game.screen.fonts[ 2 ]; 43 | game.screen.lengths[ 1 ] = game.screen.ctx.measureText( game.screen.texts[ 2 ] ).width; 44 | 45 | game.screen.midBezierPassingMultiplier = 2; 46 | game.screen.midBezierFriction = .8; 47 | game.screen.midBezierAccMultiplier = .1; 48 | game.screen.barSizeVel = .02; 49 | 50 | game.screen.pillarHeight = 6; 51 | 52 | game.logics.gravity = .3; 53 | game.logics.friction = .9; 54 | game.logics.pillars = []; 55 | 56 | game.logics.basePillarWidth = 35; 57 | game.logics.addedPillarWidth = 50; 58 | game.logics.basePillarWait = 90; 59 | game.logics.addedPillarWait = 30; 60 | game.logics.lastPillarInitialWaitDecrementer = 2; 61 | 62 | game.logics.playerThresholdVel = 6; 63 | game.logics.playerIncrementMultiplier = 1.01; 64 | game.logics.playerDecrementMultiplier = .992; 65 | 66 | game.logics.playerThresholdJumpVel = 2; 67 | 68 | game.time.updateMS = 16; 69 | 70 | game.reset(); // changing variables 71 | 72 | game.controls.menuing = true; 73 | 74 | // constants so that I don't have to calculate some stuff every time 75 | game.constants = [ 76 | -game.player.size / 2, 77 | game.screen.halfw * 1.5 - game.screen.lengths[ 0 ] / 2, 78 | game.screen.height - 30, 79 | game.screen.halfw * 1.5 - game.screen.lengths[ 0 ] / 2 - 6, 80 | game.screen.height - 48, 81 | game.screen.lengths[ 0 ] + 10, 82 | game.screen.halfw - game.screen.lengths[ 1 ] / 2 ] 83 | 84 | game.anim(); // start 85 | 86 | //event listeners 87 | 88 | game.controls.interact = function(){ 89 | 90 | if( game.controls.menuing ) 91 | game.controls.menuing = false; 92 | 93 | if( game.player.dead ){ 94 | game.reset(); 95 | game.anim(); 96 | } else if( game.controls.canInteract ) game.controls.interacted = true; 97 | } 98 | 99 | window.addEventListener( 'keydown', function( e ){ 100 | 101 | if( e.keyCode === game.controls.interactKey ) 102 | game.controls.interact(); 103 | } ); 104 | window.addEventListener( 'touchstart', game.controls.interact ); 105 | 106 | if( !localStorage.game_highScore ) 107 | localStorage.game_highScore = 0; 108 | } 109 | game.reset = function(){ 110 | 111 | game.controls.canInteract = true; 112 | game.controls.interacted = false; 113 | game.controls.lastInteraction = 0; 114 | game.controls.menuing = false; 115 | 116 | game.player.dead = false; 117 | 118 | game.player.x = game.logics.halfw * 3 / 2; 119 | game.player.y = game.logics.height - 100; 120 | game.player.vx = 0; 121 | game.player.vy = -3; 122 | game.player.ax = 0; 123 | game.player.ay = -.0005; 124 | 125 | game.player.size = 18; 126 | game.player.side = 1; 127 | game.player.rotation = 0; 128 | 129 | game.player.usedJumps = 0; 130 | game.player.maxJumps = 5; 131 | 132 | game.logics.pillars.length = 0; 133 | game.logics.lastPillar = 300; 134 | game.logics.lastPillarSide = 1; 135 | game.logics.lastPillarInitialWait = 60; 136 | 137 | game.screen.midBezier = 0; 138 | game.screen.midBezierVel = 0; 139 | 140 | game.screen.barSize = 1; 141 | 142 | game.logics.score = 0; 143 | 144 | game.time.now = Date.now(); 145 | game.time.leftOverMS = 0; 146 | } 147 | game.anim = function(){ 148 | 149 | if( !game.player.dead ) window.requestAnimationFrame( game.anim ); 150 | else return game.over(); 151 | 152 | var now = Date.now(), 153 | elapsed = game.time.leftOverMS + ( now - game.time.now ); 154 | 155 | if( elapsed > 50 ) 156 | elapsed = 0; 157 | 158 | while( elapsed > game.time.updateMS && !game.player.dead ){ 159 | 160 | game.updatePlayer(); 161 | if( !game.controls.menuing ) game.updatePillars(); 162 | 163 | elapsed -= game.time.updateMS; 164 | } 165 | 166 | if( game.player.dead ) 167 | return game.over(); 168 | 169 | game.time.leftOverMS = elapsed; 170 | game.time.now = now; 171 | 172 | game.drawBackground(); 173 | game.drawPillars(); 174 | game.drawPlayer(); 175 | game.drawTexts(); 176 | } 177 | 178 | game.over = function(){ 179 | 180 | var ctx = game.screen.ctx; 181 | 182 | ctx.globalCompositeOperation = 'source-over'; 183 | 184 | ctx.fillStyle = game.screen.colors[ 3 ]; 185 | ctx.fillRect( 0, 0, game.screen.width, game.screen.height ); 186 | 187 | ctx.fillStyle = game.screen.colors[ 4 ]; 188 | ctx.font = game.screen.fonts[ 3 ]; 189 | var text = game.screen.texts[ 1 ].replace( '', game.logics.score ); 190 | ctx.fillText( text, game.screen.halfw - ctx.measureText( text ).width / 2, game.screen.halfh - 20 ); 191 | 192 | var highscore = +localStorage.game_highScore; 193 | if( game.logics.score > highscore ) 194 | localStorage.game_highScore = game.logics.score; 195 | 196 | ctx.font = game.screen.fonts[ 2 ]; 197 | text = game.screen.texts[ 3 ].replace( '', localStorage.game_highScore ); 198 | ctx.fillText( text, game.screen.halfw - ctx.measureText( text ).width / 2, game.screen.halfh + 20 ); 199 | } 200 | game.updatePlayer = function(){ 201 | 202 | var player = game.player; 203 | 204 | var didntWarp = false; 205 | 206 | if( player.x < -player.size ){ 207 | player.x += game.logics.width + player.size; 208 | player.vx *= .8; 209 | } else if( player.x > game.logics.width + player.size ){ 210 | player.x -= game.logics.width + player.size; 211 | player.vx *= .8; 212 | } else didntWarp = true; 213 | 214 | var newSide = player.x < game.logics.halfw ? 1 : -1; 215 | 216 | if( newSide !== player.side && didntWarp ){ 217 | 218 | player.ax = 0; 219 | player.usedJumps = 0; 220 | 221 | game.screen.midBezierVel = player.vx * game.screen.midBezierPassingMultiplier; 222 | game.screen.midBezier = 0; 223 | } 224 | 225 | player.side = newSide; 226 | 227 | if( Math.abs( player.vx ) > game.logics.playerThresholdVel ) player.vx *= game.logics.playerDecrementMultiplier; 228 | else player.vx *= game.logics.playerIncrementMultiplier; // keep it constantish 229 | 230 | if( !game.controls.menuing ) player.vy += player.ay; 231 | 232 | player.x += player.vx += game.logics.gravity * player.side + ( player.ax *= game.logics.friction ); 233 | player.rotation = Math.atan( player.vy / player.vx ); // it's symmetric so... 234 | 235 | if( --game.controls.lastInteraction && game.controls.interacted && player.usedJumps < player.maxJumps ){ 236 | 237 | game.controls.lastInteraction = 10; 238 | game.controls.interacted = false; 239 | 240 | ++player.usedJumps; 241 | 242 | player.ax = -player.side / 3; 243 | player.vx -= player.side * 2; 244 | if( Math.sign( player.vx ) === player.side || Math.abs( player.vx ) < game.logics.playerThresholdJumpVel ) 245 | player.vx = -player.side * 2; 246 | 247 | } 248 | } 249 | game.updatePillars = function(){ 250 | 251 | var player = game.player; 252 | 253 | game.logics.lastPillar += player.vy; 254 | 255 | if( game.logics.lastPillar <= 0 ){ 256 | 257 | var num = 0; 258 | 259 | do { 260 | if( Math.random() < .7 ) game.logics.lastPillarSide *= -1; 261 | 262 | var x = game.logics.halfw + ( Math.random() * ( game.logics.halfw ) * game.logics.lastPillarSide ) |0, 263 | width = ( game.logics.basePillarWidth + game.logics.addedPillarWidth * Math.random() / ( num + 1 ) ) |0, 264 | found = -1; 265 | 266 | for( var i = 0; i < game.logics.pillars.length; ++i ) 267 | if( game.logics.pillars[ i ].dead ) 268 | found = i; 269 | 270 | var pillar = found > -1 ? game.logics.pillars[ found ] : {}; 271 | pillar.x = x; 272 | pillar.y = 0; 273 | pillar.warping = x + width > game.logics.width; 274 | 275 | if( pillar.warping ){ 276 | 277 | pillar.w = game.logics.width - x; 278 | pillar.warpLeft = width - pillar.w; 279 | } else pillar.w = width; 280 | 281 | pillar.dead = false; 282 | 283 | if( found === -1 ) 284 | game.logics.pillars.push( pillar ); 285 | 286 | // just a smoothener so mplethat it's easier in the beginning 287 | if( game.logics.lastPillarInitialWait > 0 ) 288 | game.logics.lastPillarInitialWait -= game.logics.lastPillarInitialWaitDecrementer; 289 | game.logics.lastPillar = game.logics.lastPillarInitialWait + ( game.logics.basePillarWait + game.logics.addedPillarWait * Math.random() ) |0; 290 | 291 | ++num; 292 | } while( Math.random() < .1 && num < 2 ); 293 | } 294 | for( var i = 0; i < game.logics.pillars.length; ++i ){ 295 | 296 | var pillar = game.logics.pillars[ i ]; 297 | 298 | if( pillar.dead ) continue; 299 | 300 | pillar.y -= player.vy; // if you go towards something it's as if they went towards you 301 | 302 | if( pillar.y > game.logics.height ){ 303 | pillar.dead = true; 304 | 305 | } else if( pillar.y > player.y && pillar.y + player.vy < player.y ){ 306 | 307 | 308 | if( ( player.x > pillar.x && player.x < pillar.x + pillar.w ) || ( pillar.warping && ( player.x <= pillar.warpLeft ) ) ){ 309 | 310 | player.dead = true; 311 | } else { 312 | ++game.logics.score 313 | } 314 | } 315 | } 316 | } 317 | game.drawBackground = function(){ 318 | 319 | game.screen.midBezier += game.screen.midBezierVel -= game.screen.midBezier * game.screen.midBezierAccMultiplier; 320 | game.screen.midBezierVel *= game.screen.midBezierFriction; 321 | 322 | var ctx = game.screen.ctx; 323 | 324 | ctx.globalCompositeOperation = 'source-over'; 325 | 326 | ctx.fillStyle = game.screen.colors[ 0 ]; 327 | ctx.fillRect( 0, 0, game.screen.halfw + ( game.screen.midBezier > 0 ? ( game.screen.midBezier / 2 )|0 : 0 ), game.screen.height ); 328 | 329 | ctx.fillStyle = game.screen.colors[ 1 ]; 330 | ctx.beginPath(); 331 | ctx.moveTo( game.screen.halfw, 0 ); 332 | ctx.lineTo( game.screen.width, 0 ); 333 | ctx.lineTo( game.screen.width, game.screen.height ); 334 | ctx.lineTo( game.screen.halfw, game.screen.height ); 335 | ctx.quadraticCurveTo( game.screen.halfw + game.screen.midBezier, game.player.y, game.screen.halfw, 0 ); 336 | ctx.fill(); 337 | } 338 | game.drawPillars = function(){ 339 | 340 | var ctx = game.screen.ctx; 341 | 342 | ctx.globalCompositeOperation = 'source-over'; 343 | ctx.fillStyle = game.screen.colors[ 2 ]; 344 | 345 | for( var i = 0; i < game.logics.pillars.length; ++i ){ 346 | 347 | var pillar = game.logics.pillars[ i ]; 348 | 349 | if( !pillar.dead ){ 350 | ctx.fillRect( pillar.x, pillar.y, pillar.w, game.screen.pillarHeight ); 351 | if( pillar.warping ) 352 | ctx.fillRect( 0, pillar.y, pillar.warpLeft, game.screen.pillarHeight ); 353 | } 354 | } 355 | } 356 | game.drawPlayer = function(){ 357 | 358 | var ctx = game.screen.ctx, 359 | player = game.player; 360 | 361 | ctx.globalCompositeOperation = 'difference'; 362 | 363 | ctx.fillStyle = game.screen.colors[ 0 ]; 364 | ctx.save(); 365 | ctx.translate( player.x, player.y ); 366 | ctx.rotate( player.rotation ); 367 | ctx.fillRect( game.constants[ 0 ], game.constants[ 0 ], player.size, player.size ); // -player.size / 2; 368 | ctx.restore(); 369 | 370 | } 371 | game.drawTexts = function(){ 372 | 373 | var ctx = game.screen.ctx; 374 | 375 | ctx.globalCompositeOperation = 'difference'; 376 | 377 | ctx.fillStyle = game.screen.colors[ 0 ]; 378 | 379 | var prop = game.screen.barSize - ( 1 - game.player.usedJumps / game.player.maxJumps ); 380 | 381 | if( prop < 0 ) 382 | game.screen.barSize += game.screen.barSizeVel; 383 | else if( prop > game.screen.barSizeVel ) 384 | game.screen.barSize -= game.screen.barSizeVel; 385 | 386 | ctx.font = game.screen.fonts[ 2 ]; 387 | 388 | ctx.fillText( game.screen.texts[ 0 ], game.constants[ 1 ], game.constants[ 2 ] ); // game.screen.halfw * 1.5 - game.screen.lengths[ 0 ] / 2; game.screen.height - 30; 389 | ctx.fillRect( game.constants[ 3 ], game.constants[ 4 ], game.screen.barSize * game.constants[ 5 ], 25 ); // game.screen.halfw * 1.5 - game.screen.lenghts[ 0 ] / 2 - 5; game.screen.height - 48; game.screen.lengths[ 0 ] + 10 390 | 391 | ctx.font = game.screen.fonts[ 1 ]; 392 | 393 | var text = game.logics.score; 394 | ctx.fillText( text, game.screen.halfw - ctx.measureText( text ).width / 2, 80 ); 395 | 396 | if( game.controls.menuing ){ 397 | 398 | ctx.font = game.screen.fonts[ 2 ]; 399 | ctx.fillText( game.screen.texts[ 2 ], game.constants[ 6 ], game.screen.halfh ); // game.screen.halfw - game.screen.lengths[ 1 ] / 2 400 | } 401 | } 402 | 403 | game.init(); --------------------------------------------------------------------------------