2017-06-15 2 views
0

J'ai les débuts d'un jeu développé en pygame avec python3, suivant les tutoriels sur www.teachyourselfpython.com Il y a un main.py, player.py et walls.py (avec respectivement classe joueur et murs). La classe de joueur contient le code pour la détection et le mouvement de collision. Malheureusement, le joueur évite la tuile, mais lors d'une collision, il se place sur le côté droit de l'écran, au lieu du mouvement attendu (il s'arrête juste). Est-ce que n'importe qui peut aider avec la logique de ceci et corriger le mouvement erroné/indésirable sur la collision. Ci-dessous, sont les trois fichiers: main.py, player.py et walls.py. MAIN.PYpygame inexact détection de collision et de mouvement

#main.py 
import pygame 
import random 
from player import Player 
from collectable import Collectable 
from walls import Wall 

pygame.init() 
BLACK=(0,0,0) 
WHITE=(255,255,255) 
RED=(255,0,0) 
GREEN =(0,255,0) 

BLUE=(0,0,255) 
GOLD=(255,215,0) 
WIDTH=500 
HEIGHT=500 
size= (WIDTH,HEIGHT) 
screen=pygame.display.set_mode(size) 
pygame.display.set_caption("The Life Game") 


done = False 
clock=pygame.time.Clock() 
wall_list=pygame.sprite.Group() 
all_sprites = pygame.sprite.Group() 
enemy_list = pygame.sprite.Group() 
player=Player() 
player.walls=wall_list 

all_sprites.add(player) 


for i in range(random.randrange(100,200)): 
     whiteStar = Collectable(WHITE, 3, 3, "White Star", "Rect") 
     whiteStar.rect.x = random.randrange(size[0]) 
     whiteStar.rect.y = random.randrange(size[1]) 
     all_sprites.add(whiteStar) 

for i in range(50): 

    enemy = Collectable(RED,6, 6,"Enemy","Ellipse") 
    enemy.rect.x = random.randrange(300) 
    enemy.rect.y = random.randrange(300) 
    enemy_list.add(enemy) 
    all_sprites.add(enemy) 

coin1 = Collectable(GOLD,50,50,"Coin","Ellipse") 
coin1.rect.x=440 
coin1.rect.y=0 
all_sprites.add(coin1) 

coin2 = Collectable(GOLD,50,50,"Coin","Ellipse") 
coin2.rect.x=0 
coin2.rect.y=440 
all_sprites.add(coin2) 

enemy = Collectable(RED,100,100,"Enemy","Ellipse") 
enemy.rect.x=70 
enemy.rect.y=230 
all_sprites.add(enemy) 

#Make the walls (x_pos,y_pos, width, height,colour) 



wall=Wall(0,0,10,600,GREEN) 
wall_list.add(wall) 
all_sprites.add(wall_list) 

wall = Wall(50, 300, 400, 10,RED) 
wall_list.add(wall) 
all_sprites.add(wall_list) 

wall = Wall(10, 200, 100, 10,BLUE) 
wall_list.add(wall) 
all_sprites.add(wall_list) 


score=0 
health=100 

#- - - - - - - - - - - - - -Main Program Loop - - - - - - - - - - - - - - - - 
def main(): 
     done=False 
     score=0 
     health=100 
     while not done: 


        #- - - - - - Main event loop (this is where code for handling keyboard and mouse clicks will go) 
     #Loop until the user clicks the 'x' button (to close program) 
        for event in pygame.event.get(): #User does something 
          if event.type == pygame.QUIT: #If the user clicked close 
             done = True #set the done flag to 'true' to exit the loop 

        keys = pygame.key.get_pressed() #checking pressed keys 
        if keys[pygame.K_LEFT]: 
        player.moveLeft(5) 
        if keys[pygame.K_RIGHT]: 
        player.moveRight(5) 
        if keys[pygame.K_UP]: 
        player.moveUp(5) 
        if keys[pygame.K_DOWN]: 
        player.moveDown(5)     


       #>>----------DRAW SECTION ----------------------------------- 
        #Clear the screen to BLACK. Any drawing commands should be put BELOW this or they will be reased with this command 
        screen.fill(BLACK) 



        #Select the font to be used (size, bold, italics, etc) 
        font_score = pygame.font.SysFont('Calibri',20,True,False) 
        font_health = pygame.font.SysFont('Calibri',20,True,False) 
       #Printing a variable (score or health) to the screen involves converting the score (if integer) to a string first.score_label = font_score.render("Score: " + str(score),True,BLACK) 
        health_label = font_health.render("Health: "+str(health),True,WHITE) 
        score_label = font_score.render("Score: " + str(score),True, WHITE) 
       #Now we can use this line of code to put the image of the text on the screen at a given position 
        screen.blit(score_label,[100,480]) 
        screen.blit(health_label,[190,480]) 


        #>>---------UPDATE SECTION/Put the logic of your game here (i.e. how objects move, when to fire them, etc) 


        all_sprites.update() 

        if coin1.collision_with(player): 
         score=score+1 
         coin1.kill() 
         coin1.rect.x=-20 
         coin1.rect.y=-330 

        if coin2.collision_with(player): 
         score=score+1 
         coin2.kill() 
         coin2.rect.x=-20 
         coin2.rect.y=-330 

        if enemy.collision_with(player): 
         health=health-25 
         enemy.kill() 
         enemy.rect.x=-20 
         enemy.rect.y=-330 

        enemy.update() 





     #-------------PRINTING VARIABLES LIKE SCORE TO SCREEN 
        #Any drawing/graphics code should go here 
        all_sprites.draw(screen) 

        #Update the screen to show whatever you have drawn 
        pygame.display.flip() 

        #Set the frames per second (e.g. 30, 60 etc) 
        clock.tick(120) 

main() 

PLAYER.PY

import pygame 
import random 
from walls import Wall 

class Player(pygame.sprite.Sprite): 
    #-------------------Define Variables here 
    speed=0 
    #------------------Initialise Constructor 
    def __init__(self): 
     pygame.sprite.Sprite.__init__(self) 
     self.image=pygame.image.load("player.png") 
     self.rect = self.image.get_rect() 

     #SET THE INITIAL SPEED TO ZERO 
     self.change_x = 0 
     self.change_y = 0 

     #--------------Fetch the rectangle object that has the dimensions of the image 
     self.rect =self.image.get_rect() 
     #---------------Define movement 
    def moveRight(self,pixels): 
     self.rect.x+=pixels 
    def moveLeft(self,pixels): 
     self.rect.x-=pixels 
    def moveUp(self,pixels): 
     self.rect.y-=pixels 
    def moveDown(self,pixels): 
     self.rect.y+=pixels 

    # Make our top-left corner the passed-in location. 
    def settopleft(): 
     self.rect = self.image.get_rect() 
     self.rect.y = y 
     self.rect.x = x 

     # Set speed vector 
     self.change_x = 0 
     self.change_y = 0 
     self.walls = None 

    def changespeed(self, x, y): 
     """ Change the speed of the player. """ 
     self.change_x += x 
     self.change_y += y 


    def update(self): 
     # Did this update cause us to hit a wall? 
     block_hit_list = pygame.sprite.spritecollide(self, self.walls, False) 
     for block in block_hit_list: 
      # If we are moving right, set our right side to the left side of 
      # the item we hit 
      if self.change_x > 0: 
       self.rect.right = block.rect.left 
      else: 
       # Otherwise if we are moving left, do the opposite. 
       self.rect.left = block.rect.right 

     # Move up/down 
       self.rect.y += self.change_y 

     # Check and see if we hit anything 
     block_hit_list = pygame.sprite.spritecollide(self, self.walls, False) 
     for block in block_hit_list: 

      # Reset our position based on the top/bottom of the object. 
      if self.change_y > 0: 
       self.rect.top = block.rect.top 
      else: 
       self.rect.top = block.rect.bottom 

WALLS.PY

import pygame 

class Wall(pygame.sprite.Sprite): 
    #Wall a player can run into 
    def __init__(self, x, y, width, height,colour): 
     #Constructor fo rthe wall that the player can run into 
     #call the parent's constructor 
     super().__init__() 

     #Make a green wall, of the size specified in paramenters 
     self.image=pygame.Surface([width,height]) 
     self.image.fill(colour) 

     #Make the "passed-in" location ,the top left corner 
     self.rect=self.image.get_rect() 
     self.rect.y=y 
     self.rect.x=x 

Attaché, zone également des images pour le joueur et bg: playerbackground image

+2

S'il vous plaît modifier votre message et fournir un [exemple minimal, complet et vérifiable] (https://stackoverflow.com/help/mcve) que nous pouvons exécuter et tester, sinon, il peut être très difficile ou même impossible de vous aider. – skrx

+0

@slrx merci pour votre suggestion. J'ai pensé que ce serait peut-être une simple erreur de logique dans le fichier du joueur. Suite à vos conseils, j'ai maintenant ajouté les trois dossiers et j'ai précisé ma question. Merci énormément à l'avance – MissComputing

+1

Ce n'est toujours pas * un exemple * minimal. Il y a beaucoup à supprimer, essayez de nous donner un bref résumé du code. Mettez tout dans un fichier au lieu de trois, analysez tous les ennemis inutiles, etc. Essayez de vous retrouver avec un simple "voici un joueur, voici un mur, si je déplace un joueur comme ça, pourquoi ne pas entrer en collision?" au lieu de nous donner tout votre code. Non seulement est-il plus facile pour nous de lire (qui lit 500 lignes?) Afin que vous obteniez plus d'aide, mais vous finissez souvent par réaliser l'erreur en analysant les choses supplémentaires. –

Répondre

0

Bon, parlons d'abord du problème du mouvement. Le code de la méthode de mise à jour Player doit d'abord déplacer le joueur le long de l'axe des X, vérifier s'il entre en collision avec un mur et s'il entre en collision, positionner sa position sur le bord du bloc. Ensuite, vous faites la même chose avec l'axe des y. Cela doit être fait de cette manière, car nous ne connaîtrions pas la direction du joueur et ne pourrions pas réinitialiser sa position au bon bord. Vous avez modifié le code pour que les attributs self.change_x et change_y ne soient plus utilisés et déplacez le joueur avec pygame.key.get_pressed à la place. Je supprimerais le bloc pygame.key.get_pressed et ferais les changements de mouvement dans la boucle d'événement. Quelques choses dans la méthode update devaient fixer ainsi, par exemple .:

if self.change_y > 0: 
    self.rect.bottom = block.rect.top 

Pour la détection de collision, vous pouvez utiliser pygame.sprite.spritecollide et passer le joueur et le groupe de sprite que vous voulez vérifier. Puis parcourez la liste retournée et faites quelque chose pour chaque sprite en collision.

Voici votre code mis à jour:

import sys 
import random 
import pygame as pg 


pg.init() 


class Wall(pg.sprite.Sprite): 
    """Wall a player can run into.""" 
    def __init__(self, x, y, width, height, colour): 
     super().__init__() 
     self.image = pg.Surface([width, height]) 
     self.image.fill(colour) 
     # Make the "passed-in" location the top left corner. 
     self.rect = self.image.get_rect(topleft=(x, y)) 


class Collectable(pg.sprite.Sprite): 
    """A collectable item.""" 

    def __init__(self, colour, x, y, image, rect): 
     super().__init__() 
     self.image = pg.Surface((5, 5)) 
     self.image.fill(colour) 
     self.rect = self.image.get_rect(topleft=(x, y)) 


class Player(pg.sprite.Sprite): 

    def __init__(self): 
     pg.sprite.Sprite.__init__(self) 
     self.image = pg.Surface((30, 30)) 
     self.image.fill((50, 150, 250)) 
     self.rect = self.image.get_rect() 
     #SET THE INITIAL SPEED TO ZERO 
     self.change_x = 0 
     self.change_y = 0 
     self.health = 100 

    def update(self): 
     # Move left/right. 
     self.rect.x += self.change_x 
     # Did this update cause us to hit a wall? 
     block_hit_list = pg.sprite.spritecollide(self, self.walls, False) 
     for block in block_hit_list: 
      # If we are moving right, set our right side to the left side of 
      # the item we hit 
      if self.change_x > 0: 
       self.rect.right = block.rect.left 
      else: 
       # Otherwise if we are moving left, do the opposite. 
       self.rect.left = block.rect.right 

     # Move up/down. 
     self.rect.y += self.change_y 
     # Check and see if we hit anything. 
     block_hit_list = pg.sprite.spritecollide(self, self.walls, False) 
     for block in block_hit_list: 
      # Reset our position based on the top/bottom of the object. 
      if self.change_y > 0: 
       self.rect.bottom = block.rect.top 
      else: 
       self.rect.top = block.rect.bottom 


BLACK = (0,0,0) 
WHITE = (255,255,255) 
GREEN = (0,255,0) 
RED = (255,0,0) 
BLUE = (0,0,255) 
GOLD = (255,215,0) 

size = (500, 500) 
screen = pg.display.set_mode(size) 
pg.display.set_caption("The Life Game") 

wall_list = pg.sprite.Group() 
all_sprites = pg.sprite.Group() 
enemy_list = pg.sprite.Group() 
coins = pg.sprite.Group() 

player = Player() 
player.walls = wall_list 
all_sprites.add(player) 

for i in range(random.randrange(100,200)): 
    x = random.randrange(size[0]) 
    y = random.randrange(size[1]) 
    whiteStar = Collectable(WHITE, x, y, "White Star", "Rect") 
    all_sprites.add(whiteStar) 

for i in range(50): 
    x = random.randrange(size[0]) 
    y = random.randrange(size[1]) 
    enemy = Collectable(RED, x, y, "Enemy","Ellipse") 
    enemy_list.add(enemy) 
    all_sprites.add(enemy) 

coin1 = Collectable(GOLD,240,200,"Coin","Ellipse") 
coin2 = Collectable(GOLD,100,340,"Coin","Ellipse") 
all_sprites.add(coin1, coin2) 
coins.add(coin1, coin2) 

# Make the walls. 
walls = [Wall(0,0,10,600,GREEN), Wall(50, 300, 400, 10,RED), 
     Wall(10, 200, 100, 10,BLUE)] 
wall_list.add(walls) 
all_sprites.add(walls) 


def main(): 
    clock = pg.time.Clock() 
    done = False 
    score = 0 
    font_score = pg.font.SysFont('Calibri',20,True,False) 
    font_health = pg.font.SysFont('Calibri',20,True,False) 
    while not done: 
     for event in pg.event.get(): 
      if event.type == pg.QUIT: 
       done = True 
      # Do the movement in the event loop by setting 
      # the player's change_x and y attributes. 
      elif event.type == pg.KEYDOWN: 
       if event.key == pg.K_LEFT: 
        player.change_x = -3 
       elif event.key == pg.K_RIGHT: 
        player.change_x = 3 
       elif event.key == pg.K_UP: 
        player.change_y = -3 
       elif event.key == pg.K_DOWN: 
        player.change_y = 3 
      elif event.type == pg.KEYUP: 
       if event.key == pg.K_LEFT and player.change_x < 0: 
        player.change_x = 0 
       elif event.key == pg.K_RIGHT and player.change_x > 0: 
        player.change_x = 0 
       elif event.key == pg.K_UP and player.change_y < 0: 
        player.change_y = 0 
       elif event.key == pg.K_DOWN and player.change_y > 0: 
        player.change_y = 0 

     # UPDATE SECTION/Put the logic of your game here (i.e. how 
     # objects move, when to fire them, etc). 
     all_sprites.update() 

     # spritecollide returns a list of the collided sprites in the 
     # passed group. Iterate over this list to do something per 
     # collided sprite. Set dokill argument to True to kill the sprite. 
     collided_enemies = pg.sprite.spritecollide(player, enemy_list, True) 
     for enemy in collided_enemies: 
      player.health -= 25 

     collided_coins = pg.sprite.spritecollide(player, coins, True) 
     for coin in collided_coins: 
      score += 1 

     # DRAW SECTION 
     screen.fill(BLACK) 
     all_sprites.draw(screen) 

     health_label = font_health.render("Health: "+str(player.health),True,WHITE) 
     score_label = font_score.render("Score: " + str(score),True, WHITE) 
     screen.blit(score_label,[100,480]) 
     screen.blit(health_label,[190,480]) 

     pg.display.flip() 
     clock.tick(60) 

if __name__ == '__main__': 
    main() 
    pg.quit() 
    sys.exit() 
+0

Ceci est très utile - merci! Très appréciée. Seriez-vous capable d'expliquer ce que fait cette ligne de code: si self.change_y> 0: self.rect.bottom = block.rect.top (Je ne comprends pas le self.rect.bottom = block.rect.top) bit. – MissComputing

+0

Les 'self.rect' et' block' sont tous deux '' pygame.Rect'' (http://www.pygame.org/docs/ref/rect.html) s. Ces rects ont beaucoup d'attributs virtuels comme 'topleft',' center', 'top' et' bottom' qui peuvent être utilisés pour changer la position du rect. Je pense que 'self.rect.bottom = block.rect.top' est en fait assez explicite, car il définit la position du bas (c'est la même chose que' self.rect.y + self.rect.height') du rect à la position supérieure (qui est la même que 'block.y') du bloc. – skrx

+0

Donc, vous pouvez aussi écrire de cette façon: 'self.rect.y = block.rect.y - self.rect.height', mais ce n'est pas mieux de lire imo. – skrx