【pygame实现星露谷物语风格游戏】5.工具的使用

本文介绍了如何在游戏开发中,利用Python的pygame库实现玩家角色按下空格键执行浇水工具动作的计时功能,包括创建计时器类、在player.py中管理计时器并处理按键输入,确保动画流畅进行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.前言

上一节完成了玩家移动动画的制作,这次要完成实用工具动作的绘制。效果就是按下空格,玩家使用工具。

二.思路

首先要明确玩家要使用什么工具,因此设置一个变量

self.selected_tool = 'water'

这里先默认使用的是浇水的工具,工具的切换会在下一节完成

接着,我们想要达成按下空格,玩家实施浇水动作,这个动作持续0.35秒(也可以是其他时间,但0.35秒的视觉效果是不错的)。

我们需要一个计时器来记录什么时候才经过了0.35秒

因此新建一个timer.py文件,在里面书写如下内容:

import pygame


class Timer:

    def __init__(self,duration,func = None):

        self.duration = duration#计时器的持续时间

        self.func = func#计时结束后要执行的函数

        self.start_time = 0#开始时间,初始化为0

        self.active = False#bool,表示是否正在计时


    def activate(self):

        self.active = True

        # pygame.time.get_ticks()与pygame.time.Clock().get_ticks()不同

        # 这里返回的是 从程序开始 到现在的时间,单位是ms

        self.start_time = pygame.time.get_ticks()


    def deactivate(self):

        self.active = False

        self.start_time = 0


    def update(self):

        current_time = pygame.time.get_ticks()

        if current_time - self.start_time >=self.duration:

            if self.func and self.start_time !=0:

                self.func()

            self.deactivate()

 

传入的参数中,duration是计时器需要记的时间,单位是ms,比如传入的duration是350,那么经过350ms后,计时器结束。func是计时器结束后会执行的函数,缺省值为None。

大体思想就是每一帧都通过pygame.time.get_ticks()来获取现在的时间,并与计时器开始计时的时间进行相减,得到的就是经过的时间,如果经过的时间大于了我们需要记的时间,就结束计时器,并且执行func函数

接下来回到player.py中,我们从timer.py中导入Timer类,并且创建一个关于工具使用的计时器。

由于以后可能其他的动作也会用到计时器,所以这里把计时器写到一个元组里,用这个元组存储以后可能会创建的所有计时器

self.timers = {

    'tool use':Timer(350,self.use_tool)

}

 

于是,self.timers['tool use']就是一个计时时间为350ms,计时结束后会执行self.use_tool的计时器

至于工具具体使用的效果,我们以后再写,因此先把这个函数pass掉

def use_tool(self):

    pass

 

由于计时器的uptade函数是要每一帧都要执行的,因此也要在player.py的update函数中调用它

下面是调用元组中所有的计时器的update函数

def update_timers(self):

    for timer in self.timers.values():

        timer.update()

 

在player.py的update函数中调用self.update_timers函数:

def update(self,dt):

    self.input()

    self.get_status()

    self.update_timers()


    self.move(dt)

    self.animate(dt)

 

接下来,就是在我们按键检测的部分,添加如下的功能:如果检测到空格按下,那么就启动self.timers['tool use']这个计时器,并且,在这个计时器启动的期间(玩家正在使用工具的期间),是不能移动或者再次使用工具的

因此,我们需要在已经使用工具的时候(self.timers['tool use'].active == True)的时候,停止对按键的检测

改动后的代码如下

def input(self):

    keys = pygame.key.get_pressed()

    #已经在使用工具的时候,停止对按键的检测

    if not self.timers['tool use'].active:

        if keys[pygame.K_UP]:

            self.direction.y = -1

            self.status = 'up'

        elif keys[pygame.K_DOWN]:

            self.direction.y = 1

            self.status = 'down'

        else:

            self.direction.y = 0#不要忘记加上这一句,不然按下键盘后再松开也不会停


        if keys[pygame.K_RIGHT]:

            self.direction.x = 1

            self.status = 'right'

        elif keys[pygame.K_LEFT]:

            self.direction.x = -1

            self.status = 'left'

        else:

            self.direction.x = 0


        if keys[pygame.K_SPACE]:

            #启动计时器

            self.timers['tool use'].activate()

            #实用工具的时候是不能移动的

            self.direction = pygame.math.Vector2()

            #要从第一张图片开始绘制

            self.frame_index = 0

 

