【pygame实现星露谷物语风格游戏】17.下雨

发布于:2024-03-17 ⋅ 阅读:(174) ⋅ 点赞:(0)

一.目标

当新的一天开始的时候,会有概率出现下雨的天气。完成下雨的动画效果,并且下雨的时候农田会被自动灌溉

二.代码的编写

首先控制下雨的代码会写到sky.py中,让我们先新建这个文件

我们创建雨滴动画效果的思路就是,把雨滴分为两种,如下图所示,一种是落到地上的,一种是在空中的。

我们书写一个雨滴精灵,图像分别为上述的两种雨滴,如果下雨,我们不断在整个屏幕的随机位置生成雨滴精灵,并且在400毫秒到500毫秒之后把它清除掉,这样就会达到雨滴落下的视觉效果。但是正在空中的雨滴,我们还需要他有一个向下的动画,这个很简单,首先判断这个雨滴是否是在空中的雨滴,这个只需要在传递参数的时候传递一个叫move的参数,如果move为true,就说明这个雨滴是正在空中的雨滴,是需要向左下方移动的,只要给他一个向左下的二维矢量当作速度,像我们之前写玩家的移动一样让它动起来即可。

首先让我们书写雨滴类

里面的代码基本都在写player类或者泥土类的时候反复写过,没什么好说的

接下来我们来写控制下雨的类

初始化就是把上述两种雨滴的图片导入进来,其中每种雨滴都有三张不同的图片,我们会随机选择一张来充当雨滴的样子。

因为我们需要在地图上的随机位置生成雨滴,所以后续会用到randint(0,地图的宽/高),所以提前获取地图的宽高

接下来是生成在地板上的雨滴的函数,生成地板上的雨滴不需要移动,所以moving设为False

接下来是生成在空中的雨滴的函数

接下来,写一个update函数,在update函数里调用这两个create函数

这样,我们在外面创建一个Rain的对象,然后调用这个对象的update方法,就会在地图的随机地方,分别生成一个落在地上的雨滴,和在空中的雨滴,经过400到500ms之后再消失。如果是雨天,我们就每一帧调用一次update函数,就会呈现朦胧细雨的效果

这就需要我们来到level.py中,先把Rain这个类导入进来

实例化对象

接下来我们需要一个bool值来表示现在是否下雨,我们需要实现随机下雨的效果,所以生成一个从0到9的随机数,如果这个数字大于8,就下雨

当然,每当进入新的一天,需要重新随机决定是否今天会下雨(下图的代码写错了,应该是 > 8 而不是 > 3)具体大于多少可以根据自己喜好调节,如果大于3,就会经常下雨

接着,再run函数中判断,如果今天下雨,就生成雨滴

至此,我们就完成了下雨的动画效果

接下来,我们需要对我们的soil.py文件进行改动,让它知道现在是否正在下雨,如果下雨了,就把农田全都浇灌了

首先实现给所有被开垦过的农田都浇水的效果,这个没什么好多说的,刚好与移除所有浇水效果翻过来

这个函数会遍历grid这个比较大的二维列表,显然是比较费时间的,所以我们不能让level的run函数调用它,来每一帧检测是否下雨,如果下雨就浇灌全部的土地,肯定会很卡。

我们只需要想想什么情况下需要自动浇灌土地,无非如下两种情况:新的一天开始了,今天是下雨天,就把所有被开垦过的土地浇水。还有一种情况是今天是下雨天,玩家开垦了新的土地,这个土地也会被自动浇水。

接下来回到level.py中,每当开始新的一天的时候,判断是否下雨,如果下雨,就调用water_all

接下来我们在下雨天新开垦土地的时候,就会自动灌溉,所以我们需要在soil.py中知道今天是否下雨,按照之前的方法,我们得再实例化Soil_layer对象的时候把self.raining当成参数传进去,不过这次我们使用另一种方法,直接在level.py中给soil_layer创建一个新的变量,当然,我们的level中有两处地方创建raining,所以别忘了两处都需要给soil_layer传递变量

这样,我们就能直接在Soil_layer类中直接使用这个变量了,我们直接在开垦土地的时候判断今天是否会下雨,如果下雨,就自动浇灌这块土地即可

此时运行程序,发现下雨天之前开垦的土地并不会被灌溉,但是新开垦的土地会被灌溉,发现问题出在:每当新开始一天的时候,需要先清除水迹,再判断是否下雨并且water_all,如果顺序反过来了,就会刚刚water_all的水迹立刻又被清楚了

这下程序就没有问题了

