一.前言
上一次,我们完成了实用工具的动画,但是并不能切换工具,这次我们要实现的功能是:按键盘左边shif键,切换工具。此外,还要完成种子的切换和使用,按下键盘右边的键,切换种子,按下右边ctrl键,使用种子。但是在这节并不会实现使用种子的动画,把他们放在以及只是因为他们切换的代码十分相似。
二.思路
分别创建列表存放工具和种子的名字,再分别创建索引,通过 改变索引的方式来获得我们正在使用的工具或者种子的名字:
同时,也写一个使用种子的函数,不过把它pass掉,留着以后再完善
#按下左边的shift键更换工具
if keys[pygame.K_LSHIFT] and not self.timers['tool switch'].active:
self.timers['tool switch'].activate()
self.tool_index += 1
self.tool_index = self.tool_index if self.tool_index < len(self.tools) else 0
self.selected_tool = self.tools[self.tool_index]
#按下左边的ctrl键,使用种子
if keys[pygame.K_RCTRL]:
self.timers['seed use'].activate()
self.direction = pygame.math.Vector2()
self.frame_index = 0
#按下右边的shift键,切换种子
if keys[pygame.K_RSHIFT] and not self.timers['seed switch'].active:
self.timers['seed switch'].activate()
self.seed_index += 1
self.seed_index = self.seed_index if self.seed_index < len(self.seeds) else 0
self.selected_seed = self.seeds[self.seed_index]
当我们按下一次键盘上的按键的时候,实际上每一次循环都会检测到键盘被按下,直到我们松开按键。所以我们自认为按下了一次左边的shift键来切换工具,实际上每一帧他都会执行一次if语句里面的代码,除非你能在一帧以内松开键盘。因此我们还需要创建切换工具和切换种子的计时器,当检测到按下一次切换的按键后,200ms内不会再进行检测了,这样就可以避免上述的问题。
相应的,我们还顺带创建了使用种子的计时器,不过目前我们还用不到这个计时器。
三.完整代码
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),
'tool switch': Timer(200),
'seed use': Timer(350, self.use_seed),
'seed switch': Timer(200),
}
self.tools = ['hoe', 'axe', 'water']
self.tool_index = 0
self.selected_tool = self.tools[self.tool_index]
self.seeds = ['corn', 'tomato']
self.seed_index = 0
self.selected_seed = self.seeds[self.seed_index]
def use_tool(self):
pass
def use_seed(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
#按下左边的shift键更换工具
if keys[pygame.K_LSHIFT] and not self.timers['tool switch'].active:
self.timers['tool switch'].activate()
self.tool_index += 1
self.tool_index = self.tool_index if self.tool_index < len(self.tools) else 0
self.selected_tool = self.tools[self.tool_index]
#按下左边的ctrl键,使用种子
if keys[pygame.K_RCTRL]:
self.timers['seed use'].activate()
self.direction = pygame.math.Vector2()
self.frame_index = 0
#按下右边的shift键,切换种子
if keys[pygame.K_RSHIFT] and not self.timers['seed switch'].active:
self.timers['seed switch'].activate()
self.seed_index += 1
self.seed_index = self.seed_index if self.seed_index < len(self.seeds) else 0
self.selected_seed = self.seeds[self.seed_index]
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)