# Moving smiley program to illustrate objects and how they can interact. # Claude Anderson. June 16, 2007 import math, pygame from pygame.locals import * def translate(point, dx, dy): '''return a point that is a translation of point by deltas dx and dy''' return (point[0] + dx, point[1] + dy) def distance (point1, point2): '''return the distance between point1 and point2''' return math.sqrt((point1[0] - point2[0])**2 + (point1[1] - point2[1])**2) class Smiley: """ A Smiley is a smiley-face object that is capable of moving. FIELDS: centerPoint: center of this smiley dx, dy, amount the smiley moves each step size: radius of this smiley isSmiling, isMoving: booleans indicating current dynamic state. """ def __init__ (self, initX, initY, dx, dy, size=40, color='red', isSmiling='true'): self.dx = dx self.dy = dy self.size = size self.color = pygame.Color(color) self.isSmiling = isSmiling self.moving = True self.centerPoint = (initX, initY) def draw(self, surface): 'draw this smiley on the surface' # Draw the head of the smiley pygame.draw.circle(surface, self.color, self.centerPoint, self.size) #Draw the black outline of the smiley - notice that I'm using a width to make outline thicker pygame.draw.circle(surface, pygame.Color('black'), self.centerPoint, self.size, int(self.size/16)) # Draw the eyes in the same places as in the original program pygame.draw.circle(surface, pygame.Color('black'), translate(self.centerPoint, -self.size//4, -self.size//3), self.size//8) pygame.draw.circle(surface, pygame.Color('black'), translate(self.centerPoint, self.size//4, -self.size//3), self.size//8) # We can make a better smile using arc # Define the surrounding rect for smile smile_rect = Rect(translate(self.centerPoint, -self.size//2, self.size//8), (self.size, self.size//2)) # Define the surrounding rect for frown frown_rect = Rect(translate(self.centerPoint, -self.size//2, self.size//4), (self.size, self.size//2)) if self.isSmiling: pygame.draw.arc(surface, pygame.Color('black'), smile_rect, math.pi,2*math.pi, self.size//16) else: pygame.draw.arc(surface, pygame.Color('black'), frown_rect, 0, math.pi, self.size//16) def move(self): '''move this smiley by its increment amount''' if self.moving: self.centerPoint = translate(self.centerPoint,self.dx,self.dy) def smile(self): '''Start smiling''' self.isSmiling = True def frown(self): '''Start frowning''' self.isSmiling = False def stop(self): '''Stop moving''' self.moving = False def isMoving(self): '''Is this object moving?''' return self.moving def collidedWith (self, otherSmiley): ''''Has this smiley collided with that other one?''' return distance(self.centerPoint, otherSmiley.centerPoint) < self.size + otherSmiley.size def scene1(): s1 = Smiley(50, 50, 2, 2) s2 = Smiley(130, 130, 5, 2, 60, 'green', False) #pygame.init() lets pygame set up all of the stuff it needs to before it can run properly pygame.init() #Creates the main window display to draw objects on screen = pygame.display.set_mode((400, 400), 0, 32) #set_caption sets the title of the window pygame.display.set_caption('Smilies!') smileys = [s1, s2] #screen.fill fills in the whole picture with the given colour screen.fill(pygame.Color('white')) #we draw these two smileys by passing the screen to their draw functions s1.draw(screen) s2.draw(screen) #after drawing, we need to call update so all the changes will take effect on the screen pygame.display.update() for i in range(70): #If we don't clear out the screen every frame, we just draw on top of the last one! Comment out this line to see what I mean. screen.fill(pygame.Color('white')) for s in smileys: s.move() #a big difference between pygame and Zelle is that we have to redraw our smiles after we move them for the change to happen s.draw(screen) # once again, we need to use update so our changes are reflected on the screen pygame.display.update() # this does the same thing as time.sleep, but we'll use the pygame version so it plays nicely with pygame pygame.time.delay(50) # this is somewhat similar to using getMouse, but it doesn't pause waiting for input and it also applies to every possible kind of input event = pygame.event.poll() # every event has a type - in this case, we only care about QUIT type events # as long as our event type isn't QUIT, we pause and then check the event again while event.type != QUIT: #delay does the same thing as sleep, but it takes an integer amount of milliseconds as its argument pygame.time.delay(100) event = pygame.event.poll() def runCollisionScene(smileys, steps): '''move the smileys in the list until time runs out or until all have collided (and therefore stopped). Notice the frowns after collisions!''' # Always initialize pygame pygame.init() screen = pygame.display.set_mode((400,400),0,32) pygame.display.set_caption("Smilies!") # This text message gets displayed if all of the smileys crash. #Doing fonts in pygame is weird. #If you want a certain font, you have to use pygame.font.match_font(font name, bold (true or false), italics (true or false). #You then create a font object using the path to the font that you find with match_font with the additional argument of size #You aren't limited to a max size of 36 in pygame! #Note that the text isn't actually drawn here - we do that later. This Font object merely lets us draw text. my_font = pygame.font.Font(pygame.font.match_font('Arial',True,False),30) moverCount = 0; # how many smileys have not crashed? # Draw 'em all and count how many are moving. #The explanation for painting is covered in the above function. screen.fill(pygame.Color('white')) for s in smileys: s.draw(screen) if s.moving: moverCount +=1 pygame.display.update() for i in range(steps): # move all smileys and check for collisions screen.fill(pygame.Color('white')) if moverCount==0: break for s in smileys: # move them s.move() s.draw(screen) for i in range(len(smileys)): # check for collisions for j in range(i+1, len(smileys)): # avoid comparing to self (and duplicate checks). if smileys[i].collidedWith(smileys[j]): for k in [i, j]: #stop both of them. if smileys[k].isMoving(): smileys[k].frown() smileys[k].stop() moverCount -= 1 pygame.display.update() pygame.time.delay(1000//24) # pause before next iteration. if moverCount == 0: #all smileys have crashed!. #Drawing text is a little weird. You use the a Font object (in this case my_font) to render your text. The arguments are as follows: #text - the text to render #anti-alias - True if you want pygame to smooth your fonts so the edges don't look as sharp #colour - the colour to use #background colour - optional - set a background to your rendered text #using render actually returns a new Surface (just like Screen is a surface). drawn_text = my_font.render('PileUp!!', True, pygame.Color('blue')) #you use a Surface's "blit" function to draw another surface on it. #So, since we want this text to appear on screen, we blit the Surface we got from rendering the text onto a location that seems suitable. screen.blit(drawn_text,(100,20)) for s in smileys: s.draw(screen) pygame.display.update() #this code is explained in the above function event = pygame.event.poll() while event.type != QUIT: pygame.time.delay(100) event = pygame.event.poll() def scene2(): runCollisionScene([Smiley(50, 50, 2, 2), Smiley(130, 200, 4, 3, 60, 'green'), Smiley(300, 70, -1, 4, 25, 'orange'), Smiley(70, 350, 3, 0, 40, 'blue1')], 150) def scene3(): runCollisionScene([Smiley(50, 50, 2, 2), Smiley(130, 200, 4, 3, 60, 'green'), Smiley(300, 70, -2, 4, 25, 'orange'), Smiley(70, 350, 3, 0, 40, 'blue1')], 200 ) def scene4(): runCollisionScene([Smiley(50, 50, 2, 2), Smiley(130, 200, 4, 3, 60, 'green'), Smiley(300, 70, -1, 5, 25, 'orange'), Smiley(70, 350, 3, 0, 40, 'blue1')], 200 ) scenes = [scene1, scene2, scene3, scene4] if __name__ == '__main__': scene = int(input('Which scene do you want? (1, 2, 3, or 4): ')) pygame.time.delay(3000) scenes[scene-1]()