三.完整代码

level.py:

import pygame

from settings import *

from player import Player

from overlay import Overlay

from sprites import *

from pytmx.util_pygame import load_pygame

from support import *

from transition import Transition

from soil import SoilLayer

from sky import Rain


class Level():

    def __init__(self):

        #得到屏幕的画面,得到的这个画面与main.py中的screen相同

        self.display_surface = pygame.display.get_surface()


        #创建精灵组

        self.all_sprites = CameraGroup()

        #具有碰撞箱的精灵组

        self.collision_sprites = pygame.sprite.Group()

        #树木精灵组

        self.tree_sprites = pygame.sprite.Group()

        #特殊区域精灵组

        self.interaction_sprites = pygame.sprite.Group()


        #调用setup方法

        self.setup()

        #创建工具和种子显示图层

        self.overlay = Overlay(self.player)

        #创建transition对象

        self.transition = Transition(self.reset,self.player)


        #实例化Rain对象

        self.rain = Rain(self.all_sprites)

        #一个bool值,表示是否正在下雨,有1/10的概率为true

        self.raining = randint(0,10) > 8

        self.soil_layer.raining = self.raining


    def setup(self):


        #土地管理类

        self.soil_layer = SoilLayer(self.all_sprites)


        #载入.tmx文件

        tmx_data = load_pygame('../data/map.tmx')


        #绘制房子与栅栏,他们都属于Generic类

        for layer in ['HouseFloor', 'HouseFurnitureBottom']:

            for x, y, surf in tmx_data.get_layer_by_name(layer).tiles():

                Generic((x * TILE_SIZE, y * TILE_SIZE), surf, self.all_sprites, LAYERS['house bottom'])


        for layer in ['HouseWalls', 'HouseFurnitureTop','Fence']:

            for x, y, surf in tmx_data.get_layer_by_name(layer).tiles():

                Generic((x * TILE_SIZE, y * TILE_SIZE), surf, [self.all_sprites, self.collision_sprites])




        #水流

        water_frames = import_folder('../graphics/water')

        for x, y, surf in tmx_data.get_layer_by_name('Water').tiles():

            Water((x * TILE_SIZE, y * TILE_SIZE), water_frames, self.all_sprites)


        #树木

        for obj in tmx_data.get_layer_by_name('Trees'):

            Tree(

                pos=(obj.x, obj.y),

                surf=obj.image,

                groups=[self.all_sprites, self.collision_sprites, self.tree_sprites],

                name=obj.name,

                player_add=self.player_add)

        #野花

        for obj in tmx_data.get_layer_by_name('Decoration'):

            WildFlower((obj.x, obj.y), obj.image,[self.all_sprites, self.collision_sprites])


        #空气墙

        for x, y, surf in tmx_data.get_layer_by_name('Collision').tiles():

            Generic((x * TILE_SIZE, y * TILE_SIZE), pygame.Surface((TILE_SIZE, TILE_SIZE)), self.collision_sprites)


        #玩家

        for obj in tmx_data.get_layer_by_name('Player'):

            if obj.name == 'Start':

                self.player = Player(

                    pos=(obj.x, obj.y),

                    group=self.all_sprites,

                    collision_sprites=self.collision_sprites,

                    tree_sprites=self.tree_sprites,

                    interaction = self.interaction_sprites,

                    soil_layer = self.soil_layer)


            if obj.name == 'Bed':

                Interaction((obj.x, obj.y), (obj.width, obj.height), self.interaction_sprites, obj.name)


        Generic(

            pos = (0,0),

            surf = pygame.image.load('../graphics/world/ground.png').convert_alpha(),

            groups = self.all_sprites,

            z = LAYERS['ground']

        )


    def run(self,dt):

        #窗口的背景设为黑色

        self.display_surface.fill('black')

        #调用精灵组的draw方法

        self.all_sprites.custom_draw(self.player)

        #调用精灵组的update方法

        self.all_sprites.update(dt)


        self.overlay.display()


        #如果在睡觉,执行相应的函数

        if self.player.sleep:

            self.transition.play()


        #如果在下雨,就生成雨滴

        if self.raining:

            self.rain.update()


    def player_add(self, item):

        #item是一个str类型的数据,代表要对哪一种物品加一

        self.player.item_inventory[item] += 1


    def reset(self):

        # 清除农田里的水渍

        self.soil_layer.remove_water()


        #每天重新随机一下是否要下雨

        self.raining = randint(0, 10) > 8

        self.soil_layer.raining = self.raining

        #如果新的一天下雨了,就自动灌溉所有被开垦过的土地

        if self.raining:

            self.soil_layer.water_all()


        #苹果重新长在树上

        for tree in self.tree_sprites.sprites():

            for apple in tree.apple_sprites.sprites():

                apple.kill()

            tree.create_fruit()



