2017-09-18 8 views
1

Cela fait des choses qui me trottent dans la tête depuis des jours. J'essaie de créer un jeu avec un joueur fixé au milieu, et un fond basé sur les tuiles qui bouge fluide derrière le joueur et génère des images de tuiles supplémentaires que le joueur se déplace. L'arrière-plan fonctionne parfaitement jusqu'à présent.Python (Pygame) - Instances d'objets se déplaçant à une vitesse différente de l'arrière-plan

Le problème se pose lorsque j'essaie d'ajouter des objets à l'arrière-plan (par exemple, un arbre). Je souhaite que cet objet reste fixé sur un certain emplacement d'arrière-plan, mais au fur et à mesure que le joueur bouge, l'objet se déplace progressivement par rapport à l'arrière-plan. C'est-à-dire que l'objet bougera au fur et à mesure que le joueur bouge (comme il se doit), mais il bougera beaucoup trop.

J'ai isolé la cause à la méthode World.update, la section self.tileShift. Si vous supprimez toute la section if/elif, le comportement semble bon (à l'exception bien sûr, l'arrière-plan ne se met pas à jour correctement, nous avons donc besoin de cette section). D'une façon ou d'une autre, lorsque le tileShift saute, tous les objets sur l'écran sautent aussi de quelques pixels. Après avoir passé plus d'une semaine de recherche et d'expérimentation, je n'arrive toujours pas à le réparer. J'ai essayé de changer l'image, en changeant le point de coupure de tileShift, en changeant l'ordre des méthodes de mise à jour d'appel, etc.

Reportez-vous à (massivement dépouillé de l'original, mais autonome) le code ci-dessous. Notez que nous avons besoin d'une image de mosaïque d'arrière-plan avec des motifs (voir la classe mondiale init), sinon vous ne pourrez pas voir le comportement que je décris, car le mouvement erroné est si progressif. Pour de meilleures performances, utilisez une image png 70x70.

Espérons que cette explication a du sens, s'il vous plaît laissez-moi savoir si des informations supplémentaires sont nécessaires. Toute aide serait grandement appréciée !!

import pygame 

# Initialisation 
SCREEN_WIDTH, SCREEN_HEIGHT = 800, 850 
pygame.init() # Initialise pygame 
window = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) # Set the window surface (the main screen of the game) 
pygame.display.set_caption("Game") # Game window caption 
window_rect = window.get_rect() 

# GLOBAL VARIABLES 
WorldMove = dict(x=0, y=0) # List for the direction and amount the background moves as the player moves 
PlayerMoveSpeed = 5 

# CLASSES 
class World(pygame.sprite.Sprite): 
    def __init__(self): 
     self.tileShift = dict(x=0, y=0) # Tracker for when the player has passed from one map tile to another 

     # Load and set up the tile images 
     self.tiles = pygame.image.load("Images\Tiles\Tile.png") 
     self.tileSize = self.tiles.get_rect().size[0] # Assumes a square size 
     self.map = pygame.Surface((self.tileSize*14,self.tileSize*15)) # Visible area surface size, based on tile size 
     self.rect = self.map.get_rect() # Create a rect attribute for the World, so it can be blitted to the window 
     self.rect.x, self.rect.y = -self.tileSize,-self.tileSize # The map is blitted starting one tile size above left of the screen (so we see no whitespace) 

    def update(self): 
     # Move the map around the player based on WorldMove, which is set in the player_move function 
     self.rect.x += WorldMove["x"] 
     self.rect.y += WorldMove["y"] 

     # Update the tileShift based on player movement so we know when they've moved onto another tile 
     if WorldMove["x"] != 0: self.tileShift["x"] -= WorldMove["x"] 
     if WorldMove["y"] != 0: self.tileShift["y"] -= WorldMove["y"] 

     # Once the self.tileShift has passed the size of one of the tile images, reset it and move self.matrix by 1 
     if self.tileShift["x"] < -self.tileSize: 
      self.tileShift["x"] = 0 # Reset the tileShift variable 
      self.rect.x = -self.tileSize # Reset the point from which the map is blitted to window to top left of visible screen 
     elif self.tileShift["x"] > self.tileSize: 
      self.tileShift["x"] = 0 
      self.rect.x = -self.tileSize 
     if self.tileShift["y"] > self.tileSize: 
      self.tileShift["y"] = 0 
      self.rect.y = -self.tileSize 
     elif self.tileShift["y"] < -self.tileSize: 
      self.tileShift["y"] = 0 
      self.rect.y = -self.tileSize 

    def draw(self): 
     # Draw the tiles in a grid in the visible area 
     for y in range(15): # Visible number of tiles on y axis 
      for x in range(14): # Visible number of tiles on x axis 
       self.map.blit(self.tiles, (self.tileSize*x, self.tileSize*y)) # Blit each tile onto self.map 
     window.blit(self.map, self.rect) # Blit self.map onto the window 

