当矩形下降斜坡时,矩形摇晃

when the rectangle descends the ramp, the rectangle shakes

我不知道是否有更好的方法来实现坡道。 首先我计算属于 hipotenuse 的点并使用 collidepoint 查看矩形和属于 hipotenuse 的任何点之间是否存在碰撞,然后我根据发生碰撞的点更新矩形。 当矩形位于坡道顶部时要小心。 矩形完美地上升到坡道,但是当矩形下降到坡道时,矩形摇晃。

import sys
import pygame
from pygame.locals import *

pygame.init()

fps = 60
fpsClock = pygame.time.Clock()

width, height = 640, 480
screen = pygame.display.set_mode((width, height))

def draw_grid():
    for y in range(0,height,32):
        pygame.draw.line(screen,'red',(0,y),(width,y))
    for x in range(0,width,32):
        pygame.draw.line(screen,'red',(x,0),(x,height))

class Ramp(pygame.sprite.Sprite):
    def __init__(self, x, y, width, height, color):
        super().__init__()
        self.image = pygame.Surface((width, height), pygame.SRCALPHA)

        #self.image.fill('green')
        pygame.draw.polygon(self.image, color,
                            points=[(0, 0), (0, height), (width, height)])
        self.rect = self.image.get_rect(topleft=(x, y))


class Player(pygame.sprite.Sprite):
    def __init__(self,x,y):
        super().__init__()
        self.image = pygame.Surface((32, 32))
        self.image.fill('blue')
        self.rect = self.image.get_rect(topleft=(x,y))
        self.speed = 5
        self.direction = pygame.math.Vector2(0,0)
        self.gravity = 0.9
        self.initial_jump = -20
        self.on_ground = True
        
    def apply_gravity(self):
        self.direction.y += self.gravity
        self.rect.y += self.direction.y

    def move(self):
        keys=pygame.key.get_pressed()
        if keys[K_LEFT]:
            self.direction.x = -self.speed
        elif keys[K_RIGHT]:
            self.direction.x = self.speed
        else:
            self.direction.x = 0
        if keys[K_UP] and self.on_ground:
            self.direction.y = self.initial_jump
            self.on_ground = False
        self.rect.x += self.direction.x
             
    
    def check_borders(self):
        if self.rect.x <= 0:
            self.rect.x = 0
        if self.rect.right >= width:
            self.rect.right = width
        if self.rect.bottom >= height:
            self.rect.bottom = height
            self.direction.y = 0
            self.on_ground = True
        if self.rect.colliderect(ramp_rect):
            if self.direction.x > 0 and abs(self.rect.right-ramp_rect.left) <= 5:
                self.rect.right = ramp_rect.left
            # ramp stuff
            for p in hypotenuse_points:
                if self.rect.collidepoint(p):
                    if self.rect.left >= ramp_rect.left:
                        self.rect.bottomleft = p
                    else:
                        self.rect.bottom = ramp_rect.top
                    self.on_ground = True
                    self.direction.y = 0
                    
    def update(self):
        self.move()
        self.apply_gravity()
        self.check_borders()

player = pygame.sprite.GroupSingle(Player(12*32,10*32))
ramp = pygame.sprite.GroupSingle(Ramp(5*32,10*32,7*32,5*32,'red'))
ramp_rect = ramp.sprite.rect

m = (ramp_rect.height)/( ramp_rect.width)
x1,y1 = ramp_rect.topleft
hypotenuse_points = [] 
for x in range(ramp_rect.left,ramp_rect.right):
    hypotenuse_points.append((x,m*(x-x1)+y1)) # Point-slope equation

while True:
    screen.fill('white')
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()    
    ramp.draw(screen)
    player.update()
    player.draw(screen)
    
    #draw_grid()
    pygame.draw.lines(screen,'black',False,hypotenuse_points,3)
    pygame.display.update()
    fpsClock.tick(fps)

你的代码没有问题。只是重力太弱了。运动是如此之快,以至于重力作用太晚了。请注意,您不是沿着斜坡向下移动,而是向右移动然后坠落。

