Week 4 Lab Sheet — Objects & Physics

Web Games Development · Objects, mouse & gravity · ~150 minutes

Objectives

  1. Create objects with constructor functions and new.
  2. Give objects their own data and methods (draw(), moveit()).
  3. Keep objects in an array and redraw them with one loop.
  4. Handle mouse input with mousedown, mousemove and mouseup.
  5. Hit-test a click against a circle using squared distance.
  6. Turn a drag into a velocity with Math.atan2, Math.cos and Math.sin.
  7. Simulate projectile motion with gravity and detect collisions.

Instructions

Create a folder named Week4 and copy the materials (chicken.jpg, feathers.gif, hill.jpg, plateau.jpg) into it. Create each file below, writing the code shown. Save and refresh after each change. Tick each checkpoint.

Files

1. cannball1.html Objectives 1, 2, 3, 7

Week4\cannball1.html

  1. [Obj 1] Create the file and add this scaffold code.
    <!DOCTYPE html>
    <html lang="en">
    <head><title>Cannonball</title></head>
    <body>
      <canvas id="canvas" width="600" height="400"></canvas>
      <br />
      <form name="f" onsubmit="return fire();">
        Horizontal <input name="hv" value="10" type="number" />
        Vertical <input name="vv" value="-25" type="number" />
        <input type="submit" value="FIRE" />
      </form>
    
      <script>
        var ctx = document.getElementById("canvas").getContext("2d");
        var everything = [];
        var tid, horvelocity, verticalvel1, verticalvel2, gravity = 2;
    
        // TODO: create Ball and Myrectangle objects with a draw() method
        // TODO: build the scene and push the objects into everything
    
        function drawall() {
          ctx.clearRect(0, 0, 600, 400);
          for (var i = 0; i < everything.length; i++) { everything[i].draw(); }
        }
        function fire() {
          // TODO: set the velocities and start the animation
          return false;
        }
        function change() {
          // TODO: apply gravity and move the ball
        }
      </script>
    </body>
    </html>
  2. [Obj 1, 2] Add the object constructors with a draw() method.
    function Ball(sx, sy, rad, style) {
      this.sx = sx; this.sy = sy; this.rad = rad; this.fillstyle = style;
      this.draw = drawball; this.moveit = moveball;
    }
    function drawball() { ctx.fillStyle = this.fillstyle; ctx.beginPath(); ctx.arc(this.sx, this.sy, this.rad, 0, Math.PI * 2, true); ctx.fill(); }
    function moveball(dx, dy) { this.sx += dx; this.sy += dy; }
    function Myrectangle(sx, sy, swidth, sheight, style) {
      this.sx = sx; this.sy = sy; this.swidth = swidth; this.sheight = sheight;
      this.fillstyle = style; this.draw = drawrects; this.moveit = moverect;
    }
    function drawrects() { ctx.fillStyle = this.fillstyle; ctx.fillRect(this.sx, this.sy, this.swidth, this.sheight); }
    function moverect(dx, dy) { this.sx += dx; this.sy += dy; }
  3. [Obj 1, 3] Create the objects and put them in an array.
    var cball = new Ball(20, 300, 10, "rgb(250,0,0)");
    var target = new Myrectangle(300, 100, 80, 200, "rgb(0,5,90)");
    var ground = new Myrectangle(0, 300, 600, 30, "rgb(10,250,0)");
    var everything = [];
    everything.push(target);
    everything.push(ground);
    everything.push(cball);
  4. [Obj 3, 7] Add change() for gravity.
    function change() {
      verticalvel2 = verticalvel1 + gravity;
      var dy = (verticalvel1 + verticalvel2) * 0.5;
      verticalvel1 = verticalvel2;
      cball.moveit(horvelocity, dy);
      drawall();
    }
  5. [Obj 1, 7] Add fire() to launch the ball.
    function fire() {
      cball.sx = 20; cball.sy = 300;
      horvelocity = Number(document.f.hv.value);
      verticalvel1 = Number(document.f.vv.value);
      drawall();
      tid = setInterval(change, 100);
      return false;
    }
Checkpoint: cannball1.html fires a ball that follows a gravity arc (Objectives 1, 2, 3, 7).

2. cannBall2.html Objectives 1, 2, 3, 6, 7

