这是因为您的变量nextkey
是不同变量的总和,每个变量都会包含一个小错误。所以你总是在添加小错误,直到它最终变得明显。
防止累积错误的方法是始终直接从源头进行计算。在你的情况下pygame.mixer.music.get_pos()
。
在下面的示例中,我们首先创建一个变量ms_per_beat
。这个值是一个常数,它定义了每个节拍应该经过多少毫秒。对于每一帧,我们time_since_last_beat
根据歌曲播放的时间进行计算。一个粗略的、不完整的例子:
bpm = 120
beats_per_ms = (bpm / 60) / 1000
ms_per_beat = 1 / beats_per_ms # How many milliseconds each beat takes.
current_beat = 0 # The beat we're currently on.
time_since_last_beat = 0 # How many milliseconds since last beat.
dot_display_time = 500 # Display the dot for 500 ms (half a second)
dot_timer = 0 # Keeps track on how long the dot has been displayed.
display_dot = False # Whether to display the dot or not.
clock = pygame.time.Clock()
while running:
dt = clock.tick(60)
current_play_time_ms = pygame.mixer.music.get_pos() / 1000
time_since_last_beat = current_play_time_ms - (current_beat * ms_per_beat)
if time_since_last_beat >= ms_per_beat:
print('Bop!')
current_beat += 1
display_dot = True
dot_timer = dot_display_time
screen.fill((0, 0, 0))
if display_dot:
dot_timer -= dt
if dot_timer <= 0:
display_dot = False
pygame.draw.circle(screen, colors[i], (500, 500), 20)
pygame.display.update()
上面的示例假设音频正好在节拍上开始。它还假设它实际上是 120 bpm。如果是 120.1 bpm,它最终会不同步。更合适的方法是分析音频的振幅峰值,然后只显示该点。如果您的音频只是节拍器,则可以使用pygame.mixer.music.get_volume()
.
dot_display_time = 500 # Display the dot for 500 ms (half a second)
dot_timer = 0 # Keeps track on how long the dot has been displayed.
display_dot = False # Whether to display the dot or not.
beat_volume_threshold = 0.7 # The volume the audio has to overcome to count as a beat.
clock = pygame.time.Clock()
while running:
dt = clock.tick(60)
if pygame.mixer.music.get_volume() >= beat_volume_threshold:
print('Bop!')
display_dot = True
dot_timer = dot_display_time
screen.fill((0, 0, 0))
if display_dot:
dot_timer -= dt
if dot_timer <= 0:
display_dot = False
pygame.draw.circle(screen, colors[i], (500, 500), 20)
pygame.display.update()
但是,我会推荐一种完全不同的方法。同步总是很麻烦,所以尽量避免它。与其让您的游戏与节拍器同步,不如让您的游戏成为节拍器。每当经过特定时间时,播放一个“bop”音频。然后您的音频和图形将始终同步,因为两者都使用相同的时钟。下面的示例播放音频,在pygame.time.set_timer
.
import pygame
# This fixes the latency issue with the pygame mixer.
pygame.mixer.pre_init(22050, -16, 2, 1024)
pygame.init()
PLAY_CLICK = pygame.USEREVENT + 1
screen = pygame.display.set_mode((400, 400))
dot_display_time = 200 # Display the dot for 500 ms (half a second)
dot_timer = 0 # Keeps track on how long the dot has been displayed.
display_dot = False # Whether to display the dot or not.
bpm = 120
beats_per_ms = (bpm / 60) / 1000
ms_per_beat = 1 / beats_per_ms # How many milliseconds each beat takes.
clock = pygame.time.Clock()
sound = pygame.mixer.Sound('bop.wav')
pygame.time.set_timer(PLAY_CLICK, int(ms_per_beat)) # Play sound repeatedly every 'beats_per_ms'.
running = True
while running:
dt = clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == PLAY_CLICK:
sound.play()
display_dot = True
dot_timer = dot_display_time
screen.fill((0, 0, 0))
if display_dot:
dot_timer -= dt
if dot_timer <= 0:
display_dot = False
pygame.draw.circle(screen, pygame.Color('green'), (200, 200), 20)
pygame.display.update()
如果您对安排活动不满意,您可以自己计算时间。
import pygame
# This fixes the latency issue with the pygame mixer.
pygame.mixer.pre_init(22050, -16, 2, 1024)
pygame.init()
font = pygame.font.Font('freesansbold.ttf', 15)
screen = pygame.display.set_mode((400, 400))
dot_display_time = 250 # Display the dot for 250 ms (quarter of a second)
dot_timer = 0 # Keeps track on how long the dot has been displayed.
display_dot = False # Whether to display the dot or not.
bpm = 120
clock = pygame.time.Clock()
sound = pygame.mixer.Sound('bop.wav')
time = 0
running = True
while running:
dt = clock.tick(60)
time += dt
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
bpm -= 5
elif event.key == pygame.K_RIGHT:
bpm += 5
# Calculate how long to wait based on the bpm.
beats_per_ms = (bpm / 60) / 1000
ms_per_beat = 1 / beats_per_ms # How many milliseconds each beat takes.
if time >= ms_per_beat:
time -= ms_per_beat
sound.play()
display_dot = True
dot_timer = dot_display_time
screen.fill((0, 0, 0))
screen.blit(font.render('BPM {}'.format(bpm), True, (255, 255, 255), (0, 0, 0)), (160, 20))
if display_dot:
dot_timer -= dt
if dot_timer <= 0:
display_dot = False
pygame.draw.circle(screen, pygame.Color('green'), (200, 200), 20)
pygame.display.update()
但是请注意,在特定时间播放声音时,pygame 似乎有困难。如果减少缓冲区(如文档中所述),效果会更好。
pygame.mixer.pre_init(22050, -16, 2, 1024)
pygame.init()