第4课 病毒防卫战
一、作品展示
二、作品分析与制作
2.1 素材简介
3个背景:游戏中,游戏成功,游戏失败
2个角色:卫士,病毒(4个造型)


2.2 游戏背景加载
任务1:创建一个宽480高650的游戏界面,显示游戏中背景,点击关闭窗口按钮,窗口关闭,游戏退出,并将所有背景角色素材载入,并设置合适的图片大小
import pygameimport syspygame.init()screen_size = width, height = (480, 650)screen = pygame.display.set_mode(screen_size)pygame.display.set_caption("病毒防卫战")# 加载背景图片,并适配屏幕bg_game = pygame.image.load('./images/bg1.png')bg_success = pygame.image.load('./images/bg2.png')bg_fail = pygame.image.load('./images/bg3.png')bg_game = pygame.transform.scale(bg_game, screen_size)bg_success = pygame.transform.scale(bg_success, screen_size)bg_fail = pygame.transform.scale(bg_fail, screen_size)# 加载角色图片boy_size = boy_width, boy_height = 80, 100boy_img = pygame.image.load('./images/boy.png')boy_img = pygame.transform.scale(boy_img, boy_size)virus_img = []virus_size = virus_width, virus_height = 30, 30for i in range(4): img = pygame.image.load("./images/virus{}.png".format(i)) pygame.transform.scale(img, virus_size) virus_img.append(img)running = Truewhile running: # 加载背景 screen.blit(bg_game, (0, 0)) # 处理事件 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False pygame.quit() sys.exit() # 更新屏幕画面 pygame.display.update()
2.2 卫士
任务2:创建一个卫士类,初始化他的位置、大小、造型和生命值,游戏中只有一个卫士角色,能够跟着鼠标移动

1)创建卫士类,设置初始方法进行初始化,显示方法show()将其显示在游戏界面上,移动方法move()使其跟着鼠标的位置移动
class Boy: def __init__(self): # 初始化位置(屏幕中间) self.x = width / 2 self.y = height / 2 # 初始化大小 self.width = boy_width self.height = boy_height # 初始化造型 self.img = boy_img # 初始化生命值 self.life = 1 def show(self): screen.blit(self.img, (self.x, self.y)) # 设置卫士移动的方法,跟随鼠标移动 def move(self, mouseX, mouseY): self.x = mouseX - self.width / 2 self.y = mouseY - self.height / 2
2)创建一个卫士对象,调用显示方法和移动方法,使其动起来

2.3 病毒
任务3:创建病毒类,初始化它的位置、大小、造型、移动速度、移动参照物,根据轮数,显示病毒,如第1轮显示1个病毒,第2轮显示2个病毒……

1)创建病毒类,类中有init()方法初始化它的信息,show()方法将其显示在游戏界面上
class Virus: def __init__(self, x, y, img_index, speed, mouse_x, mouse_y): # 初始化位置 self.x = x self.y = y # 初始化大小 self.width = virus_width self.height = virus_height # 初始化造型 self.img = virus_img[img_index] # 初始化速度 self.speed = speed # 固定鼠标的位置(当前卫士的位置) self.mouse_x = mouse_x self.mouse_y = mouse_y # 设置移动的方位距离 self.move_x = 0 self.move_y = 0 def show(self): screen.blit(self.img, (self.x, self.y))
2)创建create_virus(num, mouse_x, mouse_y)函数,用于创建指定个数病毒
num:此次创建的病毒对象个数
mouse_x:病毒移动参照物的x坐标
mouse_y:病毒移动参照物的y坐标
def create_virus(num, mouse_x, mouse_y): virus_list = [] # 该轮病毒移动的速度 speed = num * 0.4 + 0.4 * random.random() for i in range(num): # 病毒的4个随机位置,x坐标范围是0到(屏幕的宽-病毒的宽) # y坐标的范围是0到(屏幕的高-病毒的高) rand_pos = random.randint(0, 3) x = random.random() * (width - virus_width) y = random.random() * (height - virus_height) # 病毒的造型下标,随机0到3 virus_index = random.randint(0, 3) # 创建病毒 if rand_pos == 0: virus = Virus(x, 0, virus_index, speed, mouse_x, mouse_y) elif rand_pos == 1: virus = Virus(0, y, virus_index, speed, mouse_x, mouse_y) elif rand_pos == 2: virus = Virus(width - virus_width, y, virus_index, speed, mouse_x, mouse_y) else: virus = Virus(x, height - virus_height, virus_index, speed, mouse_x, mouse_y) # 将新产生的病毒存入列表 virus_list.append(virus) return virus_list
病毒的可能位置图示:

