2017-12-14 1 views
3

Ich habe Probleme, diese Kollision zu 100% zu arbeiten. Wenn ich nur eine Taste gleichzeitig drücke, scheint die Kollision gut zu funktionieren, aber wenn ich eine Taste drücke und weiter drücke, während ich kollidiere, dann drücke eine andere Taste, die Kollision scheint beide Schlüssel gleichzeitig zu berücksichtigen. Von der Forschung scheint es, dass ich separate Achsenberechnungen machen muss, aber ich bin mir nicht sicher, wie genau das geht, indem ich den Algorithmus verwende, den ich für die Kollision habe. Ich möchte, dass dies möglichst prozedural ist. Wenn jemand meinen Code mit einer funktionierenden Verfahrenslösung ändern könnte, würde ich es sehr begrüßen. Vielen Dank.Rect Kollision auf einem Pygame-Display; Drücken einer zweiten Taste, nach der Kollision, verursacht Rect zu springen

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

Die Handhabung von Kollisionen ist schwierig. Ich würde Ihnen raten - anstatt zu versuchen, das Rad neu zu erfinden - verwenden Sie eine vorhandene Bibliothek wie Box2d oder Pymunk – Omni

+0

Ich weiß, dass ich eine vorhandene Bibliothek verwenden kann, sogar pygame hat Kollisions-Behandlung. Ich erfinde das Rad für Bildungszwecke neu. Ich möchte verstehen, wie alles funktioniert. Danke für den Hinweis. – Tophiero

+0

Das ist ein großartiges Motiv. Bei einem erzwungenen Ansatz können Sie beispielsweise Kollisionen bewältigen, indem Sie Hilfskräfte erstellen, um kollidierende Objekte auseinander zu bewegen. Die Anzahl der gedrückten Tasten wäre für ein solches Modell wahrscheinlich irrelevant. Die Tatsache, dass dies in Ihrer Frage eine große Rolle spielt, impliziert jedoch aus meiner Sicht, dass Sie mehr Forschung darüber benötigen, wie solche Simulationen funktionieren. – Omni

Antwort

1

Wenn Sie Kollisionen mit Wänden zu handhaben, entlang der X- oder Y-Achse bewegen. Wenn Sie pygame.Rect s verwenden, können Sie die Attribute left, right, top und bottom verwenden, um den Player einfach auf die entsprechende Seite des Blocks zurückzusetzen.

So prüfen Sie, ob der Spieler mit einem Block kollidiert und wenn er/sie nach rechts (vx > 0) bewegt, dann setzen Sie hero.right zu block.left und das gleiche gilt für die anderen Richtungen tun. Sie müssen auch die neuen x- und y-Koordinaten zurückgeben, wenn der Spieler rect aktualisiert wurde.

Ich schlage vor, die Blöcke (rects) in eine Liste zu legen, die Sie an die Wandkollisionsfunktionen übergeben können und dann einfach eine for-Schleife verwenden.

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

Dies war die beste Antwort. Es tut genau das, wonach ich suche, und einiges, ohne auf glatte Bewegungen zu verzichten. Wie der Typ oben trennen Sie auch verschiedene Codeabschnitte mit Kommentaren. Ich werde das auch anfangen; es macht Code viel besser und besser organisiert. Vielen Dank für Ihre Zeit, Ihre Bemühungen und Ihren Rat. – Tophiero

1

Nach vielen Änderungen Kollision arbeitet

I

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

in.

Ich änderte Organisation in Code und ich verwende immer Rect() Position und Größe der Spieler zu halten und Feindes

ich, wie auch zweiten Feind hinzufügen zu testen Kollision mit vielen Elementen zu überprüfen. Zuerst stellen Sie den Player zurück, wenn er/sie kollidierte und dann tut das gleiche für die andere Achse

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

Vielen Dank für die Änderungen und Hinweise. Es hat mir gefallen, wie du alles mit Kommentaren trennst. Ich werde damit anfangen. Das einzige Problem mit diesem Code, das ich fand, war, dass es die Bewegung verlangsamt und nicht so glatt ist. Die Kollision funktioniert jedoch. – Tophiero

+0

Gibt es eine Möglichkeit, diesen Code mit der reibungslosen Bewegung arbeiten zu lassen? – Tophiero

+0

Problem ist zwei 'Tipps (FPS)' - sehr nahe beieinander - so 'dt' gibt Zeit zwischen beiden' Tipps (FPS) 'aber nicht die Zeit zum Zeichnen von Elementen. Jetzt funktioniert Code schneller. – furas