Pygame 碰撞与墙壁随机传送

时间:2021-06-29 00:42:58

标签: python pygame collision

我知道已经有一些关于墙壁碰撞的问题,但这些答案中的技术都没有帮助我。我正在做一个没有重力的自上而下的探索游戏。运动效果很好,但是一旦我制作了墙壁和碰撞,它就会开始出现故障并传送。我已经包含(并大大减少了)我认为有问题的两个函数,并在底部包含了整个代码。

def render(self):
    self.x += self.xvel
    self.app.wall_collision(self, self.xvel, 0)
    self.y += self.yvel
    self.app.wall_collision(self, 0, self.yvel)
    self.rect.topleft = (self.x, self.y)
    self.app.screen.blit(self.surf, self.rect)

按照 pygame sprite wall collision 的建议,我拆分了 x 和 y 墙碰撞代码。墙壁碰撞代码基于 https://github.com/marcusmoller/pyweek17-miner/blob/master/miner/engine.py#L202-L220

def wall_collision(self, player, xvel, yvel)
    for wall in self.maze.walls:
        if pygame.sprite.collide_rect(player, wall):
            if xvel > 0:
                player.x = wall.rect.left-player.w
            if xvel < 0:
                player.x = wall.rect.right
            if yvel > 0:
                player.y = wall.rect.top-player.h
            if yvel < 0:
                player.y = wall.rect.bottom

此代码适用于一维运动。即,拥抱一堵墙并直行效果很好,但同时按下两个移动按钮,传送开始发生。任何帮助是极大的赞赏!我已经调试了这个东西的废话。这是玩家 (x,y) 位置的一小段输出:

348.4 278.4
348.4 220
348.4 220
348.4 185
348.4 185
348.4 150
348.4 150
348.4 115
348.4 115
348.4 80
348.4 80
348.4 45

如您所见,在 y 方向上有很多随机跳跃。跳跃是 35 的原因是墙的块大小是 35x35。

import pygame
import pygame.freetype
from pygame.locals import (
    K_UP,
    K_DOWN,
    K_LEFT,
    K_RIGHT,
    K_ESCAPE,
    KEYDOWN,
    QUIT,
)

import numpy as np


class App:
    def __init__(self):
        self.running = True
        self.width = 800
        self.height = 600
        self.player = Player(self)
        self.maze = Maze(self, 35)
        pygame.init()
        self.screen = pygame.display.set_mode((self.width, self.height))
        self.clock = pygame.time.Clock()
        self.font = pygame.freetype.SysFont('Times New Roman', 10) 

        while self.running:
            pressed_keys = pygame.key.get_pressed()
            
            for event in pygame.event.get():
                if event.type == QUIT:
                    self.running = False
            self.render()
            self.player.update(pressed_keys)
            
    def render(self):
        self.screen.fill((255, 255, 255))
        text_surf, text_rect = self.font.render('({}, {}) ({}, {})'.format(self.player.x, self.player.y, self.player.xvel, self.player.yvel), (0, 0, 0), size=10)
        self.screen.blit(text_surf, (500, 300))
        self.player.move()
        self.maze.render()
        pygame.display.flip()
        self.clock.tick(60)
        

    def wall_collision(self, player, xvel, yvel):
        for wall in self.maze.walls:
            if pygame.sprite.collide_rect(self.player, wall):
                if xvel > 0:
                    self.player.x = wall.rect.left-self.player.w
                if xvel < 0:
                    self.player.x = wall.rect.right
                if yvel > 0:
                    self.player.y = wall.rect.top-self.player.h
                if yvel < 0:
                    self.player.y = wall.rect.bottom
        