class CameraGroup(pygame.sprite.Group):

    def __init__(self):

        super().__init__()

        #获取窗口

        self.display_surface = pygame.display.get_surface()

        #这是一个偏移量,代表的是玩家的实际位置与屏幕中间的矢量

        self.offset = pygame.math.Vector2()

    def custom_draw(self,player):

        self.offset.x = player.rect.centerx - SCREEN_WIDTH / 2

        self.offset.y = player.rect.centery - SCREEN_HEIGHT / 2


        for layer in LAYERS.values():#按照z轴从小到达绘制

            for sprite in sorted(self.sprites(),key = lambda sprite: sprite.rect.centery):

                if sprite.z == layer:#如果该精灵的z值等于当前要绘制的z值,才绘制

                    offset_rect = sprite.rect.copy()

                    offset_rect.center -= self.offset

                    #if sprite == player:

                        #print("player.rect.center为:(" +

                             # str(player.rect.centerx)+"," + str(player.rect.centery)+")")

                        #print("offset_rect为:(" + str(offset_rect.x)

                             # +"," +str(offset_rect.y)+")")

                    self.display_surface.blit(sprite.image,offset_rect)

 

sky.py:

import pygame

from settings import *

from sprites import Generic

from random import *

from support import import_folder


#雨滴精灵

class Drop(Generic):

    def __init__(self, surf, pos, moving, groups, z):

        #参数中moving表示是否是移动的雨滴,雨滴分为两种,已经落到地上的我们就不让他移动,在空中的我们给他一个向着左下的速度

        super().__init__(pos, surf, groups, z)

        #雨滴这个精灵的存活时间

        self.lifetime = randint(400, 500)

        #计时器的开始时间

        self.start_time = pygame.time.get_ticks()


        #如果moving为true,表示这个雨滴是需要移动的

        self.moving = moving

        #精灵移动所需要的三个参数

        if self.moving:

            self.pos = pygame.math.Vector2(self.rect.topleft)

            self.direction = pygame.math.Vector2(-2, 4)

            self.speed = randint(200, 250)


    def update(self, dt):

        #让雨滴动起来

        if self.moving:

            self.pos += self.direction * self.speed * dt

            self.rect.topleft = (round(self.pos.x), round(self.pos.y))

        #过了存活时间,把这个精灵kill掉

        if pygame.time.get_ticks() - self.start_time >= self.lifetime:

            self.kill()


#掌管下雨的类

class Rain:

    def __init__(self,all_sprites):

        self.all_sprites = all_sprites

        #导入雨滴正在落下的图片,一共三张,以列表的形式返回

        self.rain_drops = import_folder('../graphics/rain/drops/')

        # 导入雨滴落到地板的图片,一共三张,以列表的形式返回

        self.rain_floor = import_folder('../graphics/rain/floor/')

        #整张地图的宽度和高度,我们需要这两个数据取随机数,在随机的位置生成雨滴

        self.floor_w, self.floor_h = pygame.image.load('../graphics/world/ground.png').get_size()


    #生成落到地板上的雨滴

    def create_floor(self):

        Drop(

            surf=choice(self.rain_floor),

            pos=(randint(0, self.floor_w), randint(0, self.floor_h)),

            moving=False,

            groups=self.all_sprites,

            z=LAYERS['rain floor'])


    #生成在空中的雨滴

    def create_drops(self):

        Drop(

            surf=choice(self.rain_drops),

            pos=(randint(0, self.floor_w), randint(0, self.floor_h)),

            moving=True,

            groups=self.all_sprites,

            z=LAYERS['rain drops'])


    def update(self):

        self.create_floor()

        self.create_drops()

 

soil.py:

import pygame

from settings import *

from pytmx.util_pygame import load_pygame

from support import *

from random import choice


#开垦过的土地

class SoilTile(pygame.sprite.Sprite):

    def __init__(self, pos, surf, groups):

       super().__init__(groups)

       self.image = surf

       self.rect = self.image.get_rect(topleft = pos)

       self.z = LAYERS['soil']


#浇过水的土地

class WaterTile(pygame.sprite.Sprite):

    def __init__(self, pos, surf, groups):

       super().__init__(groups)

       self.image = surf

       self.rect = self.image.get_rect(topleft = pos)

       self.z = LAYERS['soil water']



