作者:城南,今天给大家分享的是pygame小游戏
首先是实现简单的AI人机五子棋对战
下面有这个五子棋的全代码,代码中我添加了很多注释。其中下图则是如何实现人机AI智能,(伪AI智能,哈哈)
供大家一起学习,上源码:
"""五子棋之人机对战"""import sysimport randomimport pygamefrom pygame.locals import *import pygame.gfxdrawfrom collections import namedtupleChessman = namedtuple('Chessman', 'Name Value Color')Point = namedtuple('Point', 'X Y')BLACK_CHESSMAN = Chessman('黑子', 1, (45, 45, 45))WHITE_CHESSMAN = Chessman('白子', 2, (219, 219, 219))offset = [(1, 0), (0, 1), (1, 1), (1, -1)]class Checkerboard: def __init__(self, line_points): self._line_points = line_points self._checkerboard = [[0] * line_points for _ in range(line_points)] def _get_checkerboard(self): return self._checkerboard checkerboard = property(_get_checkerboard) # 判断是否可落子 def can_drop(self, point): return self._checkerboard[point.Y][point.X] == 0 def drop(self, chessman, point): """ 落子 :param chessman: :param point:落子位置 :return:若该子落下之后即可获胜,则返回获胜方,否则返回 None """ # 把黑棋/白棋落子的坐标打印出来 print(f'{chessman.Name} ({point.X}, {point.Y})') self._checkerboard[point.Y][point.X] = chessman.Value # 打印获胜方出来 if self._win(point): print(f'{chessman.Name}获胜') return chessman # 判断是否赢了 def _win(self, point): cur_value = self._checkerboard[point.Y][point.X] for os in offset: if self._get_count_on_direction(point, cur_value, os[0], os[1]): return True # 判断是否赢了的代码,从这里往上看,代码都是正着写,反着看,写代码思路缺什么补什么,所以从这里开始看 # 声明一个函数,按方向数数,数满5个就获胜。 # 一个二维坐标上,判断上下、左右、两个45度直线,是否有五个相同的直连棋子,只要满足五颗子,则游戏结束: def _get_count_on_direction(self, point, value, x_offset, y_offset): count = 1 for step in range(1, 5): x = point.X + step * x_offset y = point.Y + step * y_offset if 0 <= x < self._line_points and 0 <= y < self._line_points and self._checkerboard[y][x] == value: count += 1 else: break for step in range(1, 5): x = point.X - step * x_offset y = point.Y - step * y_offset if 0 <= x < self._line_points and 0 <= y < self._line_points and self._checkerboard[y][x] == value: count += 1 else: break return count >= 5SIZE = 30 # 棋盘每个点时间的间隔Line_Points = 19 # 棋盘每行/每列点数Outer_Width = 20 # 棋盘外宽度Border_Width = 4 # 边框宽度Inside_Width = 4 # 边框跟实际的棋盘之间的间隔Border_Length = SIZE * (Line_Points - 1) + Inside_Width * 2 + Border_Width # 边框线的长度Start_X = Start_Y = Outer_Width + int(Border_Width / 2) + Inside_Width # 网格线起点(左上角)坐标SCREEN_HEIGHT = SIZE * (Line_Points - 1) + Outer_Width * 2 + Border_Width + Inside_Width * 2 # 游戏屏幕的高SCREEN_WIDTH = SCREEN_HEIGHT + 200 # 游戏屏幕的宽Stone_Radius = SIZE // 2 - 3 # 棋子半径Stone_Radius2 = SIZE // 2 + 3Checkerboard_Color = (0xE3, 0x92, 0x65) # 棋盘颜色,0x是16进制表示哦BLACK_COLOR = (0, 0, 0)WHITE_COLOR = (255, 255, 255)RED_COLOR = (200, 30, 30)BLUE_COLOR = (30, 30, 200)RIGHT_INFO_POS_X = SCREEN_HEIGHT + Stone_Radius2 * 2 + 10def print_text(screen, font, x, y, text, fcolor=(255, 255, 255)): imgText = font.render(text, True, fcolor) screen.blit(imgText, (x, y))def main(): pygame.init() screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) pygame.display.set_caption('五子棋') font1 = pygame.font.SysFont('SimHei', 32) # 字体:黑体,32号 font2 = pygame.font.SysFont('SimHei', 72) # 字体:黑体,72号 fwidth, fheight = font2.size('黑方获胜') checkerboard = Checkerboard(Line_Points) cur_runner = BLACK_CHESSMAN winner = None computer = AI(Line_Points, WHITE_CHESSMAN) # 设置黑白双方初始连子为0 black_win_count = 0 white_win_count = 0 while True: for event in pygame.event.get(): if event.type == QUIT: sys.exit() elif event.type == KEYDOWN: if event.key == K_RETURN: if winner is not None: winner = None cur_runner = BLACK_CHESSMAN checkerboard = Checkerboard(Line_Points) computer = AI(Line_Points, WHITE_CHESSMAN) elif event.type == MOUSEBUTTONDOWN: # 检测鼠标落下 if winner is None: # 检测是否有一方胜出 pressed_array = pygame.mouse.get_pressed() if pressed_array[0]: mouse_pos = pygame.mouse.get_pos() click_point = _get_clickpoint(mouse_pos) if click_point is not None: # 检测鼠标是否在棋盘内点击 if checkerboard.can_drop(click_point): winner = checkerboard.drop(cur_runner, click_point) if winner is None: # 再次判断是否有胜出 # 一个循环内检测两次,意思就是人出一次检测一下,电脑出一次检测一下。 cur_runner = _get_next(cur_runner) computer.get_opponent_drop(click_point) AI_point = computer.AI_drop() winner = checkerboard.drop(cur_runner, AI_point) if winner is not None: white_win_count += 1 cur_runner = _get_next(cur_runner) else: black_win_count += 1 else: print('超出棋盘区域') # 画棋盘 _draw_checkerboard(screen) # 画棋盘上已有的棋子 for i, row in enumerate(checkerboard.checkerboard): for j, cell in enumerate(row): if cell == BLACK_CHESSMAN.Value: _draw_chessman(screen, Point(j, i), BLACK_CHESSMAN.Color) elif cell == WHITE_CHESSMAN.Value: _draw_chessman(screen, Point(j, i), WHITE_CHESSMAN.Color) _draw_left_info(screen, font1, cur_runner, black_win_count, white_win_count) if winner: print_text(screen, font2, (SCREEN_WIDTH - fwidth) // 2, (SCREEN_HEIGHT - fheight) // 2, winner.Name + '获胜', RED_COLOR) pygame.display.flip()def _get_next(cur_runner): if cur_runner == BLACK_CHESSMAN: return WHITE_CHESSMAN else: return BLACK_CHESSMAN# 画棋盘def _draw_checkerboard(screen): # 填充棋盘背景色 screen.fill(Checkerboard_Color) # 画棋盘网格线外的边框 pygame.draw.rect(screen, BLACK_COLOR, (Outer_Width, Outer_Width, Border_Length, Border_Length), Border_Width) # 画网格线 for i in range(Line_Points): pygame.draw.line(screen, BLACK_COLOR, (Start_Y, Start_Y + SIZE * i), (Start_Y + SIZE * (Line_Points - 1), Start_Y + SIZE * i), 1) for j in range(Line_Points): pygame.draw.line(screen, BLACK_COLOR, (Start_X + SIZE * j, Start_X), (Start_X + SIZE * j, Start_X + SIZE * (Line_Points - 1)), 1) # 画星位和天元 for i in (3, 9, 15): for j in (3, 9, 15): if i == j == 9: radius = 5 else: radius = 3 # pygame.draw.circle(screen, BLACK, (Start_X + SIZE * i, Start_Y + SIZE * j), radius) pygame.gfxdraw.aacircle(screen, Start_X + SIZE * i, Start_Y + SIZE * j, radius, BLACK_COLOR) pygame.gfxdraw.filled_circle(screen, Start_X + SIZE * i, Start_Y + SIZE * j, radius, BLACK_COLOR)# 画棋子def _draw_chessman(screen, point, stone_color): # pygame.draw.circle(screen, stone_color, (Start_X + SIZE * point.X, Start_Y + SIZE * point.Y), Stone_Radius) pygame.gfxdraw.aacircle(screen, Start_X + SIZE * point.X, Start_Y + SIZE * point.Y, Stone_Radius, stone_color) pygame.gfxdraw.filled_circle(screen, Start_X + SIZE * point.X, Start_Y + SIZE * point.Y, Stone_Radius, stone_color)# 画右侧信息显示def _draw_left_info(screen, font, cur_runner, black_win_count, white_win_count): _draw_chessman_pos(screen, (SCREEN_HEIGHT + Stone_Radius2, Start_X + Stone_Radius2), BLACK_CHESSMAN.Color) _draw_chessman_pos(screen, (SCREEN_HEIGHT + Stone_Radius2, Start_X + Stone_Radius2 * 4), WHITE_CHESSMAN.Color) print_text(screen, font, RIGHT_INFO_POS_X, Start_X + 3, '玩家', BLUE_COLOR) print_text(screen, font, RIGHT_INFO_POS_X, Start_X + Stone_Radius2 * 3 + 3, '电脑', BLUE_COLOR) print_text(screen, font, SCREEN_HEIGHT, SCREEN_HEIGHT - Stone_Radius2 * 8, '战况:', BLUE_COLOR) _draw_chessman_pos(screen, (SCREEN_HEIGHT + Stone_Radius2, SCREEN_HEIGHT - int(Stone_Radius2 * 4.5)), BLACK_CHESSMAN.Color) _draw_chessman_pos(screen, (SCREEN_HEIGHT + Stone_Radius2, SCREEN_HEIGHT - Stone_Radius2 * 2), WHITE_CHESSMAN.Color) print_text(screen, font, RIGHT_INFO_POS_X, SCREEN_HEIGHT - int(Stone_Radius2 * 5.5) + 3, f'{black_win_count} 胜', BLUE_COLOR) print_text(screen, font, RIGHT_INFO_POS_X, SCREEN_HEIGHT - Stone_Radius2 * 3 + 3, f'{white_win_count} 胜', BLUE_COLOR)def _draw_chessman_pos(screen, pos, stone_color): pygame.gfxdraw.aacircle(screen, pos[0], pos[1], Stone_Radius2, stone_color) pygame.gfxdraw.filled_circle(screen, pos[0], pos[1], Stone_Radius2, stone_color)# 根据鼠标点击位置,返回游戏区坐标def _get_clickpoint(click_pos): pos_x = click_pos[0] - Start_X pos_y = click_pos[1] - Start_Y if pos_x < -Inside_Width or pos_y < -Inside_Width: return None x = pos_x // SIZE y = pos_y // SIZE if pos_x % SIZE > Stone_Radius: x += 1 if pos_y % SIZE > Stone_Radius: y += 1 if x >= Line_Points or y >= Line_Points: return None return Point(x, y)class AI: def __init__(self, line_points, chessman): self._line_points = line_points self._my = chessman self._opponent = BLACK_CHESSMAN if chessman == WHITE_CHESSMAN else WHITE_CHESSMAN self._checkerboard = [[0] * line_points for _ in range(line_points)] def get_opponent_drop(self, point): self._checkerboard[point.Y][point.X] = self._opponent.Value def AI_drop(self): point = None score = 0 for i in range(self._line_points): for j in range(self._line_points): if self._checkerboard[j][i] == 0: _score = self._get_point_score(Point(i, j)) if _score > score: score = _score point = Point(i, j) elif _score == score and _score > 0: r = random.randint(0, 100) if r % 2 == 0: point = Point(i, j) self._checkerboard[point.Y][point.X] = self._my.Value return point def _get_point_score(self, point): score = 0 for os in offset: score += self._get_direction_score(point, os[0], os[1]) return score def _get_direction_score(self, point, x_offset, y_offset): count = 0 # 落子处我方连续子数 _count = 0 # 落子处对方连续子数 space = None # 我方连续子中有无空格 _space = None # 对方连续子中有无空格 both = 0 # 我方连续子两端有无阻挡 _both = 0 # 对方连续子两端有无阻挡 # 如果是 1 表示是边上是我方子,2 表示敌方子 flag = self._get_stone_color(point, x_offset, y_offset, True) if flag != 0: for step in range(1, 6): x = point.X + step * x_offset y = point.Y + step * y_offset if 0 <= x < self._line_points and 0 <= y < self._line_points: if flag == 1: if self._checkerboard[y][x] == self._my.Value: count += 1 if space is False: space = True elif self._checkerboard[y][x] == self._opponent.Value: _both += 1 break else: if space is None: space = False else: break # 遇到第二个空格退出 elif flag == 2: if self._checkerboard[y][x] == self._my.Value: _both += 1 break elif self._checkerboard[y][x] == self._opponent.Value: _count += 1 if _space is False: _space = True else: if _space is None: _space = False else: break else: # 遇到边也就是阻挡 if flag == 1: both += 1 elif flag == 2: _both += 1 if space is False: space = None if _space is False: _space = None _flag = self._get_stone_color(point, -x_offset, -y_offset, True) if _flag != 0: for step in range(1, 6): x = point.X - step * x_offset y = point.Y - step * y_offset if 0 <= x < self._line_points and 0 <= y < self._line_points: if _flag == 1: if self._checkerboard[y][x] == self._my.Value: count += 1 if space is False: space = True elif self._checkerboard[y][x] == self._opponent.Value: _both += 1 break else: if space is None: space = False else: break # 遇到第二个空格退出 elif _flag == 2: if self._checkerboard[y][x] == self._my.Value: _both += 1 break elif self._checkerboard[y][x] == self._opponent.Value: _count += 1 if _space is False: _space = True else: if _space is None: _space = False else: break else: # 遇到边也就是阻挡 if _flag == 1: both += 1 elif _flag == 2: _both += 1 # 下面这一串score(分数)的含义:评估棋格获胜分数。 # 使计算机计算获胜分值越高的棋格,就能确定能让自己的棋子最有可能达成联机的位置,也就是最佳进攻位置, # 而一旦计算机能确定自己的最高分值的位置,计算机就具备了进攻能力。 # 同理,计算机能计算出玩家的最大分值位置,并抢先玩家获得该位置,这样计算机就具有了防御的能力。 # 在计算机下棋之前,会计算空白棋格上的获胜分数,根据分数高低获取最佳位置。 # 计算机会将棋子下在获胜分数最高的地方。 # 当已放置4颗棋子时,必须在第五个空棋格上设置绝对高的分值。也就是10000 # 当获胜组合上有部分位置已被对手的棋格占据而无法连成五子时,获胜组合上空棋格的获胜分数会直接设置为0。(四颗棋子,你把中间断了) # 当有两组及其以上的获胜组合位置交叉时,对该位置的分数进行叠加,形成分数比周围位置明显高。(五子棋中三三相连) score = 0 if count == 4: score = 10000 elif _count == 4: score = 9000 elif count == 3: if both == 0: score = 1000 elif both == 1: score = 100 else: score = 0 elif _count == 3: if _both == 0: score = 900 elif _both == 1: score = 90 else: score = 0 elif count == 2: if both == 0: score = 100 elif both == 1: score = 10 else: score = 0 elif _count == 2: if _both == 0: score = 90 elif _both == 1: score = 9 else: score = 0 elif count == 1: score = 10 elif _count == 1: score = 9 else: score = 0 if space or _space: score /= 2 return score # 判断指定位置处在指定方向上是我方子、对方子、空 def _get_stone_color(self, point, x_offset, y_offset, next): x = point.X + x_offset y = point.Y + y_offset if 0 <= x < self._line_points and 0 <= y < self._line_points: if self._checkerboard[y][x] == self._my.Value: return 1 elif self._checkerboard[y][x] == self._opponent.Value: return 2 else: if next: return self._get_stone_color(Point(x, y), x_offset, y_offset, False) else: return 0 else: return 0if __name__ == '__main__': main()
接下来是外星人入侵小游戏:
我是按照买的资料书上来做的,在这个代码里面呢,增加了一些资料上没有的功能,比如说外星人是随机产生的(资料书是创建整个外星人群),本来打算让每个外星人随机移动的,但是试了一下发现,外星人移动杂乱无章,然后后就采用了资料书上的做法,让它们作为整体移动;还有就是在射中外星人的时候,可能会产生暴击(emmm,其实是福利吧,bone,不是说伤害有暴击,这个“暴击”得分会更高一些);再一个就是随着等级增加,飞船发射子弹的宽度以及每次发射子弹的数量都会有所增加,当然,外星人以及飞船移动速度也会增加;再一个就是最高分(High score)、当前分数(score)、飞船剩余生命、等级(level)的布局和资料书有所不同,改动就大致这些吧,以后还会逐渐改善,比如增加声音啊,让外星人也能发射子弹,飞船碰到外星人的子弹也会死亡之类的功能吧。OK,废话少说,下面上代码!
alien_invasion.py:
"""该游戏主程序,尽量做到最简单"""import pygamefrom settings import Settingsfrom ship import Shipfrom pygame.sprite import Groupfrom game_stats import GameStatsfrom button import Buttonfrom score_board import Scoreboardimport game_function as gfdef run_game(): """初始化背景设置""" pygame.init() """"创建一个Settings实例""" ai_settings=Settings() """创建一个游戏窗口以及标题""" screen=pygame.display.set_mode( (ai_settings.screen_width,ai_settings.screen_height)) pygame.display.set_caption('Alien Invasion') """创建一艘飞船实例""" ship=Ship(ai_settings,screen) """创建一个用于存储子弹和外星人的编组""" bullets=Group() aliens=Group() """创建外星人群""" gf.creat_fleet(ai_settings,screen,ship,aliens) """创建存储游戏统计信息的实例,并创建记分牌""" stats = GameStats(ai_settings) sb = Scoreboard(ai_settings,screen,stats) #创建play按钮 play_button = Button(ai_settings,screen,"Play") """进入主循环""" while True: """"监视用户的操作,键盘和鼠标""" gf.check_events(ai_settings,screen,ship,aliens,bullets,stats,play_button,sb) if stats.game_active: """更新飞船""" ship.update() """更新子弹""" gf.update_bullet(ai_settings,screen,ship,aliens,bullets,stats,sb) """更新外星人""" gf.update_aliens(ai_settings,screen,ship,aliens,stats,bullets,sb) """刷新屏幕""" gf.update_screen(ai_settings,screen,ship,aliens,bullets, stats,play_button,sb)run_game()
game_function.py:
如果把这个游戏比作一个人,那么上一段代码是刚接触这个人,他给你留下的第一印象,那么下面这段代码可以说是这个人的灵魂了
import sysimport pygamefrom random import randintfrom bullet import Bulletfrom alien import Alienfrom time import sleepdef fire_bullet(ai_settings,screen,ship,bullets,stats): """开火!""" #确保屏幕上的子弹数在限制范围内 for i in range(int(stats.level/5) + 1): if len(bullets) < ai_settings.bullet_allowed: new_bullet=Bullet(ai_settings,screen,ship) bullets.add(new_bullet)def check_keydown(event,ai_settings,screen,ship,bullets,stats): """检查用户按键是否按下以及执行的任务""" if event.key == pygame.K_RIGHT: ship.moving_right=True elif event.key == pygame.K_LEFT: ship.moving_left=True elif event.key == pygame.K_UP: ship.moving_up=True elif event.key == pygame.K_DOWN: ship.moving_down=True elif event.key == pygame.K_SPACE: #发射一颗子弹,并且在限制范围内 fire_bullet(ai_settings,screen,ship,bullets,stats) def check_keyup(event,ship): """检查用户释放按键""" if event.key == pygame.K_RIGHT: ship.moving_right=False elif event.key == pygame.K_LEFT: ship.moving_left=False elif event.key == pygame.K_UP: ship.moving_up=False elif event.key == pygame.K_DOWN: ship.moving_down=False
def check_events(ai_settings,screen,ship,aliens,bullets,stats,play_button,sb): """响应键盘和鼠标事件""" for event in pygame.event.get(): if event.type == pygame.K_q: sys.exit() #如果一直按下右键或者左键,空格键,则向右或右移动或者开火 elif event.type == pygame.KEYDOWN: check_keydown(event,ai_settings,screen,ship,bullets,stats) #释放右键或左键,停止移动 elif event.type == pygame.KEYUP: check_keyup(event,ship) #点击Play按钮,开始游戏 elif event.type == pygame.MOUSEBUTTONDOWN: mouse_x,mouse_y = pygame.mouse.get_pos() check_play_button(ai_settings,screen,ship,aliens,bullets, stats,play_button,mouse_x,mouse_y,sb) def check_play_button(ai_settings,screen,ship,aliens,bullets, stats,play_button,mouse_x,mouse_y,sb): """在玩家单击Play按钮时开始游戏""" button_clicked = play_button.rect.collidepoint(mouse_x,mouse_y) if button_clicked and not stats.game_active: #重置游戏设置 ai_settings.init_dynamic_settings() #隐藏光标 pygame.mouse.set_visible(False) #重置游戏统计信息 stats.reset_stats() stats.game_active = True #重置记分牌图像 sb.prep_score() sb.prep_high_score(screen) sb.prep_level() sb.prep_ships(screen) #清空外星人和子弹列表 aliens.empty() bullets.empty() #创建一群新的外星人,并让飞船居中 creat_fleet(ai_settings,screen,ship,aliens) ship.center_ship(ai_settings) def update_screen(ai_settings,screen,ship,aliens,bullets,stats,play_button,sb): """每次循环都重绘屏幕""" screen.fill(ai_settings.bg_color) for bullet in bullets.sprites(): bullet.draw_bullet() ship.blitme() aliens.draw(screen) sb.show_score() #如果游戏处于非活动状态,绘制Play按钮 if not stats.game_active: play_button.draw_button() """"刷新屏幕,擦去旧屏幕,显示新屏幕""" pygame.display.flip()
def update_bullet(ai_settings,screen,ship,aliens,bullets,stats,sb): """更新子弹位置,并删除已经消失的子弹""" #更新子弹位置 bullets.update() #删除消失的子弹 for bullet in bullets.copy(): if bullet.rect.bottom<=0: bullets.remove(bullet) #检查是否有子弹击中外星人 check_bullet_alien_colide(ai_settings,screen,ship,aliens,bullets,stats,sb) def get_number_aliens_x(ai_settings,alien_width): """获得水平方向上外星人个数""" available_space_x = ai_settings.screen_width-2 * alien_width number_aliens_x = int(available_space_x/(2 * alien_width)) return number_aliens_xdef get_space_rows(ai_settings,ship_height,alien_height): """获得垂直方向上外星人的行数""" available_space_y = (ai_settings.screen_height-(3 * alien_height)- ship_height) number_aliens_rows = int(available_space_y/(2 * alien_height)) return number_aliens_rowsdef creat_alien(ai_settings,screen,aliens,alien_number,row_number): """根据传入的数据在某个位置创建一个外星人""" alien = Alien(ai_settings,screen) alien_width = alien.rect.width alien.x = alien_width + 2 * alien_width * alien_number alien.rect.x = alien.x alien.rect.y = (alien.rect.height +20) + 2 * alien.rect.height * row_number aliens.add(alien)def creat_random_alien_x(number_aliens_x,ai_settings,screen,aliens,row_number): """在第一行随机创建若干个外星人""" for i in range(2,number_aliens_x+2): random_number_x=randint(2,i) for alien_number in range(random_number_x - 1,random_number_x): creat_alien(ai_settings,screen,aliens,alien_number,row_number)
def creat_fleet(ai_settings,screen,ship,aliens): """创建外星人群以及获取屏幕上最大的行数和每行最多个数""" alien=Alien(ai_settings,screen) number_aliens_x = get_number_aliens_x(ai_settings,alien.rect.width) number_rows = get_space_rows(ai_settings,ship.rect.height, alien.rect.height) """随机创建行数""" for j in range(0,number_rows): random_number_y = randint(0,j) for row_number in range(random_number_y): creat_random_alien_x(number_aliens_x,ai_settings, screen,aliens,row_number)def check_fleet_edges(ai_settings,aliens): """有外星人到达屏幕边缘""" for alien in aliens.sprites(): if alien.check_edges(): change_fleet_direction(ai_settings,aliens) break def change_fleet_direction(ai_settings,aliens): """将外星人下移,并改变它们的方向""" for alien in aliens.sprites(): alien.rect.y += ai_settings.fleet_drop_speed ai_settings.fleet_direction *= -1 def update_aliens(ai_settings,screen,ship,aliens,stats,bullets,sb): """检测有外星人位于屏幕边缘或者相撞或者外星人到达底部,更新外星人的位置""" check_fleet_edges(ai_settings,aliens) aliens.update() #检测飞船与外星人的撞击 if pygame.sprite.spritecollideany(ship,aliens): ship_hit(ai_settings,screen,ship,aliens,stats,bullets,sb) #检查是否有外星人到达屏幕底部 check_aliens_bottom(ai_settings,screen,ship,aliens,stats,bullets,sb)def check_bullet_alien_colide(ai_settings,screen,ship,aliens,bullets,stats,sb): """检测子弹和外星人的碰撞""" collisions = pygame.sprite.groupcollide(bullets,aliens,True,True) #两个True可使得子弹与外星人碰撞后消失,并返回一个字典 #碰撞之后加分 if collisions: for aliens in collisions.values(): i = randint(0,10) if i>8: stats.score += (ai_settings.alien_points + 10) * len(aliens) sb.prep_score() else: stats.score += ai_settings.alien_points * len(aliens) sb.prep_score() #检查是否刷新最高分 check_high_score(stats,sb,screen) if len(aliens) == 0: #删除现有的子弹,加快游戏速度,创建新的外星人 ship.center_ship(ai_settings) bullets.empty() ai_settings.increase_speed() #提高等级 stats.level += 1 ai_settings.increase_bullet_size() sb.prep_level() creat_fleet(ai_settings,screen,ship,aliens)
def ship_hit(ai_settings,screen,ship,aliens,stats,bullets,sb): """飞船与外星人相撞,生命减1,清除外星人和子弹列表 并创建新的外星人,飞船放在屏幕底部中央位置""" if stats.ship_left > 0: stats.ship_left -= 1 sb.prep_ships(screen) #清空外星人和子弹列表 aliens.empty() bullets.empty() #新建一个飞船和外星人群 creat_fleet(ai_settings,screen,ship,aliens) ship.center_ship(ai_settings) #暂停0.5秒 sleep(0.5) else: ai_settings.bullet_width = 3 stats.game_active = False pygame.mouse.set_visible(True) def check_aliens_bottom(ai_settings,screen,ship,aliens,stats,bullets,sb): """检查是否有外星人到达底部""" screen_rect = screen.get_rect() for alien in aliens.sprites(): if alien.rect.bottom >= screen_rect.bottom: ship_hit(ai_settings,screen,ship,aliens,stats,bullets,sb) break def check_high_score(stats,sb,screen): """检查是否产生了新的最高分""" if stats.score > stats.high_score: stats.high_score = stats.score sb.prep_high_score(screen)
后面的文件基本是作为分支吧,一些属性还有一些初始化的数据,都是为game_function文件提供资源的settings.py:
class Settings(): def __init__(self): """设置长度和宽度以及背景色属性""" self.screen_width = 800 self.screen_height = 700 self.bg_color=(255,255,255) self.ship_limit = 2 """子弹设置""" self.bullet_width = 3 self.bullet_height=15 self.bullet_color=(60,60,60) self.bullet_allowed=8 """外星人移动设置""" self.fleet_drop_speed = 10 """以什么样的速度加快游戏节奏""" self.speed_up_scale = 1.1 self.init_dynamic_settings() """外星人点数的提高""" self.score_scale = 1.5 """子弹大小提高""" self.bullet_scale = 10 def init_dynamic_settings(self): self.speed = 1.5 self.bullet_speed = 3 self.alien_speed = 0.2 #fleet_direction为1表示向右移动,-1表示向左移动 self.fleet_direction = -1 #计分 self.alien_points = 10 def increase_speed(self): """提高速度设置和外星人点数设置""" self.speed *= self.speed_up_scale self.bullet_speed *= self.speed_up_scale self.alien_speed *= self.speed_up_scale self.alien_points = int(self.alien_points * self.score_scale) def increase_bullet_size(self): self.bullet_width += self.bullet_scale
ship.py:
import pygamefrom pygame.sprite import Spriteclass Ship(Sprite): def __init__(self,ai_settings,screen): """初始化飞船并设置其初始位置""" super(Ship,self).__init__() self.screen=screen #加载飞船图像并获取其外接矩形 self.image=pygame.image.load('images/ship.bmp') self.rect=self.image.get_rect()#获取飞船的矩形 self.screen_rect=screen.get_rect()#获取屏幕矩形 self.ai_settings=ai_settings #将每艘新飞船放在屏幕底部中央位置 self.rect.centerx=self.screen_rect.centerx self.rect.centery=self.screen_rect.centery self.rect.bottom=self.screen_rect.bottom self.center_x=float(self.rect.centerx) self.center_y=float(self.rect.centery) """连续检测按键,设置未按下右键为False""" self.moving_right=False self.moving_left=False self.moving_up=False self.moving_down=False def update(self): """如果连续按方向键,则一直移动,并且不超过边界""" if self.moving_right and self.rect.right < self.screen_rect.right: self.center_x+=self.ai_settings.speed #使用两个if,这样玩家同时按下两个键, #将先增大rect.centerx值,再降低,则飞船位置不变 if self.moving_left and self.rect.left > 0: self.center_x-=self.ai_settings.speed if self.moving_up and self.rect.top > 0: self.center_y-=self.ai_settings.speed if self.moving_down and self.rect.bottom < self.screen_rect.bottom: self.center_y+=self.ai_settings.speed self.rect.centerx=self.center_x self.rect.centery=self.center_y """在指定位置绘制飞船""" def blitme(self): self.screen.blit(self.image,self.rect) def center_ship(self,ai_settings): self.center_x = ai_settings.screen_width/2 self.center_y = ai_settings.screen_height - 28
alien.py:
import pygamefrom pygame.sprite import Spriteclass Alien(Sprite): def __init__(self,ai_settings,screen): super().__init__() """初始化外星人并设置其初始位置""" self.screen=screen self.ai_settings=ai_settings #加载外星人图像并设置rect属性 self.image=pygame.image.load('images/alien.bmp') self.rect=self.image.get_rect() #每个外星人都在屏幕左上角附近 self.rect.x=self.rect.width self.rect.y=self.rect.height #存储外星人准确位置 self.x=float(self.rect.x) def check_edges(self): """如果外星人碰到屏幕边缘,则返回True""" screen_rect = self.screen.get_rect() if self.rect.right >= screen_rect.right: return True elif self.rect.left <= 0: return True def update(self): """移动外星人""" self.x += (self.ai_settings.alien_speed * (self.ai_settings.fleet_direction)) self.rect.x=self.x def blitme(self): """在指定位置绘制飞船""" self.screen.blit(self.image,self.rect)
bullet.py:
import pygamefrom pygame.sprite import Spriteclass Bullet(Sprite): def __init__(self,ai_settings,screen,ship): #在飞船位置创建一个子弹对象 super(Bullet,self).__init__() self.screen=screen self.rect=pygame.Rect(0,0,ai_settings.bullet_width ,ai_settings.bullet_height) self.rect.centerx = ship.rect.centerx self.rect.top = ship.rect.top self.y=float(self.rect.y) self.color=ai_settings.bullet_color self.speed=ai_settings.bullet_speed def update(self): """向上移动子弹""" #更新子弹的小数值 self.y -= self.speed self.rect.y=self.y def draw_bullet(self): """在屏幕上绘制子弹""" pygame.draw.rect(self.screen,self.color,self.rect)
button.py:
import pygame.fontclass Button(): def __init__(self,ai_settings,screen,msg): """初始化按钮属性""" self.screen = screen self.screen_rect = screen.get_rect() #设置按钮大小以及其他属性 self.width,self.height = 200,50 self.button_color = (0, 255, 0) self.text_color = (60,60,60) self.font = pygame.font.SysFont(None,48) #创建按钮的rect对象,并使其居中 self.rect = pygame.Rect(0, 0, self.width, self.height) self.rect.center = self.screen_rect.center #按钮的标签只需要创建一次 self.prep_msg(msg) def prep_msg(self,msg): """将msg渲染为图像,并使其在按钮上居中""" self.msg_image = self.font.render(msg,True,self.text_color, self.button_color) self.msg_image_rect = self.msg_image.get_rect() self.msg_image_rect.center = self.rect.center def draw_button(self): #绘制一个用颜色填充的按钮,再绘制文本 self.screen.fill(self.button_color,self.rect) self.screen.blit(self.msg_image,self.msg_image_rect)
game_stats.py:
class GameStats(): """跟踪游戏的统计信息""" def __init__(self,ai_settings): self.ai_settings = ai_settings self.game_active = False self.reset_stats() #任何情况下都不能重置最高分 self.high_score = 0 def reset_stats(self): """初始化在游戏运行过程中可能变化的统计信息""" self.ship_left = self.ai_settings.ship_limit self.score = 0 self.level = 1 self.bullet_width = 3
scord_board.py:
import pygame.fontfrom pygame.sprite import Groupfrom ship import Shipclass Scoreboard(): """显示得分的类""" def __init__(self,ai_settings,screen,stats): """初始化显示得分的属性""" self.screen = screen self.screen_rect = screen.get_rect() self.ai_settings = ai_settings self.stats = stats #显示得分时的字体设置 self.text_color = (30,30,30) self.font = pygame.font.SysFont(None,48) #准备初始得分图像以及当前最高得分 self.prep_score() self.prep_high_score(screen) self.prep_level() self.prep_ships(screen) def prep_score(self): """将得分转化为可渲染的图像""" rounded_score = int(round(self.stats.score,-1)) score_str ="Score:"+"{:,}".format(rounded_score) self.score_image = self.font.render(score_str,True,self.text_color, self.ai_settings.bg_color) #将得分放在屏幕右上角 self.score_rect = self.score_image.get_rect() self.score_rect.right = self.screen_rect.right - 20 self.score_rect.top = 20 def prep_high_score(self,screen): """将最高得分渲染为图片""" high_score = int(round(self.stats.high_score,-1)) high_score_str = "High Score:"+"{:,}".format(high_score) self.high_score_image = self.font.render(high_score_str,True,self.text_color, self.ai_settings.bg_color) """将最高得分放在屏幕左上角""" self.high_score_rect = self.high_score_image.get_rect() self.high_score_rect.left = self.screen_rect.left + 20 self.high_score_rect.top = 20 def prep_level(self): """将等级渲染为图像""" self.level_image = self.font.render("Level:"+str(self.stats.level),True, self.text_color,self.ai_settings.bg_color) #将等级放在右下角 self.level_rect = self.level_image.get_rect() self.level_rect.right = self.score_rect.right self.level_rect.bottom = self.score_rect.bottom + 650 def show_score(self): """在屏幕上显示得分,最高分,等级""" self.screen.blit(self.score_image,self.score_rect) self.screen.blit(self.high_score_image,self.high_score_rect) self.screen.blit(self.level_image,self.level_rect) #绘制剩余飞船 self.ships.draw(self.screen) def prep_ships(self,screen): """显示剩余的飞机""" self.ships = Group() for ship_number in range(self.stats.ship_left + 1): ship =Ship(self.ai_settings,self.screen) ship.rect.x = 10 + ship_number * ship.rect.width ship.rect.y = self.screen_rect.top + 640 self.ships.add(ship)
代码的话,注释已经比较清楚了,所以就没有多费口舌去解释
下面是运行之后的截图:
当三艘飞船用光后,结束游戏
再次点击“Play”按钮,再次开始