几个小孩在屏幕上乱跑的项目,撞人要互相说对不起!
1.005.png
解平对张娟说:对不起!
张娟对解平说:对不起!
王兵对马桂珍说:对不起!
马桂珍对王兵说:对不起!
一、 架构
init.py
play.py : 主入口文件
player.py : 精灵类文件
d61f0565d821438c9b7dcd4f48d5395d.png :人物图片
所有文件全部在一个文件夹内,调用play.py运行游戏。
二、主入口文件 play.py
import pygame
from player import Player,my_group
from rich import print
pygame.init()
screen = pygame.display.set_mode((400,300))
clock,FPS = pygame.time.Clock(),30
running = True
[Player() for _ in range(5)]
step = 0
while running:
screen.fill((0, 255, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
my_group.update()
my_group.draw(screen)
pygame.display.flip()
clock.tick(FPS)
step += 1
if step>1000:
pygame.image.save(screen,f"{step/1000}.png")
pygame.quit()
这是pygame的基本框架,每个pygame项目都会这么写。
if step>1000:
pygame.image.save(screen,f"{step/1000}.png")
是游戏运行一段时间后自动截屏。
二、 精灵类文件player.py
import pygame
from pygame.sprite import Sprite
from itertools import cycle
import numpy as np
from faker import Faker
faker = Faker("zh_CN")
my_group = pygame.sprite.Group()
def collided(sprite, other):
return sprite != other and sprite.rect.colliderect(other.rect)
class Player(Sprite):
img = pygame.image.load("d61f0565d821438c9b7dcd4f48d5395d.png")
fps_p = 30
d1 = cycle([img.subsurface((0,0,32,32))]*fps_p+[img.subsurface((32,0,32,32))]*fps_p+[img.subsurface((64,0,32,32))]*fps_p+[img.subsurface((95,0,32,32))]*fps_p)
d2 = cycle([img.subsurface((0,32,32,32))]*fps_p+[img.subsurface((32,32,32,32))]*fps_p+[img.subsurface((64,32,32,32))]*fps_p+[img.subsurface((95,32,32,32))]*fps_p)
d3 = cycle([img.subsurface((0,64,32,32))]*fps_p+[img.subsurface((32,64,32,32))]*fps_p+[img.subsurface((64,64,32,32))]*fps_p+[img.subsurface((95,64,32,32))]*fps_p)
d4 = cycle([img.subsurface((0,95,32,32))]*fps_p+[img.subsurface((32,95,32,32))]*fps_p+[img.subsurface((64,95,32,32))]*fps_p+[img.subsurface((95,95,32,32))]*fps_p)
image_lib= {"stand":d1,"go_up":d4,"go_left":d2,"go_right":d3,"go_down":d1}
speed_lib= {"stand":(0,0),"go_up":(1,-5),"go_left":(-5,-1),"go_right":(5,1),"go_down":(-1,5)}
def __init__(self):
self.name = faker.name()
super().__init__()
self.state = "stand"
self.image = self.image_lib[self.state].__next__()
self.spped = self.speed_lib[self.state]
self.rect = self.image.get_rect()
self.rect.center = np.random.randint(0,300,2)
my_group.add(self)
self.steps = 0
def update(self):
self.steps += 1
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
self.state = "go_right"
if keys[pygame.K_LEFT]:
self.state = "go_left"
if keys[pygame.K_UP]:
self.state = "go_up"
if keys[pygame.K_DOWN]:
self.state = "go_down"
if keys[pygame.K_SPACE]:
self.state = "stand"
if keys[pygame.K_LCTRL]:
self.state = np.random.choice(["stand","go_up","go_left","go_right","go_down"],p=np.array([1,3,3,3,3])/np.array([1,3,3,3,3]).sum())
if self.rect.left <0:
self.state = np.random.choice(["stand","go_up","go_right","go_down"])
if self.rect.right >400:
self.state = np.random.choice(["stand","go_up","go_left","go_down"])
if self.rect.top <0:
self.state = np.random.choice(["stand","go_left","go_right","go_down"])
if self.rect.bottom >300:
self.state = np.random.choice(["stand","go_up","go_left","go_right"])
if self.steps>100:
self.state = np.random.choice(["stand","go_up","go_left","go_right","go_down"])
self.steps = 0
self.image = self.image_lib[self.state].__next__()
self.spped = self.speed_lib[self.state]
self.rect.move_ip(self.spped)
col = pygame.sprite.spritecollideany(self, my_group, collided)
if col:
print(f"{self.name}对{col.name}说:对不起!")
精灵图:
精灵图是4*4的整合图片,pygame是每帧读取的,把任务动作分解成状态类,['站着','往左','往右','往前','往后'],站着的状态图可以取'往后'。
每个状态用Python的itertools.cycle包裹,cycle的特性是无限循环,每次yield出一张图片,每个状态都有多张图片,cycle最适合pygame每次循环挤出一张图片的场景。
把状态类集合成字典:
d1 = cycle([img.subsurface((0,0,32,32))]*fps_p+[img.subsurface((32,0,32,32))]*fps_p+[img.subsurface((64,0,32,32))]*fps_p+[img.subsurface((95,0,32,32))]*fps_p)
d2 = cycle([img.subsurface((0,32,32,32))]*fps_p+[img.subsurface((32,32,32,32))]*fps_p+[img.subsurface((64,32,32,32))]*fps_p+[img.subsurface((95,32,32,32))]*fps_p)
d3 = cycle([img.subsurface((0,64,32,32))]*fps_p+[img.subsurface((32,64,32,32))]*fps_p+[img.subsurface((64,64,32,32))]*fps_p+[img.subsurface((95,64,32,32))]*fps_p)
d4 = cycle([img.subsurface((0,95,32,32))]*fps_p+[img.subsurface((32,95,32,32))]*fps_p+[img.subsurface((64,95,32,32))]*fps_p+[img.subsurface((95,95,32,32))]*fps_p)
image_lib= {"stand":d1,"go_up":d4,"go_left":d2,"go_right":d3,"go_down":d1}
fps_p 是放大的循环次数,要是没有放大,每张图片就在每个FPS里面一闪而过,根本看不清。
每张图片的取出方法:先确定当前状态,再挤出一个图片。
self.state = "stand"
self.image = self.image_lib[self.state].__next__()
每个精灵类都要重写update方法,因为没法在精灵类外部获得event事件,它里面有个特殊函数:keys = pygame.key.get_pressed(),获得需要的键盘事件,也可以把事件函数写在主文件里,那样主文件就很大很难看。
三、 碰撞检测
def collided(sprite, other):
return sprite != other and sprite.rect.colliderect(other.rect)
col = pygame.sprite.spritecollideany(self, my_group, collided)
if col:
print(f"{self.name}对{col.name}说:对不起!")
pygame 有很丰富的碰撞检测,可是都无法直接用到本项目里!它自带的检测函数:
方法 | 说明 |
---|---|
collide_rect() | 两个精灵之间的矩形检测,即矩形区域是否有交汇,返回一个布尔值。 |
collide_circle() | 两个精灵之间的圆形检测,即圆形区域是否有交汇,返回一个布尔值。 |
collide_mask() | 两个精灵之间的像素蒙版检测,更为精准的一种检测方式。 |
spritecollide() | 精灵和精灵组之间的矩形碰撞检测,一个组内的所有精灵会逐一地对另外一个单个精灵进行碰撞检测,返回值是一个列表,包含了发生碰撞的所有精灵。 |
spritecollideany() | 精灵和精灵组之间的矩形碰撞检测,上述函数的变体,当发生碰撞时,返回组内的一个精灵,无碰撞发生时,返回 None。 |
groupcollide() | 检测在两个组之间发生碰撞的所有精灵,它返回值是一个字典,将第一组中发生碰撞的精灵作为键,第二个组中发生碰撞的精灵作为值。 |
精灵与组碰撞检测,如果精灵在组内,结果会是精灵与组一直碰撞,调用
pygame.sprite.spritecollideany(self, my_group),返回错误,必须加入回调函数pygame.sprite.spritecollideany(self, my_group, collided),
collided(sprite, other)函数把spritecollideany的前两项做自己的参数,返回弹出精灵的新的组。
网友评论