1 /* 2 ___ 3 | _ \_ _ _ _ __ __ 4 | _/ || | ' \\ \ / 5 |_| \_,_|_||_/_\_\ 6 7 */ 8 /** 9 * @author magicmarkers 10 * @description A derpy HTML5/JS engine for side-scrolling brawler / beat 'em up / punchy-fighty games. 11 * @see https://github.com/magicmarkers/Punx 12 */ 13 14 // TODO: change collision engine, make pixel perfect. keep depth in mind. 15 // TODO: get audio stuff working 16 // TODO: parallax backgrounds 17 // TODO: boilerplate baddy AI 18 // TODO: boilerplate post-hit temporary invulnerability/blinkage for player 19 // TODO: boilerplate talk bubbles 20 21 /** 22 * @namespace Punx 23 */ 24 var Punx = {}; 25 /* 26 ___ ___ _ _ 27 | _ \_ _ _ _ __ __ / __|_ _(_)__| | 28 | _/ || | ' \\ \ / _ | (_ | '_| / _` | 29 |_| \_,_|_||_/_\_\ (_) \___|_| |_\__,_| 30 31 */ 32 /** 33 * @class Represents the game engine. Houses the main loop and canvas DOM node reference. 34 * @param {object} args Public field construction values. 35 */ 36 var Punx.Grid = function(args) { 37 38 /** 39 * Required: Object holding all screens, keyed by name. 40 * @type object.<string,Punx.Screen> 41 * @public 42 */ 43 this.screens = args.screens; 44 45 /** 46 * Required: Key of the initial screen. 47 * @public 48 * @type string 49 */ 50 this.screen = args.screen 51 52 /** 53 * Integer canvas width. 54 * @public 55 * @type number 56 * @default 960 57 */ 58 this.w = args.w || 960; 59 60 /** 61 * Integer canvas height. 62 * @public 63 * @type number 64 * @default 540 65 */ 66 this.h = args.h || 540; 67 68 /** 69 * Integer frames-per-second. 70 * @public 71 * @type number 72 * @default 60 73 */ 74 this.fps = args.fps || 60; 75 76 /** 77 * For debugging, causes the main loop to idle when true. 78 * @public 79 * @type boolean 80 * @default false 81 */ 82 this.idle = false; 83 84 /** 85 * Internal frame counter, increments once after every update cycle, wraps at fps. This should be treated as read-only. 86 * @public 87 * @type number 88 * @default 0 89 */ 90 this.frame = 0; 91 92 /** 93 * Viewport canvas context. 94 * @public 95 */ 96 this.ctx = null; 97 98 /** 99 * Imaginary canvas context. 100 * @public 101 */ 102 this.ictx = null; 103 104 /** 105 * Viewport canvas DOM element. This is displayed. 106 * @private 107 */ 108 this.canvas = null; 109 110 /** 111 * Imaginary canvas DOM element used as a frame-buffer for collision detection and other graphics analysis. Scratch-space. Never shown. 112 * @private 113 */ 114 this.icanvas = null; 115 116 /** 117 * The game loop, an interval that calls update() on the current screen, timed according to fps. 118 * @private 119 */ 120 this.loop = null; 121 122 /** 123 * Creates the canvases and injects the main canvas into the viewport. 124 * @public 125 * @param [parentElement="document.body"] DOM element to make the canvas a child of. 126 */ 127 this.create = function(parentElement) { 128 // viewport canvas 129 this.canvas = document.createElement('canvas'); 130 this.canvas.setAttribute('width',this.w); 131 this.canvas.setAttribute('height',this.h); 132 this.canvas.setAttribute('tabindex',0); 133 this.canvas.addEventListener('keydown',Punx.Key.onkeydown,true); 134 this.canvas.addEventListener('keyup',Punx.Key.onkeyup,true); 135 this.ctx = this.canvas.getContext('2d'); 136 this.ctx.imageSmoothingEnabled = false; 137 this.ctx.mozImageSmoothingEnabled = false; 138 this.ctx.webkitImageSmoothingEnabled = false; 139 (parentElement||document.body).appendChild(this.canvas); 140 this.canvas.focus(); 141 142 // imaginary canvas 143 this.icanvas = document.createElement('canvas'); 144 this.icanvas.setAttribute('width',this.w); 145 this.icanvas.setAttribute('height',this.h); 146 this.ictx = this.icanvas.getContext('2d'); 147 this.ictx.imageSmoothingEnabled = false; 148 this.ictx.mozImageSmoothingEnabled = false; 149 this.ictx.webkitImageSmoothingEnabled = false; 150 }; 151 152 /** 153 * Starts the main loop. 154 * @public 155 */ 156 this.start = function() { 157 this.loop = window.setInterval(this.update.bind(this),1000/this.fps); 158 }; 159 160 /** 161 * The main loop calls this once every fps. The loop is killed if any errors occur. Does nothing if idle is true. 162 * @private 163 */ 164 // the main loop calls this once every FPS. 165 // kills the loop upon error. does nothing if idle is true 166 this._update = function() { 167 if (this.idle) return; 168 try { 169 this.screen.update(); 170 this.frame = ++this.frame % this.fps; 171 } 172 catch (e) { 173 console.log(e.stack); 174 window.clearInterval(this._loop); 175 } 176 }; 177 178 }; 179 180 181 /* 182 ___ _ _ _ _ _ 183 | _ \_ _ _ _ __ __ | | | | |_(_) | 184 | _/ || | ' \\ \ / _ | |_| | _| | | 185 |_| \_,_|_||_/_\_\ (_) \___/ \__|_|_| 186 187 */ 188 189 Punx.Util = new function() { 190 191 this.PHI = (1+Math.sqrt(5))/2; // golden ratio 192 this.PHI_A = 1 - this.PHI; 193 this.PHI_B = 1 - this.PHI_A; 194 195 // determines if two boxes, a and b, are intersecting 196 this.AABB = function(a,b) { 197 return (!( 198 a.x + a.w < b.x || // a too left 199 a.x > b.x + b.w || // a too right 200 a.y + a.h < b.y || // a too bg (y) 201 a.y > b.y + b.h // a too fg (y) 202 )); 203 }; 204 205 // returns a box for the intersection of boxes a and b 206 // this may return strange things if they aren't actually intersecting. use AABB first. 207 this.AABBC = function(a,b) { 208 return new function() { 209 this.x = a.x < b.x ? b.x : a.x; 210 this.y = a.y < b.y ? b.y : a.y; 211 this.w = (a.x + a.w < b.x + b.w ? a.x + a.w : b.x + b.w) - this.x; 212 this.h = (a.y + a.h < b.y + b.h ? a.y + a.h : b.y + b.h) - this.y; 213 }; 214 }; 215 216 // imagines a box and returns a list of sprites it AABB intersects. 217 // use this for instantaneous projectiles or other things from afar. 218 // sprites is a list of sprite instances, and box is an object with x,y,w,h 219 this.scan = function(box,sprites) { 220 var intersections = []; 221 for (var i in sprites) { 222 if (Punx.Util.AABB(box,sprites[i])) intersections.append(sprites[i]); 223 } 224 return intersections; 225 }; 226 227 // returns a sorted list of sprites according to their proximity to the origin x 228 this.xSort = function(sprites,x) { 229 x=x||0; 230 return sprites.sort(function(a,b) { 231 if (a.x + a.w < x) { // a is left 232 if (b.x + b.w < x) { // b is left 233 return (a.x + a.w > b.x + b.w ? -1 : 1); 234 } 235 else { // b is right 236 return (x - (a.x + a.w) < b.x - x ? -1 : 1); 237 } 238 } 239 else { // a is right 240 if (b.x > x) { // b is right 241 return (a.x < b.x ? -1 : 1); 242 } 243 else { // b is left 244 return (a.x - x < x - (b.x + b.w) ? -1 : 1); 245 } 246 } 247 }); 248 }; 249 250 // returns a sorted list of sprites according to their proximity to the origin y 251 this.ySort = function(sprites,y) { 252 y=y||0; 253 return sprites.sort(function(a,b) { 254 if (a.y + a.h < y) { // a is above 255 if (b.y + b.h < y) { // b is above 256 return (a.y + a.h > b.y + b.h ? -1 : 1); 257 } 258 else { // b is below 259 return (y - (a.y + a.h) < b.y - y ? -1 : 1); 260 } 261 } 262 else { // a is below 263 if (b.y > y) { // b is below 264 return (a.y < b.y ? -1 : 1); 265 } 266 else { // b is above 267 return (a.y - y < y - (b.y + b.h) ? -1 : 1); 268 } 269 } 270 }); 271 }; 272 273 }; 274 275 276 /* 277 ___ _ _ _ __ 278 / __|_ _(_)__| | | |/ /___ _ _ 279 | (_ | '_| / _` | _ | ' </ -_) || | 280 \___|_| |_\__,_| (_) |_|\_\___|\_, | 281 |__/ 282 */ 283 // keyboard stuff 284 285 Punx.Key = new function() { 286 287 // key name => bool state 288 // sprites should poll this during update() and act accordingly. 289 // sprites should use internal flags to determine if they can be interrupted. 290 // example: if (Punx.Key.state['left'] && !this.busy) ... 291 this.state = {}; 292 293 // keyCode => key name 294 this._keys = { 295 '9':'tab', 296 '13':'enter', 297 '27':'escape', 298 '32':'space', 299 '37':'left', 300 '38':'up', 301 '39':'right', 302 '40':'down' 303 }; 304 for (var i=48;i<=57;i++) { this._keys[i] = String.fromCharCode(i); } // numbers 305 for (var i=65;i<=90;i++) { this._keys[i] = String.fromCharCode(i).toLowerCase(); } // letters 306 307 // document's keydown listener 308 this._keydown = function(e) { 309 e=e||window.event; 310 if (e.altGraphKey || e.metaKey || e.altKey || e.shiftKey || e.ctrlKey) return; 311 var key = this._keys[e.keyCode]; 312 if (!key) return; 313 e.preventDefault(); 314 e.stopPropagation(); 315 this.state[key] = true; 316 }.bind(this); // event handlers are wacky and need "this" explicitly bound 317 318 // document's keyup listener 319 this._keyup = function(e) { 320 e=e||window.event; 321 var key = this._keys[e.keyCode]; 322 if (!key) return; 323 e.preventDefault(); 324 e.stopPropagation(); 325 this.state[key] = false; 326 }.bind(this); // event handlers are wacky and need "this" explicitly bound 327 328 }; 329 330 331 /* 332 ___ _ _ ___ _ 333 / __|_ _(_)__| | / __| ___ _ _ _ _ __| | 334 | (_ | '_| / _` | _ \__ \/ _ \ || | ' \/ _` | 335 \___|_| |_\__,_| (_) |___/\___/\_,_|_||_\__,_| 336 337 */ 338 // in progress 339 Audio.prototype.fadeTo = function(fadeTo,callback) { 340 if (fadeTo < 0) fadeTo = 0; 341 else if (fadeTo > 1) fadeTo = 1; 342 var that = this; 343 var setVolume = function(setTo) { 344 return function() { 345 that.volume = setTo; 346 if (setTo == fadeTo && callback) callback(); 347 }; 348 }; 349 var delay = 0; 350 if (this.volume < fadeTo) { 351 for (var v=this.volume; v<=fadeTo; v+=0.1) { 352 setTimeout(setVolume(v),delay); 353 delay+=100; 354 } 355 } 356 else if (volume > fadeTo) { 357 for (var v=this.volume; v>=fadeTo; v-=0.1) { 358 setTimeout(setVolume(v),delay); 359 delay+=100; 360 } 361 } 362 else if (callback) callback(); 363 }; 364 365 Punx.Sound = new function() { 366 367 this.music = new Audio(); // swap out for one in Library 368 369 this.muted = false; 370 this.mute = function() { 371 this.muted ^= 1; 372 this.music.muted = this.muted; 373 }; 374 375 }; 376 377 378 /* 379 ___ _ _ ___ 380 / __|_ _(_)__| | / __| __ _ _ ___ ___ _ _ 381 | (_ | '_| / _` | _ \__ \/ _| '_/ -_) -_) ' \ 382 \___|_| |_\__,_| (_) |___/\__|_| \___\___|_||_| 383 384 */ 385 // a screen is a collection of sprites, such as a menu. levels are stages and extend this. 386 // only one screen is drawn to the canvas. 387 388 Punx.Screen = function(args) { 389 390 // required 391 this.grid = args['grid']; // grid reference 392 393 // optional 394 this.sprites = args['sprites']||[]; // list of Punx.Sprite instances 395 this.bg = args['bg']; // Image 396 397 // called by the grid. screens should draw themselves to the grid canvas context if they need to. 398 // this notifies sprites that they should "step" or otherwise incrementally change themselves if needed. 399 // sprites should return boolean of whether or not they changed 400 this.update = function() { 401 var updated = false; 402 for (var i in this.sprites) { 403 updated |= this.sprites[i].update(); 404 } 405 if (updated) this._draw(); 406 }; 407 408 // draw the screen 409 // sprites are drawn from "back to front", that is, sprites with more "depth" are drawn first as to mimic layering. 410 this._draw = function() { 411 this.grid.ctx.clearRect(0,0,this.grid.w,this.grid.h); 412 if (this.bg) this.grix.ctx.drawImage(this.bg,0,0); 413 for (var i in this.sprites.sort(Punx.Sprite.sort)) { 414 this.sprites[i].draw(this.grid.ctx); 415 } 416 }; 417 }; 418 419 420 /* 421 ___ _ _ ___ ___ _ 422 / __|_ _(_)__| | / __| __ _ _ ___ ___ _ _ / __| |_ __ _ __ _ ___ 423 | (_ | '_| / _` | _ \__ \/ _| '_/ -_) -_) ' \ _ \__ \ _/ _` / _` / -_) 424 \___|_| |_\__,_| (_) |___/\__|_| \___\___|_||_| (_) |___/\__\__,_\__, \___| 425 |___/ 426 */ 427 // stages are screens that are game levels. they have "curtains" and a "floor". 428 // imagine looking upon a theater stage from an ideal balcony seat. 429 // curtains are used for backgrounds and parallax. 430 // the floor is the "depth" where actors move away from and toward the "front" of the stage. 431 // the defaults define the curtains and floor in terms of the golden ratio: 432 // http://en.wikipedia.org/wiki/Golden_ratio 433 // where the longer portion represents the curtains and the shorter portion is the floor. 434 435 Punx.Screen.Stage = function(args) { 436 Punx.Screen.call(this,args); 437 438 // required 439 this.player = args['player']; // Punx.Sprite.Actor.Player instance 440 441 // optional 442 this.w = args['w'] || 960; // width of the stage (can go beyond viewport for panning) 443 this.y = args['y'] || 334; // height of curtains 444 this.h = args['h'] || 206; // height ("depth") of floor. 445 this.x = args['x'] || 0; // starting pan amount from stage left 446 this.panMargin = 200; // player distance from frame edge to start panning 447 this.pannable = args['pannable']||true; // whether to pan 448 449 // private stuff temporary to update() 450 this._sprites = []; // all in-frame sprites 451 this._actors = []; // in-frame actors 452 this._walls = []; // in-frame walls 453 454 this.update = function() { 455 var updated = false; 456 this._sprites = []; 457 this._walls = []; 458 this._actors = []; 459 460 // pan 461 if (this.pannable) with (this) { 462 // pan left if: frame left > stage left && player left < frame left margin 463 if (x > 0 && player.x < x + panMargin) { 464 // amount: margin - (player left - frame left) 465 x -= panMargin - (player.x - x); 466 if (x < 0) x = 0; // stop at min left 467 } 468 // pan right if: frame right < stage right && player right > frame right margin 469 else if (x + grid.w < w && player.x + player.w > x + grid.w - panMargin) { 470 // amount: margin - ((frame right) - (player right)) 471 x += panMargin - ((x + grid.w) - (player.x + player.w)); 472 if (x > w - grid.w) x = w - grid.w; // stop at max right 473 } 474 } 475 476 // determine active sprites, in-frame and frame-edged 477 for (var i in this.sprites) { 478 var s = this.sprites[i]; 479 if (s.x + s.w < this.x || s.x > this.x + this.grid.w) continue; // sprite is out of frame 480 this._sprites.push(s); 481 if (s instanceof Punx.Sprite.Wall) this._walls.push(s); 482 else if (s instanceof Punx.Sprite.Actor) this._actors.push(s); 483 } 484 485 // update active sprites. sprites should return boolean of whether they updated 486 for (var i in this._sprites.sort(Punx.Sprite.sort)) { 487 updated |= this._sprites[i].update(); 488 } 489 490 if (updated) { 491 this._draw(); // draw the screen 492 this._collisions(); // detect collisions and notify sprites after drawing so "reactions" occur next update cycle 493 } 494 495 }; 496 497 // returns boolean. actors should call this during their update() to determine if they can move to a given position 498 this.clipping = function(actor,x,y) { 499 // ensure actor stays on stage floor vertically. their "depth" is determined by the PHI_B of their height 500 if (y < this.y - (actor.h * Punx.Const.PHI_B) || y + actor.h > this.y + this.h) return true; 501 502 // ensure actor can only walk into frame horizontally, not out of frame 503 if (x < this.x || x + actor.w > this.x + this.grid.w) return true; 504 505 // ensure actor doesn't walk through walls 506 for (var i in this._walls) { 507 if (Punx.Util.AABB(this._walls[i],actor.w,actor.h)) return true; 508 } 509 }; 510 511 // draws the screen 512 this._draw = function() { 513 this.grid.ctx.clearRect(0,0,this.grid.w,this.grid.h); 514 for (var i in this._sprites.sort(Punx.Sprite.sort)) { 515 this._sprites[i].draw(this.grid.ctx); 516 } 517 }; 518 519 this._collisions = function() { 520 // check in-frame actors for collision 521 // this performs n(n-1)/2 AABB checks, subchecking pixels in overlapping areas 522 // a pixel is only considered collidable if its opacity is >=127 523 // when two actors collide, both are notified via actor.collide(stage,other actor) 524 // the order of notification is undefined. actors should only "be collided", not do things to effect the other actor as it might not be notified yet. 525 // TODO "z" axis 526 var _actorsLength = this._actors.length; 527 for (var i=0;i<_actorsLength;i++) { 528 var a = this._actors[i]; 529 for (var j=i+1;j<_actorsLength;j++) { 530 var b = this._actors[j]; 531 if (!Punx.Util.AABB(a,b)) continue; // no box collision, move on. 532 533 // triggers don't need pixel detection, bounding box is enough 534 if (a instanceof Punx.Sprite.Actor.Trigger || b instanceof Punx.Sprite.Actor.Trigger) { 535 a.collide(b); 536 b.collide(a); 537 continue; 538 } 539 540 // moving on to pixel detection, 541 // determine intersection 542 var c = Punx.Util.AABBC(a,b); 543 544 // get separate slices of a and b at the intersection using the scratch canvas 545 this.grid.ictx.clearRect(a.x-this.x,a.y,a.w,a.h); // clear scratch space 546 this.grid.draw(a,this.grid.ictx); // draw scratch a 547 c.a = this.grid.ictx.getImageData(c.x-this.x,c.y,c.w,c.h); // slice of a @ intersect 548 this.grid.ictx.clearRect(b.x-this.x,b.y,b.w,b.h); // clear scratch space 549 this.grid.draw(b,this.grid.ictx); // draw scratch b 550 c.b = this.grid.ictx.getImageData(c.x-this.x,c.y,c.w,c.h); // slice of b @ intersect 551 552 // compare slices on a pixel-by-pixel basis according to a resolution 553 var resolution = 5; // jump this many pixels for each check in order to speed it up. 554 // if set to 1, all pixels are checked. 555 resolution *= 4; // each pixel actually takes up 4 elements in the data. 556 557 var sliceLength = c.a.data.length; 558 for (var p=0;p<sliceLength;p+=resolution) { 559 // notify and break when two pixels collide 560 // pixels are collidable when their opacity is >= 127 561 if (c.a.data[p+3] >= 127 && c.b.data[p+3] >= 127) { 562 a.collide(b); 563 b.collide(a); 564 break; 565 } 566 } 567 } 568 } 569 }; 570 571 }; Punx.Screen.Stage.prototype = new Punx.Screen(); 572 573 /* 574 ___ _ _ ___ _ _ 575 / __|_ _(_)__| | / __|_ __ _ _(_) |_ ___ 576 | (_ | '_| / _` | _ \__ \ '_ \ '_| | _/ -_) 577 \___|_| |_\__,_| (_) |___/ .__/_| |_|\__\___| 578 |_| 579 */ 580 // sprites are stage elements that have a drawable face 581 582 Punx.Sprite = function(args) { 583 584 // required 585 this.screen = args['screen']; // Punx.Screen reference 586 this.sheet = new Image(); 587 this.sheet.src = args['sheet']; // sprite sheet url relative to html 588 this.frames = args['frames']; // { group name : [ [ x,y,w,h ] , ... ] , ... } where x,y,w,h is sheet cropping origin point 589 this.group = args['group']; // current frame group name 590 591 // optional 592 this.rate = v['rate']||10; // updates per animation step 593 this.name = args['name']||'Sprite'; 594 this.x = args['x']||0; 595 this.y = args['y']||0; 596 this.z = args['z']||1; // 0 rug, 1 standing, 2 in air, 3 sticky 597 this.w = args['w']||64; 598 this.h = args['h']||64; 599 600 this._step = 0; // animation step index, increments once per rated update prior to step() being called, wraps at length of current group 601 602 this.setGroup = function(name) { 603 this.group = name; 604 this._step %= this.frames[this.group].length; // wrap animation step index 605 }; 606 607 this.update = function() { 608 var updated = false; 609 610 // issue a tick. this is the sprite's custom logic every grid fps. 611 updated |= this.tick(); 612 613 // rate-limited steps 614 if (this.screen.grid.frame % this.rate == 0) { 615 if (this.frames[this.group].length > 1) { 616 updated = true; 617 this._step = ++this._step % this.frames[this.group].length; // increment and wrap animation step index 618 } 619 updated |= this.step(); 620 } 621 622 return updated; 623 }; 624 625 // called whenever the screen calls update() on the sprite. 626 // use this to perform logic per grid frame, return boolean if the sprite is updated 627 this.tick = function() { 628 }; 629 630 // called every rated amount of grid frames 631 // use this to perform rated logic. it may be called whether or not an animation step was made, as groups can have a single frame. 632 // return boolean of whether the sprite is updated 633 this.step = function() { 634 }; 635 636 this.draw = function(ctx) { 637 var frame = this.frames[this.group][this._step]; 638 ctx.drawImage(this.sheet,this.frame[0],this.frame[1],this.frame[2],this.frame[3],this.x - (this.screen.x||0),this.y,this.w,this.h); 639 } 640 }; 641 642 Punx.Sprite.sort = function(a,b) { 643 // sorting function that prioritizes sprites with more depth 644 // this is mainly used when figuring out the drawing order. 645 if (a instanceof Punx.Sprite.Wall ^ b instanceof Punx.Sprite.Wall) return (a instanceof Punx.Sprite.Wall ? -1 : 1); // walls always behind 646 else if (a.z != b.z) return (a.z < b.z ? -1 : 1); 647 else return (a.y + a.h < b.y + b.h ? -1 : 1); // sprite's bottom is further away 648 }; 649 650 651 /* 652 ___ _ _ ___ _ _ __ __ _ _ 653 / __|_ _(_)__| | / __|_ __ _ _(_) |_ ___ \ \ / /_ _| | | 654 | (_ | '_| / _` | _ \__ \ '_ \ '_| | _/ -_) _ \ \/\/ / _` | | | 655 \___|_| |_\__,_| (_) |___/ .__/_| |_|\__\___| (_) \_/\_/\__,_|_|_| 656 |_| 657 */ 658 // a wall is a stage element that actors cannot clip. 659 // walls are not actors, they are inert. 660 661 Punx.Sprite.Wall = function(args) { 662 Punx.Sprite.call(this,args); 663 664 }; Punx.Sprite.Wall.prototype = new Punx.Sprite(); 665 666 667 /* 668 ___ _ _ ___ _ _ _ _ 669 / __|_ _(_)__| | / __|_ __ _ _(_) |_ ___ /_\ __| |_ ___ _ _ 670 | (_ | '_| / _` | _ \__ \ '_ \ '_| | _/ -_) _ / _ \/ _| _/ _ \ '_| 671 \___|_| |_\__,_| (_) |___/ .__/_| |_|\__\___| (_) /_/ \_\__|\__\___/_| 672 |_| 673 */ 674 // actors are any non-inert stage element. these include baddies, powerups, and props. 675 // actors interact, such as via collision. 676 677 Punx.Sprite.Actor = function(args) { 678 Punx.Sprite.call(this,args); 679 680 this.name = args['name']||'Actor'; 681 682 this.busy = false; // set this to disable user input or to denote that a multi-cycle process is taking place and you dont want to be interrupted 683 684 // get collided by another actor, react to it. 685 // this is meant to be overloaded. 686 // dont modify the other actor, as notification order is undefined. it's only provided for context so you can react appropriately. 687 // chances are if its the initial collision then there will be a large swath of calls to this function within a short period of time, as 688 // collisions are checked every screen update. 689 // set flags etc accordingly to "debounce" collision notifications. 690 // for example, when the player is collided by a baddy, inflict self-damage and set a flag and timeout for recover-time invulnerability. 691 this.collide = function(actor) { 692 }; 693 694 this.move = function(stage,direction,amount) { 695 amount = amount || 5; 696 this.setGroup(direction); 697 var x = this.x; 698 var y = this.y; 699 switch (direction) { 700 case 'left' : { x -= amount; break; } 701 case 'right' : { x += amount; break; } 702 case 'up' : { y -= amount; break; } 703 case 'down' : { y += amount; break; } 704 } 705 if (!stage.clipping(this,x,y)) { 706 this.x = x; 707 this.y = y; 708 } 709 return true; 710 } 711 712 }; Punx.Sprite.Actor.prototype = new Punx.Sprite(); 713 714 715 /* 716 ___ _ _ ___ _ _ _ _ _____ _ 717 / __|_ _(_)__| | / __|_ __ _ _(_) |_ ___ /_\ __| |_ ___ _ _ |_ _| _(_)__ _ __ _ ___ _ _ 718 | (_ | '_| / _` | _ \__ \ '_ \ '_| | _/ -_) _ / _ \/ _| _/ _ \ '_| _ | || '_| / _` / _` / -_) '_| 719 \___|_| |_\__,_| (_) |___/ .__/_| |_|\__\___| (_) /_/ \_\__|\__\___/_| (_) |_||_| |_\__, \__, \___|_| 720 |_| |___/|___/ 721 */ 722 // triggers are actors that collide with just bounding box 723 724 Punx.Sprite.Actor.Trigger = function(v) { 725 Punx.Sprite.Actor.call(this,v=v||{}); 726 this.name = 'Trigger'; 727 }; Punx.Sprite.Actor.Trigger.prototype = new Punx.Sprite.Actor(); 728 729 /* 730 ___ _ _ ___ _ _ _ _ ___ _ 731 / __|_ _(_)__| | / __|_ __ _ _(_) |_ ___ /_\ __| |_ ___ _ _ | _ \ |__ _ _ _ ___ _ _ 732 | (_ | '_| / _` | _ \__ \ '_ \ '_| | _/ -_) _ / _ \/ _| _/ _ \ '_| _ | _/ / _` | || / -_) '_| 733 \___|_| |_\__,_| (_) |___/ .__/_| |_|\__\___| (_) /_/ \_\__|\__\___/_| (_) |_| |_\__,_|\_, \___|_| 734 |_| |__/ 735 */ 736 // the user 737 738 Punx.Sprite.Actor.Player = function(v) { 739 Punx.Sprite.Actor.call(this,v=v||{}); 740 this.name = 'Player'; 741 this.update = function(stage) { 742 if (!this.busy) { 743 with (Punx.Key) { 744 if (key['left'] || key['a']) this.move(stage,'left'); 745 else if (key['right'] || key['d']) this.move(stage,'right'); 746 if (key['down'] || key['s']) this.move(stage,'down'); 747 else if (key['up'] || key['w']) this.move(stage,'up'); 748 } 749 } 750 Punx.Sprite.Actor.Player.prototype.update.call(this,stage); 751 }; 752 }; Punx.Sprite.Actor.Player.prototype = new Punx.Sprite.Actor(); 753 754