first commit

main
wzc 6 months ago
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()

@ -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

@ -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
Loading…
Cancel
Save