Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import pygame as pg
- import math
- SCREEN_WIDTH, SCREEN_HEIGHT = (1280, 720)
- HALF_WIDTH, HALF_HEIGHT = SCREEN_HEIGHT // 2, SCREEN_HEIGHT // 2
- KEY_MAPPING = {
- 'left': pg.K_a,
- 'right': pg.K_d,
- 'up': pg.K_w,
- 'down': pg.K_s,
- 'shoot': pg.MOUSEBUTTONDOWN
- }
- blue = (113, 221, 238)
- class Element(pg.sprite.Sprite):
- """
- Returns an image and a rectangle of self. Also has a function
- which scales the image and rectangle when ever the screen
- changes size.
- """
- def __init__(self, img, x, y):
- pg.sprite.Sprite.__init__(self)
- try:
- self.default_image = self.image = pg.image.load(f"{img}.png").convert_alpha()
- except FileNotFoundError:
- self.default_image = self.image = img
- self.rect = self.image.get_rect(center=(x, y))
- # DO NOT USE THESE ATTRIBUTES ! THEY ARE FOR RESIZING ONLY
- self.resize_w = self.default_image.get_width()
- self.resize_h = self.default_image.get_height()
- self.resize_x = x
- self.resize_y = y
- all_elements.add(self)
- def update_size(self):
- # NEVER CALL THIS METHOD OUTSIDE OF resize_display()
- self.image, self.rect = image_rescaler(self.default_image, (self.resize_x, self.resize_y),
- (self.resize_w, self.resize_h))
- class Entity(pg.sprite.Sprite):
- def __init__(self, speed, group):
- pg.sprite.Sprite.__init__(self)
- self.speed = speed
- group.add(self)
- class CameraGroup(pg.sprite.Group):
- """
- Tne class to contain all game sprites which are visible
- when the game is active. This class creates a "camera"
- which gives an effect of screen scrolling based on
- the player.
- """
- def __init__(self):
- super().__init__()
- # camera offset
- self.offset = pg.math.Vector2()
- # ground
- self.ground_img = pg.image.load("ground.png").convert_alpha()
- self.ground = Element(self.ground_img, 0, 0)
- self.ground.rect.topleft = (0, 0)
- def center_target_camera(self, target):
- # offset based on (player coordinate - half screen size) to center player.
- self.offset.x = target.rect.centerx - HALF_WIDTH
- self.offset.y = target.rect.centery - HALF_HEIGHT
- def custom_draw(self, target):
- # call function to calculate offset
- self.center_target_camera(target)
- # draw ground
- ground_offset = self.ground.rect.topleft - self.offset
- screen.blit(self.ground.image, ground_offset)
- # iterates through all sprites in the class CameraGroup, ordered by their y position (highest to smallest)
- for sprite in sorted(self.sprites(), key=lambda sprite: sprite.rect.centery):
- offset_pos = sprite.rect.topleft - self.offset
- screen.blit(sprite.image, offset_pos)
- class Player(Element, Entity):
- """
- Player class
- """
- def __init__(self, x, y, image, speed, group):
- Entity.__init__(self, speed, group)
- Element.__init__(self, image, x, y)
- # Stores (x, y) of direction
- self.direction = pg.math.Vector2()
- # Boolean variables to check if the player is currently doing anything.
- self.shooting = False
- self.up = False
- self.down = False
- self.right = False
- self.left = False
- self.shoot_cooldown = 200
- self.start_time = self.last_shot_time = self.current_time = pg.time.get_ticks()
- def update(self):
- # Calling methods to add player functionality and animation
- self.move()
- def change_direction(self, k_pressed, boolean):
- # Changes the player's direction by checking it against a dictionary. Useful for keybind changing
- # Retrieving the key of the value "k_pressed" if it exists in KEY_MAPPING, and assigning to key_press
- if k_pressed in KEY_MAPPING.values():
- key_press = [k for k, v in KEY_MAPPING.items() if v == k_pressed][0]
- else:
- key_press = ""
- if key_press == "right":
- self.right = boolean
- if key_press == "left":
- self.left = boolean
- if key_press == "up":
- self.up = boolean
- if key_press == "down":
- self.down = boolean
- def move(self):
- # Changing the player direction based on the boolean variables in change_direction
- self.direction.x = -1 if self.left else 1 if self.right else 0
- self.direction.y = -1 if self.up else 1 if self.down else 0
- # Moving the player
- self.rect.center += self.direction * self.speed
- def shoot(self, pos):
- self.current_time = pg.time.get_ticks()
- # Allow the player to shoot (but not continuously)
- if not self.shooting and self.current_time - self.last_shot_time >= self.shoot_cooldown:
- self.last_shot_time = self.current_time
- # Setting self.shooting to True so that only one bullet is created
- self.shooting = True
- # Creating the bullet
- bullet = Bullet(self.rect.centerx, self.rect.centery, "bullet", pos, 2, a_s.camera)
- # Reset self.shooting
- self.shooting = False
- class Bullet(Element, Entity):
- def __init__(self, x, y, image, pos, speed, group):
- Entity.__init__(self, speed, group)
- Element.__init__(self, image, x, y)
- pg.sprite.Sprite.__init__(self)
- # Creating image, fipping it on the x (for easier calculation later) and scaling it.
- self.image = pg.transform.flip(self.image, True, False)
- self.image = pg.transform.scale(self.image, (32, 16))
- self.x = x
- self.y = y
- self.mousepos = pos
- # Calculating the angle of bullet, so we can rotate it depending on direction
- self.angle = math.atan2(pos[1] - y, pos[0] - x)
- self.angle_deg = (self.angle * (180 / math.pi))
- self.image = pg.transform.rotate(self.image, self.angle_deg * -1)
- # Calculating the velocity of the bullet in both directions
- dx = self.mousepos[0] - x
- dy = self.mousepos[1] - y
- total = abs(dx) + abs(dy)
- self.vel_x = (self.speed * (dx / total))
- self.vel_y = (self.speed * (dy / total))
- # Variables which allow us to kill the bullet after some time
- self.start_time = pg.time.get_ticks()
- # Lifetime of bullet before being killed, in ms
- self.lifetime = 500
- def update(self):
- # Killing the bullet after self.lifetime has passed
- if pg.time.get_ticks() - self.start_time > self.lifetime:
- self.kill()
- print("Killing")
- # Moving the bullet (accurately, self.x is float while self.rect.x can only be int)
- self.x += self.vel_x
- self.y += self.vel_y
- self.rect.x = int(self.x)
- self.rect.y = int(self.y)
- def get_font(size):
- font = pg.font.Font(None, size)
- return font
- class Scene(object):
- """
- The base Scene class.
- Raises an error if a required method is missing.
- """
- def __init__(self):
- # creating variables which are used in most scene classes
- self.bg_colour = (179, 66, 245)
- self.font_l = get_font(72)
- self.font_m = get_font(48)
- self.font_s = get_font(32)
- def render(self):
- raise NotImplementedError
- def update(self):
- raise NotImplementedError
- def handle_events(self, events):
- raise NotImplementedError
- class GameScene(Scene):
- """
- Game Scene class to handle all game logic which occurs
- when the game is active, such as movement and shooting.
- """
- def __init__(self):
- super(GameScene, self).__init__()
- # Create object instances
- self.camera = CameraGroup()
- self.player = Player(HALF_WIDTH, HALF_HEIGHT, "player", 5, self.camera)
- def render(self):
- screen.fill(blue)
- # Drawing all elements in self.camera so that they have a y-sort and the screen is centered on the player
- self.camera.custom_draw(self.player)
- def update(self):
- self.camera.update()
- def handle_events(self, events):
- for e in events:
- if e.type == KEY_MAPPING['shoot']:
- m_pos = pg.mouse.get_pos()
- pos = (m_pos[0] + self.camera.offset[0], m_pos[1] + self.camera.offset[1])
- self.player.shoot(pos)
- if e.type == pg.KEYDOWN:
- if e.key == pg.K_ESCAPE:
- # go to main menu if escape i s pressed
- self.manager.go_to(main_menu_scene)
- self.player.change_direction(e.key, True)
- if e.type == pg.KEYUP:
- self.player.change_direction(e.key, False)
- class SceneManager(object):
- def __init__(self):
- # Game begins on main_menu
- #self.go_to(main_menu_scene)
- self.go_to(game_scene)
- #self.go_to(options_scene)
- def go_to(self, scene):
- global a_s
- # Changes self.scene, so we can access the current scene
- self.scene = scene
- # Allows us to use go_to() in the active scene to change scenes rather than accessing manager every time.
- self.scene.manager = self
- # Easier way to reference the current scene
- a_s = self.scene
- def main():
- # Initialising pygame, creating the game screen window and setting the frame rate
- global screen, FPS, all_elements
- global game_scene
- pg.init()
- clock = pg.time.Clock()
- FPS = 60
- screen = pg.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
- pg.display.set_caption("Game")
- # loading assets and creating global requirements for running the game
- all_elements = pg.sprite.Group()
- # creating scene instances to avoid manager.go_to(scene) creating new scenes every time.
- game_scene = GameScene()
- manager = SceneManager()
- # main game loop
- running = True
- while running:
- # set caption to display FPS
- pg.display.set_caption(f"Game - {round(clock.get_fps(), 2)}")
- clock.tick(FPS)
- # check if user quits program
- if pg.event.get(pg.QUIT):
- running = False
- # event handler, updating and rendering for the active scene.
- manager.scene.handle_events(pg.event.get())
- manager.scene.update()
- manager.scene.render()
- # updating the display
- pg.display.update()
- if __name__ == "__main__":
- main()
- quit()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement