# animation.py # Walker M. White (wmw2) # November 17, 2014 """Module to show off how to use a subcontroller to pause an animation. As we can see in the state.py module, combining animation with click management can be quite complicated. It is often easier to handle animation in a subcontroller and turn it off in the main app. The subcontroller should always be a subclass of object. Instances of GameApp create a window to draw in, and we only want one window for drawing.""" import colormodel import random import math from game2d import * # Window Size WINDOW_WIDTH = 512 WINDOW_HEIGHT = 512 # Distance from Window Center ANIMATION_RADIUS = 100 # AMOUNT TO CHANGE THE ANGLE ANIMATION_STEP = 0.1 # Ellipse Radius ELLIPSE_RADIUS = 20 class MainApp(GameApp): """Instances represent the main application with a drawing window. This application listens for touches, pausing and restarting the animation every time there is a click. INSTANCE ATTRIBUTES: _animation [Animation]: The subcontroller to process the animation _label [GLabel]: A label to indicate that the animation is paused _paused [boolean]: Whether or not the animation is paused _last [GPoint, or None if mouse button is not down]: the last mouse position (if Button is pressed) """ def init(self): """Initialize the App with all of its attributes""" # We have to track mouse presses for clicks to work properly self._last = None # Create the subcontroller for animation self._animation = Animation(self.width,self.height) # We are not paused, but make label anyway self._paused = False self._label = GLabel(text="Paused...",font_size=24) def update(self,dt): """Process mouse events and run the animation IF not paused.""" # Check if it is a click, and not a long press if self._last is None and not self.view.touch is None: self._paused = not self._paused # Change state # Keep track of whether we are holding the mouse button down self._last = self.view.touch # Only run the animation if not paused. if not self._paused: self._animation.update() def draw(self): """Draw the animation and pause label as necessary. Note that we ALWAYS tell the animation controller to draw, even if paused. That is how we get it to look like it is frozen in place. """ if self._paused: self._label.draw(self.view) self._animation.draw(self.view) class Animation(object): """Subcontroller to animate an ellipse about a center. At each step, the update() method computes a new angle. It finds the (x,y) coordinate that corresponds to the polar coordinate (ANIMATION_RADIUS,angle) and puts the ellipse there. INSTANCE ATTRIBUTES: _angle [float]: ellipse angle from center _width [float > 0]: the width of the window to animate in _height [float > 0]: the height of the window to animate in _ellipse [GEllipse, center is (RADIUS,self.angle) in polar coords]: the ellipse to animate """ def __init__(self,width,height): """Create the animation subcontroller. Note that this is a proper initializer, because Animation is NOT a subclass of GameApp (we only want one Window). However, it needs to know the width and height of the window, so we pass them as arguments. Precondition: width, height are floats > 0.""" self._angle = 0 self._width = width self._height = height pos=self._polar_to_coord(ANIMATION_RADIUS,self._angle) self._ellipse = GEllipse(x=pos[0],y=pos[1], width=ELLIPSE_RADIUS,height=ELLIPSE_RADIUS, fillcolor=colormodel.RED) def update(self): """Animate the ellipse.""" # Change the angle self._angle = self._angle+ANIMATION_STEP % (2*math.pi) pos=self._polar_to_coord(ANIMATION_RADIUS,self._angle) self._ellipse.x = pos[0] self._ellipse.y = pos[1] def draw(self,view): """Draw the ellipse to the application window (view). Precondition: view is a GView.""" self._ellipse.draw(view) def _polar_to_coord(self,r,a): """Returns: Tuple (x,y) equal to polar coord (r,a) Precondition: r, a are floats, r >= 0""" return (r*math.cos(a)+self._width/2.0,r*math.sin(a)+self._height/2.0) # Application Code if __name__ == '__main__': MainApp(width=WINDOW_WIDTH,height=WINDOW_HEIGHT,fps=60.0).run()