其中,至于为什么self.fream_index 要重新设为0,不仅是因为在执行一个动作的时候要从第一张图片开始绘制,还因为使用工具的动作基本上都是两张图片,但是移动的动作都是四张图片,如果在使用工具之前正在移动,刚好播放到第四张图片(fream_index = 3),然后变成实用工具的状态,此时不把它清零,就会播放使用工具的第四张图片,使用工具是没有第四张图片的,就会报错,程序崩溃

接下来就是对get_status函数的改动

如果self.timers['tool use']这个计时器是正在计时的(表示正在使用工具),就把self.status变成方向 + "_" +工具名

def get_status(self):


    # idle

    if self.direction.magnitude() == 0:

        self.status = self.status.split('_')[0] + '_idle'


    if self.timers['tool use'].active:

        self.status = self.status.split('_')[0] + '_' + self.selected_tool

 

要注意,上面这两个if的顺序不能颠倒,因为使用工具的时候玩家是不动的,此时self.direction.magnitude()为0,如果颠倒顺序,就会先执行self.status = self.status.split('_')[0] + '_' + self.selected_tool,再执行 self.status = self.status.split('_')[0] + '_idle',就会出现一帧不到的实用工具动作,随后都是待机动作

综上,就完成了按下空格,执行使用工具动作动画的效果

此次在player.py中修改的内容如下图框选所示

三.完整代码

player.py:

import pygame

from settings import *

from support import *

from timer import Timer