Week4\cannBall2.html

  1. [Obj 1] Create the file and add this scaffold code (your cannonball with a rotating cannon and an angle input).
    <!DOCTYPE html>
    <html lang="en">
    <head><title>Cannonball</title></head>
    <body>
      <canvas id="canvas" width="600" height="400"></canvas>
      <br />
      <form name="f" onsubmit="return fire();">
        Velocity <input name="vo" value="10" type="number" />
        Angle <input name="ang" value="45" type="number" />
        <input type="submit" value="FIRE" />
      </form>
    
      <script>
        var ctx = document.getElementById("canvas").getContext("2d");
        var everything = [];
        var tid, outofcannon, horvelocity, verticalvel1, verticalvel2, gravity = 2;
    
        // TODO: create the target, ground, ball and cannon objects
        function drawall() {
          ctx.clearRect(0, 0, 600, 400);
          // TODO: draw each object (rotate the cannon)
        }
        function fire() {
          // TODO: convert the angle to velocity components and start the animation
          return false;
        }
        function change() {
          // TODO: apply gravity, move the ball and check the target
        }
      </script>
    </body>
    </html>
  2. [Obj 1, 2] Add the object constructors (Ball, Myrectangle, Picture) with a draw() method.
    function Ball(sx, sy, rad, style) {
      this.sx = sx; this.sy = sy; this.rad = rad; this.fillstyle = style;
      this.draw = drawball; this.moveit = moveball;
    }
    function Myrectangle(sx, sy, swidth, sheight, style) {
      this.sx = sx; this.sy = sy; this.swidth = swidth; this.sheight = sheight;
      this.fillstyle = style; this.draw = drawrects; this.moveit = moverect;
    }
    function Picture(sx, sy, swidth, sheight, file) {
      var imga = new Image(); imga.src = file;
      this.sx = sx; this.sy = sy; this.img = imga;
      this.swidth = swidth; this.sheight = sheight; this.draw = drawAnImage;
    }
    function drawball() { ctx.fillStyle = this.fillstyle; ctx.beginPath(); ctx.arc(this.sx, this.sy, this.rad, 0, Math.PI * 2, true); ctx.fill(); }
    function moveball(dx, dy) { this.sx += dx; this.sy += dy; }
    function drawrects() { ctx.fillStyle = this.fillstyle; ctx.fillRect(this.sx, this.sy, this.swidth, this.sheight); }
    function moverect(dx, dy) { this.sx += dx; this.sy += dy; }
    function drawAnImage() { ctx.drawImage(this.img, this.sx, this.sy, this.swidth, this.sheight); }
  3. [Obj 3] Add the scene constants and objects, then put them in everything.
    var cannonx = 10, cannony = 280, cannonlength = 200, cannonht = 20;
    var ballrad = 10;
    var targetx = 500, targety = 50, targetw = 85, targeth = 280;
    var htargetx = 450, htargety = 220, htargetw = 355, htargeth = 96;
    
    var cball = new Ball(cannonx + cannonlength, cannony + cannonht * 0.5, ballrad, "rgb(250,0,0)");
    var target = new Picture(targetx, targety, targetw, targeth, "hill.jpg");
    var htarget = new Picture(htargetx, htargety, htargetw, htargeth, "plateau.jpg");
    var ground = new Myrectangle(0, 300, 600, 30, "rgb(10,250,0)");
    var cannon = new Myrectangle(cannonx, cannony, cannonlength, cannonht, "rgb(40,40,0)");
    
    everything.push([target, false]);
    everything.push([ground, false]);
    everything.push([cball, false]);
    everything.push([cannon, true, 0, cannonx, cannony + cannonht * 0.5]);
  4. [Obj 1, 6, 7] Add fire() to convert the angle and launch.
    function fire() {
      var angle = Number(document.f.ang.value);
      var outofcannon = Number(document.f.vo.value);
      var angleradians = angle * Math.PI / 180;
      horvelocity = outofcannon * Math.cos(angleradians);
      verticalvel1 = -outofcannon * Math.sin(angleradians);
      everything[3][2] = -angleradians;
      cball.sx = cannonx + cannonlength * Math.cos(angleradians);
      cball.sy = cannony + cannonht * 0.5 - cannonlength * Math.sin(angleradians);
      drawall();
      tid = setInterval(change, 100);
      return false;
    }
  5. [Obj 2, 3] Add drawall() to draw every object (rotating the cannon).
    function drawall() {
      ctx.clearRect(0, 0, 600, 400);
      for (var i = 0; i < everything.length; i++) {
        var ob = everything[i];
        if (ob[1]) {
          ctx.save();
          ctx.translate(ob[3], ob[4]);
          ctx.rotate(ob[2]);
          ctx.translate(-ob[3], -ob[4]);
          ob[0].draw();
          ctx.restore();
        } else {
          ob[0].draw();
        }
      }
    }
  6. [Obj 7] Add change() to move the ball and swap the target when it is hit (hill → plateau).
    function change() {
      var dx = horvelocity;
      verticalvel2 = verticalvel1 + gravity;
      var dy = (verticalvel1 + verticalvel2) * 0.5;
      verticalvel1 = verticalvel2;
      cball.moveit(dx, dy);
      var bx = cball.sx, by = cball.sy;
      if (bx >= target.sx && bx <= target.sx + target.swidth && by >= target.sy && by <= target.sy + target.sheight) {
        clearInterval(tid);
        everything[0][0] = htarget;
        drawall();
      }
      if (by >= ground.sy) { clearInterval(tid); }
      drawall();
    }
