# coding = utf-8 import pygame import sys import math import random from pygame.sprite import Sprite from pygame.sprite import Group from Shield import Shield class Map: def __init__(self, width, height, tile_size=40): self.width = width self.height = height self.tile_size = tile_size self.tiles = [] self.circle = [0, 0, 0, 0] self.circle_r = 200 def render(self, screen): colors = { 0: (242, 234, 211), # 浅色 1: (255, 0, 0), # 红色 2: (0, 0, 255) # 蓝色 } for y in range(self.height): for x in range(self.width): tile_type = self.tiles[y][x] tile_color = colors.get(tile_type, (255, 255, 255)) # 默认为白色 rect = pygame.Rect(x * self.tile_size, y * self.tile_size, self.tile_size, self.tile_size) pygame.draw.rect(screen, tile_color, rect) # 画圆 # 如果全部为1 if self.circle[0] == 1 and self.circle[1] == 1 and self.circle[2] == 1 and self.circle[3] == 1: pygame.draw.circle(screen, (0, 255, 0), (400, 300), self.circle_r) else: for i in range(4): if self.circle[i] == 1: # 从 90*i 开始画,画 90 度 pygame.draw.arc(screen, (255, 255, 255), (400 - self.circle_r, 300 - self.circle_r, self.circle_r * 2, self.circle_r * 2), math.radians(90 * i), math.radians(90 * (i + 1)), 4) # 画两个边 pygame.draw.line(screen, (255, 255, 255), (400, 300), (400 + self.circle_r * math.cos(math.radians(90 * i)), 300 - self.circle_r * math.sin(math.radians(90 * i))), 4) pygame.draw.line(screen, (255, 255, 255), (400, 300), (400 + self.circle_r * math.cos(math.radians(90 * (i + 1))), 300 - self.circle_r * math.sin(math.radians(90 * (i + 1)))), 4) def generate_map(self): # 先生成一个空的二维数组 self.tiles = [] for y in range(self.height): row = [] for x in range(self.width): row.append(random.randint(-1, 1)) self.tiles.append(row) for y in range(self.height): for x in range(self.width): self.tiles[y][x] = self.map_func(x, y) def map_func(self, x, y): neighbors_weights = [ self.get_tile_weight(x-1, y), self.get_tile_weight(x+1, y), self.get_tile_weight(x, y-1), self.get_tile_weight(x, y+1) ] weight_sum = sum(neighbors_weights)/4 + random.uniform(-0.3, 0.3) tile_type = 0 # 默认为绿色 # 最外面一圈默认为红色 if x == 0 or y == 0 or x == self.width - 1 or y == self.height - 1: tile_type = 1 # 每隔4个格子,随机出现一个蓝色格子 elif random.random() < 0.01: tile_type = 2 return tile_type def get_tile_weight(self, x, y): if x < 0 or y < 0 or x >= self.width or y >= self.height: return 0 # 超出地图边界,默认权重为0 return self.tiles[y][x] class Bullet(pygame.sprite.Sprite): def __init__(self, position, direction, color, owner, speed=8): super().__init__() # 使用SRCALPHA色彩格式创建Surface对象 self.image = pygame.Surface((16, 16), pygame.SRCALPHA) self.image.fill(color) # pygame.draw.line(self.image, (255, 255, 255), (0, 15), (32, 15), 4) # pygame.draw.line(self.image, (255, 255, 255), (15, 0), (15, 32), 4) self.rect = self.image.get_rect() self.rect.center = position self.direction = direction self.speed = speed self.owner = owner self.collision_rect = pygame.Rect( self.rect.x, self.rect.y, self.rect.width, self.rect.height) def update(self): dx = math.cos(math.radians(self.direction)) * self.speed dy = -math.sin(math.radians(self.direction)) * self.speed self.rect.x += dx self.rect.y += dy self.collision_rect.x = self.rect.x self.collision_rect.y = self.rect.y class Mysprite(Sprite): def __init__(self, id, bot=False): super().__init__() if(bot): self.mast_image = pygame.image.load('./assets/enemy.png') else : self.mast_image = pygame.image.load('./assets/cat.png') # 读取图像 self.original_image = self.mast_image.copy() self.rect = self.mast_image.get_rect(height=32, width=32) # 获取图像矩形参数 self.frame_rect = self.rect.copy() # 声明框架参数 self.rect.x, self.rect.y = 400, 300 # self.frame_rect.width /= 1 # self.frame_rect.height /= 4 self.id = id self.frame = 0 self.last_frame = (self.rect.height // self.frame_rect.height) - 1 self.old_frame = 1 self.last_time = 0 self.speed = 4 self.direction = 1 self.toword_angle = 0 self.hp = 10 self.move_directions = [] self.collision_rect = pygame.Rect( self.rect.x, self.rect.y, self.rect.width, self.rect.height) self.last_use_time = 0 self.click_move = False self.click_move_x = 0 self.click_move_y = 0 self.bot = bot self.cool_down = 500 self.hurt_time = 0 def update(self, current_time, rate=100): if current_time >= self.last_time + rate: self.frame += 1 if self.frame > self.last_frame: self.frame = 0 self.last_time = current_time if self.bot: self.bot_update() current_time = pygame.time.get_ticks() if current_time - self.hurt_time < 100: overlay = pygame.Surface(self.image.get_size()) overlay.fill((255, 0, 0, 25)) self.image.blit(overlay, (0, 0), special_flags=pygame.BLEND_MULT) else: self.image = self.original_image.copy() if self.old_frame != self.frame: self.frame_rect.x = (self.frame // 4) * self.frame_rect.width self.frame_rect.y = (self.frame % 4) * self.frame_rect.height self.old_frame = self.frame if self.direction == 1: self.frame_rect.y = 0 # 向上 elif self.direction == 2: self.frame_rect.y = self.frame_rect.height # 向下 elif self.direction == 3: self.frame_rect.y = self.frame_rect.height * 2 # 向左 elif self.direction == 4: self.frame_rect.y = self.frame_rect.height * 3 # 向右 self.updatemove() self.image = self.mast_image.subsurface(self.frame_rect) # 这里就是在生成子表面 self.collision_rect.x = self.rect.x self.collision_rect.y = self.rect.y def attack(self): # 发射子弹 # 随机生成一个颜色 color = (random.randint(0, 255), random.randint( 0, 255), random.randint(0, 255)) bullet = Bullet(self.rect.center, self.toword_angle, color, self) bullet_group.add(bullet) def bot_update(self): # bot 的更新逻辑 # 随机移动 if random.random() < 0.05: self.startmove(random.choice(['left', 'right', 'up', 'down'])) if random.random() < 0.1: self.stopmove(random.choice(['left', 'right', 'up', 'down'])) if random.random() < 0.01: self.attack() if random.random() < 0.01: self.flash() def be_hit(self, attacker=None): self.hp -= 1 self.hurt_time = pygame.time.get_ticks() if self.hp <= 0: if(attacker != None): if(attacker.bot == False): attacker.hp += 10 attacker.cool_down = max(0, attacker.cool_down - 25) self.kill() def updatemove(self): dx = 0 dy = 0 if(self.click_move): # 判断是否到达目标点 if(abs(self.rect.centerx - self.click_move_x) < 3 and abs(self.rect.centery - self.click_move_y) < 3): self.click_move = False else: self.move(self.click_move_x - self.rect.centerx, self.click_move_y - self.rect.centery) return if 'left' in self.move_directions: dx -= 1 if 'right' in self.move_directions: dx += 1 if 'up' in self.move_directions: dy -= 1 if 'down' in self.move_directions: dy += 1 self.move(dx, dy) # 三角函数 def clickmove(self, x, y): self.click_move = True self.click_move_x = x self.click_move_y = y def move(self, dx, dy): if dx != 0 or dy != 0: angle = math.atan2(-dy, dx) self.rect.x += self.speed * math.cos(angle) if(self.rect.x < 0): self.rect.x = 0 elif(self.rect.x + self.rect.width > 800): self.rect.x = 800 - self.rect.width self.rect.y -= self.speed * math.sin(angle) if(self.rect.y < 0): self.rect.y = 0 elif(self.rect.y + self.rect.height > 600): self.rect.y = 600 - self.rect.height # 判断是否和其他玩家碰撞 for player in group.sprites(): if player.id != self.id: if self.collision_rect.colliderect(player.collision_rect): self.rect.x -= self.speed * math.cos(angle) self.rect.y += self.speed * math.sin(angle) self.resetmove() break for enemy in enemy_group.sprites(): if self.collision_rect.colliderect(enemy.collision_rect): self.rect.x -= self.speed * math.cos(angle) self.rect.y += self.speed * math.sin(angle) self.resetmove() break self.toword_angle = math.degrees(angle) if(dy < 0): self.direction = 4 elif(dy > 0): self.direction = 1 if(dx < 0): self.direction = 2 elif(dx > 0): self.direction = 3 def resetmove(self): self.move_directions = [] def startmove(self, direction): self.click_move = False self.move_directions.append(direction) def stopmove(self, direction): if direction in self.move_directions: self.move_directions.remove(direction) def flash(self): if pygame.time.get_ticks() - self.last_use_time < self.cool_down: return self.last_use_time = pygame.time.get_ticks() for i in range(4): color = (random.randint(0, 255), random.randint( 0, 255), random.randint(0, 255)) bullet = Bullet(self.rect.center, self.toword_angle + i * 30 - 45, color, self, 16) bullet_group.add(bullet) pygame.init() font = pygame.font.Font("./assets/MineCraft.ttf", 24) screen = pygame.display.set_mode((800, 600)) color = (255, 255, 255) tick = pygame.time.Clock() # 第一行初始化了一个Clock对象 player1 = Mysprite(1) player2 = Mysprite(2) group = Group() bullet_group = Group() shields = pygame.sprite.Group() shield1 = Shield(player1) shield2 = Shield(player2) shields.add(shield1) shields.add(shield2) group.add(player1) group.add(player2) player1.rect.center = (200, 300) player2.rect.center = (600, 300) enemy_group = pygame.sprite.Group() enemy_timer_interval = 5000 enemy_timer_event = pygame.USEREVENT + 1 pygame.time.set_timer(enemy_timer_event, enemy_timer_interval) map_tile_size = 8 map_width = screen.get_width() // map_tile_size map_height = screen.get_height() // map_tile_size map_obj = Map(map_width, map_height, map_tile_size) map_obj.generate_map() while True: tick.tick(60) # 给tick方法加上的参数就成为了游戏绘制的最大帧率 for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() if event.type == pygame.KEYDOWN: # 键盘事件 if player1 in group: if event.key == pygame.K_a: player1.startmove('left') elif event.key == pygame.K_d: player1.startmove('right') elif event.key == pygame.K_w: player1.startmove('up') elif event.key == pygame.K_s: player1.startmove('down') elif event.key == pygame.K_v: player1.attack() elif event.key == pygame.K_b: player1.flash() if player2 in group: if event.key == pygame.K_LEFT: player2.startmove('left') elif event.key == pygame.K_RIGHT: player2.startmove('right') elif event.key == pygame.K_UP: player2.startmove('up') elif event.key == pygame.K_DOWN: player2.startmove('down') elif event.key == pygame.K_COMMA: player2.attack() elif event.key == pygame.K_PERIOD: player2.flash() elif event.key == pygame.K_1: map_obj.circle[0] = 1 - map_obj.circle[0] elif event.key == pygame.K_2: map_obj.circle[1] = 1 - map_obj.circle[1] elif event.key == pygame.K_3: map_obj.circle[2] = 1 - map_obj.circle[2] elif event.key == pygame.K_4: map_obj.circle[3] = 1 - map_obj.circle[3] elif event.type == pygame.KEYUP: if player1 in group: if event.key == pygame.K_a: player1.stopmove('left') elif event.key == pygame.K_d: player1.stopmove('right') elif event.key == pygame.K_w: player1.stopmove('up') elif event.key == pygame.K_s: player1.stopmove('down') if player2 in group: if event.key == pygame.K_LEFT: player2.stopmove('left') elif event.key == pygame.K_RIGHT: player2.stopmove('right') elif event.key == pygame.K_UP: player2.stopmove('up') elif event.key == pygame.K_DOWN: player2.stopmove('down') elif event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: player1.clickmove(event.pos[0], event.pos[1]) elif event.button == 3: player2.clickmove(event.pos[0], event.pos[1]) if event.type == enemy_timer_event: enemy = Mysprite(len(enemy_group.sprites()) + 3, True) enemy.rect.center = (random.randint(0, 800), random.randint(0, 600)) enemy_group.add(enemy) screen.fill(color) ticks = pygame.time.get_ticks() map_obj.render(screen) group.update(ticks) bullet_group.update() enemy_group.update(ticks) # shields.update() group.draw(screen) bullet_group.draw(screen) enemy_group.draw(screen) # shields.draw(screen) if player1 in group: if player1.hp > 0: # 左上角血量显示 player1_hp_text = font.render( "HP " + str(player1.hp), True, (0, 0, 0)) screen.blit(player1_hp_text, (10, 10)) # 下面是技能冷却时间显示,显示一个条形,总宽度 100px if pygame.time.get_ticks() - player1.last_use_time < 500: pygame.draw.rect(screen, (255, 255, 0), (10, 30, 100, 10)) # 从左向右 pygame.draw.rect(screen, (0, 128, 255), (10, 30, (pygame.time.get_ticks() - player1.last_use_time) / 5, 10)) else: pygame.draw.rect(screen, (0, 128, 255), (10, 30, 100, 10)) if player2 in group: if player2.hp > 0: # 右上角血量显示 player2_hp_text = font.render( "HP " + str(player2.hp), True, (0, 0, 0)) hp_text_width = player2_hp_text.get_width() screen.blit(player2_hp_text, (screen.get_width() - hp_text_width - 10, 10)) # 下面是技能冷却时间显示,显示一个条形,总宽度 100px if pygame.time.get_ticks() - player2.last_use_time < 500: pygame.draw.rect(screen, (255, 255, 0), (screen.get_width() - 110, 30, 100, 10)) # 从右向左 pygame.draw.rect(screen, (0, 128, 255), (screen.get_width() - 110 + 100 - (pygame.time.get_ticks() - player2.last_use_time) / 5, 30, (pygame.time.get_ticks() - player2.last_use_time) / 5, 10)) else: pygame.draw.rect(screen, (0, 128, 255), (screen.get_width() - 110, 30, 100, 10)) for bullet in bullet_group.sprites(): for player in group.sprites(): if bullet.collision_rect.colliderect(player.collision_rect) and bullet.owner.id != player.id: player.be_hit() bullet.kill() for enemy in enemy_group.sprites(): if bullet.collision_rect.colliderect(enemy.collision_rect) and bullet.owner.bot != True: enemy.be_hit(bullet.owner) bullet.kill() pygame.display.update() pygame.event.pump()