你的代码当然有一个问题。由于 pygame.Rect 应该表示屏幕上的一个区域,因此 pygame.Rect 对象只能存储整数数据。

The coordinates for Rect objects are all integers. [...]

当对象的新位置分配给 Rect 对象时,坐标的小数部分会丢失。如果每帧都这样做,位置误差会随着时间累积。

如果要以浮点精度存储对象位置,则必须将对象的位置分别存储在单独的变量和属性中,并同步 pygame.Rect 对象。 round坐标并将其分配给矩形的位置。

我建议计算播放器下方坡道的高度,而不是点列表:

if self.rect.colliderect(ramp_rect):
    ratio = ramp_rect.height / ramp_rect.width
    self.rect.bottom = ramp_rect.bottom - (ramp_rect.right - max(self.rect.left, ramp_rect.left)) * ratio
    self.y = self.rect.y

完整示例:

import sys
import pygame
from pygame.locals import *

pygame.init()

fps = 60
fpsClock = pygame.time.Clock()

width, height = 640, 480
screen = pygame.display.set_mode((width, height))

def draw_grid():
    for y in range(0,height,32):
        pygame.draw.line(screen,'red',(0,y),(width,y))
    for x in range(0,width,32):
        pygame.draw.line(screen,'red',(x,0),(x,height))

class Ramp(pygame.sprite.Sprite):
    def __init__(self, x, y, width, height, color):
        super().__init__()
        self.image = pygame.Surface((width, height), pygame.SRCALPHA)

        #self.image.fill('green')
        pygame.draw.polygon(self.image, color,
                            points=[(0, 0), (0, height), (width, height)])
        self.rect = self.image.get_rect(topleft=(x, y))

class Player(pygame.sprite.Sprite):
    def __init__(self,x,y):
        super().__init__()
        self.image = pygame.Surface((32, 32))
        self.image.fill('blue')
        self.rect = self.image.get_rect(topleft=(x,y))
        self.x, self.y = self.rect.topleft
        self.speed = 5
        self.direction = pygame.math.Vector2(0,0)
        self.gravity = 0.9
        self.initial_jump = -20
        self.on_ground = True
        
    def apply_gravity(self):
        self.direction.y += self.gravity
        self.y += self.direction.y
        self.rect.y = round(self.y)

    def move(self):
        keys = pygame.key.get_pressed()
        self.direction.x = (keys[K_RIGHT] - keys[K_LEFT]) * self.speed
        if keys[K_UP] and self.on_ground:
            self.direction.y = self.initial_jump
            self.on_ground = False
        self.x += self.direction.x
        self.rect.x = round(self.x)
              
    def check_borders(self):
        if self.rect.x <= 0:
            self.rect.x = 0
            self.x = self.rect.x
        if self.rect.right >= width:
            self.rect.right = width
            self.x = self.rect.x
        if self.rect.bottom >= height:
            self.rect.bottom = height
            self.direction.y = 0
            self.on_ground = True
            self.y = self.rect.y
        
        if self.rect.colliderect(ramp_rect):
            if self.old_rect.right-1 <= ramp_rect.left:
                self.rect.right = ramp_rect.left
                self.x = self.rect.x
            else:
                ratio = ramp_rect.height / ramp_rect.width
                bottom = ramp_rect.bottom - (ramp_rect.right - max(self.rect.left, ramp_rect.left)) * ratio
                if self.on_ground or self.rect.bottom > bottom:
                    self.rect.bottom = bottom
                    self.y = self.rect.y
                    self.direction.y = 0
                    self.on_ground = True
                    
    def update(self):
        self.old_rect = self.rect.copy()
        self.move()
        self.apply_gravity()
        self.check_borders()

player = pygame.sprite.GroupSingle(Player(12*32,10*32))
ramp = pygame.sprite.GroupSingle(Ramp(5*32,10*32,7*32,5*32,'red'))
ramp_rect = ramp.sprite.rect

m = (ramp_rect.height)/( ramp_rect.width)
x1,y1 = ramp_rect.topleft

while True:
    screen.fill('white')
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()    
    ramp.draw(screen)
    player.update()
    player.draw(screen)
    
    pygame.display.update()
    fpsClock.tick(fps)