Как правильно реализовать реакцию на столкновение в Pygame? - PullRequest
0 голосов
/ 05 июня 2019

Я пытаюсь запрограммировать простой нисходящий сканер подземелий в Pygame, и я уже наткнулся на контрольно-пропускной пункт при разработке своей реакции на столкновение.До сих пор я программировал движение игрока, обнаружение столкновений (знает только, когда игрок касается стены) и вращение игрока к мыши, последнее из которых отключено, чтобы упростить это решение.

Я пробовалобычный метод перемещения персонажа, проверка на столкновение, изменение позиции персонажа, если это необходимо, а затем отрисовка всех элементов на экране.

import pygame
from pygame.locals import *
import os
import sys
import math

os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()
pygame.font.init()
clock = pygame.time.Clock()
myfont = pygame.font.SysFont('sourcecodeproblack', 12)

BLACK = [0, 0, 0]
WHITE = [255, 255, 255]
WALLS = [220, 40, 30]

SIZE = [240, 240]
DSIZE = [480, 480]
TSIZE = [720, 720]

dub = False
trip = False

def normal():
    global screen
    screen = pygame.display.set_mode(SIZE)
    pygame.display.set_caption("METAL PACKET")

def full():
    global screen
    screen = pygame.display.set_mode((SIZE), pygame.FULLSCREEN)
    pygame.display.set_caption("METAL PACKET: Fullscreen Edition")

def doubled():
    global screen
    global window
    global res
    global invscale
    invscale = 1/2
    res = DSIZE
    window = pygame.display.set_mode(DSIZE)
    screen = pygame.Surface(SIZE)
    pygame.display.set_caption("METAL PACKET: Double Boogaloo")

def doubledfull():
    global screen
    global window
    global res
    global invscale
    invscale = 1/2
    res = DSIZE
    window = pygame.display.set_mode((DSIZE), pygame.FULLSCREEN)
    screen = pygame.Surface(SIZE)
    pygame.display.set_caption("METAL PACKET: Fullscreen Boogaloo")

def tripled ():
    global screen
    global window
    global res
    global invscale
    invscale = 1/3
    res = TSIZE
    window = pygame.display.set_mode(TSIZE)
    screen = pygame.Surface(SIZE)
    pygame.display.set_caption("METAL PACKET: Trifecta of Resolution")

def tripledfull ():
    global screen
    global window
    global res
    global invscale
    invscale = 1/3
    res = TSIZE
    window = pygame.display.set_mode((TSIZE), pygame.FULLSCREEN)
    screen = pygame.Surface(SIZE)
    pygame.display.set_caption("METAL PACKET: Fullscreen Trifecta Edition")

print("")
print("1: 240 x 240")
print("2: 480 x 480")
print("3: 720 x 720")
print("4: 240 x 240 Fullscreen")
print("5: 480 x 480 Fullscreen")
print("6: 720 x 720 Fullscreen")
print("")
res = input("Choose a video mode. ")
if res == ("1"):
    normal()
if res == ("2"):
    doubled()
    dub = True
if res == ("3"):
    tripled()
    trip = True
if res == ("4"):
    full()
if res == ("5"):
    doubledfull()
    dub = True
if res == ("6"):
    tripledfull()
    trip = True

