topical media & game development

talk show tell print

mobile-game-ch21-pong-public-js-quintus-platformer.js / js



  
  Quintus.Platformer = function(Q) {
  
    Q.gravityY = 9.8*100;
    Q.gravityX = 0;
    Q.dx = 0.05;
  
    Q.register('2d',{
  
      added: function() {
        var entity = this.entity;
        _(entity.p).defaults({
          vx: 0,
          vy: 0,
          ax: 0,
          ay: 0,
          gravity: 1,
          collisionMask: 1
        });
  
        entity.bind('step',this,"step");
        if(Q.debug) {
          entity.bind('draw',this,'debugDraw');
        }
      },
  
      extend: {
  
        collisionPoints: function(points) {
          var p = this.p, w = p.w, h = p.h;
          if(!points) {
            p.col = {
              top:   [ [w/2, 0]],
              left:  [ [0, h/3], [0, 2*h/3]],
              bottom:[ [w/2, h]],
              right: [ [w, h/3], [w, 2*h/3]]
            }
          } else {
            p.col = points;
          }
        }
  
      },
  
      step: function(stepData) {
        var p = this.entity.p,
            dtStep = stepData.dt,
            dt = dtStep;
  
        while(dtStep > 0) {
          dt = Math.min(1/30,dtStep);
          // Updated based on the velocity and acceleration
          p.vx += p.ax * dt + Q.gravityX * dt * p.gravity;
          p.vy += p.ay * dt + Q.gravityY * dt * p.gravity;
  
          p.x += p.vx * dt;
          p.y += p.vy * dt;
  
          this.entity.parent.collide(this.entity);
          dtStep -= 1/30;
        }
  
   
      },
  
      debugDraw: function(ctx) {
        var p = this.entity.p;
  
        ctx.save();
        ctx.fillStyle = "black";
        if(p.col) {
          _.each(p.col,function(points,dir) {
            for(var i=0;i<points.length;i++) {
              ctx.fillRect(p.x + points[i][0] - 2,
                           p.y + points[i][1] - 2,
                           4,4);
            }
          });
        }
        ctx.restore();
  
      }
  
    });
  
    
    Q.TileLayer = Q.Sprite.extend({
  
      init: function(props) {
        this._super(_(props).defaults({
          tileW: 32,
          tileH: 32,
          blockTileW: 10,
          blockTileH: 10,
          type: 1
        }));
  
        if(this.p.dataAsset) {
          this.load(this.p.dataAsset);
        }
  
        this.blocks = [];
        this.p.blockW = this.p.tileW * this.p.blockTileW;
        this.p.blockH = this.p.tileH * this.p.blockTileH;
        this.colBounds = {}; 
        this.directions = [ 'top','left','bottom','right'];
      },
  
      checkBounds: function(pos,col,start) {
        start = start || 0;
        for(var i=0;i<4;i++) {
          var dir = this.directions[(i+start)%4];
          var result = this.checkPoints(pos,col[dir],dir);
          if(result) {
            result.start = i+1;
            return result;
          }
        }
        return false;
      },
  
      checkPoints: function(pos,pts,which) {
        for(var i=0,len=pts.length;i<len;i++) {
          var result = this.checkPoint(pos.x+pts[i][0],pos.y+pts[i][1],which);
          if(result) {
            result.point = pts[i];
            return result;
          }
        }
        return false;
      },
  
      checkPoint: function(x,y,which) {
        var p = this.p,
            tileX = Math.floor((x - p.x) / p.tileW),
            tileY = Math.floor((y - p.y) / p.tileH);
  
        if(p.tiles[tileY] && p.tiles[tileY][tileX] > 0) {
          this.colBounds.tile = p.tiles[tileY][tileX];
          this.colBounds.direction = which;
          switch(which) {
            case 'top':
              this.colBounds.destX = x;
              this.colBounds.destY = (tileY+1)*p.tileH + p.y + Q.dx;
              break;
            case 'bottom':
              this.colBounds.destX = x;
              this.colBounds.destY = tileY*p.tileH + p.y - Q.dx;
              break;
            case 'left':
              this.colBounds.destX = (tileX+1)*p.tileW + p.x + Q.dx;
              this.colBounds.destY = y;
              break;
            case 'right':
              this.colBounds.destX = tileX*p.tileW + p.x - Q.dx;
              this.colBounds.destY = y;
              break;
          }
          return this.colBounds;
        }
        return false;
      },
  
      load: function(dataAsset) {
        var data = _.isString(dataAsset) ?  Q.asset(dataAsset) : dataAsset;
  
        this.p.tiles = data;
        this.p.rows = data.length;
        this.p.cols = data[0].length;
        this.p.w = this.p.rows * this.p.tileH;
        this.p.h = this.p.cols * this.p.tileW;
      },
  
      setTile: function(x,y,tile) {
        var p = this.p,
            blockX = Math.floor(x/p.blockTileW),
            blockY = Math.floor(y/p.blockTileH);
  
        if(blockX >= 0 && blockY >= 0 &&
           blockX < this.p.cols &&
           blockY <  this.p.cols) {
          this.p.tiles[y][x] = tile;
          if(this.blocks[blockY]) {
            this.blocks[blockY][blockX] = null;
          }
        }
      },
  
      prerenderBlock: function(blockX,blockY) {
        var p = this.p,
            tiles = p.tiles,
            sheet = this.sheet(),
            blockOffsetX = blockX*p.blockTileW,
            blockOffsetY = blockY*p.blockTileH;
  
        if(blockOffsetX < 0 || blockOffsetX >= this.p.cols ||
           blockOffsetY < 0 || blockOffsetY >= this.p.rows) {
             return;
        }
  
        var canvas = document.createElement('canvas'),
            ctx = canvas.getContext('2d');
  
        canvas.width = p.blockW;
        canvas.height= p.blockH;
  
        this.blocks[blockY] = this.blocks[blockY] || {};
        this.blocks[blockY][blockX] = canvas;
  
        for(var y=0;y<p.blockTileH;y++) {
          if(tiles[y+blockOffsetY]) {
            for(var x=0;x<p.blockTileW;x++) {
              if(tiles[y+blockOffsetY][x+blockOffsetX]) {
                sheet.draw(ctx,
                           x*p.tileW,
                           y*p.tileH,
                           tiles[y+blockOffsetY][x+blockOffsetX]);
              }
            }
          }
        }
      },
  
      drawBlock: function(ctx, blockX, blockY) {
        var p = this.p,
            startX = Math.floor(blockX * p.blockW + p.x),
            startY = Math.floor(blockY * p.blockH + p.y);
  
        if(!this.blocks[blockY] || !this.blocks[blockY][blockX]) {
          this.prerenderBlock(blockX,blockY);
        }
  
        if(this.blocks[blockY]  && this.blocks[blockY][blockX]) {
          ctx.drawImage(this.blocks[blockY][blockX],startX,startY);
        }
      },
  
      draw: function(ctx) {
        var p = this.p,
            viewport = this.parent.viewport,
            viewW = Q.width / viewport.scale,
            viewH = Q.height / viewport.scale,
            startBlockX = Math.floor((viewport.x - p.x) / p.blockW),
            startBlockY = Math.floor((viewport.y - p.y) / p.blockH),
            endBlockX = Math.floor((viewport.x + viewW - p.x) / p.blockW),
            endBlockY = Math.floor((viewport.y + viewH - p.y) / p.blockH);
  
        for(var y=startBlockY;y<=endBlockY;y++) {
          for(var x=startBlockX;x<=endBlockX;x++) {
            this.drawBlock(ctx,x,y);
          }
        }
      }
  
    });
  
    Q.PlatformStage = Q.Stage.extend({
  
      collisionLayer: function(layer) {
        this.collision = this.insert(layer);
      },
  
      _tileCollision: function(obj,start) {
        if(obj.p.col) {
          var result = this.collision.checkBounds(obj.p,obj.p.col,start);
          if(result) {
            return result;
          }
        }
        return false;
      },
  
      _hitTest: function(obj,collision) {
        if(obj != this && this != collision && 
           this.p.type && (this.p.type & obj.p.collisionMask )) {
          var col = Q.overlap(obj,this);
          return col ? this : false;
        }
        return false;
      },
    
      collide: function(obj) {
        var col;
        if(obj.p.collisionMask & this.collision.p.type) {
          while(col = this._tileCollision(obj,col ? col.start : 0)) {
            if(col) {
              var destX = col.destX - col.point[0],
                  destY = col.destY - col.point[1];
  
              obj.p.x = destX;
              obj.p.y = destY;
              if(col.direction == 'top' || col.direction == 'bottom') {
                obj.p.vy = 0;
              } else {
                obj.p.vx = 0;
              }
              obj.trigger('hit',this.collision);
              obj.trigger('hit.tile',col);
            }
          }
        }
  
        col = this.detect(this._hitTest,obj,this.collision);
        if(col) {
          obj.trigger('hit',col);
          obj.trigger('hit.sprite',col);
        }
      }
  
    });
  
  };
  
  


(C) Æliens 04/09/2009

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.