diff --git a/background.py b/background.py new file mode 100644 index 0000000..4ffa496 --- /dev/null +++ b/background.py @@ -0,0 +1,25 @@ +import pygame +import pygame.locals +import sys +import os + +import config + +# import window +class Background(object): + def __init__(self, background, DSS, speed = 0): + self.background = background + self.x_coordinate = 0 + self.speed = speed + self.DSS = DSS + + def draw_static_background(self): + slef.speed = 0 + pass + + def draw_dynamic_background(self): + rel_x = self.x_coordinate % (self.background).get_rect().width + (self.DSS).blit((self.background), (rel_x - (self.background).get_rect().width, 0)) + if rel_x < config.window_width: + (self.DSS).blit((self.background), (rel_x, 0)) + self.x_coordinate -= self.speed diff --git a/buttons.py b/buttons.py new file mode 100644 index 0000000..2fe38f8 --- /dev/null +++ b/buttons.py @@ -0,0 +1,11 @@ +import pygame +import pygame.locals +import sys +import os + +# Принимает нажатия любых клавиш +def events(): + for event in pygame.event.get(): + if event.type == pygame.QUIT: + pygame.quit() + sys.exit() diff --git a/config.py b/config.py new file mode 100644 index 0000000..e859b2c --- /dev/null +++ b/config.py @@ -0,0 +1,88 @@ +import os +import sys +import pygame + +FPS = 120 + +# define display surface +window_width = 1120 +window_high = 800 +window_size = (window_width, window_high) +area = window_high*window_width + + +# Hero parametrs +hero_start_HP = 100 +hero_start_x = 500 +hero_start_y = 400 +hero_speed = 5 +hero_bullet_damage = 50 +hero_taran_damage = 50 +hero_frames_per_bullet = 20 + + +#Enemy parametrs +enemy_frames_per_bullet = 40 +enemy_speed = 0.5 +enemy_random_y = 100 # Размер отступа от краёв экрана, где не сможетпоявляться враг +enemy_delt_coord = 100 # Расстояние между врагами при их создании +enemy_bullet_indent = 6 # Расстояние от координаты врага до координаты создания его патрона +enemy_trajectory_amplitude = 0 # Характеристики траетрории врагов, 0 - если нужно случайное поведение +enemy_trajectory_phase = 0 +enemy_trajectory_frequency = 0 +enemy_taran_damage = 50 +enemy_bullet_damage = 25 +enemy_start_HP = 100 + +#Common parametrs +frames_update = 5 +image_size = 15 # Размер спрайта персонажей +move_right = 1 +move_left = -1 +impact_parameter = 5 # Прицельный параметр - дельта на которой происходит столкновение +bullet_speed = 8 + + + +# background speed +level = 2 + +# paths +resource_path = os.path.abspath("resources") # The resource folder path +image_path = os.path.join(resource_path, 'images') # The image folder path + +# upload images and sprites +bullet_sprites = [pygame.image.load(os.path.join(image_path, 'bullet.png'))] + +# спрайты взрыва участников +sprites_death = [pygame.image.load(os.path.join(image_path, 'boom0.png')), + pygame.image.load(os.path.join(image_path, 'boom1.png')), + pygame.image.load(os.path.join(image_path, 'boom2.png')), + pygame.image.load(os.path.join(image_path, 'boom3.png')), + pygame.image.load(os.path.join(image_path, 'boom4.png'))] + +# Главный герой +player_sprites = [pygame.image.load(os.path.join(image_path, 'spacecraft0.png')), + pygame.image.load(os.path.join(image_path, 'spacecraft1.png')), + pygame.image.load(os.path.join(image_path, 'spacecraft2.png')), + pygame.image.load(os.path.join(image_path, 'spacecraft3.png')), + pygame.image.load(os.path.join(image_path, 'spacecraft4.png')), + pygame.image.load(os.path.join(image_path, 'spacecraft5.png')), + pygame.image.load(os.path.join(image_path, 'spacecraft6.png')), + pygame.image.load(os.path.join(image_path, 'spacecraft7.png')), + pygame.image.load(os.path.join(image_path, 'spacecraft8.png')), + pygame.image.load(os.path.join(image_path, 'spacecraft9.png')), + pygame.image.load(os.path.join(image_path, 'spacecraft10.png')), + pygame.image.load(os.path.join(image_path, 'spacecraft11.png')), + pygame.image.load(os.path.join(image_path, 'spacecraft12.png')), + pygame.image.load(os.path.join(image_path, 'spacecraft13.png'))] + +# Спрайты врагов быстрого типа +fast_ship_sprites = [pygame.image.load(os.path.join(image_path, 'enemy/fast_ship0.png')), + pygame.image.load(os.path.join(image_path, 'enemy/fast_ship1.png')), + pygame.image.load(os.path.join(image_path, 'enemy/fast_ship2.png'))] + +# Спрайты фона(красное небо, город, туман) +city = pygame.image.load(os.path.join(image_path, "CityBackground.png")) +sky = pygame.image.load(os.path.join(image_path, "red.png")) +mist = pygame.image.load(os.path.join(image_path, "fullfog.png")) diff --git a/howto.md b/howto.md index b23fbb6..5f099a5 100644 --- a/howto.md +++ b/howto.md @@ -1,8 +1,29 @@ -Запуск: -1) ./main.py +**Необходимый и достаточный минимум для запуска игры:** -Управление: -1) перемещеие: ← → ↑ ↓ +- python 3.X +- pygame -Выход: -По нажатию крестика в окне +------------------------------------------------------------------------------------------------------ +**Запуск:** + +- поместить все файлы в одну папку +- перейти в папку(там должен быть main.py) +- запуск: python3 ./main.py + +------------------------------------------------------------------------------------------------------ +**В игре:** + +- управление клавишами ←, →, ↑, ↓, SPACE +- выход из игры - крестик на окне игры +- все константы вынесены в config.py +- более подробное описание - комментарии в файлах + +------------------------------------------------------------------------------------------------------ +**Релиз игры на уровне MVP:** + +- реализованы основные паттерны программирования +- минимальная графика +- ограничено движение персонажей размерами окна +- реализовано столкновение всех персонажей друг с другом и с -пулями(попасть можно, но пока что сложно) + +------------------------------------------------------------------------------------------------------ diff --git a/main.py b/main.py new file mode 100644 index 0000000..34c4553 --- /dev/null +++ b/main.py @@ -0,0 +1,51 @@ +import pygame +import pygame.locals +import sys +import os +import math +import random +random.seed() # Иначе рандом начальных координат будет один и тот же для всех врагов + +import background +import config +import window +import buttons +import players + +# setup pygame +pygame.init() +CLOCK = pygame.time.Clock() + +window.create_window() + +# Создаём постоянные фоновых рисунков +city_back = (config.city).convert() +color_back = (config.sky).convert() +mist_back = (config.mist).convert_alpha(window.screen) + +# background objects creation +city_background = background.Background(city_back, + window.screen, + config.level/4) +mist_background = background.Background(mist_back, + window.screen, + config.level/2) + +game = players.Game() + +# main loop +while True: + # проверка всех нажатий + buttons.events() + + # создаём динамичныйфон + city_background.draw_dynamic_background() + mist_background.draw_dynamic_background() + + # Отслеживание всех нажатых кнопок + key_press = pygame.key.get_pressed() + + game.tick(key_press) + + pygame.display.update() + CLOCK.tick(config.FPS) diff --git a/players.py b/players.py new file mode 100644 index 0000000..6215410 --- /dev/null +++ b/players.py @@ -0,0 +1,269 @@ +import pygame +import pygame.locals +import math +import random +import sys + +import config +import window + +# Класс родитель(наследует участников: главный герой, враги, пули) +class Ship(object): + def __init__(self, HP, x, y, + speed_x, speed_y, + sprites, sprites_death, damage): + self.HP = HP + self.x = 0 + self.y = 0 + self.set_coord(x, y) + self.speed_x = speed_x + self.speed_y = speed_y + self.sprites = sprites + self.sprites_death = sprites_death + self.damage = damage + self.anim_count = 0 + self.anim_count_death = 0 + + # Жив ли участник + def is_alive(self): + if self.HP >= 0: + return True + elif self.anim_count_death // config.frames_update < len(self.sprites_death): + return True + else: + return False + + # Получить спрайты участника + def get_sprite(self): + self.anim_count += 1 + return self.sprites[(self.anim_count // config.frames_update) % len(self.sprites)] + + # Получить спрайты уничтожения участника + def get_sprite_death(self): + self.anim_count_death += 1 + return self.sprites_death[((self.anim_count_death - 1) // config.frames_update) % len(self.sprites_death)] + + # Геттер координат + def get_coord(self): + return self.x, self.y + + # Сеттер координат + def set_coord(self, x, y): + if ((x < config.window_width - config.image_size and x > 0) and + (y < config.window_high - config.image_size and y > 0)): + self.x = x + self.y = y + else: + print('bad coords!') + + # Обновление координат - движение + def new_coord(self): + pass + + # Отрисовка + def draw(self): + if self.HP >= 0: + window.screen.blit(self.get_sprite(), (self.x, self.y)) + else: + window.screen.blit(self.get_sprite_death(), (self.x, self.y)) + + # Получение урона + def on_damage(self, d): + if self.HP >= 0: + self.HP -= d + + def get_damage(self): + return self.damage + + def fire(self): + pass + +# Ребёнок класса Ship - пули врагов и героя +class Bullet(Ship): + def __init__(self, x, y, speed_x, + sprites, sprites_death, + damage, direction): + HP = 0 + speed_y = 0 + Ship.__init__(self, HP, x, y, + speed_x, speed_y, + sprites, sprites_death, damage) + self.direction = direction + + def set_coord(self, x, y): + if ((x < config.window_width - config.image_size and x > 0) and + (y < config.window_high - config.image_size and y > config.image_size)): + self.x = x + self.y = y + else: + self.on_damage(1) + + def new_coord(self): + new_x = self.x + self.direction*self.speed_x + new_y = self.y + self.set_coord(new_x, new_y) + +# Ребёнок класса Ship - герой игры +class Hero(Ship): + + def new_coord(self, keys): + new_x = new_y = 0 + if keys[pygame.K_LEFT]: + new_x = self.x - self.speed_x*1 + if keys[pygame.K_RIGHT]: + new_x = self.x + self.speed_x*1 + if keys[pygame.K_UP]: + new_y = self.y - self.speed_y*1 + if keys[pygame.K_DOWN]: + new_y = self.y + self.speed_y*1 + if new_x == 0: + new_x = self.x + if new_y == 0: + new_y = self.y + self.set_coord(new_x, new_y) + + # Реализация выстрелов + def fire(self, keys): + if keys[pygame.K_SPACE] and self.anim_count % config.hero_frames_per_bullet == 0: + return Bullet(self.x + 6, self.y, config.bullet_speed, + config.bullet_sprites, config.sprites_death, + config.hero_bullet_damage, config.move_right) + +# Ребёнок класса Ship - враги +class Enemy(Ship): + def __init__(self, HP = config.enemy_start_HP, x = 800, y = 0, + speed_x = config.enemy_speed, + speed_y = config.enemy_speed, + sprites = config.fast_ship_sprites, + sprites_death = config.sprites_death, + damage = config.enemy_taran_damage, + amplitude = config.enemy_trajectory_amplitude, + phase = config.enemy_trajectory_phase, + frequency = config.enemy_trajectory_frequency): + if y == 0: + y = random.randrange(config.enemy_random_y, + config.window_high - config.enemy_random_y, + config.enemy_delt_coord) + if speed_x == 0: + speed_x = 0 + if speed_y == 0: + speed_y = 0 + Ship.__init__(self, HP, x, y, + speed_x, speed_y, sprites, + sprites_death, damage) + if amplitude == 0: + amplitude = min((self.y), (800 - self.y)) + if phase == 0: + phase = random.randrange(0, 6, 1) + if frequency == 0: + frequency = random.random()*0.01 + self.phase = phase + self.frequency = frequency + self.amplitude = amplitude + self.start_x = x + self.start_y = y + + def set_coord(self, x, y): + if ((x < config.window_width - config.image_size and x > 0) and + (y < config.window_high - config.image_size and y > config.image_size)): + self.x = x + self.y = y + else: + self.on_damage(self.HP+1) + + def new_coord(self): + new_x = self.x - self.speed_x*1 + new_y = self.amplitude*math.sin((self.frequency*self.x + self.phase)/2)*math.sin(self.frequency*self.x + self.phase) + self.start_y + self.set_coord(new_x, new_y) + + def fire(self): + if self.anim_count % config.enemy_frames_per_bullet == 0: + return Bullet(self.x - config.enemy_bullet_indent, + self.y, + config.bullet_speed, + config.bullet_sprites, + config.sprites_death, + config.enemy_taran_damage, + config.move_left) + +# Фабрика участников +class Ships_Factory(object): + def __init__(self): + self.list = [] + + def add(self, s: Ship): + if s is not None: + self.list.append(s) + + def get(self, index): + return self.list[index] + + def remove(self, index): + self.list.remove(index) + + def get_list(self): + return self.list + + def draw(self): + for each in self.list: + each.draw() + + def new_coord(self): + for each in self.list: + if each.is_alive(): + each.new_coord() + else: + self.list.remove(each) + +# Игровой класс- создание классов участников, отприсовка, движение +class Game(object): + def __init__(self): + self.player = Hero(config.hero_start_HP, + config.hero_start_x, + config.hero_start_y, + config.hero_speed, + config.hero_speed, + config.player_sprites, + config.sprites_death, + config.hero_taran_damage) + self.Enemy_Factory = Ships_Factory() + self.Enemy_Factory.add(Enemy()) + self.Enemy_Factory.add(Enemy()) + self.Enemy_Factory.add(Enemy()) + self.bullets = Ships_Factory() + + def draw(self): + self.player.draw() + self.Enemy_Factory.draw() + self.bullets.draw() + + def new_coord(self, keys): + if self.player.is_alive(): + self.player.new_coord(keys) + else: + print('End game!!!') + sys.exit() + self.Enemy_Factory.new_coord() + self.bullets.new_coord() + + def tick(self, keys): + self.draw() + self.new_coord(keys) + self.bullets.add(self.player.fire(keys)) + for each in self.Enemy_Factory.get_list(): + self.bullets.add(each.fire()) + for each in self.Enemy_Factory.get_list(): + self.collide(self.player, each) + for each in self.bullets.get_list(): + self.collide(self.player, each) + for each1 in self.Enemy_Factory.get_list(): + for each2 in self.bullets.get_list(): + self.collide(each1, each2) + + # Проверка столкновений + def collide(self, A: Ship, B: Ship): + x_A, y_A = A.get_coord() + x_B, y_B = B.get_coord() + if math.sqrt((x_A - x_B)**2 + (y_A -y_B)**2) < config.impact_parameter: + A.on_damage(B.get_damage()) + B.on_damage(A.get_damage()) diff --git a/resources/images/CityBackground.png b/resources/images/CityBackground.png new file mode 100644 index 0000000..0553b23 Binary files /dev/null and b/resources/images/CityBackground.png differ diff --git a/resources/images/boom0.png b/resources/images/boom0.png new file mode 100644 index 0000000..f67c111 Binary files /dev/null and b/resources/images/boom0.png differ diff --git a/resources/images/boom1.png b/resources/images/boom1.png new file mode 100644 index 0000000..fff1892 Binary files /dev/null and b/resources/images/boom1.png differ diff --git a/resources/images/boom2.png b/resources/images/boom2.png new file mode 100644 index 0000000..bb9731b Binary files /dev/null and b/resources/images/boom2.png differ diff --git a/resources/images/boom3.png b/resources/images/boom3.png new file mode 100644 index 0000000..a3a9b61 Binary files /dev/null and b/resources/images/boom3.png differ diff --git a/resources/images/boom4.png b/resources/images/boom4.png new file mode 100644 index 0000000..6f42688 Binary files /dev/null and b/resources/images/boom4.png differ diff --git a/resources/images/bullet.png b/resources/images/bullet.png new file mode 100644 index 0000000..d82ef25 Binary files /dev/null and b/resources/images/bullet.png differ diff --git a/resources/images/enemy/fast_ship0.png b/resources/images/enemy/fast_ship0.png new file mode 100644 index 0000000..d8a7824 Binary files /dev/null and b/resources/images/enemy/fast_ship0.png differ diff --git a/resources/images/enemy/fast_ship1.png b/resources/images/enemy/fast_ship1.png new file mode 100644 index 0000000..1826312 Binary files /dev/null and b/resources/images/enemy/fast_ship1.png differ diff --git a/resources/images/enemy/fast_ship2.png b/resources/images/enemy/fast_ship2.png new file mode 100644 index 0000000..215e41c Binary files /dev/null and b/resources/images/enemy/fast_ship2.png differ diff --git a/resources/images/fullfog.png b/resources/images/fullfog.png new file mode 100644 index 0000000..8925e9e Binary files /dev/null and b/resources/images/fullfog.png differ diff --git a/resources/images/red.png b/resources/images/red.png new file mode 100644 index 0000000..b1f65fb Binary files /dev/null and b/resources/images/red.png differ diff --git a/resources/images/spacecraft0.png b/resources/images/spacecraft0.png new file mode 100644 index 0000000..37da019 Binary files /dev/null and b/resources/images/spacecraft0.png differ diff --git a/resources/images/spacecraft1.png b/resources/images/spacecraft1.png new file mode 100644 index 0000000..448433a Binary files /dev/null and b/resources/images/spacecraft1.png differ diff --git a/resources/images/spacecraft10.png b/resources/images/spacecraft10.png new file mode 100644 index 0000000..960397b Binary files /dev/null and b/resources/images/spacecraft10.png differ diff --git a/resources/images/spacecraft11.png b/resources/images/spacecraft11.png new file mode 100644 index 0000000..b8f1642 Binary files /dev/null and b/resources/images/spacecraft11.png differ diff --git a/resources/images/spacecraft12.png b/resources/images/spacecraft12.png new file mode 100644 index 0000000..1940804 Binary files /dev/null and b/resources/images/spacecraft12.png differ diff --git a/resources/images/spacecraft13.png b/resources/images/spacecraft13.png new file mode 100644 index 0000000..18337cb Binary files /dev/null and b/resources/images/spacecraft13.png differ diff --git a/resources/images/spacecraft2.png b/resources/images/spacecraft2.png new file mode 100644 index 0000000..08c308c Binary files /dev/null and b/resources/images/spacecraft2.png differ diff --git a/resources/images/spacecraft3.png b/resources/images/spacecraft3.png new file mode 100644 index 0000000..e8bbf47 Binary files /dev/null and b/resources/images/spacecraft3.png differ diff --git a/resources/images/spacecraft4.png b/resources/images/spacecraft4.png new file mode 100644 index 0000000..9d39261 Binary files /dev/null and b/resources/images/spacecraft4.png differ diff --git a/resources/images/spacecraft5.png b/resources/images/spacecraft5.png new file mode 100644 index 0000000..01d47c6 Binary files /dev/null and b/resources/images/spacecraft5.png differ diff --git a/resources/images/spacecraft6.png b/resources/images/spacecraft6.png new file mode 100644 index 0000000..7d90679 Binary files /dev/null and b/resources/images/spacecraft6.png differ diff --git a/resources/images/spacecraft7.png b/resources/images/spacecraft7.png new file mode 100644 index 0000000..ee35034 Binary files /dev/null and b/resources/images/spacecraft7.png differ diff --git a/resources/images/spacecraft8.png b/resources/images/spacecraft8.png new file mode 100644 index 0000000..97f2961 Binary files /dev/null and b/resources/images/spacecraft8.png differ diff --git a/resources/images/spacecraft9.png b/resources/images/spacecraft9.png new file mode 100644 index 0000000..5d920c2 Binary files /dev/null and b/resources/images/spacecraft9.png differ diff --git a/window.py b/window.py new file mode 100644 index 0000000..2e2a2f2 --- /dev/null +++ b/window.py @@ -0,0 +1,11 @@ +import pygame +import pygame.locals +import sys +import os +import config + +# Создание окна игры +screen = pygame.display.set_mode(config.window_size) +def create_window(): + os.environ['SDL_VIDEO_WINDOW_POS'] = "50,50" + pygame.display.set_caption("Game")