class Walls(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.currentsprite = ()
    def draw(self):
        self.image = pygame.image.load(self.currentsprite)
        screen.blit(self.image, [0, 0])

room1 = Walls()
room1.currentsprite = ("room1.png")

collidelist = pygame.sprite.Group()

class WallColliders(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self, collidelist)
        self.rectval = []
    def collide(self):
        self.rect = (self.rectval)

topwall = WallColliders()
topwall.rectval = [0, 0, 240, 20]
bottomwall = WallColliders()
bottomwall.rectval = [0, 160, 240, 20]
leftwall = WallColliders()
leftwall.rectval = [0, 0, 20, 180]

class Player(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.collide = 0
        self.x = 0
        self.y = 0
        self.speed = 0
        self.currentsprite = ("pac.png")
        self.image = pygame.image.load(self.currentsprite)
        self.rect = self.image.get_rect()
    def collidedetect(self):
        if pygame.sprite.spritecollideany(player, collidelist):
            self.collide = 1
        else:
            self.collide = 0
    def move(self):
        key = pygame.key.get_pressed()
        if key[K_LEFT] or key[ord("a")]:
                self.x -= self.speed
                if self.collide == 1:
                    self.x += self.speed
                    self.collide = 0
        if key[K_RIGHT] or key[ord("d")]:
                self.x += self.speed
                if self.collide == 1:
                    self.x -= self.speed
                    self.collide = 0
        if key[K_UP] or key[ord("w")]:
                self.y -= self.speed
                if self.collide == 1:
                    self.y += self.speed
                    self.collide = 0
        if key[K_DOWN] or key[ord("s")]:
                self.y += self.speed
                if self.collide == 1:
                    self.y -= self.speed
                    self.collide = 0
        if self.collide == 1:
            print("collide")
    def rotate(self):
        pos = pygame.mouse.get_pos()
        if dub == True or trip == True:
            pos = pos[0] * invscale, pos[1] * invscale
            mouse_x, mouse_y = pos
        mouse_x, mouse_y = pos
        rel_x, rel_y = mouse_x - self.x, mouse_y - self.y
        self.angle = (180 / math.pi) * -math.atan2(rel_y, rel_x) 
        self.rotimage = pygame.transform.rotozoom(self.image, self.angle, 1)
        self.rect = self.rotimage.get_rect(center=(self.rect.center))
    def monitor(self):
        textsurface = myfont.render(str(self.angle), False, (255, 255, 255))
        screen.blit(textsurface,(50,210))
    def draw(self):
        #screen.blit(self.rotimage, [self.x, self.y])
        screen.blit(self.image, [self.x, self.y])
        self.rect = pygame.Rect(self.x, self.y, 20, 20)

player = Player()
player.x = 29
player.y = 89
player.speed = 3

done = False

while not done:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
        if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            print("")
            print("See you next time.")
            pygame.quit()
            sys.exit(0)
            done = True

    screen.fill(BLACK)

    room1.draw()

    for x in collidelist:
        x.collide()

    player.collidedetect()
    player.move()
    #player.rotate()
    #player.monitor()
    player.draw()

    if dub == True or trip == True:        
        pygame.transform.scale(screen, res, window)
    pygame.display.flip()
    clock.tick(60)

pygame.quit()

По какой-то причине это приведет игрока к стене, если он не нажметдвигайтесь по диагонали вместо того, чтобы вывести игрока из стены и сбросить столкновение.Я был бы очень признателен, если бы кто-то мог прочитать мой код и предложить какие-либо улучшения, оптимизацию или решение.Любой пример кода был бы хорош.Спасибо.

1 Ответ

0 голосов
/ 05 июня 2019

Не переусердствуйте. Когда игрок перемещается, рассчитайте новую позицию и обновите свойство .rect объекта Player:

class Player(pygame.sprite.Sprite):

    # [...]

    def move(self):
        key = pygame.key.get_pressed()
        if key[K_LEFT] or key[ord("a")]:
                self.x -= self.speed
        if key[K_RIGHT] or key[ord("d")]:
                self.x += self.speed
        if key[K_UP] or key[ord("w")]:
                self.y -= self.speed
        if key[K_DOWN] or key[ord("s")]:
                self.y += self.speed
        self.updaterect()

    def updaterect(self):
        self.rect = pygame.Rect(self.x, self.y, 20, 20)

В основном цикле:

  • резервное копирование позиции игрока
  • переместить игрока
  • проверка на столкновение
  • если произошло столкновение, то восстановить положение игрока
while not done:

    # [...]

    pos = (player.x, player.y)
    player.move()
    player.collidedetect()
    if player.collide:
        player.x, player.y = pos
        player.updaterect()
        player.collide = False

    player.draw()

    # [...]

Конечно, это можно сделать и методом:

class Player(pygame.sprite.Sprite):

    # [...]

    def moveandcollide(self):
        pos = (self.x, self.y)
        self.move()
        self.collidedetect()
        if player.collide:
            self.x, self.y = pos
            self.updaterect()
            self.collide = False
while not done:

    # [...]

    player.moveandcollide()
...