想象一下冬夜,鹅毛大雪纷纷扬扬飘下,覆盖在地上,树顶上……,我一直想用代码模拟一下这个场景,今天总算完成了一个,虽然效果不怎么样,但是基本思路是有了。这个场景需要三种对象,一种是会被雪覆盖的对象,例如树木、房屋、道路等,一种是雪,我这个程序里简单用一种精灵模拟,更好的做法应该是用粒子系统,还有一种是积雪。雪与被雪覆盖的对象或其上面覆盖的积雪发生碰撞,在碰撞发生处生成一个积雪。每过一段时间,最上面的积雪发生溶化。按上面的思路,被雪覆盖的对象只用了一棵树(还是下雪的时候不可能存在的树),编写的程序运行截图如下:
可以看到树顶和路上有了一点点积雪。
代码如下:
import os
import random
import pygame
screen_width = 500
screen_height = 600
pygame.init()
screen = pygame.display.set_mode((screen_width, screen_height))
imgs_path = os.path.join(os.path.dirname(os.path.dirname(__file__)),'imgs')
tree_img = pygame.image.load(os.path.join(imgs_path, 'tree.png')).convert()
snow_image = pygame.image.load(os.path.join(imgs_path, 'snow.png')).convert()
snow_image.set_colorkey((0, 0, 0))
snow_cover_image = pygame.image.load(os.path.join(imgs_path, 'snow_cover.png')).convert()
tree_img = pygame.transform.scale(tree_img, (200,300))
tree_img.set_colorkey((0,0,0))
running = True
last_loop_time = 0
#最后一次融化积雪的时间
last_cover_time = 0
# 预备融化的积雪
cover = [0 for _ in range(screen_width)]
'''树'''
class Tree(pygame.sprite.Sprite):
def __init__(self, image, screen, left, bottom):
pygame.sprite.Sprite.__init__(self)
self.screen = screen
self.image = image
self.mask = pygame.mask.from_surface(self.image)
self.rect = self.image.get_rect()
self.rect.left = left
self.rect.bottom = bottom
'''绘制自身'''
def draw(self):
self.screen.blit(self.image, self.rect)
'''雪花'''
class Snow(pygame.sprite.Sprite):
def __init__(self, image, screen):
pygame.sprite.Sprite.__init__(self)
self.screen = screen
self.last_rotate_time = 0
# 将雪花缩放成随机生成的大小
self.image = pygame.transform.scale(image,
(random.randrange(2,5),
random.randrange(2,5)))
self.mask = pygame.mask.from_surface(self.image)
self.image_origin = self.image
self.rect = self.image.get_rect()
self.rect.x = random.randrange(0, screen_width)
self.rect.y = random.randrange(-1000, -10)
self.speedx = random.randrange(-3, 3)
self.speedy = random.randrange(1, 3)
self.rotate_angle = 0
self.rotate_speed = random.randrange(-5, 5)
'''绘制自身'''
def draw(self):
self.screen.blit(self.image, self.rect)
'''
由于岩石图片不是一个真圆,每次旋转后图片的中心会发生偏移,
经过多次旋转后图片会偏移到非预期位置。为防止这种情况发生,每
次旋转后需要将图片的中心设置为原始图片的中心,每次旋转都从原
始状态开始
'''
def rotate(self):
now = pygame.time.get_ticks()
if now - self.last_rotate_time > 30:
self.rotate_angle = (self.rotate_angle + self.rotate_speed) % 360
old_center = self.rect.center
# 每次旋转都从原始状态开始
self.image = pygame.transform.rotate(self.image_origin, self.rotate_angle)
self.rect = self.image.get_rect()
# 将旋转后的图片的中心调整为原始图片的中心
self.rect.center = old_center
self.last_time = now
def update(self):
self.rect.x += self.speedx
self.rect.y += self.speedy
self.rotate()
cover_floor = screen_height - snow_cover_image.get_rect().height
# 精灵矩形区域被移出屏幕外后重置其位置
if self.rect.left > screen_width or
self.rect.right < 0:
self.rect.x = random.randrange(0, screen_width)
self.rect.y = random.randrange(-100, -40)
elif self.rect.left < screen_width and
self.rect.right >= 0 and self.rect.top > cover_floor :
snow_cover = Snow_cover(snow_cover_image, screen, self.rect.x, cover_floor)
snow_covers.add(snow_cover)
cover[self.rect.x] = snow_cover
self.rect.x = random.randrange(0, screen_width)
self.rect.y = random.randrange(-100, -40)
'''积雪'''
class Snow_cover(pygame.sprite.Sprite):
def __init__(self, image, screen, left, top):
pygame.sprite.Sprite.__init__(self)
self.screen = screen
self.image = image
self.rect = self.image.get_rect()
self.rect.left = left
self.rect.top = top
'''绘制自身'''
def draw(self):
self.screen.blit(self.image,self.rect)
#场景上放一棵树
tree = Tree(tree_img, screen, 130, screen_height)
#场景中的积雪
snow_covers = pygame.sprite.Group()
#场景中的雪花
snows = pygame.sprite.Group()
def update():
global last_cover_time
now = pygame.time.get_ticks()
# 每100000毫秒化一次雪
if (now - last_cover_time) > 100000:
last_cover_time = now
for i in range(screen_width):
if cover[i]!=0:
cover[i].kill()
cover[i] = 0
#更新背景
screen.fill((0,0,0),(0,0,screen_width,screen_height))
#绘制树
tree.draw()
#绘制雪花和积雪
for snow in snows:
snow.update()
snow.draw()
for snow_cover in snow_covers:
snow_cover.draw()
pygame.display.update()
def check_collide():
# 雪撞上积雪
hits = pygame.sprite.groupcollide(snows, snow_covers, False, False)
# 在屏幕范围内,生成一个新积雪,更新预备融化的积雪数组
for hit in hits:
if hit.rect.left >=0 and hit.rect.left< screen_width:
snow_cover = Snow_cover(snow_cover_image, screen, hit.rect.left, hit.rect.bottom - 2)
snow_covers.add(snow_cover)
cover[hit.rect.left] = snow_cover
hit.rect.x = random.randrange(0, screen_width)
hit.rect.y = random.randrange(-100, -40)
# 雪撞上树,生成一个新积雪,更新预备融化的积雪数组
hits = pygame.sprite.spritecollide(tree, snows, True, pygame.sprite.collide_mask)
for hit in hits:
snow_cover = Snow_cover(snow_cover_image, screen, hit.rect.left, hit.rect.bottom - 1)
snow_covers.add(snow_cover)
cover[hit.rect.left] = snow_cover
hit.rect.x = random.randrange(0, screen_width)
hit.rect.y = random.randrange(-100, -40)
for i in range(300):
snow = Snow(snow_image, screen)
snows.add(snow)
while running:
now = pygame.time.get_ticks()
if now - last_loop_time >= 1000 / 60:
last_loop_time = now
for event in pygame.event.get():
key_pressed = pygame.key.get_pressed()
# 按下窗体上的关闭按钮或者ESC键则停止主事件循环
if event.type == pygame.QUIT or key_pressed[pygame.K_ESCAPE]:
running = False
if running:
check_collide()
update()