class Player(pygame.sprite.Sprite):

    def __init__(self,pos,group):

        #这个参数可以传一个精灵组,这样就会自动把该精灵加入到该精灵组中

        #也可以为空,这样需要在外面手动调用精灵组的add函数来将这个精灵加入到精灵组中

        super().__init__(group)


        self.import_assets()

        self.status = 'down_idle'

        self.frame_index = 0


        #这里的变量名一定要叫image,因为这是它父类Sprite规定的

        self.image = self.animations[self.status][self.frame_index]



        #这个get_rect()也是父类中设置的方法

        #返回值是有很多,大概有x,y,centerx,centery,center,width,height这几类

        #大概就是image的x坐标,y坐标,中心的x坐标,中心的y坐标,中心点的坐标,宽度,高度等

        #参数可以不填,那么位置就默认是(0,0),也可以填一个列表,比如(100,100),那么初始的位置就是(100,100)

        #也可以是center = 一个坐标,这表示设置该图像的中心在这个坐标上

        #同样的这里的变量名也一定要叫rect,这是父类规定的

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


        #创建一个二维的向量,参数不填默认是(0,0)

        self.direction = pygame.math.Vector2()#速度的方向

        self.pos = pygame.math.Vector2(self.rect.center)#位置

        self.speed = 200#速度


        self.timers = {

            'tool use':Timer(350,self.use_tool)

        }

        self.selected_tool = 'water'


    def use_tool(self):

        pass

    def import_assets(self):

        self.animations = {'up': [], 'down': [], 'left': [], 'right': [],

                           'right_idle': [], 'left_idle': [], 'up_idle': [], 'down_idle': [],

                           'right_hoe': [], 'left_hoe': [], 'up_hoe': [], 'down_hoe': [],

                           'right_axe': [], 'left_axe': [], 'up_axe': [], 'down_axe': [],

                           'right_water': [], 'left_water': [], 'up_water': [], 'down_water': []}


        for animation in self.animations.keys():

            full_path = '../graphics/character/' + animation

            self.animations[animation] = import_folder(full_path)


    def animate(self, dt):


        # 4 是比较合适的数字

        # 数字 决定做动作的快慢

        # 做一个动作需要 1/4秒

        # fream_index += dt 的话,要经过1s,才能变成下一个整数,做下一个动作

        # fream_index += 4*dt,那么增加的速度就快了四倍,经过1/4秒就能做下一个动作了

        self.frame_index += 4 * dt

        # print(self.frame_index)

        if self.frame_index >= len(self.animations[self.status]):

            self.frame_index = 0


        self.image = self.animations[self.status][int(self.frame_index)]


    def input(self):

        keys = pygame.key.get_pressed()

        #已经在使用工具的时候,停止对按键的检测

        if not self.timers['tool use'].active:

            if keys[pygame.K_UP]:

                self.direction.y = -1

                self.status = 'up'

            elif keys[pygame.K_DOWN]:

                self.direction.y = 1

                self.status = 'down'

            else:

                self.direction.y = 0#不要忘记加上这一句,不然按下键盘后再松开也不会停


            if keys[pygame.K_RIGHT]:

                self.direction.x = 1

                self.status = 'right'

            elif keys[pygame.K_LEFT]:

                self.direction.x = -1

                self.status = 'left'

            else:

                self.direction.x = 0


            if keys[pygame.K_SPACE]:

                #启动计时器

                self.timers['tool use'].activate()

                #实用工具的时候是不能移动的

                self.direction = pygame.math.Vector2()

                #要从第一张图片开始绘制

                self.frame_index = 0


    def get_status(self):


        # idle

        if self.direction.magnitude() == 0:

            # self.status += '_idle'

            # 上面这种方法不可取因为每一帧都会在字符串后面加上一个_idel

            # 所以status会变成 xxx_idle_idle_idle

            # 实际上当出现两个_idle的时候就已经报错了

            # 下面这种方法

            # split('_')是把一个字符串 以 '_' 为分节符分开

            # 他返回的是一个列表

            # 比如 a_b_c_d

            # 返回的就是[a,b,c,d]

            # 所以下面的【0】获取的就是_之前的内容

            self.status = self.status.split('_')[0] + '_idle'


        if self.timers['tool use'].active:

            self.status = self.status.split('_')[0] + '_' + self.selected_tool


    def update_timers(self):

        for timer in self.timers.values():

            timer.update()


    def move(self,dt):

        #向量归一化,比如一个n维向量为(x1,x2,x3...,xn)

        #那么向量归一化的操作就是等比例缩放x1到xn的大小让 根号下(x1的平方+x2的平方+...+xn的平方) 等于1

        #归一化的目的是如果同时按右键和上,那么direction就会变成(1,1),他的速度向量就是一个大小为根2,方向右上的向量了

        #magnitude()返回的是一个float类型的数据,他的大小为根号下(x1的平方+x2的平方+...+xn的平方)

        if self.direction.magnitude() > 0:#这表示如果有按键按下,如果向量里面全是0,会使归一化中的数学计算出现错误

            self.direction = self.direction.normalize()


        #位移 = 方向 * 速度 * 变化的时间()

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

        self.rect.centerx = self.pos.x


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

        self.rect.centery = self.pos.y


    def update(self,dt):

        self.input()

        self.get_status()

        self.update_timers()


        self.move(dt)

        self.animate(dt)

 

timer.py:

import pygame


class Timer:

    def __init__(self,duration,func = None):

        self.duration = duration#计时器的持续时间

        self.func = func#计时结束后要执行的函数

        self.start_time = 0#开始时间,初始化为0

        self.active = False#bool,表示是否正在计时


    def activate(self):

        self.active = True

        # pygame.time.get_ticks()与pygame.time.Clock().get_ticks()不同

        # 这里返回的是 从程序开始 到现在的时间,单位是ms

        self.start_time = pygame.time.get_ticks()


    def deactivate(self):

        self.active = False

        self.start_time = 0


    def update(self):

        current_time = pygame.time.get_ticks()

        if current_time - self.start_time >=self.duration:

            if self.func and self.start_time !=0:

                self.func()

            self.deactivate()

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

owooooow

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值