主代码
'''
Function:
外星人入侵小游戏
'''
import os
import sys
import cfg
import random
import pygame
from modules import *
'''开始游戏'''
def startGame(screen):
clock = pygame.time.Clock()
# 加载字体
font = pygame.font.SysFont('arial', 18)
if not os.path.isfile('score'):
f = open('score', 'w')
f.write('0')
f.close()
with open('score', 'r') as f:
highest_score = int(f.read().strip())
# 敌方
enemies_group = pygame.sprite.Group()
for i in range(55):
if i < 11:
enemy = enemySprite('small', i, cfg.WHITE, cfg.WHITE)
elif i < 33:
enemy = enemySprite('medium', i, cfg.WHITE, cfg.WHITE)
else:
enemy = enemySprite('large', i, cfg.WHITE, cfg.WHITE)
enemy.rect.x = 85 + (i % 11) * 50
enemy.rect.y = 120 + (i // 11) * 45
enemies_group.add(enemy)
boomed_enemies_group = pygame.sprite.Group()
en_bullets_group = pygame.sprite.Group()
ufo = ufoSprite(color=cfg.RED)
# 我方
myaircraft = aircraftSprite(color=cfg.GREEN, bullet_color=cfg.WHITE)
my_bullets_group = pygame.sprite.Group()
# 用于控制敌方位置更新
# --移动一行
enemy_move_count = 24
enemy_move_interval = 24
enemy_move_flag = False
# --改变移动方向(改变方向的同时集体下降一次)
enemy_change_direction_count = 0
enemy_change_direction_interval = 60
enemy_need_down = False
enemy_move_right = True
enemy_need_move_row = 6
enemy_max_row = 5
# 用于控制敌方发射子弹
enemy_shot_interval = 100
enemy_shot_count = 0
enemy_shot_flag = False
# 游戏进行中
running = True
is_win = False
# 主循环
while running:
screen.fill(cfg.BLACK)
for event in pygame.event.get():
# --点右上角的X或者按Esc键退出游戏
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
# --射击
if event.type == pygame.MOUSEBUTTONDOWN:
my_bullet = myaircraft.shot()
if my_bullet:
my_bullets_group.add(my_bullet)
# --我方子弹与敌方/UFO碰撞检测
for enemy in enemies_group:
if pygame.sprite.spritecollide(enemy, my_bullets_group, True, None):
boomed_enemies_group.add(enemy)
enemies_group.remove(enemy)
myaircraft.score += enemy.reward
if pygame.sprite.spritecollide(ufo, my_bullets_group, True, None):
ufo.is_dead = True
myaircraft.score += ufo.reward
# --更新并画敌方
# ----敌方子弹
enemy_shot_count += 1
if enemy_shot_count > enemy_shot_interval:
enemy_shot_flag = True
enemies_survive_list = [enemy.number for enemy in enemies_group]
shot_number = random.choice(enemies_survive_list)
enemy_shot_count = 0
# ----敌方移动
enemy_move_count += 1
if enemy_move_count > enemy_move_interval:
enemy_move_count = 0
enemy_move_flag = True
enemy_need_move_row -= 1
if enemy_need_move_row == 0:
enemy_need_move_row = enemy_max_row
enemy_change_direction_count += 1
if enemy_change_direction_count > enemy_change_direction_interval:
enemy_change_direction_count = 1
enemy_move_right = not enemy_move_right
enemy_need_down = True
# ----每次下降提高移动和射击速度
enemy_move_interval = max(15, enemy_move_interval-3)
enemy_shot_interval = max(50, enemy_move_interval-10)
# ----遍历更新
for enemy in enemies_group:
if enemy_shot_flag:
if enemy.number == shot_number:
en_bullet = enemy.shot()
en_bullets_group.add(en_bullet)
if enemy_move_flag:
if enemy.number in range((enemy_need_move_row-1)*11, enemy_need_move_row*11):
if enemy_move_right:
enemy.update('right', cfg.SCREENSIZE[1])
else:
enemy.update('left', cfg.SCREENSIZE[1])
else:
enemy.update(None, cfg.SCREENSIZE[1])
if enemy_need_down:
if enemy.update('down', cfg.SCREENSIZE[1]):
running = False
is_win = False
enemy.change_count -= 1
enemy.draw(screen)
enemy_move_flag = False
enemy_need_down = False
enemy_shot_flag = False
# ----敌方爆炸特效
for boomed_enemy in boomed_enemies_group:
if boomed_enemy.boom(screen):
boomed_enemies_group.remove(boomed_enemy)
del boomed_enemy
# --敌方子弹与我方飞船碰撞检测
if not myaircraft.one_dead:
if pygame.sprite.spritecollide(myaircraft, en_bullets_group, True, None):
myaircraft.one_dead = True
if myaircraft.one_dead:
if myaircraft.boom(screen):
myaircraft.resetBoom()
myaircraft.num_life -= 1
if myaircraft.num_life < 1:
running = False
is_win = False
else:
# ----更新飞船
myaircraft.update(cfg.SCREENSIZE[0])
# ----画飞船
myaircraft.draw(screen)
if (not ufo.has_boomed) and (ufo.is_dead):
if ufo.boom(screen):
ufo.has_boomed = True
else:
# ----更新UFO
ufo.update(cfg.SCREENSIZE[0])
# ----画UFO
ufo.draw(screen)
# --画我方飞船子弹
for bullet in my_bullets_group:
if bullet.update():
my_bullets_group.remove(bullet)
del bullet
else:
bullet.draw(screen)
# --画敌方子弹
for bullet in en_bullets_group:
if bullet.update(cfg.SCREENSIZE[1]):
en_bullets_group.remove(bullet)
del bullet
else:
bullet.draw(screen)
if myaircraft.score > highest_score:
highest_score = myaircraft.score
# --得分每增加2000我方飞船增加一条生命
if (myaircraft.score % 2000 == 0) and (myaircraft.score > 0) and (myaircraft.score != myaircraft.old_score):
myaircraft.old_score = myaircraft.score
myaircraft.num_life = min(myaircraft.num_life + 1, myaircraft.max_num_life)
# --敌人都死光了的话就胜利了
if len(enemies_group) < 1:
is_win = True
running = False
# --显示文字
# ----当前得分
showText(screen, 'SCORE: ', cfg.WHITE, font, 200, 8)
showText(screen, str(myaircraft.score), cfg.WHITE, font, 200, 24)
# ----敌人数量
showText(screen, 'ENEMY: ', cfg.WHITE, font, 370, 8)
showText(screen, str(len(enemies_group)), cfg.WHITE, font, 370, 24)
# ----历史最高分
showText(screen, 'HIGHEST: ', cfg.WHITE, font, 540, 8)
showText(screen, str(highest_score), cfg.WHITE, font, 540, 24)
# ----FPS
showText(screen, 'FPS: ' + str(int(clock.get_fps())), cfg.RED, font, 8, 8)
# --显示剩余生命值
showLife(screen, myaircraft.num_life, cfg.GREEN)
pygame.display.update()
clock.tick(cfg.FPS)
with open('score', 'w') as f:
f.write(str(highest_score))
return is_win
'''主函数'''
def main():
# 初始化
pygame.init()
pygame.display.set_caption('外星人入侵 ')
screen = pygame.display.set_mode(cfg.SCREENSIZE)
pygame.mixer.init()
pygame.mixer.music.load(cfg.BGMPATH)
pygame.mixer.music.set_volume(0.4)
pygame.mixer.music.play(-1)
while True:
is_win = startGame(screen)
endInterface(screen, cfg.BLACK, is_win)
'''run'''
if __name__ == '__main__':
main()
四.cfg
'''配置文件'''
import os
'''一些颜色'''
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GREEN = (50, 250, 5)
RED = (255, 0, 0)
'''FPS'''
FPS = 60
'''背景音乐'''
BGMPATH = os.path.join(os.getcwd(), 'resources/bgm.mp3')
'''屏幕大小'''
SCREENSIZE = (800, 600)
五.README
# Introduction
https://mp.weixin.qq.com/s/9UylZkV3sVTQLjThIaVObg
# Environment
```
OS: Windows10
Python: Python3.5+(have installed necessary dependencies)
```
# Usage
```
Step1:
pip install -r requirements.txt
Step2:
run "python Game13.py"
```
# Game Display
![giphy](demonstration/running.gif)
requirements:pygame
六.sprites
'''
Function:
定义精灵类
'''
import sys
import pygame
'''我方飞船类'''
class aircraftSprite(pygame.sprite.Sprite):
def __init__(self, color, bullet_color, **kwargs):
pygame.sprite.Sprite.__init__(self)
# 生命值
self.num_life = 3
self.max_num_life = 5
# 最小单元
self.cell = [3, 3]
self.num_cols = 15
self.num_rows = 8
# 用于碰撞检测
self.rect = pygame.Rect(0, 550, self.cell[0] * self.num_cols, self.cell[0] * self.num_rows)
# 填充颜色区域
self.filled_cells = [7,21,22,23,36,37,38,46,47,48,49,50,51,52,53,54,55,56,57,58,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119]
# 飞船颜色
self.color = color
# 飞船子弹颜色
self.bullet_color = bullet_color
# 子弹是否在冷却中
self.is_cooling = False
self.init_count = 35
self.cooling_count = self.init_count
# 得分
self.score = 0
# 避免重复增加生命值
self.old_score = -1
self.resetBoom()
'''射击'''
def shot(self):
if self.is_cooling:
return None
self.is_cooling = True
self.cooling_count = self.init_count
return myBulletSprite(self.rect.x + self.rect.width // 2, self.rect.y, self.bullet_color)
'''在屏幕上画出来'''
def draw(self, screen):
for i in range(0, len(self.filled_cells)):
y = self.filled_cells[i] // self.num_cols
x = self.filled_cells[i] % self.num_cols
rect = [x * self.cell[0] + self.rect[0], y * self.cell[1] + self.rect[1], self.cell[0], self.cell[1]]
pygame.draw.rect(screen, self.color, rect)
'''更新飞船位置等信息'''
def update(self, WIDTH):
# 位置信息
x = pygame.mouse.get_pos()[0] - (self.rect.width // 2)
if x < 0:
x = pygame.mouse.get_pos()[0]
elif x > WIDTH - self.rect.width:
x = WIDTH - self.rect.width
self.rect.x = x
# 子弹信息
if self.is_cooling:
self.cooling_count -= 1
if self.cooling_count == 0:
self.is_cooling = False
'''被击中后爆炸'''
def boom(self, screen):
self.boomed_rect.x = self.rect.x
self.boomed_rect.y = self.rect.y
self.boomed_count += 1
if self.boomed_count % 1 == 0:
self.boomed_frame += 1
for i in range(0, len(self.boomed_filled_cells)):
y = self.boomed_filled_cells[i] // self.boomed_num_cols
x = self.boomed_filled_cells[i] % self.boomed_num_cols
rect = [x * self.boomed_cell[0] + self.boomed_rect[0], y * self.boomed_cell[1] + self.boomed_rect[1], self.boomed_cell[0], self.boomed_cell[1]]
pygame.draw.rect(screen, self.color, rect)
if self.boomed_frame > 4:
return True
else:
return False
'''重置爆炸所用到的数据'''
def resetBoom(self):
# 被击中爆炸时用
self.one_dead = False
self.boomed_filled_cells = [3,7,12,15,17,20,24,30,36,40,44,45,53,54,58,62,68,74,78,81,83,86,91,95]
self.boomed_cell = [3, 3]
self.boomed_num_cols = 11
self.boomed_num_rows = 9
self.boomed_rect = pygame.Rect(0, 0, self.boomed_num_cols*self.boomed_cell[0], self.boomed_num_rows*self.boomed_cell[1])
# 控制每帧的时间
self.boomed_count = 0
# 爆炸特效当前帧
self.boomed_frame = 0
'''ufo类'''
class ufoSprite(pygame.sprite.Sprite):
def __init__(self, color, **kwargs):
pygame.sprite.Sprite.__init__(self)
# 击中该类获得的奖励
self.reward = 200
self.color = color
self.reset()
'''在屏幕上画出来'''
def draw(self, screen):
if self.is_dead:
return None
for i in range(0, len(self.filled_cells)):
y = self.filled_cells[i] // self.num_cols
x = self.filled_cells[i] % self.num_cols
rect = [x * self.cell[0] + self.rect[0], y * self.cell[1] + self.rect[1], self.cell[0], self.cell[1]]
pygame.draw.rect(screen, self.color, rect)
'''更新UFO位置等信息'''
def update(self, WIDTH):
if self.rect.x + self.rect.width < 0 or self.rect.x > WIDTH:
self.rect.x += self.low_speed
else:
self.rect.x += self.high_speed
if self.rect.x > WIDTH + 500:
self.reset()
'''被击中后爆炸'''
def boom(self, screen):
self.boomed_rect.x = self.rect.x
self.boomed_rect.y = self.rect.y
self.boomed_count += 1
if self.boomed_count % 1 == 0:
self.boomed_frame += 1
for i in range(0, len(self.boomed_filled_cells)):
y = self.boomed_filled_cells[i] // self.boomed_num_cols
x = self.boomed_filled_cells[i] % self.boomed_num_cols
rect = [x * self.boomed_cell[0] + self.boomed_rect[0], y * self.boomed_cell[1] + self.boomed_rect[1], self.boomed_cell[0], self.boomed_cell[1]]
pygame.draw.rect(screen, self.color, rect)
if self.boomed_frame > 4:
return True
else:
return False
'''重置'''
def reset(self):
self.cell = [3, 3]
self.num_cols = 16
self.num_rows = 7
self.rect = pygame.Rect(-500 - self.num_cols * self.cell[0], 60, self.num_cols * self.cell[0], self.num_rows * self.cell[1])
self.filled_cells = [5,6,7,8,9,10,19,20,21,22,23,24,25,26,27,28,34,35,36,37,38,39,40,41,42,43,44,45,49,50,52,53,55,56,58,59,61,62,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,82,83,84,87,88,91,92,93,99,108]
self.low_speed = 1
self.high_speed = 2
self.is_dead = False
# 被击中爆炸时用
self.has_boomed = False
self.boomed_filled_cells = [3,7,12,15,17,20,24,30,36,40,44,45,53,54,58,62,68,74,78,81,83,86,91,95]
self.boomed_cell = [3, 3]
self.boomed_num_cols = 11
self.boomed_num_rows = 9
self.boomed_rect = pygame.Rect(0, 0, self.boomed_num_cols*self.boomed_cell[0], self.boomed_num_rows*self.boomed_cell[1])
self.boomed_count = 0
self.boomed_frame = 0
'''敌方类'''
class enemySprite(pygame.sprite.Sprite):
def __init__(self, category, number, color, bullet_color, **kwargs):
pygame.sprite.Sprite.__init__(self)
self.cell = [3, 3]
# 编号
self.number = number
# 种类
self.category = category
if category == 'small':
self.reward = 20
self.num_cols = 8
self.num_rows = 8
self.rect = pygame.Rect(0, 0, self.num_cols * self.cell[0], self.num_rows * self.cell[1])
self.filled_cells = [[3,4,10,11,12,13,17,18,19,20,21,22,24,25,27,28,30,31,32,33,34,35,36,37,38,39,42,45,49,51,52,54,56,58,61,63],
[3,4,10,11,12,13,17,18,19,20,21,22,24,25,27,28,30,31,32,33,34,35,36,37,38,39,41,43,44,46,48,55,57,62]]
elif category == 'medium':
self.reward = 15
self.num_cols = 11
self.num_rows = 8
self.rect = pygame.Rect(0, 0, self.num_cols * self.cell[0], self.num_rows * self.cell[1])
self.filled_cells = [[2,8,11,14,18,21,22,24,25,26,27,28,29,30,32,33,34,35,37,38,39,41,42,43,44,45,46,47,48,49,50,51,52,53,54,56,57,58,59,60,61,62,63,64,68,74,78,86],
[2,8,14,18,24,25,26,27,28,29,30,34,35,37,38,39,41,42,44,45,46,47,48,49,50,51,52,53,54,55,57,58,59,60,61,62,63,65,66,68,74,76,80,81,83,84]]
elif category == 'large':
self.reward = 10
self.num_cols = 12
self.num_rows = 8
self.rect = pygame.Rect(0, 0, self.num_cols * self.cell[0], self.num_rows * self.cell[1])
self.filled_cells = [[4,5,6,7,13,14,15,16,17,18,19,20,21,22,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,41,42,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,62,63,64,67,68,69,73,74,77,78,81,82,86,87,92,93],
[4,5,6,7,13,14,15,16,17,18,19,20,21,22,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,41,42,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,63,64,67,68,74,75,77,78,80,81,84,85,94,95]]
self.color = color
self.bullet_color = bullet_color
self.speed = [8, 20]
self.change_count = 0
self.change_flag = False
# 被击中爆炸时用
self.boomed_filled_cells = [3,7,12,15,17,20,24,30,36,40,44,45,53,54,58,62,68,74,78,81,83,86,91,95]
self.boomed_cell = [3, 3]
self.boomed_num_cols = 11
self.boomed_num_rows = 9
self.boomed_rect = pygame.Rect(0, 0, self.boomed_num_cols * self.boomed_cell[0], self.boomed_num_rows * self.boomed_cell[1])
self.boomed_count = 0
self.boomed_frame = 0
'''射击'''
def shot(self):
return enemyBulletSprite(self.rect.x + self.rect.width // 2, self.rect.y, self.bullet_color)
'''在屏幕上画出来'''
def draw(self, screen):
if self.change_count > 50:
self.change_count = 0
self.change_flag = not self.change_flag
if self.change_flag:
for i in range(0, len(self.filled_cells[0])):
y = self.filled_cells[0][i] // self.num_cols
x = self.filled_cells[0][i] % self.num_cols
rect = [x * self.cell[0] + self.rect[0], y * self.cell[1] + self.rect[1], self.cell[0], self.cell[1]]
pygame.draw.rect(screen, self.color, rect)
else:
for i in range(0, len(self.filled_cells[1])):
y = self.filled_cells[1][i] // self.num_cols
x = self.filled_cells[1][i] % self.num_cols
rect = [x * self.cell[0] + self.rect[0], y * self.cell[1] + self.rect[1], self.cell[0], self.cell[1]]
pygame.draw.rect(screen, self.color, rect)
'''更新敌方位置等信息'''
def update(self, direction, HEIGHT):
# 用于改变形状
self.change_count += 1
# 更新位置信息
if direction == 'right':
self.rect.x += self.speed[0]
elif direction == 'left':
self.rect.x -= self.speed[0]
elif direction == 'down':
self.rect.y += self.speed[1]
if self.rect.y >= HEIGHT - self.rect.height:
return True
else:
return False
'''被击中后爆炸'''
def boom(self, screen):
self.boomed_rect.x = self.rect.x
self.boomed_rect.y = self.rect.y
self.boomed_count += 1
if self.boomed_count % 1 == 0:
self.boomed_frame += 1
for i in range(0, len(self.boomed_filled_cells)):
y = self.boomed_filled_cells[i] // self.boomed_num_cols
x = self.boomed_filled_cells[i] % self.boomed_num_cols
rect = [x * self.boomed_cell[0] + self.boomed_rect[0], y * self.boomed_cell[1] + self.boomed_rect[1], self.boomed_cell[0], self.boomed_cell[1]]
pygame.draw.rect(screen, self.color, rect)
if self.boomed_frame > 4:
return True
else:
return False
'''我方子弹精灵类'''
class myBulletSprite(pygame.sprite.Sprite):
def __init__(self, x, y, color, **kwargs):
pygame.sprite.Sprite.__init__(self)
self.cell = [2, 2]
self.num_cols = 1
self.num_rows = 4
self.rect = pygame.Rect(x, y, self.num_cols * self.cell[0], self.num_rows * self.cell[1])
self.filled_cells = [0,1,2,3]
self.speed = 8
self.color = color
'''在屏幕上画出来'''
def draw(self, screen):
for i in range(0, len(self.filled_cells)):
y = self.filled_cells[i] // self.num_cols
x = self.filled_cells[i] % self.num_cols
rect = [x * self.cell[0] + self.rect[0], y * self.cell[1] + self.rect[1], self.cell[0], self.cell[1]]
pygame.draw.rect(screen, self.color, rect)
'''更新子弹位置等信息'''
def update(self):
self.rect.y -= self.speed
if self.rect.y + self.rect.height < 0:
return True
else:
return False
'''敌方子弹精灵类'''
class enemyBulletSprite(pygame.sprite.Sprite):
def __init__(self, x, y, color):
pygame.sprite.Sprite.__init__(self)
self.cell = [3, 3]
self.num_cols = 3
self.num_rows = 7
self.rect = pygame.Rect(x, y, self.num_cols * self.cell[0], self.num_rows * self.cell[1])
self.filled_cells = [[0,4,8,10,12,16,20],
[2,4,6,10,14,16,18]]
self.change_count = 0
self.change_flag = False
self.speed = 4
self.color = color
'''在屏幕上画出来'''
def draw(self, screen):
if self.change_count > 2:
self.change_count = 0
self.change_flag = not self.change_flag
if self.change_flag:
for i in range(0, len(self.filled_cells[0])):
y = self.filled_cells[0][i] // self.num_cols
x = self.filled_cells[0][i] % self.num_cols
rect = [x * self.cell[0] + self.rect[0], y * self.cell[1] + self.rect[1], self.cell[0], self.cell[1]]
pygame.draw.rect(screen, self.color, rect)
else:
for i in range(0, len(self.filled_cells[1])):
y = self.filled_cells[1][i] // self.num_cols
x = self.filled_cells[1][i] % self.num_cols
rect = [x * self.cell[0] + self.rect[0], y * self.cell[1] + self.rect[1], self.cell[0], self.cell[1]]
pygame.draw.rect(screen, self.color, rect)
'''更新子弹位置等信息'''
def update(self, HEIGHT):
# 用于改变子弹形状的计数
self.change_count += 1
# 位置信息
self.rect.y += self.speed
if self.rect.y > HEIGHT:
return True
else:
return False