pygame不准确的碰撞检测和移动

时间:2017-06-15 19:18:48

标签: python pygame game-physics collision

我使用python3在pygame中开发游戏的开始,遵循www.teachyourselfpython.com上的教程。有一个main.py,player.py和walls.py(分别包含播放器和墙类)。播放器类包含用于碰撞检测和移动的代码。不幸的是,玩家确实避开了瓷砖,但是在碰撞时,移动到屏幕右侧的位置,而不是预期的移动(只是停止)。是否有人能够帮助解决这个问题的逻辑并纠正碰撞时的错误/不良运动。下面是三个文件:main.py,player.py和walls.py。 的 MAIN.PY

#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

附上,区域也是玩家和bg的图像:player background image

1 个答案:

答案 0 :(得分:0)

好的,让我们首先谈谈运动问题。 Player更新方法中的代码应首先沿x轴移动玩家,检查他是否与墙碰撞,如果他发生碰撞,将其位置设置为块边缘。然后你用y轴做同样的事情。必须以这种方式完成,因为我们不会知道玩家的方向,也不能将他的位置重置到正确的边缘。您似乎更改了代码,以便不再使用self.change_xchange_y属性,而是使用pygame.key.get_pressed移动播放器。我删除pygame.key.get_pressed块并在事件循环中执行移动更改。 update方法中的一些内容也必须修复,例如:

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

对于碰撞检测,您可以使用pygame.sprite.spritecollide并传递要检查的玩家和精灵组。然后迭代返回的列表并为每个碰撞的精灵做一些事情。

这是您的更新代码:

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()