Source code for pygtails

"""A simple wrapper around pygame.

Game        implements engine functionality. Subclass to build games.
GameObject  A simple class to provide a more intuitive approach to gamedev.
"""

#TODO: Create a MonoBehaviour-esque class to provide more flexibility (and also
#      so I'm not redefining and redocumenting the same ten methods twice.

import pygame
import sys

from pygame.time import Clock
from pygame.event import Event

[docs]class Game(object): """A class that handles pygame events, input, and mouse-collision. *resolution* is a 2-tuple of integers that specify the width and height of the screen. *title* is a string used as the title of the window. *flags* is an integer flag representing the different controls over the display mode that are active. For a full list of the different flags, see :ref:`Pygame Display Mode Flags`. For more information on how flags work, see :doc:`the Flags tutorial <flag-tut>`. Public Methods: | main, quit, on_focus, on_key_down, on_key_up, on_mouse_move, | on_mouse_up, on_mouse_down, on_resize, update, add_object, | destroy_object, key_is_pressed Instance variables: | screen """ def __init__(self, resolution, title, flags=0, depth=0): pygame.init() self._screen = pygame.display.set_mode(resolution, flags, depth) pygame.display.set_caption(title) self._cur_id = 0 self._objects = {} self._contains_mouse = {} self._clicked = {} self._keys_pressed = pygame.key.get_pressed() self._handle = {pygame.QUIT: self.quit, pygame.ACTIVEEVENT: self.on_focus, pygame.KEYDOWN: self.on_key_down, pygame.KEYUP: self.on_key_up, pygame.MOUSEMOTION: self.on_mouse_move, pygame.MOUSEBUTTONUP: self.on_mouse_up, pygame.JOYAXISMOTION: self.on_joy_move, pygame.JOYBALLMOTION: self.on_joy_ball_move, pygame.JOYHATMOTION: self.on_joy_hat_move, pygame.JOYBUTTONDOWN: self.on_joy_button_down, pygame.MOUSEBUTTONDOWN: self.on_mouse_down, pygame.VIDEORESIZE: self.on_resize, pygame.VIDEOEXPOSE: self.on_expose, pygame.USEREVENT: self.on_user_event}
[docs] def main(self): """The main loop. Call this to run the game.""" while True: for event in pygame.event.get(): self._handle[event.type](event) self._keys_pressed = pygame.key.get_pressed() buttons = pygame.mouse.get_pressed() pos = pygame.mouse.get_pos() rel = pygame.mouse.get_rel() event = Event(pygame.MOUSEMOTION, buttons=buttons, pos=pos, rel=rel) for obj in self._contains_mouse.values(): obj.on_mouse_stay(event) self.update() for obj in self._objects.values(): obj.update()
[docs] def quit(self, event): """The method called when the exit button is pressed. *event* is a pygame ``QUIT`` event. It has no event attributes. This method is predefined as:: pygame.quit() sys.exit() Redefine it if you need more control. """ pygame.quit() sys.exit()
[docs] def on_focus(self, event): """This method is called whenever the window loses or gains focus. *event* is a pygame ``ACTIVEEVENT`` event. It contains the event attributes ``gain`` and ``state``. *event.gain* is an integer. It has a value of 1 when the window comes into focus or when the mouse enters the window. It has a value of 0 when the window goes out of focus or when the mouse leaves the window. *event.state* is an integer. It has a value of 1 when the mouse exits or leaves the window. It has a value of 2 when the window gains or loses focus. This method is not predefined. """ pass
[docs] def on_key_down(self, event): """This method is called whenever a key is pressed. *event* is a pygame ``KEYDOWN`` event. It contains the event attributes ``unicode``, ``key``, and ``mod``. *event.unicode* is the unicode representation of the key being pressed. *event.key* is a pygame keycode representing the key being pressed. For a full list key constants, see :ref:`Pygame Keycodes`. *event.mod* is a pygame key mod flag representing the "modulating" keys (shift, ctrl, alt, etc.) being pressed when the current key was pressed. For a list of these flags, see :ref:`Pygame Key Mod Flags`. This method is not predefined. """ pass
[docs] def on_key_up(self, event): """This method is called whenever a key is released. *event* is a pygame ``KEYUP`` event. It contains the event attributes ``key`` and ``mod``. *event.key* is a pygame keycode representing the key being released. For a full list key constants, see :ref:`Pygame Keycodes`. *event.mod* is a pygame key mod flag representing the "modulating" keys (shift, ctrl, alt, etc.) pressed when the current key was released. For a full list of these flags, see :ref:`Pygame Key Mod Flags`. This method is not predefined. """ pass
[docs] def on_mouse_move(self, event): """This method is called whenever the mouse is moved. *event* is a pygame ``MOUSEMOTION`` event. It contains the event attributes ``pos``, ``rel``, and ``buttons``. *event.pos* is a 2-tuple of integers representing the x and y coordinates of the mouse. *event.rel* is a 2-tuple of integers representing the change in x and y coordinates since the last time this function was called. *event.buttons* is a 3-tuple of integers representing the amount of mouse buttons being pressed. Index 0 represents the left mouse button, 1 represents the middle mouse button, 2 represents the right mouse button. If the mouse button is down, the value is 1, 0 if it's up. This method is predefined to implement the on_mouse_[enter, exit, drag] functions. If you aren't satisfied with the implementation, feel free to redefine it. If you want to keep the implementation but also add additional functionality call super().on_mouse_move(event) when you're redefining the function. """ #TODO: Add support for sleeping vs awake objects for ID, obj in self._objects.items(): mouse_is_colliding = event.pos in obj if not obj._contains_mouse and mouse_is_colliding: self._contains_mouse[ID] = obj obj._contains_mouse = True obj.on_mouse_enter(event) elif obj._contains_mouse and not mouse_is_colliding: del self._contains_mouse[ID] obj._contains_mouse = False obj.on_mouse_exit(event) for obj in self._clicked.values(): obj.on_mouse_drag(event)
[docs] def on_mouse_up(self, event): """This method is called whenever a mouse button is released. *event* is a pygame ``MOUSEBUTTONUP`` event. It contains the event attributes ``pos`` and ``button``. *event.pos* is a 2-tuple of integers representing the x and y coordinates of the mouse when it was released. *event.button* is an integer representing the button being released. 1 represents the left mouse button, 2 represents the middle mouse button, and 3 represents the right mouse button. This method is predefined to implement the GameObject.on_mouse_up method and to update internal data about whether or not an object is clicked. To redefine this method while keeping the implementation call super().on_mouse_up(event) at the top of your function. """ if event.button == 1: for obj in self._clicked.values(): obj.on_mouse_up(event) self._clicked.clear()
[docs] def on_mouse_down(self, event): """This method is called whenever a mouse button is pressed. *event* is a pygame ``MOUSEBUTTONDOWN`` event. It contains the event attributes ``pos`` and ``button``. *event.pos* is a 2-tuple of integers representing the x and y coordinates of the mouse when it was released. *event.button* is an integer representing the button being pressed. 1 represents the left mouse button, 2 represents the middle mouse button, and 3 represents the right mouse button. This method is predefined to implement the GameObject.on_mouse_down method and to update internal data bout whether or not an object is clicked. To redefine this method while keeping the implementation, call super().on_mouse_up(event) at the top of your function. """ if event.button == 1: for obj in self._contains_mouse.values(): obj.on_mouse_down(event) self._clicked.update(self._contains_mouse)
def on_joy_move(self, event): pass def on_joy_ball_move(self, event): pass def on_joy_hat_move(self, event): pass def on_joy_button_down(self, event): pass
[docs] def on_resize(self, event): """This method is called whenever the window is resized. *event* is a pygame ``VIDEORESIZE`` event. it contains the event attributes ``size``, ``w``, and ``h``. *event.size* is a 2-tuple of integers representing the width and height of the screen. *event.w* is an integer representing the width of the screen. *event.h* is an integer representing the height of the screen. This method is not predefined. """ pass
def on_expose(self, event): pass def on_user_event(self, event): pass
[docs] def update(self): """This method is called every frame. This method is not predefined. """ pass
[docs] def add_object(self, other): """Add a GameObject ``other`` to the Game and return its id.""" # TODO: provide full documentation for the functions and attributes # to implement if not GameObject obj_id = self._cur_id self._objects[obj_id] = other self._cur_id += 1 return obj_id
[docs] def destroy_object(self, _id): """Destroys the object with the given id from the game. Note: Does not "undraw" the object. This must be done manually (for now) """ del self._objects[_id] for name in ("contains_mouse", "clicked"): D = getattr(self, "_"+name) if _id in D: del D[_id]
[docs] def key_is_pressed(self, key): """Return True if a key is pressed, False if not. *key* is pygame keycode. For a full list of keycodes, see :ref:`Pygame Keycodes`. """ return self._keys_pressed[key]
@property def screen(self): """The pygame Surface used to draw and blit images to the screen.""" return self._screen
[docs]class GameObject(object): """A simple class to (hopefully) make pygame more intuitive. *game* is the pygame.Game that this GameObject will be added to. Intializing a GameObject modifies internal data in the Game it's instantiated by. Public Methods: | update, on_mouse_enter, on_mouse_exit, on_mouse_stay, on_mouse_down, | on_mouse_up, on_mouse_drag, move Instance Variables: | game, ID """ def __init__(self, game): self._game = game self._contains_mouse = False self._id = game.add_object(self)
[docs] def update(self): """This method is called every frame. This method is not predefined. """ pass
[docs] def on_mouse_enter(self, event): """This method is called whenever the mouse enters this object. *event* is a pygame ``MOUSEMOTION`` event. It contains the event attributes ``pos``, ``rel``, and ``buttons``. *event.pos* is a 2-tuple of integers representing the x and y coordinates of the mouse. *event.rel* is a 2-tuple of integers representing the change in x and y coordinates since the last time this function was called. *event.buttons* is a 3-tuple of integers representing the amount of mouse buttons being pressed. Index 0 represents the left mouse button, 1 represents the middle mouse button, 2 represents the right mouse button. If the mouse button is down, the value is 1, 0 if it's up. This method is not predefined. """ pass
[docs] def on_mouse_exit(self, event): """This method is called whenever the mouse exits this object. *event* is a pygame ``MOUSEMOTION`` event. It contains the event attributes ``pos``, ``rel``, and ``buttons``. *event.pos* is a 2-tuple of integers representing the x and y coordinates of the mouse. *event.rel* is a 2-tuple of integers representing the change in x and y coordinates since the last time this function was called. *event.buttons* is a 3-tuple of integers representing the amount of mouse buttons being pressed. Index 0 represents the left mouse button, 1 represents the middle mouse button, 2 represents the right mouse button. If the mouse button is down, the value is 1, 0 if it's up. This method is not predefined. """ pass
[docs] def on_mouse_stay(self, event): """This method is called each frame the mouse is within this object. *event* is a pygame ``MOUSEMOTION`` event. It contains the event attributes ``pos``, ``rel``, and ``buttons``. *event.pos* is a 2-tuple of integers representing the x and y coordinates of the mouse. *event.rel* is a 2-tuple of integers representing the change in x and y coordinates since the last time this function was called. *event.buttons* is a 3-tuple of integers representing the amount of mouse buttons being pressed. Index 0 represents the left mouse button, 1 represents the middle mouse button, 2 represents the right mouse button. If the mouse button is down, the value is 1, 0 if it's up. This method is not predefined. """ pass
[docs] def on_mouse_drag(self, event): """This method is called each frame this object is dragged by the mouse. *event* is a pygame ``MOUSEMOTION`` event. It contains the event attributes ``pos``, ``rel``, and ``buttons``. *event.pos* is a 2-tuple of integers representing the x and y coordinates of the mouse. *event.rel* is a 2-tuple of integers representing the change in x and y coordinates since the last time this function was called. *event.buttons* is a 3-tuple of integers representing the amount of mouse buttons being pressed. Index 0 represents the left mouse button, 1 represents the middle mouse button, 2 represents the right mouse button. If the mouse button is down, the value is 1, 0 if it's up. This method is not predefined. """ pass
[docs] def on_mouse_down(self, event): """This method is called when the mouse is pressed inside this object. *event* is a pygame ``MOUSEBUTTONDOWN`` event. It contains the event attributes ``pos`` and ``button``. *event.pos* is a 2-tuple of integers representing the x and y coordinates of the mouse when it was released. *event.button* is an integer representing the button being pressed. 1 represents the left mouse button, 2 represents the middle mouse button, and 3 represents the right mouse button. This method is not predefined. """ pass
[docs] def on_mouse_up(self, event): """This method is called on mouse up if this object is clicked. *event* is a pygame ``MOUSEBUTTONUP`` event. It contains the event attributes ``pos`` and ``button``. *event.pos* is a 2-tuple of integers representing the x and y coordinates of the mouse when it was released. *event.button* is an integer representing the button being released. 1 represents the left mouse button, 2 represents the middle mouse button, and 3 represents the right mouse button. This method is not predefined. """ pass
[docs] def destroy(self): """Deletes this object from the game world.""" self.game.destroy_object(self.ID)
@property def game(self): """The pygtails.Game object that this object is a part of.""" return self._game @property def ID(self): """An integer that represents this object's id.""" return self._id
[docs]class Circle(GameObject): """A GameObject with a circular "hitmask". *game* is the Game this object is a part of. *corner* is a 2-tuple of integers representing the x and y coordinates of the upper-left corner of the bounding square of circle. *radius* is a numeric value representing the radius of the circle. Initializing a Circle will modify internal data in the Game it's instantiated with. Public Methods: | update, on_mouse_enter, on_mouse_exit, on_mouse_stay, on_mouse_down, | on_mouse_up, on_mouse_drag, move Instance Variables: | game, ID, center, corner, radius """ def __init__(self, game, corner, radius): super().__init__(game) x, y = corner self._corner = corner self._center = x+radius, y+radius self._radius = radius @property def corner(self): """A 2-tuple of integers representing the center of the circle. Setting this will change the ``corner`` and ``center`` attributes. """ return self._corner @corner.setter def corner(self, other): x, y = other self._corner = other self._center = x+self.radius, y+self.radius @property def radius(self): """An integer representing the radius of the circle. Setting this will change the ``radius`` and ``center`` attributes. """ return self._radius @radius.setter def radius(self, other): self._center = (self._x+radius, self._y+radius) self._radius = radius @property def center(self): """A 2-tuple of integers representing the center of the circle. Setting this will change the ``center`` and ``corner`` attributes. """ return self._center @center.setter def center(self, other): x, y = other corner = x-self.radius, y-self.radius self._corner = corner self._center = other def __contains__(self, other): otherx, othery = other x, y = self.center return self.radius**2 >= (x-otherx)**2 + (y-othery)**2
[docs]class Rectangle(GameObject): """A GameObject with a rectangular "hitmask". *game* is the Game this object is a part of. *corner* is a 2-tuple of integers representing the x and y coordinates of the upper-left corner of the rectangle. *width* is an integer representing the width of the rectangle. *height* is an integer representing the height of the rectangle. Initializing a Rectangle will modify internal data in the Game it's instantiated with. Public Methods: | update, on_mouse_enter, on_mouse_exit, on_mouse_stay, on_mouse_down, | on_mouse_up, on_mouse_drag, move Instance Variables: | game, ID, corner, width, height """ def __init__(self, game, corner, width, height): super().__init__(game) self._corner = corner x, y = corner self._width = width self._height = height self._corners = ((x,y), (x+width,y), (x+width,y+height), (x,y+height)) @property def corner(self): """The upper left corner of the rectangle. A 2-tuple of integers that represent the x and y coordinates of the upper-left corner of the rectangle. This attribute is mutable. """ return self._corner @corner.setter def corner(self, other): x, y = other self._corner = other @property def width(self): """An integer that represents the width of the rectangle. This attribute is mutable. """ return self._width @width.setter def width(self, other): self._width = other @property def height(self): """An integer that represents the height of the rectangle. This attribute is mutable. """ return self._height @height.setter def height(self, other): self._height = other @property def corners(self): """A tuple of all of the corners of the rectangle. A 2-dimensional tuple, where the inner tuples are 2-tuples of integers representing the x and y coordinates of the different corners of the rectangle. The order that the points appear are top-left, top-right, bottom-right, bottom-left. This attribute is immutable. """ return self._corners def __contains__(self, other): otherx, othery = other x, y = self.corner contains = (x <= otherx <= x+self.width and y <= othery <= y+self.height) return contains