Checkpoint: the cannon rotates and can hit the target (Objectives 1, 2, 3, 6, 7).

3. slingshot.html Objectives 1, 2, 3, 4, 5, 6, 7

Week4\slingshot.html

  1. [Obj 7] Create the file and add this scaffold code.
    <!DOCTYPE html>
    <html lang="en">
    <head><title>Slingshot pulling back</title></head>
    <body>
      <canvas id="canvas" width="1200" height="600"></canvas>
      <br />
      Mouse down and drag ball, then release to shoot.
    
      <script>
        var ctx, canvas1, everything = [], tid;
        var ballrad = 10, ballradsq = ballrad * ballrad;
        var inmotion = false, horvelocity, verticalvel1, verticalvel2, gravity = 2;
        var chicken = new Image();  chicken.src = "chicken.jpg";
        var feathers = new Image(); feathers.src = "feathers.gif";
    
        // TODO: create Sling, Ball and Picture objects
        function init() {
          ctx = document.getElementById("canvas").getContext("2d");
          canvas1 = document.getElementById("canvas");
          // TODO: add mousedown, mousemove and mouseup listeners
          // TODO: build the scene and draw it
        }
        // TODO: add findball(), moveit(), finish(), drawall() and change()
      </script>
    </body>
    </html>
  2. [Obj 1, 2] Add the object constructors and create the objects.
    var startrockx = 100, startrocky = 240;
    function Sling(bx, by, s1x, s1y, s2x, s2y, s3x, s3y, style) {
      this.bx = bx; this.by = by; this.s1x = s1x; this.s1y = s1y;
      this.s2x = s2x; this.s2y = s2y; this.s3x = s3x; this.s3y = s3y;
      this.strokeStyle = style; this.draw = drawsling; this.moveit = movesling;
    }
    function drawsling() {
      ctx.strokeStyle = this.strokeStyle; ctx.lineWidth = 4;
      ctx.beginPath();
      ctx.moveTo(this.bx, this.by); ctx.lineTo(this.s1x, this.s1y);
      ctx.moveTo(this.bx, this.by); ctx.lineTo(this.s2x, this.s2y);
      ctx.moveTo(this.s1x, this.s1y); ctx.lineTo(this.s2x, this.s2y); ctx.lineTo(this.s3x, this.s3y);
      ctx.stroke();
    }
    function movesling(dx, dy) { this.bx += dx; this.by += dy; this.s1x += dx; this.s1y += dy; this.s2x += dx; this.s2y += dy; this.s3x += dx; this.s3y += dy; }
    function Ball(sx, sy, rad, style) {
      this.sx = sx; this.sy = sy; this.rad = rad; this.fillstyle = style;
      this.draw = drawball; this.moveit = moveball;
    }
    function drawball() { ctx.fillStyle = this.fillstyle; ctx.beginPath(); ctx.arc(this.sx, this.sy, this.rad, 0, Math.PI * 2, true); ctx.fill(); }
    function moveball(dx, dy) { this.sx += dx; this.sy += dy; }
    function Myrectangle(sx, sy, swidth, sheight, style) {
      this.sx = sx; this.sy = sy; this.swidth = swidth; this.sheight = sheight;
      this.fillstyle = style; this.draw = drawrects;
    }
    function drawrects() { ctx.fillStyle = this.fillstyle; ctx.fillRect(this.sx, this.sy, this.swidth, this.sheight); }
    function Picture(sx, sy, swidth, sheight, imga) {
      this.sx = sx; this.sy = sy; this.img = imga; this.swidth = swidth; this.sheight = sheight; this.draw = drawAnImage;
    }
    function drawAnImage() { ctx.drawImage(this.img, this.sx, this.sy, this.swidth, this.sheight); }
    
    var mysling = new Sling(startrockx, startrocky, startrockx + 80, startrocky - 10, startrockx + 80, startrocky + 10, startrockx + 70, startrocky + 180, "rgb(120,20,10)");
    var cball = new Ball(startrockx, startrocky, ballrad, "rgb(250,0,0)");
    var target = new Picture(700, 210, 209, 179, chicken);
    var ground = new Myrectangle(0, 370, 1200, 30, "rgb(10,250,0)");
  3. [Obj 3, 4] Add init() to wire the mouse events and build the scene.
    function init() {
      ctx = document.getElementById("canvas").getContext("2d");
      canvas1 = document.getElementById("canvas");
      canvas1.addEventListener("mousedown", findball, false);
      canvas1.addEventListener("mousemove", moveit, false);
      canvas1.addEventListener("mouseup", finish, false);
      everything.push(target);
      everything.push(ground);
      everything.push(mysling);
      everything.push(cball);
      drawall();
    }
  4. [Obj 4, 5] Add findball() (hit-test) and distsq().
    function findball(ev) {
      var mx = ev.layerX, my = ev.layerY;
      if (distsq(mx, my, cball.sx, cball.sy) < ballradsq) { inmotion = true; drawall(); }
    }
    function distsq(x1, y1, x2, y2) { return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); }
  5. [Obj 4] Add moveit() to drag the ball and the sling.
    function moveit(ev) {
      if (!inmotion) { return; }
      cball.sx = ev.pageX; cball.sy = ev.pageY;
      mysling.bx = ev.pageX; mysling.by = ev.pageY;
      drawall();
    }
  6. [Obj 5, 6] Add finish() to turn the drag into a velocity (squared distance + atan2).
    function finish(ev) {
      if (!inmotion) { return; }
      inmotion = false;
      var outofcannon = distsq(mysling.bx, mysling.by, mysling.s1x, mysling.s1y) / 700;
      var angleradians = -Math.atan2(mysling.s1y - mysling.by, mysling.s1x - mysling.bx);
      horvelocity = outofcannon * Math.cos(angleradians);
      verticalvel1 = -outofcannon * Math.sin(angleradians);
      tid = setInterval(change, 100);
    }
  7. [Obj 2, 3] Add drawall() and change() (gravity + hit the chicken).
    function drawall() {
      ctx.clearRect(0, 0, 1200, 600);
      for (var i = 0; i < everything.length; i++) { everything[i].draw(); }
    }
    function change() {
      var dx = horvelocity;
      verticalvel2 = verticalvel1 + gravity;
      var dy = (verticalvel1 + verticalvel2) * 0.5;
      verticalvel1 = verticalvel2;
      cball.moveit(dx, dy);
      var bx = cball.sx, by = cball.sy;
      if (bx >= target.sx + 40 && bx <= target.sx + target.swidth - 40 && by >= target.sy + 40 && by <= target.sy + target.sheight - 40) {
        target.img = feathers;
      }
      if (by >= ground.sy) { clearInterval(tid); }
      drawall();
    }
Checkpoint: dragging launches the ball and hitting the chicken changes it to feathers (Objectives 1-7).

Marking rubric

CriterionPointsScore
Objects built with constructors and new3
Objects stored in an array and redrawn in a loop2
Mouse events wired correctly3
Drag turned into velocity with atan2 / cos / sin3
Gravity arc and collision detection work3
Runs with no console errors1
Total15

Common errors

Progress and quiz scores are also tracked on the Week 4 lab page in the class website.