class Player(pygame.sprite.Sprite): 
    def __init__(self): 
     self.image = pygame.Surface((35, 35)) 
     self.image.fill((0, 0, 0)) 
     self.rect = (SCREEN_WIDTH/2, SCREEN_HEIGHT/2) # Player is always in middle of screen 

    def player_move(self): 
     global WorldMove # Make sure that we're referring to and updating the global variable for the world movement 

     key = pygame.key.get_pressed() 
     x, y = 0, 0 

     if key[pygame.K_w] or key[pygame.K_UP]: y = PlayerMoveSpeed 
     if key[pygame.K_d] or key[pygame.K_RIGHT]: x = -PlayerMoveSpeed 
     if key[pygame.K_a] or key[pygame.K_LEFT]: x = PlayerMoveSpeed 
     if key[pygame.K_s] or key[pygame.K_DOWN]: y = -PlayerMoveSpeed 

     if x != 0 and y != 0: # If more than one key pressed, move diagonally 
      WorldMove["x"] = int(x/1.5) 
      WorldMove["y"] = int(y/1.5) 
     else: 
      WorldMove["x"] = x 
      WorldMove["y"] = y 

    def draw(self): 
     window.blit(self.image, self.rect) 

class Object(pygame.sprite.Sprite): 

    def __init__(self): 
     self.image = pygame.Surface((50,100)) 
     self.image.fill((50,50,250)) 
     self.rect = self.image.get_rect() 
     self.rect.center = window_rect.center 
     self.rect.x, self.rect.y = 100, 100 # Spawn location of the object 

    def update(self): # Move the object as the world moves around the player 
     self.rect.x += WorldMove["x"] 
     self.rect.y += WorldMove["y"] 

    def draw(self): # Blit the object onto the screen at its location 
     window.blit(self.image, self.rect) 

# Set Objects 
world = World() 
object = Object() 
player = Player() 

# Main Loop 
gameRunning = True 

while gameRunning: 

    # Player events 
    for event in pygame.event.get(): 

     if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE): 
      gameRunning = False 

     player.player_move() # Move player based on movement key(s) pressed (if any) 

    # Class updates and drawing to the screen 
    window.fill((255, 255, 255)) # Fill the window with background white 
    world.update() 
    world.draw() 
    object.update() 
    object.draw() 
    player.draw() 

    pygame.display.update() # Refresh the display 

# End - only reaches this point if gameRunning = False 
pygame.quit() 
+0

Dans le cas où il vous aide à faire plus de recherche, c'est appelé * parallax * défilement. – jwg

Répondre

0

Au lieu de mettre l'self.rect de l'instance World--self.tileSize, l'intérieur ou à décrémenter les attributs rect et l'self.tileShift par le self.tileSize:

if self.tileShift["x"] < -self.tileSize: 
    self.tileShift["x"] += self.tileSize 
    self.rect.x -= self.tileSize 
elif self.tileShift["x"] > self.tileSize: 
    self.tileShift["x"] -= self.tileSize 
    self.rect.x += self.tileSize 
if self.tileShift["y"] > self.tileSize: 
    self.tileShift["y"] -= self.tileSize 
    self.rect.y += self.tileSize 
elif self.tileShift["y"] < -self.tileSize: 
    self.tileShift["y"] += self.tileSize 
    self.rect.y -= self.tileSize 
+0

Ça l'a fait! Merci beaucoup – Matthew