3 votes

Collision d'un rect sur un écran pygame ; l'appui sur une deuxième touche, après la collision, fait sauter le rect

Je n'arrive pas à faire fonctionner cette collision à 100%. Si je n'appuie que sur une touche à la fois, la collision semble fonctionner correctement, mais si j'appuie sur une touche et que je continue d'appuyer, pendant la collision, puis que j'appuie sur une autre touche, la collision semble prendre en compte les deux touches en même temps. En faisant des recherches, il semble que je doive faire des calculs d'axes séparés, mais je ne sais pas exactement comment le faire, en utilisant l'algorithme que j'ai pour la collision. Je veux que ce soit un style procédural, si possible. Si quelqu'un peut modifier mon code, avec une solution procédurale qui fonctionne, j'apprécierais grandement. Merci de votre compréhension.

import pygame as pg
import sys
from math import fabs

pg.init()

width = 600
height = 600

gameDisplay = pg.display.set_mode((width, height))
pg.display.set_caption('Block')

white = (255, 255, 255)
red = (255, 0, 0)

clock = pg.time.Clock()
closed = False
FPS = 60
Player_Speed = 200
x, y = 270, 0
vx = 0
vy = 0
collision = False

def Collision(hero, enemy):
    global vx, vy, x, y, collision
    deltay = fabs(block.centery - ENEMY.centery)
    deltax = fabs(block.centerx - ENEMY.centerx)
    if deltay < ENEMY.height and deltax < ENEMY.width:
        collision = True
        if vx > 0:
            vx = 0
            x = ENEMY[0] - block[2] 
        if vx < 0:
            vx = 0
            x = ENEMY[0] + 30
        if vy > 0:
            vy = 0
            y = ENEMY[1] - block[3] 
        if vy < 0:
            vy = 0
            y = ENEMY[1] + 30
    else:
        collision = False

def xy_Text(x, y):
    font = pg.font.SysFont("Courier", 16, True)
    text = font.render("X: " + str(round(x)), True, (0,150,0))
    text1 = font.render("Y: " + str(round(y)), True, (0,150,0))
    gameDisplay.blit(text, (0,0))
    gameDisplay.blit(text1, (0,14))

while not closed:
    for event in pg.event.get():
        if event.type == pg.QUIT:
            closed = True

        dt = clock.tick(FPS)/1000
        vx, vy = 0, 0

        keys = pg.key.get_pressed()

        if keys[pg.K_ESCAPE]:
            closed = True
        if keys[pg.K_LEFT] or keys[pg.K_a]:
            vx = -Player_Speed
        if keys[pg.K_RIGHT] or keys[pg.K_d]:
            vx = Player_Speed
        if keys[pg.K_UP] or keys[pg.K_w]:
            vy = -Player_Speed
        if keys[pg.K_DOWN] or keys[pg.K_s]:
            vy = Player_Speed
        if vx != 0 and vy != 0:
            vx *= 0.7071
            vy *= 0.7071

    gameDisplay.fill(white)
    ENEMY = pg.draw.rect(gameDisplay, red, (270, 270, 30, 30))
    block = pg.draw.rect(gameDisplay, (0, 150, 0), (x, y, 30, 30))
    xy_Text(x, y)
    x += vx * dt
    y += vy * dt
    Collision(block, ENEMY)
    pg.display.update()
    clock.tick(FPS)

pg.quit()
sys.exit()

1voto

furas Points 9923

Après de nombreux changements, la collision fonctionne

J'ai déménagé

x += vx * dt
y += vy * dt

en collision.

J'ai modifié l'organisation dans le code et j'utilise toujours Rect() pour conserver la position et la taille du joueur et de l'ennemi

J'ai également ajouté un deuxième ennemi pour tester comment vérifier les collisions avec plusieurs éléments.

import pygame as pg
import sys
from math import fabs

# - functions --- (lower_case_names)

