'''lecture_32_pacman.py Simple implementation of the game Pac-Man Oliver W. Layton CS 151: Computational Thinking: Visual Media Spring 2021 TODO: Create `PacMan` class - Constructor: Setup black screen, custom pac-man turtle shape UPDATE 1: Create `setupEvents` method with keypress events, and `play` to listen for events and start main game loop. UPDATE 2: Create `placePellets(nPellets)` which returns a list of pellets (Turtle objects that are the pellets). - Add timer event for checking collisions with pellets, have the method queue up another timer event so that it can repeat. UPDATE 3: Use "mouse" (or trackpad) clicks to perform an action (e.g. change background color) - Create changeColor(self, x, y) ''' import turtle import random class PacMan: numTimes = 0 def __init__(self, width=800, height=800, nPellets=50): self.screen = turtle.Screen() self.screen.setup(width=width, height=height) self.screen.bgcolor('black') self.screen.title('PacMan') # Makes my custom PacMan image accessible so that turtle can take on its shape self.screen.register_shape('pacman.gif') self.player = turtle.Turtle() # Set turtle shape to be the pacman image self.player.shape('pacman.gif') self.player.penup() # Amount pacman moves on each keypress self.distance = 20 # Make the pellets self.screen.tracer(False) # List of pellets is an instance variable self.pellets = self.makePellets(nPellets) self.screen.tracer(True) # Setup events self.setupEvents() # self.screen.exitonclick() def setupEvents(self): '''Define all the events and associated actions to take e.g. Keyboard presses ''' # We are NOT calling self.moveUp method NOW, so we do NOT use parenthesis # self.moveUp() <-- actually function now, NOT what we want. # NOTE: self. in front of moveUp bc this is a METHOD inside the class, NOT a function outside. # 2nd parameter is string representing the keyboard button name (you can look up all on # turtle website) self.screen.onkeypress(self.moveUp, 'Up') self.screen.onkeypress(self.moveDown, 'Down') self.screen.onkeypress(self.moveLeft, 'Left') self.screen.onkeypress(self.moveRight, 'Right') # Create a timer event: call checkCollisions every 250 msec to see if the player is close # to any pellet # NOTE: Not calling it now, so no () parentheses here # Second param: How far in the future (in msec) should we call the method self.checkCollisions self.screen.ontimer(self.checkCollisions, 250) # 2nd parameter is which mouse button to use for event # (e.g. left click, right click, scroll wheel click) # left click is default is 1 self.screen.onclick(self.changeBackgroundColor, 1) def moveUp(self): '''Moves pacman up by `self.distance` ''' self.player.setheading(90) self.player.forward(self.distance) def moveDown(self): '''Moves pacman down up by `self.distance` ''' self.player.setheading(270) self.player.forward(self.distance) def moveLeft(self): '''Moves pacman left by `self.distance` ''' self.player.setheading(180) self.player.forward(self.distance) def moveRight(self): '''Moves pacman right by `self.distance` ''' self.player.setheading(0) self.player.forward(self.distance) def makePellets(self, nPellets): '''Make a list of Turtle objects (that look like pellets). Place them in a random spot on the canvas. Return the list of pellets ('Turtles') ''' pellets = [] for i in range(nPellets): pellet = turtle.Turtle() pellet.shape('circle') pellet.color('white') pellet.penup() pellet.goto(600*random.random() - 300, 600*random.random() - 300) pellets.append(pellet) return pellets def checkCollisions(self): '''Checks for whether we have a collision between the player (Pacman) and one of the pellets. ''' PacMan.numTimes = PacMan.numTimes + 1 print('hi from 250 msec in the future!', PacMan.numTimes) for pellet in self.pellets: # see how close the current pellet is to our pacman player # consider both x and y values of the pellet and the player player_x = self.player.xcor() player_y = self.player.ycor() pellet_x = pellet.xcor() pellet_y = pellet.ycor() # 10 defines how close player/pacman needs to get to pellet to register a collision # 10 plays role as collision radius: higher values = collisions from farther way. # smaller values mean player has to get very close to pellet for it to go away. if abs(player_x - pellet_x) < 10 and abs(player_y - pellet_y) < 10: pellet.hideturtle() self.screen.ontimer(self.checkCollisions, 250) def changeBackgroundColor(self, x, y): '''Change background color when user left clicks on mouse/trackpad For onclick events, method needs to have x, y parameters: where on canvas the click happened. This is required, even if you don' plan on using the (x,y) ''' self.screen.bgcolor(random.random(), random.random(), random.random()) def play(self): # Actually accept events/wait for things to happen, and actually take action. # "Tell the screen to listen for events" self.screen.listen() # Generalization of exitonclick — hold the window open, allows game to run 'indefinitely' # until we intentionally close out (x out), allow to interact/play the game. # Think of as infinite while loop...this should be last step, we don't keep going beyond this # point self.screen.mainloop() # won't get here # print('hi') if __name__ == '__main__': # game = PacMan(nPellets=100) game = PacMan() game.play()