#管理所有土地的类

class SoilLayer:

    def __init__(self,all_sprites):

        #all_sprites精灵组

        self.all_sprites = all_sprites

        #创建开垦过的土地精灵组

        self.soil_sprites = pygame.sprite.Group()

        #浇过水的土地精灵组

        self.water_sprites = pygame.sprite.Group()

        #导入开垦过的土地的图片

        self.soil_surf = pygame.image.load('../graphics/soil/o.png')

        #获取浇水图片,一共有三张

        self.water_surfs = import_folder('../graphics/soil_water')


        self.create_soil_grid()

        self.create_hit_rects()


    def create_soil_grid(self):

        #导入地图的图片

        ground = pygame.image.load('../graphics/world/ground.png')

        #地图的宽度,高度除以64,就是一共有多少块(64*64)的土地

        h_tiles, v_tiles = ground.get_width() // TILE_SIZE, ground.get_height() // TILE_SIZE

        #创建一个三维列表,最一开始,所有土地的属性都是空的

        self.grid = [[[] for col in range(h_tiles)] for row in range(v_tiles)]

        #如果是可以被耕种的土地块,就给他的列表添加一个标记'F'

        for x, y, _ in load_pygame('../data/map.tmx').get_layer_by_name('Farmable').tiles():

            self.grid[y][x].append('F')


    def create_hit_rects(self):

        #列表里存放的是所有能被耕种的土地,数据类型是pygame里提供的Rect数据类型,包含了坐标和宽高

        self.hit_rects = []

        for index_row, row in enumerate(self.grid):

            for index_col, cell in enumerate(row):

                #如果是能被耕种的

                if 'F' in cell:

                    x = index_col * TILE_SIZE

                    y = index_row * TILE_SIZE

                    rect = pygame.Rect(x, y, TILE_SIZE, TILE_SIZE)

                    self.hit_rects.append(rect)


    def get_hit(self, point):

        #point是传入的参数,代表的是玩家挥舞的锄头的落点的坐标

        for rect in self.hit_rects:

            #如果该可耕种的土地块与玩家的锄头坐标发生了重叠,说明玩家锄的就是这块地,就给他的属性再加上一个标记'X'

            if rect.collidepoint(point):

                #相反,这里得把pygame的坐标系转换成tmx文件中的坐标系

                x = rect.x // TILE_SIZE

                y = rect.y // TILE_SIZE


                if 'F' in self.grid[y][x]:

                    self.grid[y][x].append('X')

                    SoilTile(

                        pos=(rect.x,rect.y),

                        surf=self.soil_surf,

                        groups=[self.all_sprites, self.soil_sprites])

                    if self.raining:

                        self.water(point)

                    #self.create_soil_tiles()


    def water(self, target_pos):

        for soil_sprite in self.soil_sprites.sprites():

            if soil_sprite.rect.collidepoint(target_pos):

                x = soil_sprite.rect.x // TILE_SIZE

                y = soil_sprite.rect.y // TILE_SIZE

                #给grid添加”W“标记

                self.grid[y][x].append('W')


                pos = soil_sprite.rect.topleft

                #随机选择三张之中的一张图片,来作为这个精灵的image

                surf = choice(self.water_surfs)

                WaterTile(pos, surf, [self.all_sprites, self.water_sprites])


    def remove_water(self):

        #删除所有的精灵

        for sprite in self.water_sprites.sprites():

            sprite.kill()

        #同时别忘了清理grid中的标记

        for row in self.grid:

            for cell in row:

                if 'W' in cell:

                    cell.remove('W')


    #给所有被开垦过的农田浇水

    def water_all(self):

        for index_row, row in enumerate(self.grid):

            for index_col, cell in enumerate(row):

                if 'X' in cell and 'W' not in cell:

                    cell.append('W')

                    x = index_col * TILE_SIZE

                    y = index_row * TILE_SIZE

                    WaterTile((x, y), choice(self.water_surfs), [self.all_sprites, self.water_sprites])


    """

    def create_soil_tiles(self):

        self.soil_sprites.empty()

        for index_row, row in enumerate(self.grid):

            for index_col, cell in enumerate(row):

                if 'X' in cell:

                    SoilTile(

                        pos=(index_col * TILE_SIZE, index_row * TILE_SIZE),

                        surf=self.soil_surf,

                        groups=[self.all_sprites, self.soil_sprites])"""

 


网站公告

今日签到

点亮在社区的每一天
去签到