def check_collision(player, enemy1, enemy2):
    global player_vx, player_vy

    # --- X ---

    player.x += player_vx * dt

    # enemy 1

    deltay = fabs(player.centery - enemy1.centery)
    deltax = fabs(player.centerx - enemy1.centerx)

    if deltay < enemy1.height and deltax < enemy1.width:
        if player_vx > 0:
            player_vx = 0
            player.x = enemy1.x - player.w
        elif player_vx < 0:
            player_vx = 0
            player.x = enemy1.x + player.w

    # enemy 2

    deltay = fabs(player.centery - enemy2.centery)
    deltax = fabs(player.centerx - enemy2.centerx)

    if deltay < enemy2.height and deltax < enemy2.width:
        if player_vx > 0:
            player_vx = 0
            player.x = enemy2.x - player.w
        elif player_vx < 0:
            player_vx = 0
            player.x = enemy2.x + player.w

    # --- Y ---

    player.y += player_vy * dt

    # enemy 1

    deltay = fabs(player.centery - enemy1.centery)
    deltax = fabs(player.centerx - enemy1.centerx)

    if deltay < enemy1.height and deltax < enemy1.width:
        if player_vy > 0:
            player_vy = 0
            player.y = enemy1.y - player.h
        elif player_vy < 0:
            player_vy = 0
            player.y = enemy1.y + player.w

    # enemy 2

    deltay = fabs(player.centery - enemy2.centery)
    deltax = fabs(player.centerx - enemy2.centerx)

    if deltay < enemy2.height and deltax < enemy2.width:
        if player_vy > 0:
            player_vy = 0
            player.y = enemy2.y - player.h
        elif player_vy < 0:
            player_vy = 0
            player.y = enemy2.y + player.w

def xy_text(screen, x, y):
    font = pg.font.SysFont("Courier", 16, True)

    text = font.render("X: " + str(round(x)), True, (0,150,0))
    screen.blit(text, (0,0))

    text = font.render("Y: " + str(round(y)), True, (0,150,0))
    screen.blit(text, (0,14))

# --- constants --- (UPPER_CASE_NAMES)

WIDTH = 600
HEIGHT = 600

WHITE = (255, 255, 255)
RED = (255, 0, 0)

FPS = 60

# --- main --- (lower_case_names)

player_speed = 200
player_vx = 0
player_vy = 0

player = pg.Rect(270, 0, 30, 30)
enemy1 = pg.Rect(270, 270, 30, 30)
enemy2 = pg.Rect(240, 300, 30, 30)

# - init -
pg.init()

game_display = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption('Block')

# - mainloop -

clock = pg.time.Clock()

closed = False
while not closed:

    dt = clock.tick(FPS)/1000

    # - events -

    for event in pg.event.get():
        if event.type == pg.QUIT:
            closed = True

    keys = pg.key.get_pressed()

    player_vx = 0
    player_vy = 0

    if keys[pg.K_ESCAPE]:
        closed = True
    if keys[pg.K_LEFT] or keys[pg.K_a]:
        player_vx = -player_speed
    if keys[pg.K_RIGHT] or keys[pg.K_d]:
        player_vx = player_speed
    if keys[pg.K_UP] or keys[pg.K_w]:
        player_vy = -player_speed
    if keys[pg.K_DOWN] or keys[pg.K_s]:
        player_vy = player_speed
    if player_vx != 0 and player_vy != 0:
        player_vx *= 0.7071
        player_vy *= 0.7071

    # - updates -

    check_collision(player, enemy1, enemy2)

    # - draws -

    game_display.fill(WHITE)
    pg.draw.rect(game_display, RED, enemy1)
    pg.draw.rect(game_display, RED, enemy2)
    pg.draw.rect(game_display, (0, 150, 0), player)
    xy_text(game_display, player.x, player.y)
    pg.display.update()
    clock.tick(FPS)

# - end -

pg.quit()
sys.exit()

1voto

skrx Points 13884

