Week 4 — Objects & Physics
Real games are built from objects, not loose variables. This week you organise the scene into reusable objects, follow the mouse, and fire a projectile along a realistic gravity arc.
🎯 Objectives
- Create objects with constructor functions and the
newkeyword. - Give objects their own data and methods (e.g.
draw(),moveit()). - Keep every object in an array and redraw them all with one loop.
- Handle mouse input with
mousedown,mousemoveandmouseup. - Hit-test a click against a circle using squared distance.
- Turn a drag into a velocity using
Math.atan2,Math.cosandMath.sin. - Simulate projectile motion with gravity and velocity integration.
- Detect collisions with the target and the ground.
🛠 Weekly tasks
- Objective 1 — create objects with constructor functions and the
newkeyword: Activity 1 + buildcannball1.html. - Objective 2 — give objects their own data and methods (
draw(),moveit()): Activity 2 + buildcannball1.html. - Objective 3 — keep every object in an array and redraw them with one loop: Activity 3 + build
cannball1.html. - Objective 4 — handle mouse input with
mousedown,mousemoveandmouseup: Activity 4 + buildslingshot.html. - Objective 5 — hit-test a click against a circle using squared distance: Activity 5 + build
slingshot.html. - Objective 6 — turn a drag into a velocity with
Math.atan2,Math.cosandMath.sin: Activity 6 + buildcannBall2.htmlandslingshot.html. - Objective 7 — simulate projectile motion with gravity and velocity integration: Activity 7 + build
cannball1.htmlandcannBall2.html. - Objective 8 — detect collisions with the target and the ground: Activity 8 + build
slingshot.html. - Build your own
slingshot_yourname.html— using everything you have learned; keep a score of how many times you hit the chicken. (All objectives)
🎓 Lecture activity Open lecture slides →
A 90-minute session of 35 slides. Each concept is taught four ways: the big idea, a short example with its live output, a line-by-line explanation, and a 5-minute “try it” task. The lab work at 1:00 uses the Lab activity below.
Session plan
- 0:0010 minWarm-upReview questions on last week
- 0:1010 minGame & objectivesThis week’s game, objectives and key words
- 0:2035 minConceptsBig idea → code with live output → line by line → try it
- 0:555 minCheck understandingCommon mistakes and a 4-question quiz
- 1:0025 minLabBuild the files in the Lab activity below
- 1:255 minWrap-upBuild-your-own task, marking guide and recap
Concepts this week
- 1. Objects & arrays — Objects that draw themselves
- 2. Gravity — Gravity bends the path
- 3. Angles & rotation — Aim with an angle
- 4. Mouse events — Press, drag, release
The slides open as a page on this site. Press F for full screen, the arrow keys to move, N for speaker notes, and P to save as PDF (turn on “Background graphics”).
🧪 Lab activity: work through the files Printable lab sheet
Create a folder named Week4 on your PC. Work through the files below
in order, creating each one and writing the code shown. Download the materials
(chicken.jpg, feathers.gif, hill.jpg, plateau.jpg) into the
folder. Save each file and refresh the browser.
Want a head start? A scaffold file starter.html is provided — choose it in the
preview dropdown, copy it into your Week4 folder, and complete its TODOs.
-
Create your Week4 folder
On your own PC — do this once before starting.
- Create a folder named
Week4on your PC. - Download the materials above into it.
- Open your
Week4folder in VS Code or Sublime Text.
- Create a folder named
-
cannball1.html Objectives 1, 2, 3, 7
Week4\cannball1.html — your first object, moving under gravity.
- [Obj 1, 7] Create
cannball1.htmland add the page: the canvas, the form and the variables.onLoad="init();"runs the setup, and the form callsfire().<!DOCTYPE html> <html lang="en"> <head> <title>Cannonball</title> <style> form { width: 330px; margin: 20px; background-color: brown; padding: 20px; } </style> </head> <body onLoad="init();"> <canvas id="canvas" width="600" height="400"> Your browser doesn't support the HTML5 element canvas. </canvas> <br /> <form name="f" id="f" onSubmit="return fire();"> Set velocities and fire the cannonball. <br /> Horizontal <input name="hv" id="hv" value="10" type="number" min="-100" max="100" /> <br /> Vertical <input name="vv" id="vv" value="-25" type="number" min="-100" max="100" /> <input type="submit" value="FIRE" /> </form> <script> var cwidth = 600, cheight = 400; var ctx, everything = [], tid; var horvelocity, verticalvel1, verticalvel2, gravity = 2; var iballx = 20, ibally = 300; // the constructors, the objects and the functions go here </script> </body> </html> - [Obj 1, 2] Add the
Ballconstructor with its owndrawandmoveitmethods, then theMyrectangleconstructor.function Ball(sx, sy, rad, style) { this.sx = sx; this.sy = sy; this.rad = rad; this.fillstyle = style; this.draw = drawball; // the object's own method 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; } - [Obj 1, 3] Create the objects with
newand push them into theeverythingarray.var cball = new Ball(iballx, ibally, 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)"); everything.push(target); everything.push(ground); everything.push(cball); - [Obj 3] Add
init()anddrawall().drawall()clears the canvas and callsdraw()on every object — the same two lines draw the whole scene.function init() { ctx = document.getElementById("canvas").getContext("2d"); drawall(); } function drawall() { ctx.clearRect(0, 0, cwidth, cheight); for (var i = 0; i < everything.length; i++) { everything[i].draw(); } } - [Obj 7, 8] Add
change(): integrate the vertical velocity with gravity (using the average of the old and new velocity for a smooth arc), then stop the timer when the ball reaches the target or the ground.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); } if (by >= ground.sy) { clearInterval(tid); } drawall(); } - [Obj 1, 7] Add
fire(): reset the ball to its start, read the two numbers, draw, then start the timer.function fire() { cball.sx = iballx; cball.sy = ibally; horvelocity = Number(document.f.hv.value); verticalvel1 = Number(document.f.vv.value); drawall(); tid = setInterval(change, 100); return false; } - Save and refresh, then fire a few shots.
- [Obj 1, 7] Create
-
cannBall2.html Objectives 1, 2, 3, 6, 7
Week4\cannBall2.html — cannon with angle and a hill target.
- [Obj 1] Create
cannBall2.htmland add this scaffold code (your cannonball with a rotating cannon and an angle input).<!DOCTYPE html> <html lang="en"> <head> <title>Cannonball</title> <style> form { width: 330px; margin: 20px; background-color: brown; padding: 20px; } </style> </head> <body onLoad="init();"> <canvas id="canvas" width="600" height="400"> Your browser doesn't support the HTML5 element canvas. </canvas> <br /> <form name="f" id="f" onSubmit="return fire();"> Set velocity, angle and fire cannonball. <br /> Velocity out of cannon <input name="vo" id="vo" value="10" type="number" min="-100" max="100" /> <br /> Angle <input name="ang" id="ang" value="0" type="number" min="0" max="80" /> <input type="submit" value="FIRE" /> </form> <script> var cwidth = 600, cheight = 400; var ctx; var everything = []; var tid, outofcannon, horvelocity, verticalvel1, verticalvel2, gravity = 2; // TODO: create the target, ground, ball and cannon objects // TODO: add init() function drawall() { ctx.clearRect(0, 0, cwidth, cheight); // 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> - [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 = moveball; } 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; 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 drawrects() { ctx.fillStyle = this.fillstyle; ctx.fillRect(this.sx, this.sy, this.swidth, this.sheight); } function drawAnImage() { ctx.drawImage(this.img, this.sx, this.sy, this.swidth, this.sheight); } - [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)"); var targetindex = everything.length; everything.push([target, false]); everything.push([ground, false]); var ballindex = everything.length; everything.push([cball, false]); var cannonindex = everything.length; // remember this to rotate the cannon later everything.push([cannon, true, 0, cannonx, cannony + cannonht * 0.5]); - [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[cannonindex][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; } - [Obj 2, 3] Add
drawall()to draw every object (rotating the cannon).function drawall() { ctx.clearRect(0, 0, cwidth, cheight); 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(); } } } - [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.splice(targetindex, 1, [htarget, false]); // swap hill -> plateau everything.splice(ballindex, 1); // remove the ball on a hit drawall(); } if (by >= ground.sy) { clearInterval(tid); } drawall(); } - [Obj 1, 2] Add
init(), called byonLoad, to get the context and draw the first frame.function init() { ctx = document.getElementById("canvas").getContext("2d"); drawall(); } - Save and refresh, then find the best angle and velocity.
- [Obj 1] Create
-
slingshot.html Objectives 1, 2, 3, 4, 5, 6, 7
Week4\slingshot.html — drag the ball to launch it at the chicken.
- [Obj 7] Create
slingshot.htmland add this scaffold code.<!DOCTYPE html> <html lang="en"> <head><title>Slingshot pulling back</title></head> <body onLoad="init();"> <canvas id="canvas" width="1200" height="600"> Your browser doesn't support the HTML5 element canvas. </canvas> <br /> Mouse down and drag the ball. Releasing the mouse button will shoot the slingshot. <script> var cwidth = 1200, cheight = 600; 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> - [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)"); - [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(); } - [Obj 4, 5] Add
findball()(hit-test) anddistsq().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); } - [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(); } - [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); } - [Obj 2, 3] Add
drawall()andchange()(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(); } - Save and refresh, then drag and release the ball.
- [Obj 7] Create
📝 Tasks for this file:
In slingshot.html the mouse position comes from ev.pageX/pageY. When you
release, the length of the drag sets the launch speed and the direction sets the angle.
📚 The projectile maths
- Speed is based on drag length:
distsq(bx,by,s1x,s1y)/700. - Angle uses
Math.atan2(s1y-by, s1x-bx). - Horizontal velocity is constant; vertical velocity grows by
gravityeach frame. - The ball moves by the average of the old and new vertical velocity for a smooth arc.