3)创建show_virus(virus_list)函数,显示所有的病毒
def show_virus(virus_list): for i in range(len(virus_list)): virus_list[i].show()
4)调用create_virus()函数和show_virus()函数,将本轮病毒显示在游戏窗口上。只有窗口上没有病毒,才产生新的一轮病毒

任务4:所有病毒可以朝着卫士的方向一直移动
1)在Virus病毒类中创建setMoveXY()方法设置病毒需要移动的方位距离,创建move()方法,使得病毒可以移动
def setMoveXY(self): # 获取病毒到卫士的x,y轴的向量 dx = self.mouse_x - self.x dy = self.mouse_y - self.y # 两者重合时不移动 if dx == 0 and dy == 0: return # 获取两者直线距离(欧几里德距离) dis = math.hypot(dx, dy) # 确定移动的方位向量 self.move_x = dx / dis self.move_y = dy / dis def move(self): # 移动 self.x += self.move_x * self.speed self.y += self.move_y * self.speed
设置x,y方向上的移动距离图示:

2)在创建病毒的方法中,调用setMoveXY()函数,设置病毒移动的初始方向

3)创建move_virus(virus_list)函数,能让所有病毒都移动
def move_virus(virus_list): for i in range(len(virus_list)): virus_list[i].move()
4)在while running中调用病毒移动的方法,让该函数生效

2.4 越界监测与碰撞监测
任务5:在游戏窗口指定位置显示游戏轮数与卫士的生命值
1)创建fill_text(text, position)显示文字函数,用于后续显示相关文字
def fill_text(text, position): textFont = pygame.font.Font("./fonts/WRYH.ttf", 25) newText = textFont.render(text, True, (255, 0, 0)) screen.blit(newText, position)
2)在while running中调用该方法显示需要显示的文字

任务6:如果所有病毒已经超过游戏界面的范围,则新的一轮病毒开始攻击
1)创建outSide(virus_list)方法,监测病毒是否已经越界,如果已经越界,则移除该病毒
def outSide(virus_list): for i in range(len(virus_list)): virus = virus_list[i] if virus.x <= -virus_width or virus.x >= width or virus.y <= -virus_height or virus.y >= height: virus_list.remove(virus) break
2)在while running中调用该方法

任务7:碰撞检测,如果卫士碰到病毒,病毒消失,卫士的生命值减1,如果卫士坚持5轮病毒,则游戏胜利,显示胜利背景,如果卫士的生命值为0,则游戏失败,显示失败背景
1)创建碰撞监测函数collision(virus_list, boy)用于监测病毒和卫士之间是否有碰撞

如上图,以病毒图片的矩形红框为参照物,如果不在这个范围内,则没有碰撞
def collision(virus_list, boy): for i in range(len(virus_list)): virus = virus_list[i] # 矩形范围监测 if boy.x < virus.x - boy.width or boy.x > virus.x + virus.width: return False if boy.y < virus.y - boy.height or boy.y > virus.y + virus.height: return False
但是即使处在矩形范围内,仍有可能没有碰撞,因为卫士图片矩形和病毒图片矩形之间仍然有空白缝隙,则可以做像素级别的碰撞监测

def collision(virus_list, boy): for i in range(len(virus_list)): virus = virus_list[i] # 矩形范围监测 if boy.x < virus.x - boy.width or boy.x > virus.x + virus.width: return False if boy.y < virus.y - boy.height or boy.y > virus.y + virus.height: return False # 像素级别监测 virus_alpha = virus.img boy_alpha = boy.img for x in range(virus_alpha.get_width()): for y in range(virus_alpha.get_height()): # 如果这一点不是透明的 if virus_alpha.get_at((x, y)) != (0, 0, 0, 0): # 获取这一点在窗口上的位置 xx, yy = x + virus.x, y + virus.y # 获取这一点相对于卫士的位置 xxx, yyy = int(xx - boy.x), int(yy - boy.y) # 如果在卫士的图片范围内,且卫士的这一点也不是透明的,则说明撞上了 if (0 <= xxx <= boy.width and 0 <= yyy <= boy.height and boy_alpha.get_at((xxx, yyy)) != (0, 0, 0, 0)): virus_list.remove(virus) # 如果碰到了,病毒消失 return True return False