Si vous voulez gérer les collisions avec les murs, déplacez-vous d'abord le long de l'axe x ou y, faites reculer le joueur s'il est entré en collision, puis faites de même sur l'autre axe. Si vous utilisez pygame.Rect vous pouvez utiliser leurs left , right , top y bottom afin de replacer facilement le lecteur sur le côté correspondant du bloc.

Vérifie donc si le joueur entre en collision avec un bloc et s'il s'est déplacé vers la droite ( vx > 0 ), puis fixer hero.right a block.left et faites de même pour les autres directions. Vous devez également renvoyer les nouvelles coordonnées x et y si le rect du joueur a été mis à jour.

Je suggère de placer les blocs (rectangles) dans un fichier blocks que vous pouvez passer aux fonctions de collision des murs et utiliser ensuite une boucle for.

from math import fabs
import pygame as pg

pg.init()

width = 600
height = 600

gameDisplay = pg.display.set_mode((width, height))

white = (255, 255, 255)
red = (255, 0, 0)

clock = pg.time.Clock()
closed = False
FPS = 60
Player_Speed = 200
x, y = 270, 0
vx = 0
vy = 0
# Define the font in the global scope.
FONT = pg.font.SysFont("Courier", 16, True)

# Better don't use global variables.
def handle_horizontal_collisions(hero, blocks, x, vx):
    """Sets the player back to the left or right side."""
    for block in blocks:
        if hero.colliderect(block):
            if vx > 0:
                hero.right = block.left
            elif vx < 0:
                hero.left = block.right
            return hero.x  # Need to update the actual `x` position.
    return x

def handle_vertical_collisions(hero, blocks, y, vy):
    """Sets the player back to the top or bottom side."""
    for block in blocks:
        if hero.colliderect(block):
            if vy > 0:
                hero.bottom = block.top
            elif vy < 0:
                hero.top = block.bottom
            return hero.y  # Need to update the actual `y` position.
    return y

def xy_Text(x, y):
    text = FONT.render("X: " + str(round(x)), True, (0,150,0))
    text1 = FONT.render("Y: " + str(round(y)), True, (0,150,0))
    gameDisplay.blit(text, (0,0))
    gameDisplay.blit(text1, (0,14))

# Use pygame.Rects for the player and blocks.
player = pg.Rect(x, y, 30, 30)
# Put the blocks into a list.
blocks = []
for rect in (pg.Rect(200, 200, 60, 30), pg.Rect(230, 230, 60, 30)):
    blocks.append(rect)

dt = 0

while not closed:
    for event in pg.event.get():
        if event.type == pg.QUIT:
            closed = True
        # clock.tick should not be called in the event loop.

    vx, vy = 0, 0
    # key.get_pressed should also not be in the event loop.
    keys = pg.key.get_pressed()

    if keys[pg.K_ESCAPE]:
        closed = True
    if keys[pg.K_LEFT] or keys[pg.K_a]:
        vx = -Player_Speed
    if keys[pg.K_RIGHT] or keys[pg.K_d]:
        vx = Player_Speed
    if keys[pg.K_UP] or keys[pg.K_w]:
        vy = -Player_Speed
    if keys[pg.K_DOWN] or keys[pg.K_s]:
        vy = Player_Speed
    if vx != 0 and vy != 0:
        vx *= 0.7071
        vy *= 0.7071

    # Game logic.
    x += vx * dt
    player.x = x
    x = handle_horizontal_collisions(player, blocks, x, vx)

    y += vy * dt
    player.y = y
    y = handle_vertical_collisions(player, blocks, y, vy)

    # Rendering.
    gameDisplay.fill(white)
    for block in blocks:
        pg.draw.rect(gameDisplay, red, block)
    pg.draw.rect(gameDisplay, (0, 150, 0), player)

    xy_Text(x, y)
    pg.display.update()
    dt = clock.tick(FPS)/1000

pg.quit()

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X