2017-01-13 7 views
0

私は、画面の端からはね返っているボックスを示すアニメーションを作成しようとしています。そして、私は時間ベースのアニメーションと汚れた長方形を使ってこれを達成しようとしています。Pygameの雑然としたアニメーション

私はボックスをアニメーション化できました。しかし、アニメーションはかなり不安定です。

30 FPS:ここで私が話しているものを説明する2本のビデオです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() 

は何も私がありますchoppinessを減らすことができますか?また、私はPygameの初心者ですので、私が間違っている/非効率的に何かを見たら、私に教えてください。

私は、12 GBのRAMを搭載した64ビットのWindows 7、i5-6300uマシンでアニメーションを実行しています。私はPython 2.7.12とPygame 1.9.2を使用しています。

ありがとうございます!

+1

多くの不要な属性。 'self.x_i'、' self.old_x_i'(そしてy's)は代わりにローカル変数です。 'self.d_x'は' 200'と 'self.d_y = -200'となりますので、' self.px_per_second'は必要ありません。 'self.box1'はローカル変数でもあります。簡単にするために、 '_old_rect = pygame.Rect(self.x、self.y、self.height、self.width)'で新しい四角形を作成すると、xとyを暗黙的に整数に変換するので、必要はありません自分でそれをする。あなたのビデオに基づいて、問題はあなたの汚れた矩形にあるようです。私はしばらくの間見ていますが、私はいくつかの簡単なヒントを共有するかもしれないと思った。 –

答えて

1

私は少し時間があり、私はこのプログラムを書く方法を示すかもしれないと思った。私はすべてのことをコメントで説明しようとしました。私はあなたのObjectTaskクラスを削除しました。すべてが単一の関数に収まり、機能はきちんとしています(私の意見では、読み込み/デバッグが簡単です)。

変数をグローバル変数(taskオブジェクト)から読み込むのではなく、メソッドに渡すように変数を渡すように変更しました。プログラムをよりモジュラで簡単に変更/リファクタリングできるようになりました。

最後に、いくつかの属性を1つにまとめました。 xyが接続されているので、共通の属性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 
関連する問題