CMU 15-112: Fundamentals of Programming and Computer Science
Class Notes: OOPy Animation


  1. OOPy Animation Introduction
  2. Example: OOPy Dots Demo

  1. OOPy Animation Introduction
    In OOPy animation, we use Object-Oriented Programming to organize and control animation code. This is useful for large and complicated animation projects that need more organization than the base template can provide. Think of how hard it would be to program something like Mario all in one animation file!

    When building an OOPy animation, first identify what the main objects of the animation are. Each of those objects will have its own model (the data associated with the object), view (appearance of the object), and controller (ways that the object interacts with the animation).

    Model: The model will become the object's attributes and state, and should be defined in the __init__ function. Each object can then be defined in the animation framework's appStarted(app) function and stored in the app, to set up the state of the game.

    View: An object's view can be defined as a draw(self, canvas) method inside the object's class, where the draw method displays the given object. The main animation framework can then call obj.draw(canvas) on all the objects stored in the app.

    Controller: Finally, each object can define its own controllers inside its class as well. These will also be called directly by the main animation framework's controllers. Many objects may have their own controller methods that can be called by keyPressed, mousePressed, or timerFired (such as move()).

  2. Example: OOPy Dots Demo
    # oopyDotsDemo.py # starts with betterDotsDemo and adds: # * a dotCounter that counts all the instances of Dot or its subclasses # * a MovingDot subclass of Dot that scrolls horizontally # * a FlashingMovingDot subclass of MovingDot that flashes and moves from cmu_112_graphics import * import random #################################### # customize these functions #################################### def distance(x0, y0, x1, y1): return ((y1-y0)**2 + (x1-x0)**2)**0.5 class Dot(object): def __init__(self, x, y): self.x = x self.y = y self.r = random.randint(20, 50) self.color = random.choice(['cyan', 'green', 'yellow']) self.score = 0 def getBounds(self): return self.x-self.r, self.y-self.r, self.x+self.r, self.y+self.r def draw(self, canvas): x0, y0, x1, y1 = self.getBounds() canvas.create_oval(x0, y0, x1, y1, fill=self.color) canvas.create_text((x0+x1)/2, (y0+y1)/2, text=self.score) def clicked(self, x, y): return distance(self.x, self.y, x, y) <= self.r def onTimerFired(self, canvasWidth): pass class MovingDot(Dot): def onTimerFired(self, canvasWidth): speed = 5 self.x = (self.x + speed) % canvasWidth class FlashingMovingDot(MovingDot): def __init__(self, x, y): super().__init__(x, y) self.timer = 0 self.isFlashing = False def onTimerFired(self, canvasWidth): super().onTimerFired(canvasWidth) # do the regular moving part self.timer += 1 if self.timer % 5 == 0: self.isFlashing = not self.isFlashing def draw(self, canvas): if self.isFlashing: x0, y0, x1, y1 = self.getBounds() canvas.create_rectangle(x0, y0, x1, y1, fill="light gray") # still do the regular drawing super().draw(canvas) def appStarted(app): app.dots = [] def mousePressed(app, event): clicked = False for dot in app.dots: if dot.clicked(event.x, event.y): dot.score += 1 clicked = True if clicked: return dotType = len(app.dots) % 3 if dotType == 0: app.dots.append(Dot(event.x, event.y)) elif dotType == 1: app.dots.append(MovingDot(event.x, event.y)) else: app.dots.append(FlashingMovingDot(event.x, event.y)) def keyPressed(app, event): # use event.key pass def timerFired(app): for dot in app.dots: dot.onTimerFired(app.width) def redrawAll(app, canvas): for dot in app.dots: dot.draw(canvas) runApp()