我试图创建一个动画,显示一个框从屏幕边缘反弹。并且,我试图使用基于时间的动画和脏矩形来完成此任务。
我能够为盒子制作动画;然而,动画非常不稳定。这是两个视频,说明了我在谈论的内容:
30 FPS:https://www.youtube.com/watch?v=0de8ENxn7GQ
60 FPS:https://www.youtube.com/watch?v=b5sXgeOlgHU
这是我的代码:
import sys
import random
import pygame
class Box:
def __init__(self, x, y):
self.width = 38
self.height = 38
self.color = (255, 0, 0)
self.x = x
self.y = y
self.old_x = x
self.old_y = y
self.d_x = 1
self.d_y = -1
self.px_per_second = 200
def move(self):
self.old_x = self.x
self.old_y = self.y
if self.x <= 0:
self.d_x *= -1
if self.x + self.width >= task.width:
self.d_x *= -1
if self.y <= 0:
self.d_y *= -1
if self.y + self.height >= task.height:
self.d_y *= -1
self.x += ((self.px_per_second*self.d_x)*
(task.ms_from_last_frame/1000.0))
self.y += ((self.px_per_second*self.d_y)*
(task.ms_from_last_frame/1000.0))
def draw(self):
self.x_i = int(self.x)
self.y_i = int(self.y)
self.old_x_i = int(self.old_x)
self.old_y_i = int(self.old_y)
_old_rect = (pygame.Rect(self.old_x_i, self.old_y_i,
self.width, self.height))
_new_rect = (pygame.Rect(self.x_i, self.y_i, self.width, self.height))
if _old_rect.colliderect(_new_rect):
task.dirty_rects.append(_old_rect.union(_new_rect))
else:
task.dirty_rects.append(_old_rect)
task.dirty_rects.append(_new_rect)
pygame.draw.rect(task.screen, task.bg_color, _old_rect)
pygame.draw.rect(task.screen, (self.color), _new_rect)
class ObjectTask:
def __init__(self, width, height):
pygame.init()
self.max_fps = 60
self.clock = pygame.time.Clock()
self.width = width
self.height = height
self.bg_color = (255, 255, 255)
self.dirty_rects = []
self.screen = pygame.display.set_mode((self.width, self.height),
pygame.FULLSCREEN)
self.screen.fill(self.bg_color)
pygame.display.update()
def animation_loop(self):
self.box1 = Box(self.width/2, self.height/2)
while 1:
self.ms_from_last_frame = self.clock.tick(self.max_fps)
self.box1.move()
self.box1.draw()
pygame.display.update(self.dirty_rects)
self.dirty_rects = []
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
if __name__ == "__main__":
task = ObjectTask(726, 546)
task.animation_loop()
我能做些什么来减少波动吗?另外,我是Pygame的新手,所以如果你看到我做错了/效率不高的任何事情,请告诉我。
我在64位Windows 7,i5-6300u机器上运行动画,内存为12 GB。我使用的是Python 2.7.12和Pygame 1.9.2。
提前致谢!
答案 0 :(得分:1)
我有一段时间,并认为我可能会展示我将如何编写此程序。我试图解释评论中的所有内容。我删除了你的ObjectTask
课程,因为一切都可以放在一个函数中,而且函数很整齐(在我看来,它们更快,更容易阅读/调试)。
我也更改了它,因此您将变量传递给方法而不是从全局变量(task
对象)中读取它,因为它使程序更加模块化,更容易更改/重构。
最后,我将一些属性合并为一个。 x
和y
已关联,因此我将它们放入一个公共属性position
。
import pygame
pygame.init()
# It's convenient if all your visible objects inherit 'pygame.sprite.Sprite'. It allows you to use sprite groups and
# some useful collision detection functions.
class Box(pygame.sprite.Sprite):
def __init__(self, x, y, screen_width, screen_height):
super(Box, self).__init__() # Initialize the 'pygame.sprite.Sprite' (the super class).
self.size = (38, 38)
self.color = (255, 0, 0)
self.position = pygame.math.Vector2(x, y) # Vector is a 2-element tuple with some neat methods shown later.
self.velocity = pygame.math.Vector2(200, -200)
self.screen_size = (screen_width, screen_height)
# Instead of drawing the rectangle onto the screen we're creating an image which we 'blit' (put) onto the screen
self.image = pygame.Surface(self.size)
self.image.fill(self.color)
# We create a rectangle same size as our image and where the top left of the rectangle is positioned at our
# 'position'.
self.rect = self.image.get_rect(topleft=self.position)
# Changing name to update. Game objects usually have two methods 'update' and 'draw', where 'update' is the function
# that will be called every game loop and update the object, and 'draw' draws the game object every game loop.
def update(self, dt): # 'dt' (delta time) is what you formerly called task.ms_from_last_frame.
# unpack the variables so it's easier to read. Not necessary, but nice.
width, height = self.size
x, y = self.position
screen_width, screen_height = self.screen_size
if x <= 0 or x + width >= screen_width: # Only one if-statement needed.
self.velocity[0] *= -1 # First element is the x-velocity.
if y <= 0 or y + height >= screen_height:
self.velocity[1] *= -1 # Second element is y-velocity.
# Addition and subtraction works element-wise with vectors. Adding our two vectors 'position' and 'velocity':
# position += velocity <=> position = position + velocity
# is the same as this:
# position_x, position_y = position_x + velocity_x, position_y + position_y
#
# Multiplication and division works on all elements. So 'velocity * dt / 1000' is the same as:
# velocity_x * dt / 1000, velocity_y * dt / 1000
self.position += self.velocity * dt / 1000
# Instead of creating a new rect we just move the top left corner of our rect to where we calculated our
# position to be.
self.rect.topleft = self.position
# 'surface' is the Surface object you want to draw on (in this case the screen).
def draw(self, surface):
# This puts our image unto the screen at the given position.
surface.blit(self.image, self.position)
def main():
max_fps = 60
clock = pygame.time.Clock()
bg_color = (255, 255, 255)
width, height = 726, 546
screen = pygame.display.set_mode((width, height))
screen.fill(bg_color)
box1 = Box(width / 2, height / 2, width, height)
sprite_group = pygame.sprite.Group(box1)
while 1:
# First handle time.
dt = clock.tick(max_fps)
# Then handle events.
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit() # 'pygame.quit' does not quit the program! It only uninitialize the pygame modules.
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
quit()
# Update your objects.
sprite_group.update(dt) # Calls the update method on all sprites in the group.
# Draw your objects.
screen.fill(bg_color) # Clear the screen.
sprite_group.draw(screen) # Draws all sprites on the screen using the 'image' and 'rect' attributes.
# Display everything you've drawn.
pygame.display.update()
if __name__ == "__main__":
main()
在这个例子中,我并没有因为跟踪脏的行为而烦恼。我已经体验到导致抖动的原因。我提供的这个例子应该可以正常工作,但总会有一些抖动。如果您想尝试跟踪脏的rects,请更改:
sprite_group = pygame.sprite.Group(box1)
到
sprite_group = pygame.sprite.RenderUpdates(box1)
和
sprite_group.draw(screen) # Draws all sprites on the screen using the 'image' and 'rect' attributes.
# Display everything you've drawn.
pygame.display.update()
到
dirty_rects = sprite_group.draw(screen) # Draws all sprites on the screen using the 'image' and 'rect' attributes.
# Display everything you've drawn.
pygame.display.update(dirty_rects) # Update only the areas that have changes