class Player(pygame.sprite.Sprite):
    def __init__(self, app):
        super(Player,self).__init__()
        self.app = app
        self.x = 100
        self.y = 100
        self.xvel = 0
        self.yvel = 0
        self.max_speed = 8
        self.acc = self.max_speed / 2
        self.de_acc = self.max_speed * 0.8

        self.h = 25
        self.w = 25
        
        self.surf = pygame.Surface((self.h, self.w))
        self.surf.fill((0, 0, 204))
        self.rect = self.surf.get_rect()
        self.rect.topleft = self.x, self.y
        
    def update(self, pressed_keys):
        if pressed_keys[K_UP]:
            self.yvel = max(self.yvel - self.acc, -self.max_speed)
        if pressed_keys[K_DOWN]:
            self.yvel = min(self.yvel + self.acc, self.max_speed)
        if pressed_keys[K_LEFT]:
            self.xvel = max(self.xvel - self.acc, -self.max_speed)
        if pressed_keys[K_RIGHT]:
            self.xvel = min(self.xvel + self.acc, self.max_speed)

        if not pressed_keys[K_UP] and not pressed_keys[K_DOWN]:
            if self.yvel > 0:
                self.yvel = max(0, self.yvel - self.de_acc)
            if self.yvel < 0:
                self.yvel = min(0, self.yvel + self.de_acc)
                
        if not pressed_keys[K_LEFT] and not pressed_keys[K_RIGHT]:
            if self.xvel > 0:
                self.xvel = max(0, self.xvel - self.de_acc)
            if self.xvel < 0:
                self.xvel = min(0, self.xvel + self.de_acc)

    def move(self):
        self.x += self.xvel
        self.app.wall_collision(self, self.xvel, 0)
        self.y += self.yvel
        self.app.wall_collision(self, 0, self.yvel)
        self.rect.topleft = (self.x, self.y)
        self.app.screen.blit(self.surf, self.rect)






        
class Maze:
    def __init__(self, app, size):
        super(Maze, self).__init__()
        self.maze = np.array([[1,1,1,1,1,1,1,1,1,1],
                     [1,0,0,0,0,0,0,0,0,1],
                     [1,0,0,0,0,0,0,0,0,1],
                     [1,0,1,1,1,1,1,1,0,1],
                     [1,0,1,0,0,0,0,0,0,1],
                     [1,0,1,0,1,1,1,1,0,1],
                     [1,0,0,0,0,0,0,0,0,1],
                     [1,1,1,1,1,1,1,1,1,1]])
        self.size = size
        self.app = app
        self.draw()

    def draw(self):
        self.walls = pygame.sprite.Group()
        for i, row in enumerate(self.maze):
            for j, el in enumerate(row):
                if el == 1:
                    x = i * self.size
                    y = j* self.size
                    self.walls.add(Wall(x, y, self.size))
                    
    def render(self):
        for wall in self.walls:
            self.app.screen.blit(wall.surf, wall.rect)
            

class Wall(pygame.sprite.Sprite):
    def __init__(self, x, y, size):
        super(Wall, self).__init__()
        self.surf = pygame.Surface((size, size))
        self.x = x
        self.y = y
        self.rect = self.surf.get_rect()
        self.rect.topleft = (self.x, self.y)
    


app = App()
pygame.quit()

1 个答案:

答案 0 :(得分:1)

玩家的位置被正确地限制在墙壁上,但玩家的速度仍在增加。如果速度足够大,玩家将一步“跳过”墙壁。当玩家撞墙时,您需要将速度设置为 0。
此外,您需要考虑玩家在墙的哪一侧,并且需要更新玩家的 .rect 属性:

class App:
    # [...]

    def wall_collision(self, player, xvel, yvel):
        for wall in self.maze.walls:
            if pygame.sprite.collide_rect(self.player, wall):
                if xvel > 0 and self.player.rect.left < wall.rect.left:
                    self.player.rect.right = wall.rect.left
                    self.player.x = self.player.rect.x
                    self.xvel = 0
                if xvel < 0 and self.player.rect.right > wall.rect.right:
                    self.player.rect.left = wall.rect.right
                    self.player.x = self.player.rect.x
                    self.xvel = 0
                if yvel > 0 and self.player.rect.top < wall.rect.top:
                    self.player.rect.bottom = wall.rect.top
                    self.player.y = self.player.rect.y
                    self.yvel = 0
                if yvel < 0 and self.player.rect.bottom > wall.rect.bottom:
                    self.player.rect.top = wall.rect.bottom
                    self.player.y = self.player.rect.y
                    self.yvel = 0