parent
f702481361
commit
6eeba99520
@ -0,0 +1,147 @@
|
||||
import threading
|
||||
|
||||
import pygame
|
||||
import sys
|
||||
import os
|
||||
import random
|
||||
|
||||
|
||||
class TankGame(object):
|
||||
_instance_lock = threading.Lock()
|
||||
_init_flag = False
|
||||
|
||||
def __init__(self, config=None):
|
||||
if not self._init_flag:
|
||||
if not config:
|
||||
raise Exception('Config was not specified while initializing game instance!')
|
||||
|
||||
self.__screen = None
|
||||
self.__levels = None
|
||||
self.__sounds = {}
|
||||
self.__multiplayer_mode = False
|
||||
self.__config = config
|
||||
self.__is_win = False
|
||||
self.__quit_game_flag = False
|
||||
self._init_flag = True
|
||||
self.__last_level = None
|
||||
self.__start()
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if not hasattr(TankGame, "_instance"):
|
||||
with TankGame._instance_lock:
|
||||
if not hasattr(TankGame, "_instance"):
|
||||
TankGame._instance = object.__new__(cls)
|
||||
return TankGame._instance
|
||||
|
||||
@property
|
||||
def sounds(self):
|
||||
return self.__sounds
|
||||
|
||||
@property
|
||||
def screen(self):
|
||||
return self.__screen
|
||||
|
||||
@property
|
||||
def quit_game_flag(self):
|
||||
return self.__quit_game_flag
|
||||
|
||||
@quit_game_flag.setter
|
||||
def quit_game_flag(self, quit_game_flag):
|
||||
self.__quit_game_flag = quit_game_flag
|
||||
|
||||
@property
|
||||
def config(self):
|
||||
return self.__config
|
||||
|
||||
@property
|
||||
def level(self):
|
||||
return self.__level
|
||||
|
||||
@property
|
||||
def level_file(self):
|
||||
|
||||
weights = [1] * len(self.__levels)
|
||||
if self.__last_level is not None:
|
||||
weights[self.__last_level] = 0
|
||||
|
||||
self.__last_level = random.choices(range(len(self.__levels)), weights=weights)[0]
|
||||
return self.__levels[self.__last_level]
|
||||
# return self.__levels[self.__level]
|
||||
|
||||
@property
|
||||
def is_win(self):
|
||||
return self.__is_win
|
||||
|
||||
@is_win.setter
|
||||
def is_win(self, is_win):
|
||||
self.__is_win = is_win
|
||||
|
||||
@property
|
||||
def multiplayer_mode(self):
|
||||
return self.__multiplayer_mode
|
||||
|
||||
@multiplayer_mode.setter
|
||||
def multiplayer_mode(self, multiplayer_mode):
|
||||
self.__multiplayer_mode = multiplayer_mode
|
||||
|
||||
def init_game_window(self, size_tuple=None):
|
||||
if size_tuple is None:
|
||||
self.__screen = pygame.display.set_mode(
|
||||
(self.config.WIDTH, self.config.HEIGHT)
|
||||
)
|
||||
else:
|
||||
self.__screen = pygame.display.set_mode(size_tuple)
|
||||
|
||||
def __init_sounds(self):
|
||||
for sound, file in self.config.AUDIO_PATHS.items():
|
||||
self.__sounds[sound] = pygame.mixer.Sound(file)
|
||||
self.__sounds[sound].set_volume(1)
|
||||
|
||||
def __load_level_templates(self):
|
||||
self.__levels = [
|
||||
os.path.join(
|
||||
self.config.LEVELFILEDIR,
|
||||
filename
|
||||
) for filename in sorted(os.listdir(self.config.LEVELFILEDIR))
|
||||
]
|
||||
|
||||
def __enter_loop(self):
|
||||
from modules.views import ViewManager
|
||||
ViewManager().show('GameStart')
|
||||
level = 0
|
||||
while True:
|
||||
self.__level = level
|
||||
ViewManager().show('SwitchLevel')
|
||||
ViewManager().show('GameLevelView')
|
||||
if not self.is_win:
|
||||
ViewManager().show('GameOver')
|
||||
level = 0
|
||||
else:
|
||||
level += 1
|
||||
if self.quit_game_flag:
|
||||
break
|
||||
|
||||
|
||||
# while True:
|
||||
# for level in range(len(self.__levels)):
|
||||
# self.__level = level
|
||||
# ViewManager().show('SwitchLevel')
|
||||
# ViewManager().show('GameLevelView')
|
||||
# if not self.is_win:
|
||||
# break
|
||||
#
|
||||
# ViewManager().show('GameOver')
|
||||
# if self.quit_game_flag:
|
||||
# break
|
||||
|
||||
def __init_game(self):
|
||||
pygame.init()
|
||||
pygame.mixer.init()
|
||||
pygame.display.set_caption(self.config.TITLE)
|
||||
self.init_game_window()
|
||||
self.__init_sounds()
|
||||
self.__load_level_templates()
|
||||
|
||||
def __start(self):
|
||||
self.__init_game()
|
||||
self.__enter_loop()
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,39 @@
|
||||
##############################################
|
||||
# Explain: #
|
||||
# Grid: 26 * 26 #
|
||||
# Grid SIZE: 24 * 24 pixels #
|
||||
# S: Space B: Brick I: Iron #
|
||||
# R: River1/2 C: Ice T: Tree #
|
||||
##############################################
|
||||
%TOTALENEMYNUM:1
|
||||
%MAXENEMYNUM:6
|
||||
%HOMEPOS:12,24
|
||||
%HOMEAROUNDPOS:11,23 12,23 13,23 14,23 11,24 14,24 11,25 14,25
|
||||
%PLAYERTANKPOS:8,24 16,24
|
||||
%ENEMYTANKPOS:0,0 12,0 24,0
|
||||
S S S S S S S S S S S S S S S S S S S S S S S S S S
|
||||
S S S S S S S S S S S S S S S S S S S S S S S S S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B I I B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B I I B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S S S S S S S S S B B S S B B S S
|
||||
S S B B S S B B S S S S S S S S S S B B S S B B S S
|
||||
S S S S S S S S S S B B S S B B S S S S S S S S S S
|
||||
S S S S S S S S S S B B S S B B S S S S S S S S S S
|
||||
S S S S B B B B S S S S S S S S S S B B B B S S S S
|
||||
I I S S B B B B S S S S S S S S S S B B B B S S I I
|
||||
S S S S S S S S S S B B S S B B S S S S S S S S S S
|
||||
S S S S S S S S S S B B B B B B S S S S S S S S S S
|
||||
S S B B S S B B S S B B B B B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S S S S S S S S S B B S S B B S S
|
||||
S S B B S S B B S S S S S S S S S S B B S S B B S S
|
||||
S S B B S S B B S S S B B B B S S S B B S S B B S S
|
||||
S S S S S S S S S S S B S S B S S S S S S S S S S S
|
||||
S S S S S S S S S S S B S S B S S S S S S S S S S S
|
@ -0,0 +1,39 @@
|
||||
##############################################
|
||||
# Explain: #
|
||||
# Grid: 26 * 26 #
|
||||
# Grid SIZE: 24 * 24 pixels #
|
||||
# S: Space B: Brick I: Iron #
|
||||
# R: River1/2 C: Ice T: Tree #
|
||||
##############################################
|
||||
%TOTALENEMYNUM:45
|
||||
%MAXENEMYNUM:9
|
||||
%HOMEPOS:12,24
|
||||
%HOMEAROUNDPOS:11,23 12,23 13,23 14,23 11,24 14,24 11,25 14,25
|
||||
%PLAYERTANKPOS:8,24 16,24
|
||||
%ENEMYTANKPOS:0,0 12,0 24,0
|
||||
S S S S S S S S S S S S S S S S S S S S S S S S S S
|
||||
S S S S S S S S S S S S S S S S S S S S S S S S S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B I I B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B I I B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S S S S S S S S S B B S S B B S S
|
||||
S S B B S S B B S S S S S S S S S S B B S S B B S S
|
||||
S S S S S S S S S S R R R R R R S S S S S S S S S S
|
||||
S S S S S S S S S S R R R R R R S S S S S S S S S S
|
||||
S S S S T T T T S S S S S S S S S S T T T T S S S S
|
||||
I I S S T T T T S S S S S S S S S S T T T T S S I I
|
||||
S S S S S S S S S S B B S S B B S S S S S S S S S S
|
||||
S S S S S S S S S S B B B B B B S S S S S S S S S S
|
||||
S S B B S S B B S S B B B B B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S S S S S S S S S B B S S B B S S
|
||||
S S B B S S B B S S S S S S S S S S B B S S B B S S
|
||||
S S B B S S B B S S S B B B B S S S B B S S B B S S
|
||||
S S S S S S S S S S S B S S B S S S S S S S S S S S
|
||||
S S S S S S S S S S S B S S B S S S S S S S S S S S
|
@ -0,0 +1,39 @@
|
||||
##############################################
|
||||
# Explain: #
|
||||
# Grid: 26 * 26 #
|
||||
# Grid SIZE: 24 * 24 pixels #
|
||||
# S: Space B: Brick I: Iron #
|
||||
# R: River1/2 C: Ice T: Tree #
|
||||
##############################################
|
||||
%TOTALENEMYNUM:60
|
||||
%MAXENEMYNUM:12
|
||||
%HOMEPOS:12,24
|
||||
%HOMEAROUNDPOS:11,23 12,23 13,23 14,23 11,24 14,24 11,25 14,25
|
||||
%PLAYERTANKPOS:8,24 16,24
|
||||
%ENEMYTANKPOS:0,0 12,0 24,0
|
||||
S S S S S S S S S S S S S S S S S S S S S S S S S S
|
||||
S S S S S S S S S S S S S S S S S S S S S S S S S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B I I B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B I I B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S S S S S S S S S B B S S B B S S
|
||||
S S B B S S B B S S S S S S S S S S B B S S B B S S
|
||||
S S S S S S S S S S R R R R R R S S S S S S S S S S
|
||||
S S S S S S S S S S R R R R R R S S S S S S S S S S
|
||||
S S S S C C C C S S S S S S S S S S C C C C S S S S
|
||||
I I S S C C C C S S S S S S S S S S C C C C S S I I
|
||||
S S S S S S S S S S B B S S B B S S S S S S S S S S
|
||||
S S S S S S S S S S B B B B B B S S S S S S S S S S
|
||||
S S B B S S B B S S B B B B B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S S S S S S S S S B B S S B B S S
|
||||
S S B B S S B B S S S S S S S S S S B B S S B B S S
|
||||
S S B B S S B B S S S B B B B S S S B B S S B B S S
|
||||
S S S S S S S S S S S B S S B S S S S S S S S S S S
|
||||
S S S S S S S S S S S B S S B S S S S S S S S S S S
|
@ -0,0 +1,39 @@
|
||||
##############################################
|
||||
# Explain: #
|
||||
# Grid: 26 * 26 #
|
||||
# Grid SIZE: 24 * 24 pixels #
|
||||
# S: Space B: Brick I: Iron #
|
||||
# R: River1/2 C: Ice T: Tree #
|
||||
##############################################
|
||||
%TOTALENEMYNUM:60
|
||||
%MAXENEMYNUM:12
|
||||
%HOMEPOS:12,24
|
||||
%HOMEAROUNDPOS:11,23 12,23 13,23 14,23 11,24 14,24 11,25 14,25
|
||||
%PLAYERTANKPOS:8,24 16,24
|
||||
%ENEMYTANKPOS:0,0 12,0 24,0
|
||||
S S S S S S S S S S S S S S S S S S S S S S S S S S
|
||||
S S S S S S S S S S S S S S S S S S S S S S S S S S
|
||||
S S T T S S T T S S T T S S T T S S T T S S T T S S
|
||||
S S T T S S T T S S T T S S T T S S T T S S T T S S
|
||||
S S T T S S T T S S T T S S T T S S T T S S T T S S
|
||||
S S T T S S T T S S T T S S T T S S T T S S T T S S
|
||||
S S T T S S T T S S T T I I T T S S T T S S T T S S
|
||||
S S T T S S T T S S T T I I T T S S T T S S T T S S
|
||||
S S T T S S T T S S T T S S T T S S T T S S T T S S
|
||||
T T T T T T T T T T T T T T T T T T T T T T T T T T
|
||||
T T T T T T T T T T T T T T T T T T T T T T T T T T
|
||||
R R R R R R R R R R R R R R R R R R R R R R R R R R
|
||||
R R R R R R R R R R R R R R R R R R R R R R R R R R
|
||||
S S S S C C C C S S S S S S S S S S C C C C S S S S
|
||||
S S S S C C C C S S S S S S S S S S C C C C S S S S
|
||||
S S S S S S S S S S B B S S B B S S S S S S S S S S
|
||||
S S S S S S S S S S B B B B B B S S S S S S S S S S
|
||||
S S B B S S B B S S B B B B B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S B B S S B B S S B B S S B B S S
|
||||
S S B B S S B B S S S S S S S S S S B B S S B B S S
|
||||
S S B B S S B B S S S S S S S S S S B B S S B B S S
|
||||
S S B B S S B B S S S B B B B S S S B B S S B B S S
|
||||
S S S S S S S S S S S B S S B S S S S S S S S S S S
|
||||
S S S S S S S S S S S B S S B S S S S S S S S S S S
|
@ -0,0 +1,39 @@
|
||||
##############################################
|
||||
# Explain: #
|
||||
# Grid: 26 * 26 #
|
||||
# Grid SIZE: 24 * 24 pixels #
|
||||
# S: Space B: Brick I: Iron #
|
||||
# R: River1/2 C: Ice T: Tree #
|
||||
##############################################
|
||||
%TOTALENEMYNUM:60
|
||||
%MAXENEMYNUM:12
|
||||
%HOMEPOS:12,24
|
||||
%HOMEAROUNDPOS:11,23 12,23 13,23 14,23 11,24 14,24 11,25 14,25
|
||||
%PLAYERTANKPOS:8,24 16,24
|
||||
%ENEMYTANKPOS:0,0 12,0 24,0
|
||||
S S S S S S S S S S S S S S S S S S S S S S S S S S
|
||||
S S S S S S S S S S S S S S S S S S S S S S S S S S
|
||||
S S R R S S R R S S R R S S R R S S R R S S R R S S
|
||||
S S R R S S R R S S R R S S R R S S R R S S R R S S
|
||||
S S R R S S R R S S R R S S R R S S R R S S R R S S
|
||||
S S R R S S R R S S R R S S R R S S R R S S R R S S
|
||||
S S R R S S R R S S R R S S R R S S R R S S R R S S
|
||||
S S R R S S R R S S R R S S R R S S R R S S R R S S
|
||||
S S R R S S R R S S R R S S R R S S R R S S R R S S
|
||||
C C C C C C C C C C C C C C C C C C C C C C C C C C
|
||||
C C C C C C C C C C C C C C C C C C C C C C C C C C
|
||||
S S S S S S S S S S S S S S S S S S S S S S S S S S
|
||||
S S S S S S S S S S S S S S S S S S S S S S S S S S
|
||||
S S S S T T T T S S S S R R S S S S T T T T S S S S
|
||||
S S S S T T T T S S S S R R S S S S T T T T S S S S
|
||||
S S S S S S S S S S C C R R C C S S S S S S S S S S
|
||||
S S S S S S S S S S C C C C C C S S S S S S S S S S
|
||||
S S C C S S C C S S C C C C C C S S C C S S C C S S
|
||||
S S C C S S C C S S C C S S C C S S C C S S C C S S
|
||||
S S C C S S C C S S C C S S C C S S C C S S C C S S
|
||||
S S C C S S C C S S C C S S C C S S C C S S C C S S
|
||||
S S C C S S C C S S S S S S S S S S C C S S C C S S
|
||||
S S B B S S B B S S S S S S S S S S B B S S B B S S
|
||||
S S B B S S B B S S S B B B B S S S B B S S B B S S
|
||||
C C C C C C C C C C C B S S B C C C C C C C C C C C
|
||||
C C C C C C C C C C C B S S B C C C C C C C C C C C
|
@ -0,0 +1,7 @@
|
||||
from modules.sprites.bullet import Bullet
|
||||
from modules.sprites.foods import Foods
|
||||
from modules.sprites.home import Home
|
||||
from modules.sprites.scenes import SceneFactory, SceneElement, Ice, Brick, Tree, River, Iron
|
||||
from modules.sprites.tanks import TankFactory, EnemyTank, PlayerTank, Tank
|
||||
from modules.sprites.groups import SceneElementsGroup, EntityGroup
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,36 @@
|
||||
|
||||
import pygame
|
||||
|
||||
'''子弹'''
|
||||
|
||||
|
||||
class Bullet(pygame.sprite.Sprite):
|
||||
|
||||
def __init__(self, direction, position, config, tank, enhanced=False, speed=20):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.image = pygame.image.load(config.BULLET_IMAGE_PATHS.get(direction.value))
|
||||
self.width, self.height = config.WIDTH, config.HEIGHT
|
||||
self.border_len = config.BORDER_LEN
|
||||
self.direction = direction
|
||||
self.rect = self.image.get_rect()
|
||||
self.position = self.rect.center = position
|
||||
self.tank = tank
|
||||
|
||||
# 地图边缘宽度
|
||||
|
||||
# 是否为加强版子弹(加强版可碎铁墙)
|
||||
self.enhanced = enhanced
|
||||
# 子弹速度
|
||||
self.speed = speed
|
||||
|
||||
def move(self):
|
||||
# 移动子弹, 若子弹越界, 则返回True, 否则为False
|
||||
self.rect = self.rect.move(self.direction.value[0] * self.speed, self.direction.value[1] * self.speed)
|
||||
return (self.rect.top < self.border_len) or (self.rect.bottom > self.height) or (
|
||||
self.rect.left < self.border_len) or (self.rect.right > self.width)
|
||||
|
||||
def kill(self):
|
||||
if not self.tank.infinity_bullet:
|
||||
self.tank.bullet_count -= 20
|
||||
|
||||
super().kill()
|
@ -0,0 +1,30 @@
|
||||
import pygame
|
||||
import random
|
||||
from .scenes import SceneElement
|
||||
|
||||
|
||||
class Foods(SceneElement):
|
||||
BOOM = 'boom'
|
||||
IRON = 'iron'
|
||||
CLOCK = 'clock'
|
||||
GUN = 'gun'
|
||||
TANK = 'tank'
|
||||
PROTECT = 'protect'
|
||||
STAR = 'star'
|
||||
|
||||
# 食物类. 用于获得奖励
|
||||
def __init__(self, food_image_paths, screensize):
|
||||
random_position = (random.randint(100, screensize[0]-100), random.randint(100, screensize[1]-100))
|
||||
random_food = random.choice(list(food_image_paths.keys()))
|
||||
self.__name = random_food
|
||||
super().__init__(random_position, food_image_paths.get(self.__name))
|
||||
|
||||
self.exist_time = 1000
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
return self.__name
|
||||
|
||||
def update(self):
|
||||
self.exist_time -= 1
|
||||
return True if self.exist_time < 0 else False
|
@ -0,0 +1,158 @@
|
||||
import pygame
|
||||
|
||||
from modules.sprites import tanks
|
||||
from modules.sprites.bullet import Bullet
|
||||
from modules.sprites.foods import Foods
|
||||
from modules.sprites.home import Home
|
||||
from modules.sprites import SceneElement, Ice, Brick, Tree, River, Iron
|
||||
|
||||
|
||||
class SceneElementsGroup(object):
|
||||
def __init__(self):
|
||||
self.__ice_group = pygame.sprite.Group()
|
||||
self.__iron_group = pygame.sprite.Group()
|
||||
self.__brick_group = pygame.sprite.Group()
|
||||
self.__tree_group = pygame.sprite.Group()
|
||||
self.__river_group = pygame.sprite.Group()
|
||||
|
||||
@property
|
||||
def ice_group(self):
|
||||
return self.__ice_group
|
||||
|
||||
@property
|
||||
def iron_group(self):
|
||||
return self.__iron_group
|
||||
|
||||
@property
|
||||
def brick_group(self):
|
||||
return self.__brick_group
|
||||
|
||||
@property
|
||||
def tree_group(self):
|
||||
return self.__tree_group
|
||||
|
||||
@property
|
||||
def river_group(self):
|
||||
return self.__river_group
|
||||
|
||||
def add(self, scene_element: SceneElement):
|
||||
if isinstance(scene_element, Ice):
|
||||
self.ice_group.add(scene_element)
|
||||
elif isinstance(scene_element, Brick):
|
||||
self.brick_group.add(scene_element)
|
||||
elif isinstance(scene_element, Tree):
|
||||
self.tree_group.add(scene_element)
|
||||
elif isinstance(scene_element, River):
|
||||
self.river_group.add(scene_element)
|
||||
elif isinstance(scene_element, Iron):
|
||||
self.iron_group.add(scene_element)
|
||||
|
||||
def draw(self, screen: pygame.Surface, layer: int):
|
||||
if layer == 1:
|
||||
self.ice_group.draw(screen)
|
||||
self.river_group.draw(screen)
|
||||
elif layer == 2:
|
||||
self.brick_group.draw(screen)
|
||||
self.iron_group.draw(screen)
|
||||
self.tree_group.draw(screen)
|
||||
|
||||
|
||||
class EntityGroup(object):
|
||||
def __init__(self):
|
||||
self.__player_tanks = pygame.sprite.Group()
|
||||
self.__enemy_tanks = pygame.sprite.Group()
|
||||
self.__player_bullets = pygame.sprite.Group()
|
||||
self.__enemy_bullets = pygame.sprite.Group()
|
||||
self.__foods = pygame.sprite.Group()
|
||||
|
||||
@property
|
||||
def player_tanks(self):
|
||||
return self.__player_tanks
|
||||
|
||||
@property
|
||||
def enemy_tanks(self):
|
||||
return self.__enemy_tanks
|
||||
|
||||
@property
|
||||
def player_bullets(self):
|
||||
return self.__player_bullets
|
||||
|
||||
@property
|
||||
def enemy_bullets(self):
|
||||
return self.__enemy_bullets
|
||||
|
||||
@property
|
||||
def foods(self):
|
||||
return self.__foods
|
||||
|
||||
def add(self, entity: pygame.sprite):
|
||||
if isinstance(entity, tanks.PlayerTank):
|
||||
self.player_tanks.add(entity)
|
||||
elif isinstance(entity, tanks.EnemyTank):
|
||||
self.enemy_tanks.add(entity)
|
||||
elif isinstance(entity, Foods):
|
||||
self.foods.add(entity)
|
||||
elif isinstance(entity, Bullet):
|
||||
if isinstance(entity.tank, tanks.PlayerTank):
|
||||
self.player_bullets.add(entity)
|
||||
elif isinstance(entity.tank, tanks.EnemyTank):
|
||||
self.enemy_bullets.add(entity)
|
||||
else:
|
||||
raise TypeError('Unknown Entity Type')
|
||||
|
||||
def clear_enemy_tanks(self):
|
||||
self.enemy_tanks.empty()
|
||||
|
||||
def remove(self, entity: pygame.sprite):
|
||||
if isinstance(entity, tanks.PlayerTank):
|
||||
self.player_tanks.remove(entity)
|
||||
elif isinstance(entity, tanks.EnemyTank):
|
||||
self.enemy_tanks.remove(entity)
|
||||
elif isinstance(entity, Foods):
|
||||
self.foods.remove(entity)
|
||||
elif isinstance(entity, Bullet):
|
||||
if isinstance(entity.tank, tanks.PlayerTank):
|
||||
self.player_bullets.remove(entity)
|
||||
elif isinstance(entity.tank, tanks.EnemyTank):
|
||||
self.enemy_bullets.remove(entity)
|
||||
else:
|
||||
raise TypeError('Unknown Entity Type')
|
||||
|
||||
def draw(self, screen: pygame.Surface, layer: int):
|
||||
if layer == 1:
|
||||
self.player_bullets.draw(screen)
|
||||
self.enemy_bullets.draw(screen)
|
||||
self.player_tanks.draw(screen)
|
||||
for tank in self.player_tanks:
|
||||
tank.draw(screen)
|
||||
self.enemy_tanks.draw(screen)
|
||||
elif layer == 2:
|
||||
self.foods.draw(screen)
|
||||
|
||||
def update(self, scene_elements: SceneElementsGroup, home: Home):
|
||||
# 更新并画我方子弹
|
||||
for bullet in self.player_bullets:
|
||||
if bullet.move():
|
||||
bullet.kill()
|
||||
# 更新并画敌方子弹
|
||||
for bullet in self.enemy_bullets:
|
||||
if bullet.move():
|
||||
bullet.kill()
|
||||
# 更新并画我方坦克
|
||||
for tank in self.player_tanks:
|
||||
tank.update()
|
||||
# 更新并画敌方坦克
|
||||
for tank in self.enemy_tanks:
|
||||
self.remove(tank)
|
||||
remove_flag, bullet = tank.update(
|
||||
scene_elements, self.player_tanks, self.enemy_tanks, home
|
||||
)
|
||||
self.add(tank)
|
||||
if isinstance(bullet, Bullet):
|
||||
self.add(bullet)
|
||||
if remove_flag:
|
||||
self.remove(tank)
|
||||
# 更新食物
|
||||
for food in self.foods:
|
||||
if food.update():
|
||||
self.remove(food)
|
@ -0,0 +1,25 @@
|
||||
import pygame
|
||||
from .scenes import SceneElement
|
||||
|
||||
|
||||
class Home(SceneElement):
|
||||
# 大本营
|
||||
def __init__(self, position, imagefile, walls_position):
|
||||
super().__init__(position, imagefile[0])
|
||||
self.__destroyed_image = imagefile[1]
|
||||
self.__destroyed = False
|
||||
self.walls_position = walls_position
|
||||
|
||||
|
||||
@property
|
||||
def destroyed(self):
|
||||
return self.__destroyed
|
||||
|
||||
@destroyed.setter
|
||||
def destroyed(self, destroyed):
|
||||
self.__destroyed = destroyed
|
||||
if destroyed:
|
||||
self.image = pygame.image.load(self.__destroyed_image)
|
||||
|
||||
def draw(self, screen):
|
||||
screen.blit(self.image, self.rect)
|
@ -0,0 +1,66 @@
|
||||
import pygame
|
||||
|
||||
|
||||
class SceneElement(pygame.sprite.Sprite):
|
||||
def __init__(self, position, imagefile, blitimage=False):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.image = pygame.image.load(imagefile)
|
||||
if blitimage:
|
||||
self.image = pygame.Surface((24, 24))
|
||||
for i in range(2):
|
||||
for j in range(2):
|
||||
self.image.blit(pygame.image.load(imagefile), (12 * i, 12 * j))
|
||||
self.rect = self.image.get_rect()
|
||||
self.rect.left, self.rect.top = position
|
||||
|
||||
|
||||
class Brick(SceneElement):
|
||||
# 砖墙
|
||||
pass
|
||||
|
||||
|
||||
class Iron(SceneElement):
|
||||
# 铁墙
|
||||
pass
|
||||
|
||||
|
||||
class Ice(SceneElement):
|
||||
# 冰
|
||||
def __init__(self, position, imagefile):
|
||||
super().__init__(position, imagefile, True)
|
||||
|
||||
|
||||
class River(SceneElement):
|
||||
# 河流
|
||||
def __init__(self, position, imagefile):
|
||||
super().__init__(position, imagefile, True)
|
||||
|
||||
|
||||
class Tree(SceneElement):
|
||||
# 树
|
||||
def __init__(self, position, imagefile):
|
||||
super().__init__(position, imagefile, True)
|
||||
|
||||
|
||||
class SceneFactory(object):
|
||||
BRICK = 'brick'
|
||||
IRON = 'iron'
|
||||
RIVER_1 = 'river1'
|
||||
RIVER_2 = 'river2'
|
||||
ICE = 'ice'
|
||||
TREE = 'tree'
|
||||
SCENE_MAPS = {
|
||||
BRICK: Brick,
|
||||
IRON: Iron,
|
||||
RIVER_1: River,
|
||||
RIVER_2: River,
|
||||
ICE: Ice,
|
||||
TREE: Tree,
|
||||
}
|
||||
|
||||
def __init__(self, game_config):
|
||||
self.__scene_images = game_config.SCENE_IMAGE_PATHS
|
||||
|
||||
def create_element(self, position, element_type):
|
||||
return SceneFactory.SCENE_MAPS[element_type](position, self.__scene_images.get(element_type))
|
||||
|
@ -0,0 +1,466 @@
|
||||
import pygame
|
||||
import random
|
||||
|
||||
from modules.TankGame import TankGame
|
||||
from modules.sprites import groups
|
||||
|
||||
from modules.sprites.foods import Foods
|
||||
from modules.sprites.bullet import Bullet
|
||||
from enum import Enum
|
||||
from pygame.sprite import spritecollide
|
||||
|
||||
|
||||
|
||||
|
||||
class DIRECTION(Enum):
|
||||
UP = (0, -1)
|
||||
DOWN = (0, 1)
|
||||
LEFT = (-1, 0)
|
||||
RIGHT = (1, 0)
|
||||
|
||||
@classmethod
|
||||
def list(cls):
|
||||
return [DIRECTION.UP, DIRECTION.DOWN, DIRECTION.LEFT, DIRECTION.RIGHT]
|
||||
|
||||
@classmethod
|
||||
def random(cls):
|
||||
return random.choice([DIRECTION.UP, DIRECTION.DOWN, DIRECTION.LEFT, DIRECTION.RIGHT])
|
||||
|
||||
|
||||
class COLLISION:
|
||||
WITH_TANK = 0b00001
|
||||
WITH_HOME = 0b00010
|
||||
WITH_BORDER = 0b00100
|
||||
WITH_SCENE_ELEMENTS = 0b01000
|
||||
|
||||
|
||||
class Tank(pygame.sprite.Sprite):
|
||||
def __init__(self, game_config):
|
||||
super().__init__()
|
||||
self.__game_config = game_config
|
||||
# 坦克轮子转动效果
|
||||
self._switch_count = 0
|
||||
self._switch_time = 1
|
||||
self._switch_pointer = False
|
||||
# 移动缓冲
|
||||
self._move_cache_time = 4
|
||||
self._move_cache_count = 0
|
||||
# 爆炸
|
||||
self._boom_last_time = 5
|
||||
self._boom_count = 0
|
||||
self._booming_flag = False
|
||||
|
||||
self._level = 0
|
||||
self._speed = 8
|
||||
# 地图边缘宽度/屏幕大小
|
||||
self._border_len = game_config.BORDER_LEN
|
||||
self._screen_size = [game_config.WIDTH, game_config.HEIGHT]
|
||||
self._init_resources()
|
||||
|
||||
self.bullet_count = 0
|
||||
self._is_bullet_cooling = False
|
||||
self._bullet_config = {
|
||||
0: {
|
||||
'speed': 8,
|
||||
'enhanced': False
|
||||
},
|
||||
1: {
|
||||
'speed': 10,
|
||||
'enhanced': False
|
||||
},
|
||||
2: {
|
||||
'speed': 10,
|
||||
'enhanced': True
|
||||
},
|
||||
'count': 1,
|
||||
'infinity': False
|
||||
}
|
||||
|
||||
@property
|
||||
def infinity_bullet(self):
|
||||
return self._bullet_config['infinity']
|
||||
|
||||
@property
|
||||
def bullet_limit(self):
|
||||
return self._bullet_config['count']
|
||||
|
||||
@property
|
||||
def _game_config(self):
|
||||
return self.__game_config
|
||||
|
||||
def shoot(self):
|
||||
if self._booming_flag:
|
||||
return False
|
||||
if not self._is_bullet_cooling:
|
||||
if not self.infinity_bullet:
|
||||
if self.bullet_count >= self.bullet_limit:
|
||||
return False
|
||||
else:
|
||||
self.bullet_count += 1
|
||||
|
||||
self._is_bullet_cooling = True
|
||||
position = (self.rect.centerx + self._direction.value[0], self.rect.centery + self._direction.value[1])
|
||||
bullet = Bullet(direction=self._direction, position=position, tank=self, config=self._game_config)
|
||||
configkey = self._level
|
||||
if configkey >= 2:
|
||||
configkey = 2
|
||||
bullet.speed = self._bullet_config[configkey]['speed']
|
||||
bullet.enhanced = self._bullet_config[configkey]['enhanced']
|
||||
return bullet
|
||||
|
||||
return False
|
||||
|
||||
@property
|
||||
def image(self):
|
||||
if self._booming_flag:
|
||||
return self._boom_image
|
||||
return self._tank_direction_image.subsurface((48 * int(self._switch_pointer), 0), (48, 48))
|
||||
|
||||
def decrease_level(self):
|
||||
if self._booming_flag:
|
||||
return False
|
||||
self._level -= 1
|
||||
self._tank_image = pygame.image.load(self._level_images[self._level]).convert_alpha()
|
||||
self._update_direction(self._direction)
|
||||
# self.image = self._tank_direction_image.subsurface((48 * int(self._switch_pointer), 0), (48, 48))
|
||||
if self._level < 0:
|
||||
self._booming_flag = True
|
||||
return True if self._level < 0 else False
|
||||
|
||||
'''设置坦克方向'''
|
||||
def _update_direction(self, direction):
|
||||
self._direction = direction
|
||||
if self._direction == DIRECTION.UP:
|
||||
self._tank_direction_image = self._tank_image.subsurface((0, 0), (96, 48))
|
||||
elif self._direction == DIRECTION.DOWN:
|
||||
self._tank_direction_image = self._tank_image.subsurface((0, 48), (96, 48))
|
||||
elif self._direction == DIRECTION.LEFT:
|
||||
self._tank_direction_image = self._tank_image.subsurface((0, 96), (96, 48))
|
||||
elif self._direction == DIRECTION.RIGHT:
|
||||
self._tank_direction_image = self._tank_image.subsurface((0, 144), (96, 48))
|
||||
|
||||
def _init_resources(self):
|
||||
config = self._game_config
|
||||
self._bullet_images = config.BULLET_IMAGE_PATHS
|
||||
self._boom_image = pygame.image.load(config.OTHER_IMAGE_PATHS.get('boom_static'))
|
||||
pass
|
||||
|
||||
def roll(self):
|
||||
# 为了坦克轮动特效切换图片
|
||||
self._switch_count += 1
|
||||
if self._switch_count > self._switch_time:
|
||||
self._switch_count = 0
|
||||
self._switch_pointer = not self._switch_pointer
|
||||
|
||||
def move(self, direction, scene_elems, player_tanks_group, enemy_tanks_group, home):
|
||||
# 爆炸时无法移动
|
||||
if self._booming_flag:
|
||||
return
|
||||
# 方向不一致先改变方向
|
||||
if self._direction != direction:
|
||||
self._update_direction(direction)
|
||||
self._switch_count = self._switch_time
|
||||
self._move_cache_count = self._move_cache_time
|
||||
# 移动(使用缓冲)
|
||||
self._move_cache_count += 1
|
||||
if self._move_cache_count < self._move_cache_time:
|
||||
return
|
||||
self._move_cache_count = 0
|
||||
|
||||
new_position = (self._direction.value[0] * self._speed, self._direction.value[1] * self._speed)
|
||||
old_rect = self.rect
|
||||
self.rect = self.rect.move(new_position)
|
||||
# --碰到场景元素
|
||||
collisons = 0
|
||||
cannot_passthrough = [scene_elems.brick_group, scene_elems.iron_group, scene_elems.river_group]
|
||||
for i in cannot_passthrough:
|
||||
if spritecollide(self, i, False, None):
|
||||
self.rect = old_rect
|
||||
collisons |= COLLISION.WITH_SCENE_ELEMENTS
|
||||
|
||||
|
||||
|
||||
# --碰到其他玩家坦克/碰到敌方坦克
|
||||
if spritecollide(self, player_tanks_group, False, None) or spritecollide(self, enemy_tanks_group, False, None):
|
||||
collisons |= COLLISION.WITH_TANK
|
||||
self.rect = old_rect
|
||||
|
||||
# --碰到玩家大本营
|
||||
if pygame.sprite.collide_rect(self, home):
|
||||
collisons |= COLLISION.WITH_HOME
|
||||
self.rect = old_rect
|
||||
|
||||
# --碰到边界
|
||||
if self.rect.left < self._border_len:
|
||||
self.rect.left = self._border_len
|
||||
collisons |= COLLISION.WITH_BORDER
|
||||
elif self.rect.right > self._screen_size[0] - self._border_len:
|
||||
collisons |= COLLISION.WITH_BORDER
|
||||
self.rect.right = self._screen_size[0] - self._border_len
|
||||
elif self.rect.top < self._border_len:
|
||||
collisons |= COLLISION.WITH_BORDER
|
||||
self.rect.top = self._border_len
|
||||
elif self.rect.bottom > self._screen_size[1] - self._border_len:
|
||||
collisons |= COLLISION.WITH_BORDER
|
||||
self.rect.bottom = self._screen_size[1] - self._border_len
|
||||
|
||||
if collisons == 0 and spritecollide(self, scene_elems.ice_group, False, None):
|
||||
self.rect = self.rect.move(new_position)
|
||||
|
||||
return collisons
|
||||
|
||||
|
||||
class PlayerTank(Tank):
|
||||
def __init__(self, name, position, game_config, **kwargs):
|
||||
super().__init__(game_config=game_config)
|
||||
self._level_images = self._game_config.PLAYER_TANK_IMAGE_PATHS.get(name)
|
||||
|
||||
# 玩家1/玩家2
|
||||
self.name = name
|
||||
# 初始坦克方向
|
||||
self.__init_direction = DIRECTION.UP
|
||||
# 初始位置
|
||||
self.__init_position = position
|
||||
# 保护罩
|
||||
self.__protected = False
|
||||
self.__protected_mask_flash_time = 25
|
||||
self.__protected_mask_flash_count = 0
|
||||
self.__protected_mask_pointer = False
|
||||
# 坦克生命数量
|
||||
self.health = 3
|
||||
# 重置
|
||||
self.__reborn()
|
||||
|
||||
def _init_resources(self):
|
||||
super()._init_resources()
|
||||
# 保护罩
|
||||
self.__protected_mask = pygame.image.load(self._game_config.OTHER_IMAGE_PATHS.get('protect'))
|
||||
|
||||
def update(self):
|
||||
# 坦克子弹冷却更新
|
||||
if self._is_bullet_cooling:
|
||||
self._bullet_cooling_count += 1
|
||||
if self._bullet_cooling_count >= self._bullet_cooling_time:
|
||||
self._bullet_cooling_count = 0
|
||||
self._is_bullet_cooling = False
|
||||
# 无敌状态更新
|
||||
if self.protected:
|
||||
self.__protected_count += 1
|
||||
if self.__protected_count > self.__protected_time:
|
||||
self.protected = False
|
||||
self.__protected_count = 0
|
||||
# 爆炸状态更新
|
||||
if self._booming_flag:
|
||||
self._boom_count += 1
|
||||
if self._boom_count > self._boom_last_time:
|
||||
self._boom_count = 0
|
||||
self._booming_flag = False
|
||||
self.__reborn()
|
||||
|
||||
def improve_level(self):
|
||||
# 提高坦克等级
|
||||
if self._booming_flag:
|
||||
return False
|
||||
self._level = min(self._level + 1, len(self._level_images) - 1)
|
||||
self._tank_image = pygame.image.load(self._level_images[self._level]).convert_alpha()
|
||||
self._update_direction(self._direction)
|
||||
return True
|
||||
|
||||
def decrease_level(self):
|
||||
# 降低坦克等级
|
||||
res = super().decrease_level()
|
||||
if self._level < 0:
|
||||
self.health -= 1
|
||||
|
||||
return res
|
||||
|
||||
def add_health(self):
|
||||
# 增加生命值
|
||||
self.health += 1
|
||||
|
||||
@property
|
||||
def protected(self):
|
||||
return self.__protected
|
||||
|
||||
@protected.setter
|
||||
def protected(self, protected):
|
||||
self.__protected = protected
|
||||
|
||||
def draw(self, screen):
|
||||
# 画我方坦克
|
||||
screen.blit(self.image, self.rect)
|
||||
if self.protected:
|
||||
self.__protected_mask_flash_count += 1
|
||||
if self.__protected_mask_flash_count > self.__protected_mask_flash_time:
|
||||
self.__protected_mask_pointer = not self.__protected_mask_pointer
|
||||
self.__protected_mask_flash_count = 0
|
||||
screen.blit(self.__protected_mask.subsurface((48 * self.__protected_mask_pointer, 0), (48, 48)), self.rect)
|
||||
|
||||
def __reborn(self):
|
||||
# 重置坦克, 重生的时候用
|
||||
|
||||
# 移动缓冲, 用于避免坦克连续运行不方便调整位置
|
||||
self._move_cache_time = 4
|
||||
self._move_cache_count = 0
|
||||
# 是否无敌状态
|
||||
self.protected = False
|
||||
self.__protected_time = 1500
|
||||
self.__protected_count = 0
|
||||
# 坦克移动速度
|
||||
self._speed = 8
|
||||
# 子弹冷却时间
|
||||
self._bullet_cooling_time = 30
|
||||
self._bullet_cooling_count = 0
|
||||
self._is_bullet_cooling = False
|
||||
# 坦克等级
|
||||
self._level = 0
|
||||
|
||||
# 坦克图片
|
||||
self._tank_image = pygame.image.load(self._level_images[self._level]).convert_alpha()
|
||||
self._update_direction(self.__init_direction)
|
||||
self.rect = self.image.get_rect()
|
||||
self.rect.left, self.rect.top = self.__init_position
|
||||
|
||||
|
||||
'''敌方坦克类'''
|
||||
|
||||
|
||||
class EnemyTank(Tank):
|
||||
def __init__(self, position, game_config, **kwargs):
|
||||
super().__init__(game_config=game_config)
|
||||
enemy_level_images = self._game_config.ENEMY_TANK_IMAGE_PATHS
|
||||
self.__tank_type = random.choices(['0', '1', '2'], weights=[10, 10, TankGame().level+10])[0]#random.choice(list(enemy_level_images.keys()))
|
||||
self._level_images = enemy_level_images.get(self.__tank_type)
|
||||
self._bullet_config[2]['enhanced'] = False
|
||||
|
||||
self._level = int(self.__tank_type) #random.randint(0, len(self._level_images) - 2)
|
||||
# 子弹冷却时间
|
||||
self._bullet_cooling_time = 120 - self._level * 10
|
||||
self._bullet_cooling_count = 0
|
||||
self._is_bullet_cooling = False
|
||||
# 用于给刚生成的坦克播放出生特效
|
||||
self._is_borning = True
|
||||
self._borning_left_time = 90
|
||||
# 坦克是否可以行动(玩家坦克捡到食物clock可以触发为True)
|
||||
self.is_keep_still = False
|
||||
self.keep_still_time = 500
|
||||
self.keep_still_count = 0
|
||||
|
||||
# 坦克移动速度,等级越高速度越低
|
||||
self._speed = 10 - self._level * 3
|
||||
|
||||
self.__food = None
|
||||
# 坦克出场特效
|
||||
appear_image = pygame.image.load(self._game_config.OTHER_IMAGE_PATHS.get('appear')).convert_alpha()
|
||||
self.__appear_images = [
|
||||
appear_image.subsurface((0, 0), (48, 48)),
|
||||
appear_image.subsurface((48, 0), (48, 48)),
|
||||
appear_image.subsurface((96, 0), (48, 48))
|
||||
]
|
||||
if random.random() <= 0.3 * self._level:
|
||||
# if (random.random() >= 0.6) and (self._level == len(self._level_images) - 2):
|
||||
# self._level += 1
|
||||
self.__food = Foods(food_image_paths=self._game_config.FOOD_IMAGE_PATHS, screensize=self._screen_size)
|
||||
|
||||
# 坦克图片路径
|
||||
self._tank_image = pygame.image.load(self._level_images[self._level]).convert_alpha()
|
||||
self._direction = DIRECTION.random()
|
||||
self._update_direction(self._direction)
|
||||
self.rect = self.image.get_rect()
|
||||
self.rect.left, self.rect.top = position
|
||||
# 坦克爆炸图
|
||||
|
||||
@property
|
||||
def food(self):
|
||||
return self.__food
|
||||
|
||||
def clear_food(self):
|
||||
self.__food = None
|
||||
|
||||
@property
|
||||
def image(self):
|
||||
if self._is_borning:
|
||||
return self.__appear_images[(90 - self._borning_left_time // 10) % 3]
|
||||
return super().image
|
||||
|
||||
def update(self, scene_elems, player_tanks_group, enemy_tanks_group, home):
|
||||
# 死后爆炸
|
||||
remove_flag = False
|
||||
bullet = None
|
||||
if self._booming_flag:
|
||||
|
||||
self._boom_count += 1
|
||||
if self._boom_count > self._boom_last_time:
|
||||
self._boom_count = 0
|
||||
self._booming_flag = False
|
||||
remove_flag = True
|
||||
return remove_flag, bullet
|
||||
|
||||
# 禁止行动时不更新
|
||||
if self.is_keep_still:
|
||||
self.keep_still_count += 1
|
||||
if self.keep_still_count > self.keep_still_time:
|
||||
self.is_keep_still = False
|
||||
self.keep_still_count = 0
|
||||
return remove_flag, bullet
|
||||
|
||||
# 播放出生特效
|
||||
if self._is_borning:
|
||||
self._borning_left_time -= 1
|
||||
if self._borning_left_time < 0:
|
||||
self._is_borning = False
|
||||
# 出生后实时更新
|
||||
else:
|
||||
# --坦克移动
|
||||
self.move(self._direction, scene_elems, player_tanks_group, enemy_tanks_group, home)
|
||||
self.roll()
|
||||
# --坦克子弹冷却更新
|
||||
if self._is_bullet_cooling:
|
||||
self._bullet_cooling_count += 1
|
||||
if self._bullet_cooling_count >= self._bullet_cooling_time:
|
||||
self._bullet_cooling_count = 0
|
||||
self._is_bullet_cooling = False
|
||||
# --能射击就射击
|
||||
bullet = self.shoot()
|
||||
return remove_flag, bullet
|
||||
|
||||
def random_change_direction(self, exclude_current_direction=False):
|
||||
list = DIRECTION.list()
|
||||
if exclude_current_direction:
|
||||
list.remove(self._direction)
|
||||
|
||||
self._update_direction(random.choice(list))
|
||||
self._switch_count = self._switch_time
|
||||
self._move_cache_count = self._move_cache_time
|
||||
|
||||
def move(self, direction, scene_elems, player_tanks_group, enemy_tanks_group, home):
|
||||
# 遇到障碍物考虑改变方向
|
||||
collisions = super().move(direction, scene_elems, player_tanks_group, enemy_tanks_group, home)
|
||||
if collisions is None or collisions == 0:
|
||||
return
|
||||
|
||||
change_direction = False
|
||||
if collisions & COLLISION.WITH_SCENE_ELEMENTS & COLLISION.WITH_BORDER:
|
||||
change_direction = True
|
||||
self.random_change_direction(change_direction)
|
||||
|
||||
def set_still(self):
|
||||
self.is_keep_still = True
|
||||
|
||||
|
||||
class TankFactory(object):
|
||||
ENEMY_TANK = 0
|
||||
PLAYER1_TANK = 1
|
||||
PLAYER2_TANK = 2
|
||||
|
||||
def __init__(self, config):
|
||||
self.__game_config = config
|
||||
|
||||
def create_tank(self, position, tank_type):
|
||||
if tank_type == TankFactory.ENEMY_TANK:
|
||||
return EnemyTank(position=position, game_config=self.__game_config)
|
||||
elif tank_type == TankFactory.PLAYER1_TANK:
|
||||
return PlayerTank(name='player1', position=position, game_config=self.__game_config)
|
||||
elif tank_type == TankFactory.PLAYER2_TANK:
|
||||
return PlayerTank(name='player2', position=position, game_config=self.__game_config)
|
||||
|
@ -0,0 +1,45 @@
|
||||
import pygame,sys
|
||||
|
||||
from modules.TankGame import TankGame
|
||||
|
||||
|
||||
class AbstractView(object):
|
||||
def __init__(self):
|
||||
self._init_resources()
|
||||
self._init_logo()
|
||||
|
||||
@property
|
||||
def config(self):
|
||||
return TankGame().config
|
||||
|
||||
def _init_resources(self):
|
||||
pass
|
||||
|
||||
def _init_logo(self):
|
||||
pass
|
||||
|
||||
def _init_text(self):
|
||||
pass
|
||||
|
||||
def _init_bottons(self):
|
||||
pass
|
||||
|
||||
def _draw_interface(self):
|
||||
pass
|
||||
|
||||
def _main_loop(self):
|
||||
while True:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
self._draw_interface()
|
||||
pass
|
||||
|
||||
def show(self):
|
||||
TankGame().init_game_window()
|
||||
self._init_text()
|
||||
self._init_bottons()
|
||||
self._main_loop()
|
||||
pass
|
||||
|
@ -0,0 +1,432 @@
|
||||
import sys
|
||||
import pygame
|
||||
import random
|
||||
from modules.TankGame import TankGame
|
||||
from modules.sprites import *
|
||||
from modules.views.AbstractView import AbstractView
|
||||
|
||||
from modules.sprites.tanks import DIRECTION
|
||||
from pygame.sprite import spritecollide, groupcollide, collide_rect
|
||||
|
||||
|
||||
class GameLevelView(AbstractView):
|
||||
|
||||
def _init_resources(self):
|
||||
config = self.config
|
||||
self.__sounds = TankGame().sounds
|
||||
self.__other_images = config.OTHER_IMAGE_PATHS
|
||||
self.__home_images = config.HOME_IMAGE_PATHS
|
||||
self.__background_img = pygame.image.load(self.__other_images.get('background'))
|
||||
self.__font = pygame.font.Font(config.FONTPATH, config.HEIGHT // 35)
|
||||
|
||||
self.__border_len = config.BORDER_LEN
|
||||
self.__grid_size = config.GRID_SIZE
|
||||
self.__screen_width, self.__screen_height = config.WIDTH, config.HEIGHT
|
||||
self.__panel_width = config.PANEL_WIDTH
|
||||
self.__tank_factory = TankFactory(self.config)
|
||||
self.__scene_factory = SceneFactory(self.config)
|
||||
self.__scene_elements = None
|
||||
|
||||
def _init_text(self):
|
||||
color_white = (255, 255, 255)
|
||||
self.__fix_text_tips = {
|
||||
1: {'text': 'Operate-P1'},
|
||||
2: {'text': 'K_w: Up'},
|
||||
3: {'text': 'K_s: Down'},
|
||||
4: {'text': 'K_a: Left'},
|
||||
5: {'text': 'K_d: Right'},
|
||||
6: {'text': 'K_SPACE: Shoot'},
|
||||
8: {'text': 'Operate-P2:'},
|
||||
9: {'text': 'K_UP: Up'},
|
||||
10: {'text': 'K_DOWN: Down'},
|
||||
11: {'text': 'K_LEFT: Left'},
|
||||
12: {'text': 'K_RIGHT: Right'},
|
||||
13: {'text': 'K_KP0: Shoot'},
|
||||
15: {'text': 'State-P1:'},
|
||||
19: {'text': 'State-P2:'},
|
||||
}
|
||||
for pos, tip in self.__fix_text_tips.items():
|
||||
tip['render'] = self.__font.render(tip['text'], True, color_white)
|
||||
tip['rect'] = tip['render'].get_rect()
|
||||
tip['rect'].left, tip['rect'].top = self.__screen_width + 5, self.__screen_height * pos / 30
|
||||
|
||||
pass
|
||||
|
||||
'''开始游戏'''
|
||||
def _init_game_window(self):
|
||||
TankGame().init_game_window(
|
||||
(self.config.WIDTH + self.config.PANEL_WIDTH, self.config.HEIGHT)
|
||||
)
|
||||
|
||||
def __play_sound(self, sound):
|
||||
self.__sounds[sound].play()
|
||||
|
||||
def __dispatch_player_operation(self):
|
||||
key_pressed = pygame.key.get_pressed()
|
||||
key_maps = {
|
||||
'dir': {
|
||||
self.__tank_player1: {
|
||||
pygame.K_w: DIRECTION.UP,
|
||||
pygame.K_s: DIRECTION.DOWN,
|
||||
pygame.K_a: DIRECTION.LEFT,
|
||||
pygame.K_d: DIRECTION.RIGHT,
|
||||
},
|
||||
self.__tank_player2: {
|
||||
pygame.K_UP: DIRECTION.UP,
|
||||
pygame.K_DOWN: DIRECTION.DOWN,
|
||||
pygame.K_LEFT: DIRECTION.LEFT,
|
||||
pygame.K_RIGHT: DIRECTION.RIGHT,
|
||||
},
|
||||
},
|
||||
'fire': {
|
||||
self.__tank_player1: pygame.K_SPACE,
|
||||
self.__tank_player2: pygame.K_KP0,
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
# 玩家一, WSAD移动, 空格键射击
|
||||
player_tank_list = []
|
||||
if self.__tank_player1.health >= 0:
|
||||
player_tank_list.append(self.__tank_player1)
|
||||
if TankGame().multiplayer_mode and (self.__tank_player1.health >= 0):
|
||||
player_tank_list.append(self.__tank_player2)
|
||||
|
||||
for tank in player_tank_list:
|
||||
for key, dir in key_maps['dir'][tank].items():
|
||||
if key_pressed[key]:
|
||||
self.__entities.remove(tank)
|
||||
tank.move(dir, self.__scene_elements, self.__entities.player_tanks,
|
||||
self.__entities.enemy_tanks, self.__home)
|
||||
tank.roll()
|
||||
self.__entities.add(tank)
|
||||
break
|
||||
|
||||
if key_pressed[key_maps['fire'][tank]]:
|
||||
bullet = tank.shoot()
|
||||
if bullet:
|
||||
self.__play_sound('fire') if tank._level < 2 else self.__play_sound('Gunfire')
|
||||
self.__entities.add(bullet)
|
||||
|
||||
def __dispatch_food_effect(self, food: Foods, player_tank: PlayerTank):
|
||||
self.__play_sound('add')
|
||||
|
||||
if food.type == Foods.BOOM:
|
||||
for _ in self.__entities.enemy_tanks:
|
||||
self.__play_sound('bang')
|
||||
self.__total_enemy_num -= len(self.__entities.enemy_tanks)
|
||||
self.__entities.clear_enemy_tanks()
|
||||
elif food.type == Foods.CLOCK:
|
||||
for enemy_tank in self.__entities.enemy_tanks:
|
||||
enemy_tank.set_still()
|
||||
elif food.type == Foods.GUN:
|
||||
player_tank.improve_level()
|
||||
elif food.type == Foods.IRON:
|
||||
for x, y in self.__home.walls_position:
|
||||
self.__scene_elements.add(
|
||||
self.__scene_factory.create_element((x, y), SceneFactory.IRON)
|
||||
)
|
||||
elif food.type == Foods.PROTECT:
|
||||
player_tank.protected = True
|
||||
elif food.type == Foods.STAR:
|
||||
player_tank.improve_level()
|
||||
player_tank.improve_level()
|
||||
elif food.type == Foods.TANK:
|
||||
player_tank.add_health()
|
||||
|
||||
self.__entities.foods.remove(food)
|
||||
|
||||
def __dispatch_collisions(self):
|
||||
collision_results = {
|
||||
'group': {},
|
||||
'sprite': {},
|
||||
'foreach_sprite': {},
|
||||
}
|
||||
for (collision, args) in self.__collisions['group'].items():
|
||||
collision_results['group'][collision] = groupcollide(*args)
|
||||
|
||||
for (collision, args) in self.__collisions['sprite'].items():
|
||||
collision_results['sprite'][collision] = spritecollide(*args)
|
||||
|
||||
for (collision, args) in self.__collisions['foreach_sprite'].items():
|
||||
arg_list = list(args)
|
||||
sprite_list = arg_list[0]
|
||||
for sprite in sprite_list:
|
||||
arg_list[0] = sprite
|
||||
args = tuple(arg_list)
|
||||
collision_results['foreach_sprite'][sprite] = spritecollide(*args)
|
||||
|
||||
for bullet in self.__entities.player_bullets:
|
||||
collision_result = spritecollide(bullet, self.__scene_elements.iron_group, bullet.enhanced, None)
|
||||
if collision_result:
|
||||
bullet.kill()
|
||||
|
||||
for player_tank in self.__entities.player_tanks:
|
||||
for food in self.__entities.foods:
|
||||
collision_result = collide_rect(player_tank, food)
|
||||
if collision_result:
|
||||
self.__dispatch_food_effect(food, player_tank)
|
||||
|
||||
# --我方子弹撞敌方坦克
|
||||
for tank in self.__entities.enemy_tanks:
|
||||
if collision_results['foreach_sprite'][tank]:
|
||||
if tank.food:
|
||||
self.__entities.add(tank.food)
|
||||
tank.clear_food()
|
||||
if tank.decrease_level():
|
||||
self.__play_sound('bang')
|
||||
self.__total_enemy_num -= 1
|
||||
|
||||
# --敌方子弹撞我方坦克
|
||||
for tank in self.__entities.player_tanks:
|
||||
if collision_results['foreach_sprite'][tank]:
|
||||
if tank.protected:
|
||||
self.__play_sound('blast')
|
||||
else:
|
||||
if tank.decrease_level():
|
||||
self.__play_sound('bang')
|
||||
if tank.health < 0:
|
||||
self.__entities.remove(tank)
|
||||
|
||||
if collision_results['sprite']['PlayerBulletWithHome'] or collision_results['sprite']['EnemyBulletWithHome']:
|
||||
self.__is_win_flag = False
|
||||
self.__has_next_loop = False
|
||||
self.__play_sound('bang')
|
||||
self.__home.destroyed = True
|
||||
|
||||
if collision_results['group']['PlayerTankWithTree']:
|
||||
self.__play_sound('hit')
|
||||
|
||||
def _draw_interface(self):
|
||||
screen = TankGame().screen
|
||||
screen.fill((0, 0, 0))
|
||||
screen.blit(self.__background_img, (0, 0))
|
||||
self.__scene_elements.draw(screen, 1)
|
||||
self.__entities.draw(screen, 1)
|
||||
self.__scene_elements.draw(screen, 2)
|
||||
self.__home.draw(screen)
|
||||
self.__entities.draw(screen, 2)
|
||||
self.__draw_game_panel()
|
||||
pygame.display.flip()
|
||||
|
||||
def _main_loop(self):
|
||||
clock = pygame.time.Clock()
|
||||
# cheat for test
|
||||
# self.__tank_player1.improve_level()
|
||||
# self.__tank_player1.improve_level()
|
||||
# self.__tank_player1.protected = True
|
||||
while self.__has_next_loop:
|
||||
# 用户事件捕捉
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
# --敌方坦克生成
|
||||
elif event.type == self.__generate_enemies_event:
|
||||
if self.max_enemy_num > len(self.__entities.enemy_tanks):
|
||||
for position in self.__enemy_spawn_point:
|
||||
if len(self.__entities.enemy_tanks) == self.__total_enemy_num:
|
||||
break
|
||||
enemy_tank = self.__tank_factory.create_tank(position, TankFactory.ENEMY_TANK)
|
||||
if spritecollide(enemy_tank, self.__entities.enemy_tanks, False, None) or\
|
||||
spritecollide(enemy_tank, self.__entities.player_tanks, False, None):
|
||||
del enemy_tank
|
||||
else:
|
||||
self.__entities.add(enemy_tank)
|
||||
# --用户按键
|
||||
self.__dispatch_player_operation()
|
||||
# 碰撞检测
|
||||
self.__dispatch_collisions()
|
||||
self.__entities.update(self.__scene_elements, self.__home)
|
||||
self._draw_interface()
|
||||
# 我方坦克都挂了
|
||||
if len(self.__entities.player_tanks) == 0:
|
||||
self.__is_win_flag = False
|
||||
self.__has_next_loop = False
|
||||
# 敌方坦克都挂了
|
||||
if self.__total_enemy_num <= 0:
|
||||
self.__is_win_flag = True
|
||||
self.__has_next_loop = False
|
||||
|
||||
clock.tick(60)
|
||||
|
||||
def __init_collision_config(self):
|
||||
self.__collisions = {
|
||||
'group': {
|
||||
'PlayerBulletWithBrick': (self.__entities.player_bullets, self.__scene_elements.brick_group, True, True),
|
||||
'EnemyBulletWithBrick': (self.__entities.enemy_bullets, self.__scene_elements.brick_group, True, True),
|
||||
'EnemyBulletWithIron': (self.__entities.enemy_bullets, self.__scene_elements.iron_group, True, False),
|
||||
'BulletWithBullet': (self.__entities.player_bullets, self.__entities.enemy_bullets, True, True),
|
||||
'PlayerTankWithTree': (self.__entities.player_tanks, self.__scene_elements.tree_group, False, False),
|
||||
},
|
||||
'sprite': {
|
||||
'EnemyBulletWithHome': (self.__home, self.__entities.enemy_bullets, True, None),
|
||||
'PlayerBulletWithHome': (self.__home, self.__entities.player_bullets, True, None),
|
||||
|
||||
},
|
||||
'foreach_sprite': {
|
||||
'PlayerTankWithEnemyBullet': (self.__entities.player_tanks, self.__entities.enemy_bullets, True, None),
|
||||
'EnemyTankWithPlayerBullet': (self.__entities.enemy_tanks, self.__entities.player_bullets, True, None),
|
||||
}
|
||||
}
|
||||
|
||||
def __init_tanks(self):
|
||||
self.__tank_player1 = self.__tank_factory.create_tank(
|
||||
self.__player_spawn_point[0], TankFactory.PLAYER1_TANK
|
||||
)
|
||||
self.__entities.add(self.__tank_player1)
|
||||
|
||||
self.__tank_player2 = None
|
||||
if TankGame().multiplayer_mode:
|
||||
self.__tank_player2 = self.__tank_factory.create_tank(
|
||||
self.__player_spawn_point[1], TankFactory.PLAYER2_TANK
|
||||
)
|
||||
self.__entities.add(self.__tank_player2)
|
||||
# 敌方坦克
|
||||
for position in self.__enemy_spawn_point:
|
||||
self.__entities.add(
|
||||
self.__tank_factory.create_tank(position, TankFactory.ENEMY_TANK)
|
||||
)
|
||||
|
||||
def __init_user_event(self):
|
||||
self.__generate_enemies_event = pygame.constants.USEREVENT
|
||||
pygame.time.set_timer(self.__generate_enemies_event, 20000)
|
||||
|
||||
def __init_entities(self):
|
||||
self.__entities = EntityGroup()
|
||||
|
||||
def show(self):
|
||||
self._init_game_window()
|
||||
self._init_text()
|
||||
self.__load_level_file()
|
||||
self.__init_entities()
|
||||
self.__init_user_event()
|
||||
self.__init_tanks()
|
||||
self.__init_collision_config()
|
||||
self.__play_sound('start')
|
||||
self.__is_win_flag = False
|
||||
self.__has_next_loop = True
|
||||
self._main_loop()
|
||||
TankGame().is_win = self.__is_win_flag
|
||||
|
||||
|
||||
'''显示游戏面板'''
|
||||
def __draw_game_panel(self):
|
||||
color_white = (255, 255, 255)
|
||||
dynamic_text_tips = {
|
||||
16: {'text': 'Health: %s' % self.__tank_player1.health},
|
||||
17: {'text': 'Level: %s' % self.__tank_player1._level},
|
||||
23: {'text': 'Game Level: %s' % (TankGame().level + 1)},
|
||||
24: {'text': 'Remain Enemy: %s' % self.__total_enemy_num}
|
||||
}
|
||||
if self.__tank_player2:
|
||||
dynamic_text_tips[20] = {'text': 'Health: %s' % self.__tank_player2.health}
|
||||
dynamic_text_tips[21] = {'text': 'Level: %s' % self.__tank_player2._level}
|
||||
else:
|
||||
dynamic_text_tips[20] = {'text': 'Health: %s' % None}
|
||||
dynamic_text_tips[21] = {'text': 'Level: %s' % None}
|
||||
|
||||
for pos, tip in dynamic_text_tips.items():
|
||||
tip['render'] = self.__font.render(tip['text'], True, color_white)
|
||||
tip['rect'] = tip['render'].get_rect()
|
||||
tip['rect'].left, tip['rect'].top = self.__screen_width + 5, self.__screen_height * pos / 30
|
||||
|
||||
screen = TankGame().screen
|
||||
for pos, tip in self.__fix_text_tips.items():
|
||||
screen.blit(tip['render'], tip['rect'])
|
||||
for pos, tip in dynamic_text_tips.items():
|
||||
screen.blit(tip['render'], tip['rect'])
|
||||
|
||||
|
||||
def __load_level_file(self):
|
||||
self.__scene_elements = groups.SceneElementsGroup()
|
||||
|
||||
elems_map = {
|
||||
'B': SceneFactory.BRICK,
|
||||
'I': SceneFactory.IRON,
|
||||
'C': SceneFactory.ICE,
|
||||
'T': SceneFactory.TREE,
|
||||
'R': SceneFactory.RIVER_1
|
||||
}
|
||||
|
||||
home_walls_position = []
|
||||
home_position = ()
|
||||
|
||||
f = open(TankGame().level_file, errors='ignore')
|
||||
num_row = -1
|
||||
for line in f.readlines():
|
||||
line = line.strip('\n')
|
||||
# 注释
|
||||
if line.startswith('#') or (not line):
|
||||
continue
|
||||
# 敌方坦克总数量
|
||||
elif line.startswith('%TOTALENEMYNUM'):
|
||||
self.__total_enemy_num = 15 + TankGame().level #int(line.split(':')[-1])
|
||||
# 场上敌方坦克最大数量
|
||||
elif line.startswith('%MAXENEMYNUM'):
|
||||
self.max_enemy_num = 15 + TankGame().level #int(line.split(':')[-1])
|
||||
# 大本营位置
|
||||
elif line.startswith('%HOMEPOS'):
|
||||
home_position = line.split(':')[-1]
|
||||
home_position = [
|
||||
int(home_position.split(',')[0]), int(home_position.split(',')[1])
|
||||
]
|
||||
home_position = (
|
||||
self.__border_len + home_position[0] * self.__grid_size,
|
||||
self.__border_len + home_position[1] * self.__grid_size
|
||||
)
|
||||
# 大本营周围位置
|
||||
elif line.startswith('%HOMEAROUNDPOS'):
|
||||
home_walls_position = line.split(':')[-1]
|
||||
home_walls_position = [
|
||||
[
|
||||
int(pos.split(',')[0]), int(pos.split(',')[1])
|
||||
] for pos in home_walls_position.split(' ')
|
||||
]
|
||||
home_walls_position = [
|
||||
(
|
||||
self.__border_len + pos[0] * self.__grid_size, self.__border_len + pos[1] * self.__grid_size
|
||||
) for pos in home_walls_position
|
||||
]
|
||||
# 我方坦克初始位置
|
||||
elif line.startswith('%PLAYERTANKPOS'):
|
||||
self.__player_spawn_point = line.split(':')[-1]
|
||||
self.__player_spawn_point = [
|
||||
[
|
||||
int(pos.split(',')[0]), int(pos.split(',')[1])
|
||||
] for pos in self.__player_spawn_point.split(' ')
|
||||
]
|
||||
self.__player_spawn_point = [
|
||||
(
|
||||
self.__border_len + pos[0] * self.__grid_size, self.__border_len + pos[1] * self.__grid_size
|
||||
) for pos in self.__player_spawn_point
|
||||
]
|
||||
# 敌方坦克初始位置
|
||||
elif line.startswith('%ENEMYTANKPOS'):
|
||||
self.__enemy_spawn_point = line.split(':')[-1]
|
||||
self.__enemy_spawn_point = [
|
||||
[
|
||||
int(pos.split(',')[0]), int(pos.split(',')[1])
|
||||
] for pos in self.__enemy_spawn_point.split(' ')
|
||||
]
|
||||
self.__enemy_spawn_point = [
|
||||
(
|
||||
self.__border_len + pos[0] * self.__grid_size, self.__border_len + pos[1] * self.__grid_size
|
||||
) for pos in self.__enemy_spawn_point
|
||||
]
|
||||
# 地图元素
|
||||
else:
|
||||
num_row += 1
|
||||
for num_col, elem in enumerate(line.split(' ')):
|
||||
position = self.__border_len + num_col * self.__grid_size, self.__border_len + num_row * self.__grid_size
|
||||
|
||||
scene_element = None
|
||||
if elem in elems_map:
|
||||
scene_element = self.__scene_factory.create_element(position, elems_map[elem])
|
||||
elif elem == 'R':
|
||||
scene_element = self.__scene_factory.create_element(
|
||||
position, random.choice([SceneFactory.RIVER_1, SceneFactory.RIVER_2])
|
||||
)
|
||||
if scene_element is not None:
|
||||
self.__scene_elements.add(scene_element)
|
||||
self.__home = Home(position=home_position, imagefile=self.__home_images, walls_position=home_walls_position)
|
@ -0,0 +1,95 @@
|
||||
import sys
|
||||
import pygame
|
||||
|
||||
from modules.TankGame import TankGame
|
||||
from modules.views.AbstractView import AbstractView
|
||||
|
||||
|
||||
class GameOverView(AbstractView):
|
||||
|
||||
def _init_resources(self):
|
||||
config = self.config
|
||||
self.__background_img = pygame.image.load(config.OTHER_IMAGE_PATHS.get('background'))
|
||||
self.__gameover_logo = pygame.image.load(config.OTHER_IMAGE_PATHS.get('gameover'))
|
||||
self.__gameover_logo = pygame.transform.scale(self.__gameover_logo, (150, 75))
|
||||
self.__font = pygame.font.Font(config.FONTPATH, config.WIDTH // 12)
|
||||
self.__tank_cursor = pygame.image.load(
|
||||
config.PLAYER_TANK_IMAGE_PATHS.get('player1')[0]
|
||||
).convert_alpha().subsurface((0, 144), (48, 48))
|
||||
|
||||
def _init_text(self):
|
||||
config = self.config
|
||||
if TankGame().is_win:
|
||||
self.__font_render = self.__font.render('Congratulations, You win!', True, (255, 255, 255))
|
||||
else:
|
||||
self.__font_render = self.__font.render('Sorry, You fail!', True, (255, 0, 0))
|
||||
self.__font_rect = self.__font_render.get_rect()
|
||||
self.__font_rect.centerx, self.__font_rect.centery = config.WIDTH / 2, config.HEIGHT / 3
|
||||
self.__restart_render_white = self.__font.render('RESTART', True, (255, 255, 255))
|
||||
self.__restart_render_red = self.__font.render('RESTART', True, (255, 0, 0))
|
||||
self.__quit_render_white = self.__font.render('QUIT', True, (255, 255, 255))
|
||||
self.__quit_render_red = self.__font.render('QUIT', True, (255, 0, 0))
|
||||
|
||||
def _init_logo(self):
|
||||
config = self.config
|
||||
self.__gameover_logo_rect = self.__gameover_logo.get_rect()
|
||||
self.__gameover_logo_rect.midtop = config.WIDTH / 2, config.HEIGHT / 8
|
||||
|
||||
def _draw_interface(self):
|
||||
screen = TankGame().screen
|
||||
screen.blit(self.__background_img, (0, 0))
|
||||
|
||||
if self.__gameover_show_flag:
|
||||
screen.blit(self.__gameover_logo, self.__gameover_logo_rect)
|
||||
|
||||
screen.blit(self.__font_render, self.__font_rect)
|
||||
|
||||
if not self.__is_quit_game:
|
||||
screen.blit(self.__tank_cursor, self.__tank_rect)
|
||||
screen.blit(self.__restart_render_red, self.__restart_rect)
|
||||
screen.blit(self.__quit_render_white, self.__quit_rect)
|
||||
else:
|
||||
screen.blit(self.__tank_cursor, self.__tank_rect)
|
||||
screen.blit(self.__restart_render_white, self.__restart_rect)
|
||||
screen.blit(self.__quit_render_red, self.__quit_rect)
|
||||
|
||||
def _init_bottons(self):
|
||||
config = self.config
|
||||
self.__tank_rect = self.__tank_cursor.get_rect()
|
||||
self.__restart_rect = self.__restart_render_white.get_rect()
|
||||
self.__restart_rect.left, self.__restart_rect.top = config.WIDTH / 2.4, config.HEIGHT / 2
|
||||
self.__quit_rect = self.__quit_render_white.get_rect()
|
||||
self.__quit_rect.left, self.__quit_rect.top = config.WIDTH / 2.4, config.HEIGHT / 1.6
|
||||
|
||||
def _main_loop(self):
|
||||
gameover_flash_time = 25
|
||||
gameover_flash_count = 0
|
||||
self.__gameover_show_flag = True
|
||||
self.__is_quit_game = False
|
||||
|
||||
clock = pygame.time.Clock()
|
||||
while True:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
elif event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_RETURN:
|
||||
TankGame().quit_game_flag = self.__is_quit_game
|
||||
return
|
||||
elif event.key in [pygame.K_UP, pygame.K_DOWN, pygame.K_w, pygame.K_s]:
|
||||
self.__is_quit_game = not self.__is_quit_game
|
||||
|
||||
gameover_flash_count += 1
|
||||
if gameover_flash_count > gameover_flash_time:
|
||||
self.__gameover_show_flag = not self.__gameover_show_flag
|
||||
gameover_flash_count = 0
|
||||
if self.__is_quit_game:
|
||||
self.__tank_rect.right, self.__tank_rect.top = self.__quit_rect.left - 10, self.__quit_rect.top
|
||||
else:
|
||||
self.__tank_rect.right, self.__tank_rect.top = self.__restart_rect.left - 10, self.__restart_rect.top
|
||||
|
||||
self._draw_interface()
|
||||
|
||||
pygame.display.update()
|
||||
clock.tick(60)
|
@ -0,0 +1,92 @@
|
||||
|
||||
import sys
|
||||
import pygame
|
||||
|
||||
from modules.TankGame import TankGame
|
||||
from modules.views.AbstractView import AbstractView
|
||||
|
||||
|
||||
class GameStartView(AbstractView):
|
||||
|
||||
def _init_resources(self):
|
||||
config = self.config
|
||||
self.__background_img = pygame.image.load(config.OTHER_IMAGE_PATHS.get('background'))
|
||||
self.__font = pygame.font.Font(config.FONTPATH, config.WIDTH // 20)
|
||||
self.__logo_img = pygame.image.load(config.OTHER_IMAGE_PATHS.get('logo'))
|
||||
self.__logo_img = pygame.transform.scale(self.__logo_img, (446, 70))
|
||||
self.__tank_cursor = pygame.image.load(
|
||||
config.PLAYER_TANK_IMAGE_PATHS.get('player1')[0]
|
||||
).convert_alpha().subsurface((0, 144), (48, 48))
|
||||
|
||||
def _init_logo(self):
|
||||
config = self.config
|
||||
self.__logo_rect = self.__logo_img.get_rect()
|
||||
self.__logo_rect.centerx, self.__logo_rect.centery = config.WIDTH / 2, config.HEIGHT // 4
|
||||
|
||||
def _init_text(self):
|
||||
color_white = (255, 255, 255)
|
||||
color_red = (255, 0, 0)
|
||||
config = self.config
|
||||
self.__player_render_white = self.__font.render('1 PLAYER', True, color_white)
|
||||
self.__player_render_red = self.__font.render('1 PLAYER', True, color_red)
|
||||
self.__players_render_white = self.__font.render('2 PLAYERS', True, color_white)
|
||||
self.__players_render_red = self.__font.render('2 PLAYERS', True, color_red)
|
||||
self.__game_tip = self.__font.render('press <Enter> to start', True, color_white)
|
||||
self.__game_tip_rect = self.__game_tip.get_rect()
|
||||
self.__game_tip_rect.centerx, self.__game_tip_rect.top = config.WIDTH / 2, config.HEIGHT / 1.4
|
||||
|
||||
def _init_bottons(self):
|
||||
config = self.config
|
||||
self.__player_rect = self.__player_render_white.get_rect()
|
||||
self.__player_rect.left, self.__player_rect.top = config.WIDTH / 2.8, config.HEIGHT / 2.5
|
||||
self.__players_rect = self.__players_render_white.get_rect()
|
||||
self.__players_rect.left, self.__players_rect.top = config.WIDTH / 2.8, config.HEIGHT / 2
|
||||
self.__tank_rect = self.__tank_cursor.get_rect()
|
||||
|
||||
def _draw_interface(self):
|
||||
screen = TankGame().screen
|
||||
screen.blit(self.__background_img, (0, 0))
|
||||
screen.blit(self.__logo_img, self.__logo_rect)
|
||||
if self.__game_tip_show_flag:
|
||||
screen.blit(self.__game_tip, self.__game_tip_rect)
|
||||
|
||||
if not self.__is_multiplayer_mode:
|
||||
screen.blit(self.__tank_cursor, self.__tank_rect)
|
||||
screen.blit(self.__player_render_red, self.__player_rect)
|
||||
screen.blit(self.__players_render_white, self.__players_rect)
|
||||
else:
|
||||
screen.blit(self.__tank_cursor, self.__tank_rect)
|
||||
screen.blit(self.__player_render_white, self.__player_rect)
|
||||
screen.blit(self.__players_render_red, self.__players_rect)
|
||||
|
||||
def _main_loop(self):
|
||||
game_tip_flash_time = 25
|
||||
game_tip_flash_count = 0
|
||||
self.__game_tip_show_flag = True
|
||||
self.__is_multiplayer_mode = False
|
||||
clock = pygame.time.Clock()
|
||||
while True:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
elif event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_RETURN:
|
||||
TankGame().multiplayer_mode = self.__is_multiplayer_mode
|
||||
return
|
||||
elif event.key == pygame.K_UP or event.key == pygame.K_DOWN or event.key == pygame.K_w or event.key == pygame.K_s:
|
||||
self.__is_multiplayer_mode = not self.__is_multiplayer_mode
|
||||
|
||||
game_tip_flash_count += 1
|
||||
if game_tip_flash_count > game_tip_flash_time:
|
||||
self.__game_tip_show_flag = not self.__game_tip_show_flag
|
||||
game_tip_flash_count = 0
|
||||
|
||||
if self.__is_multiplayer_mode:
|
||||
self.__tank_rect.right, self.__tank_rect.top = self.__players_rect.left - 10, self.__players_rect.top
|
||||
else:
|
||||
self.__tank_rect.right, self.__tank_rect.top = self.__player_rect.left - 10, self.__player_rect.top
|
||||
|
||||
self._draw_interface()
|
||||
pygame.display.update()
|
||||
clock.tick(60)
|
@ -0,0 +1,80 @@
|
||||
import sys
|
||||
import pygame
|
||||
|
||||
from modules.TankGame import TankGame
|
||||
from modules.views.AbstractView import AbstractView
|
||||
|
||||
|
||||
class SwitchLevelView(AbstractView):
|
||||
|
||||
def _init_resources(self):
|
||||
config = self.config
|
||||
self.__loadbar = pygame.image.load(config.OTHER_IMAGE_PATHS.get('loadbar')).convert_alpha()
|
||||
self.__background_img = pygame.image.load(config.OTHER_IMAGE_PATHS.get('background'))
|
||||
self.__logo_img = pygame.image.load(config.OTHER_IMAGE_PATHS.get('logo'))
|
||||
self.__logo_img = pygame.transform.scale(self.__logo_img, (446, 70))
|
||||
self.__font = pygame.font.Font(config.FONTPATH, config.WIDTH // 20)
|
||||
self.__tank_cursor_img = pygame.image.load(
|
||||
config.ENEMY_TANK_IMAGE_PATHS.get('1')[0]
|
||||
).convert_alpha().subsurface((0, 144), (48, 48))
|
||||
|
||||
def _init_text(self):
|
||||
config = self.config
|
||||
self.__font_render = self.__font.render(
|
||||
'LEVEL%d' % (TankGame().level + 1),
|
||||
True,
|
||||
(255, 255, 255) # White
|
||||
)
|
||||
self.__font_rect = self.__font_render.get_rect()
|
||||
self.__font_rect.centerx, self.__font_rect.centery = config.WIDTH / 2, config.HEIGHT / 2
|
||||
|
||||
def _init_loadbar(self):
|
||||
config = self.config
|
||||
self.__loadbar_rect = self.__loadbar.get_rect()
|
||||
self.__loadbar_rect.centerx, self.__loadbar_rect.centery = config.WIDTH / 2, config.HEIGHT / 1.4
|
||||
self.__tank_rect = self.__tank_cursor_img.get_rect()
|
||||
self.__tank_rect.left = self.__loadbar_rect.left
|
||||
self.__tank_rect.centery = self.__loadbar_rect.centery
|
||||
|
||||
def _init_logo(self):
|
||||
self.__logo_rect = self.__logo_img.get_rect()
|
||||
self.__logo_rect.centerx, self.__logo_rect.centery = self.config.WIDTH / 2, self.config.HEIGHT // 4
|
||||
|
||||
def _draw_interface(self):
|
||||
screen = TankGame().screen
|
||||
screen.blit(self.__background_img, (0, 0))
|
||||
screen.blit(self.__logo_img, self.__logo_rect)
|
||||
screen.blit(self.__font_render, self.__font_rect)
|
||||
screen.blit(self.__loadbar, self.__loadbar_rect)
|
||||
screen.blit(self.__tank_cursor_img, self.__tank_rect)
|
||||
pygame.draw.rect(
|
||||
screen,
|
||||
(192, 192, 192), # Gray
|
||||
(
|
||||
self.__loadbar_rect.left + 8,
|
||||
self.__loadbar_rect.top + 8,
|
||||
self.__tank_rect.left - self.__loadbar_rect.left - 8,
|
||||
self.__tank_rect.bottom - self.__loadbar_rect.top - 16
|
||||
)
|
||||
)
|
||||
self.__tank_rect.left += 8
|
||||
|
||||
def _main_loop(self):
|
||||
load_time_left = self.__loadbar_rect.right - self.__tank_rect.right + 8
|
||||
clock = pygame.time.Clock()
|
||||
|
||||
while True:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
if load_time_left <= 0:
|
||||
return
|
||||
self._draw_interface()
|
||||
load_time_left -= 8
|
||||
pygame.display.update()
|
||||
clock.tick(60)
|
||||
|
||||
def show(self):
|
||||
self._init_loadbar()
|
||||
super().show()
|
@ -0,0 +1,30 @@
|
||||
import threading
|
||||
|
||||
from modules.views import *
|
||||
|
||||
|
||||
class ViewManager(object):
|
||||
_instance_lock = threading.Lock()
|
||||
_init_flag = False
|
||||
|
||||
def __init__(self):
|
||||
self.__views = {
|
||||
'SwitchLevel': SwitchLevelView(),
|
||||
'GameOver': GameOverView(),
|
||||
'GameStart': GameStartView(),
|
||||
'GameLevelView': GameLevelView(),
|
||||
}
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if not hasattr(ViewManager, "_instance"):
|
||||
with ViewManager._instance_lock:
|
||||
if not hasattr(ViewManager, "_instance"):
|
||||
ViewManager._instance = object.__new__(cls)
|
||||
return ViewManager._instance
|
||||
|
||||
def show(self, view: str):
|
||||
if view not in self.__views:
|
||||
raise Exception('View is not found!')
|
||||
else:
|
||||
self.__views[view].show()
|
||||
|
@ -0,0 +1,6 @@
|
||||
from modules.views.GameOverView import GameOverView
|
||||
from modules.views.GameStartView import GameStartView
|
||||
from modules.views.SwitchLevelView import SwitchLevelView
|
||||
from modules.views.GameLevelView import GameLevelView
|
||||
from modules.views.AbstractView import AbstractView
|
||||
from modules.views.ViewManager import ViewManager
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue