Pygame实现自定义的生命游戏
仅是尝试,还有待提高(v0)。
主要目的:想了解如何交互,获取鼠标点击位置并进行相应操作。
此次生命游戏的规则:
每一轮用户通过鼠标点击增加一个细胞;
所有细胞向其八邻域中的一个分裂新细胞;
细胞的八邻域若多于3个细胞,则认为营养不足、毒素增多,该细胞凋亡;
细胞最多只能存活三轮。
1 引入需要的包
import os
import sys
import time
import pygame
import random
import numpy as np
2 定义生命游戏
# 生命游戏的规则,用numpy数组存储
def count_score(current_cells):
'''
现在存在的细胞总数
'''
return np.sum(current_cells)
def add_new_cell(x,y,current_cells):
'''
加入一个新细胞
'''
current_cells[x][y] = 1
def add_cells_lives(current_cells,cells_life):
'''
对所有细胞增加细胞周期
'''
for i in range(len(cells_life)):
for j in range(len(cells_life[0])):
if(current_cells[i][j]==1):
cells_life[i][j] = cells_life[i][j]+1
def cells_death1(current_cells,cells_life):
'''
进行细胞凋亡操作(细胞活多于3周期,则死亡)
'''
for i in range(len(cells_life)):
for j in range(len(cells_life[0])):
if(cells_life[i][j] > 3):
cells_life[i][j] = 0
current_cells[i][j] = 0
def cells_death2(current_cells,cells_life):
'''
进行细胞凋亡操作(细胞周围八邻域多于3个细胞,则死亡)
'''
xn = [-1,-1,-1,0,1,1,1,0]
yn = [1,0,-1,-1,-1,0,1,1]
for i in range(len(current_cells)):
for j in range(len(current_cells[0])):
if(current_cells[i][j]==1):
count_n_cells = 0
for k in range(8):
xx = i+xn[k]
yy = j+yn[k]
if(xx>=0 and xx<len(current_cells) and yy>=0 and yy<len(current_cells[0]) and current_cells[xx][yy]==1):
count_n_cells += 1
if(count_n_cells>3):
current_cells[i][j] = 0
cells_life[i][j] = 0
def cells_division(current_cells):
'''
细胞分裂(细胞周围如果有空位,则分裂)(简便起见,先检测到哪就往哪分裂)
'''
xn = [-1,-1,-1,0,1,1,1,0]
yn = [1,0,-1,-1,-1,0,1,1]
for i in range(len(current_cells)):
for j in range(len(current_cells[0])):
if(current_cells[i][j]==1):
for k in range(8):
xx = i+xn[k]
yy = j+yn[k]
if(xx >=0 and xx<len(current_cells) and yy>=0 and yy<len(current_cells[0]) and current_cells[xx][yy]==0):
current_cells[xx][yy] = 2 # 先标记为2,避免新分裂的细胞再次分裂
break
for i in range(len(current_cells)):
for j in range(len(current_cells[0])):
if(current_cells[i][j]==2):
current_cells[i][j] = 1
def one_circle(x,y,current_cells,cells_life):
'''
一次交互,增加细胞,细胞分裂,细胞凋亡
'''
if(current_cells[x][y]==1): # 如果x,y位置已经有细胞,则返回
return
else:
add_new_cell(x, y, current_cells)
cells_division(current_cells)
add_cells_lives(current_cells,cells_life)
cells_death1(current_cells,cells_life)
cells_death2(current_cells,cells_life)
score = count_score(current_cells) # 可以将现存细胞数目打印出来
print(score)
3 定义游戏的相关信息
# 定义游戏窗口宽高,行数列数等信息
WIDTH = 700
HEIGHT = 700
NUMGRID = 16 # 一行/列的方格数
GRIDSIZE = 36 # 方格大小
XMARGIN = (WIDTH - NUMGRID*GRIDSIZE) // 2 # 边缘大小
YMARGIN = (HEIGHT - NUMGRID*GRIDSIZE) // 2
# 初始化数组
score = 0 # 生存的细胞数目
current_cells = np.zeros((NUMGRID,NUMGRID)) # 记录格子是否有细胞,bool
cells_life = np.zeros((NUMGRID,NUMGRID)) # 记录细胞存活的周期
用于判断鼠标点击的方格位置:
# 记录矩形的信息,之后需要往窗口中贴图
BOARDRECTS = []
for x in range(WIDTH):
BOARDRECTS.append([])
for y in range(HEIGHT):
r = pygame.Rect((XMARGIN+x*GRIDSIZE),(YMARGIN+y*GRIDSIZE),GRIDSIZE,GRIDSIZE)
BOARDRECTS[x].append(r)
def CheckValidClick(pos):
'''
检查点击是否在面板上
'''
for x in range(WIDTH):
for y in range(HEIGHT):
if(BOARDRECTS[x][y].collidepoint(pos[0],pos[1])):
ans = [x,y]
return ans
return None
准备细胞图片和输出信息:
# 准备细胞图片
CELL = pygame.image.load('C:\\Users\\Administrator\\Desktop\\cell.jpg') # 加载图像
CELL = pygame.transform.scale(CELL, (GRIDSIZE-2,GRIDSIZE-2)) # 改变图像大小
# 定义需要显示的信息:当前细胞数目、已进行的轮数、总分
CELLNUMS = 0
ROUND = 0
SCORE = 0
POS1 = [20,20] # 分别需要显示的位置
POS2 = [300,20]
POS3 = [520,20]
pygame.font.init() # 输出字体
font1 = pygame.font.Font(None,30)
4 游戏初始设置
# 初始化pygame,窗口大小,窗口名称
pygame.init()
screen = pygame.display.set_mode((WIDTH,HEIGHT))
pygame.display.set_caption('Life Game')
# 初始化背景和网格
screen.fill((255,255,220)) # 填充背景
for x in range(NUMGRID): # 画网格
for y in range(NUMGRID):
rect = pygame.Rect((XMARGIN+x*GRIDSIZE,YMARGIN+y*GRIDSIZE,GRIDSIZE,GRIDSIZE))
pygame.draw.rect(screen,(255,165,0),rect,2)
pygame.display.flip()
pygame.display.update()
5 游戏主循环
CLICKX = None # 鼠标点击的位置,全局变量
CLICKY = None
while True: # 游戏主循环
# 初始化背景和网格,每次都初始化原因:细胞在不断变化,若不这样,已经死亡的细胞贴图会一直存在;为了简便,每次都初始化再重新贴图
screen.fill((255,255,220)) # 填充窗口颜色
for x in range(NUMGRID): # 画网格
for y in range(NUMGRID):
rect = pygame.Rect((XMARGIN+x*GRIDSIZE,YMARGIN+y*GRIDSIZE,GRIDSIZE,GRIDSIZE))
pygame.draw.rect(screen,(255,165,0),rect,2)
ClickedSpace = None # 鼠标点击的位置
for event in pygame.event.get(): # 获取事件(如鼠标键盘点击)
if(event.type==pygame.QUIT): # 窗口关闭按钮被按下
pygame.quit()
sys.exit() # 结束程序
elif(event.type==pygame.MOUSEBUTTONDOWN): # 鼠标被按下
CLICKX,CLICKY = event.pos # 记录鼠标位置
print(CLICKX,CLICKY)
elif(event.type==pygame.MOUSEBUTTONUP): # 鼠标抬起
if(event.pos==(CLICKX,CLICKY)): # 检查鼠标位置
ClickedSpace = CheckValidClick(event.pos) # 用之前写的函数检查点击是否在合法区域,返回属于哪个方格
print('ClickedSpace: ',ClickedSpace)
# 开始游戏
if(ClickedSpace!=None): # 方格位置合法
if(current_cells[ClickedSpace[0]][ClickedSpace[1]]==1): # 若点击之处已有细胞,则跳出继续,这里是为了避免round数不合理增加
continue
one_circle(ClickedSpace[0],ClickedSpace[1], current_cells, cells_life) # 一次生命游戏交互
CELLNUMS = count_score(current_cells) # 计算并显示信息
ROUND = ROUND+1
SCORE = CELLNUMS + ROUND
surface1 = font1.render('Cellnums: %d'%CELLNUMS,1,(125,125,255))
surface2 = font1.render('Round: %d'%ROUND,1,(125,125,255))
surface3 = font1.render('Score: %d'%SCORE,1,(125,125,255))
screen.blit(surface1,POS1)
screen.blit(surface2,POS2)
screen.blit(surface3,POS3)
pygame.display.update()
# 遍历数组,在有细胞的地方换上细胞
for i in range(NUMGRID):
for j in range(NUMGRID):
if(current_cells[i][j]==1):
screen.blit(CELL,(BOARDRECTS[i][j][0],BOARDRECTS[i][j][1]))
pygame.display.update()
pygame.display.flip()
不足之处:
1、游戏的规则可以继续改进:比如只改变点击点周围的细胞的情况等。不管怎么点都不会得到一个差的结果,规则设置得没有让人思考的欲望;
2、采用每个循环都重新绘制,可以寻找动态显示出细胞分裂、凋亡的方法。
学习到的点:
1、pygame需要在循环中不断获取事件,不断更新,以达到动态游戏的目的。所以,如果没有这一步,窗口会显示无响应。
2、显示图像:先创建surface,再blit,再update。
3、获取鼠标点击相应位置的方法。