Outline
Other Material
The purpose of this chapter is to once again illustrate the design of a program as a universe of interacting agents working together to produce the desired outcome.
Every graphical object has a link field and a region of space.
Wall = object link : Wall; (* data fields *) region : Rect; (* factor used to reflect striking balls *) convertFactor : real; procedure initialize (* initialization function *) (left, top, right, bottom : integer; cf : real); procedure draw; (* draw wall *) (* notify wall that a ball has struck *) procedure hitBy (aBall : Ball); end;
procedure Wall.initialize (left, top, right, bottom : integer; cf : real); begin (* initialize conversion factor *) convertFactor := cf; link := nil; (* set up region for wall *) SetRect (region, left, top, right, bottom); end;
procedure Wall.draw; begin PaintRect (region); end; procedure hole.draw; begin PaintOval (region); end; procedure Ball.draw; begin if self = CueBall then (* draw an open circle *) FrameOval (region) else (* draw a filled circle *) PaintOval (region) end;
procedure Wall.hitBy (aBall : Ball); begin (* bounce the ball off the wall *) aBall.setDirection(convertFactor - aBall.direction); end; procedure Hole.hitBy (aBall : Ball); begin (* drain energy from ball, remove it *) aBall.energy := 0.0; aBall.erase; (* move ball *) if aBall = CueBall then aBall.setCenter(50, 100) else begin saveRack := saveRack + 1; aBall.setCenter (10 + saveRack * 15, 250); end; (* redraw ball *) aBall.draw; end; procedure Ball.hitBy (aBall : Ball); var da : real; begin (* cut the energy of the hitting ball in half *) aBall.energy := aBall.energy / 2; (* and add it to our own *) energy := energy + aBall.energy; (* set our new direction *) setDirection(hitAngle(self.x - aBall.x, self.y - aBall.y)); (* and set the hitting balls direction *) da := aBall.direction - direction; aBall.setDirection (aBall.direction + da); end;
procedure Ball.update; var hptr : Hole; wptr : Wall; bptr : Ball; dx, dy : integer; theIntersection : Rect; begin if energy > 0.5 then begin ballMoved := true; (* erase ball *) erase; (* decrease energy *) energy := energy - 0.05; (* move ball *) dx := trunc(5.0 * cos(direction)); dy := trunc(5.0 * sin(direction)); offsetRect(region, dx, dy); (* redraw ball *) draw; (* see if we hit a hole *) hptr := listOfHoles; while (hptr <> nil) do if SectRect (region, hptr.region, theIntersection) then begin hptr.hitBy(self); hptr := nil; end else hptr := hptr.link; (* see if we hit a wall *) wptr := listOfWalls; while (wptr <> nil) do if SectRect (region, wptr.region, theIntersection) then begin wptr.hitBy(self); wptr := nil; end else wptr := wptr.link; (* see if we hit a ball *) bptr := listOfBalls; bhit := nil; while (bptr <> nil) do if (bptr <> self) and SectRect (region, bptr.region, theIntersection) then begin bptr.hitBy(self); bptr := nil; end else bptr := bptr.link; end; end;
procedure mouseButtonDown (x, y : integer); var bptr : Ball; begin (* give the cue ball some energy *) CueBall.energy := 20.0; (* and a direction *) CueBall.setDirection(hitAngle (cueBall.x - x, cueBall.y - y)); (* then update as long as any ball moves *) ballMoved := true; while ballMoved do begin ballMoved := false; bptr := listOfBalls; while bptr <> nil do begin bptr.update; bptr := bptr.link; end; end; end;
The program can be greatly simplfied by making all graphical objects inherit from a common base class.
We will have much more to say about inheritance, and the power that comes from the use of inheritance, in subsequent chapters.