AL-2020/venv/Lib/site-packages/pygame/examples/mask.py

177 lines
5.4 KiB
Python
Raw Normal View History

2020-05-15 17:25:01 +02:00
#!/usr/bin/env python
"""A pgyame.mask collition detection example
exports main()
This module can also be run as a stand-alone program, excepting
one or more image file names as command line arguments.
"""
import sys, random
import pygame, pygame.image, pygame.surface, pygame.time, pygame.display
def maskFromSurface(surface, threshold = 127):
#return pygame.mask.from_surface(surface, threshold)
mask = pygame.mask.Mask(surface.get_size())
key = surface.get_colorkey()
if key:
for y in range(surface.get_height()):
for x in range(surface.get_width()):
if surface.get_at((x,y)) != key:
mask.set_at((x,y),1)
else:
for y in range(surface.get_height()):
for x in range (surface.get_width()):
if surface.get_at((x,y))[3] > threshold:
mask.set_at((x,y),1)
return mask
def vadd(x,y):
return [x[0]+y[0],x[1]+y[1]]
def vsub(x,y):
return [x[0]-y[0],x[1]-y[1]]
def vdot(x,y):
return x[0]*y[0]+x[1]*y[1]
class Sprite:
def __init__(self, surface, mask = None):
self.surface = surface
if mask:
self.mask = mask
else:
self.mask = maskFromSurface(self.surface)
self.setPos([0,0])
self.setVelocity([0,0])
def setPos(self,pos):
self.pos = [pos[0],pos[1]]
def setVelocity(self,vel):
self.vel = [vel[0],vel[1]]
def move(self,dr):
self.pos = vadd(self.pos,dr)
def kick(self,impulse):
self.vel[0] += impulse[0]
self.vel[1] += impulse[1]
def collide(self,s):
"""Test if the sprites are colliding and
resolve the collision in this case."""
offset = [int(x) for x in vsub(s.pos,self.pos)]
overlap = self.mask.overlap_area(s.mask,offset)
if overlap == 0:
return
"""Calculate collision normal"""
nx = (self.mask.overlap_area(s.mask,(offset[0]+1,offset[1])) -
self.mask.overlap_area(s.mask,(offset[0]-1,offset[1])))
ny = (self.mask.overlap_area(s.mask,(offset[0],offset[1]+1)) -
self.mask.overlap_area(s.mask,(offset[0],offset[1]-1)))
if nx == 0 and ny == 0:
"""One sprite is inside another"""
return
n = [nx,ny]
dv = vsub(s.vel,self.vel)
J = vdot(dv,n)/(2*vdot(n,n))
if J > 0:
"""Can scale up to 2*J here to get bouncy collisions"""
J *= 1.9
self.kick([nx*J,ny*J])
s.kick([-J*nx,-J*ny])
return
"""Separate the sprites"""
c1 = -overlap/vdot(n,n)
c2 = -c1/2
self.move([c2*nx,c2*ny])
s.move([(c1+c2)*nx,(c1+c2)*ny])
def update(self,dt):
self.pos[0] += dt*self.vel[0]
self.pos[1] += dt*self.vel[1]
def main(*args):
"""Display multiple images bounce off each other using collition detection
Positional arguments:
one or more image file names.
This pygame.masks demo will display multiple moving sprites bouncing
off each other. More than one sprite image can be provided.
"""
if len(args) == 0:
raise ValueError("Require at least one image file name: non given")
print ('Press any key to quit')
screen = pygame.display.set_mode((640,480))
images = []
masks = []
for impath in args:
images.append(pygame.image.load(impath).convert_alpha())
masks.append(maskFromSurface(images[-1]))
numtimes = 10
import time
t1 = time.time()
for x in range(numtimes):
m = maskFromSurface(images[-1])
t2 = time.time()
print ("python maskFromSurface :%s" % (t2-t1))
t1 = time.time()
for x in range(numtimes):
m = pygame.mask.from_surface(images[-1])
t2 = time.time()
print ("C pygame.mask.from_surface :%s" % (t2-t1))
sprites = []
for i in range(20):
j = i % len(images)
s = Sprite(images[j],masks[j])
s.setPos((random.uniform(0,screen.get_width()),
random.uniform(0,screen.get_height())))
s.setVelocity((random.uniform(-5,5),random.uniform(-5,5)))
sprites.append(s)
pygame.time.set_timer(pygame.USEREVENT,33)
while 1:
event = pygame.event.wait()
if event.type == pygame.QUIT:
return
elif event.type == pygame.USEREVENT:
"""Do both mechanics and screen update"""
screen.fill((240,220,100))
for i in range(len(sprites)):
for j in range(i+1,len(sprites)):
sprites[i].collide(sprites[j])
for s in sprites:
s.update(1)
if s.pos[0] < -s.surface.get_width()-3:
s.pos[0] = screen.get_width()
elif s.pos[0] > screen.get_width()+3:
s.pos[0] = -s.surface.get_width()
if s.pos[1] < -s.surface.get_height()-3:
s.pos[1] = screen.get_height()
elif s.pos[1] > screen.get_height()+3:
s.pos[1] = -s.surface.get_height()
screen.blit(s.surface,s.pos)
pygame.display.update()
elif event.type == pygame.KEYDOWN:
return
if __name__ == '__main__':
if len(sys.argv) < 2:
print ('Usage: mask.py <IMAGE> [<IMAGE> ...]')
print ('Let many copies of IMAGE(s) bounce against each other')
print ('Press any key to quit')
else:
main(*sys.argv[1:])