# controller.py # Walker White (wmw2), Lillian Lee (ljl2), and Steve Marschner (srm2) # November 17, 2014 """Breakout-like Controller module for lecture demos.""" import colormodel import random import math from game2d import * ### CONSTANTS ### # Window Size WINDOW_WIDTH = 512 WINDOW_HEIGHT = 512 # Gravity of surface GRAVITY = -0.2 # Upward velocity ROCKET_VY = 1.2 # For the explosion PARTICLE_DIAMETER = 5 MAX_INIT_VEL = 5 PARTICLES_PER_SHELL = 50 ### CONTROLLER CLASS ### class Pyro(GameApp): """Instance is a controller for lecture demos about interaction and animation. This class extends GameController and implements the various methods necessary for an interactive application. Method initialize is called once at the beginning. Method update is called repeatedly to do animation. Attribute _state keeps track of the current game state. The on_touch methods handle mouse (or finger) input. INSTANCE ATTRIBUTES: view [GView]: the view (inherited from Game) rockets [list of Rocket]: list of rockets to animate sparks [list of Spark]: list of sparks to animate lastclick [GPoint, None if mouse not down last frame] : last position clicked """ # THREE MAIN METHODS def init(self): """Initialize the program state.""" self.rockets = [] self.sparks = [] self.lastclick = None # Have not clicked mouse yet. def update(self,dt): """Animate a single frame. Called every 1/60 of a second.""" # helper method to process the mouse click self._addOnClick() # move all the rockets for rocket in self.rockets: rocket.move() # Handle a rocket if it explodes if rocket.isExploded(): # MUST use getter here self._explode_rocket(rocket) # Delete any exploded rockets i = 0 while i < len(self.rockets): if self.rockets[i].isExploded(): # MUST use getter here del self.rockets[i] else: i += 1 # move all the sparks for spark in self.sparks: spark.move() # Delete any sparks out of view i = 0 while i < len(self.sparks): if self.sparks[i].y < -10: del self.sparks[i] else: i += 1 def draw(self): """Draw all particles in the view.""" for rocket in self.rockets: rocket.draw(self.view) for spark in self.sparks: spark.draw(self.view) # HELPER METHODS def _explode_rocket(self, rocket): """Explode the ship ship rocket""" color = colormodel.RGB(random.randrange(256),random.randrange(256),random.randrange(256)) for i in range(PARTICLES_PER_SHELL): spark = Spark(rocket.x, rocket.y, color) self.sparks.append(spark) def _addOnClick(self): """Check for a click and add a Rocket if necessary. A 'click' is the animation frame after the mouse is pressed for the first time in a while (so _lastclick is None).""" touch = self.view.touch # View stores the touch information if self.lastclick is None and not touch is None: # Click happened. Add rocket to particle list. rocket = Rocket(touch.x, touch.y) self.rockets.append(rocket) # Update lastclick self.lastclick = touch ### MODEL CLASSES ### class Spark(GEllipse): """Instances represent particles created in shell explosions. Instance Variables (Beyond those in GEllipse): _vx [float]: velocity in x direction _vy [float]: velocity in y direction """ def __init__(self, x, y, color=colormodel.WHITE): """New particle at (x,y) with random initial velocity and given color.""" GEllipse.__init__(self, x=x, y=y, width=PARTICLE_DIAMETER, height=PARTICLE_DIAMETER, fillcolor=color) self._vy = random.uniform(-MAX_INIT_VEL,MAX_INIT_VEL) self._vx = math.sqrt(MAX_INIT_VEL**2 - self._vy**2) * math.sin(random.uniform(0,2*math.pi)) def move(self): self.x += self._vx self.y += self._vy self._vy += GRAVITY class Rocket(GEllipse): """Instances represent rockets that will generate explosions later. Instance Variables (Beyond those in GEllipse): _trigger_y [float]: y coordinate at which rocket will explode _exploded [bool, True if self.y > self.trigger_y]: True if the rocket has exploded """ # Must use getters to access with an object of another class def isExploded(self): """Returns: True if rocket has exploded, False otherwise""" return self._exploded def __init__(self, x, y): """New rocket headed for an explosion at (x,y).""" GEllipse.__init__(self, x=x, y=0, width=PARTICLE_DIAMETER, height=PARTICLE_DIAMETER, fillcolor=colormodel.GRAY) self._trigger_y = y self._exploded = False def move(self): """Move the rocket and set it to explode.""" self.y += ROCKET_VY if self.y > self._trigger_y: self._exploded = True # Script Code if __name__ == '__main__': Pyro(width=WINDOW_WIDTH,height=WINDOW_HEIGHT,fps=30.0).run()