Python pygame坠落碰撞导致玩家上下摆动

Python pygame坠落碰撞导致玩家上下摆动,python,pygame,collision,Python,Pygame,Collision,我试图在pygame中创建一个下降效果,但我被困在一个特定的问题上。也就是说,每当玩家摔倒并与平台相撞时,我的玩家职业就会开始上下摆动。我确信这与我的更新循环有关,但我不确定它到底是什么。我尝试过几种方法,比如重新排列层次结构,但都没有用。我最终在网上搜索,但没有得到答案。如果有人能帮上忙,我会非常感激 谢谢 import pygame as pg import os #vector VEC = pg.math.Vector2 def paint(parent, color=None):

我试图在pygame中创建一个下降效果,但我被困在一个特定的问题上。也就是说,每当玩家摔倒并与平台相撞时,我的玩家职业就会开始上下摆动。我确信这与我的更新循环有关,但我不确定它到底是什么。我尝试过几种方法,比如重新排列层次结构,但都没有用。我最终在网上搜索,但没有得到答案。如果有人能帮上忙,我会非常感激

谢谢

import pygame as pg
import os

#vector
VEC = pg.math.Vector2

def paint(parent, color=None):
    """ fills the background for surfaces.
        color: for none sprite surfaces optional parameter if given
    """

    #set background color based on surface type
    if color:
        #class based surfaces or sprites
        background = pg.Color(color)
    else:
        #none sprite surfaces usuallly not class
        background = pg.Color(parent.background)

    #check for image attribure
    #if found fill the image's backround
    if hasattr(parent, "image"):
        parent.image.fill(background)
    #if "image" not found fill the surface itself
    else:
        parent.fill(background)



class Asset(pg.sprite.Sprite):
    """ asset class functions as base class for various assets in this case
        either a player asset or a block asset group """

    #if the given family name(see bellow in constructor) is block,
    #all instances of block will be added to this group else its ignored
    GROUP = pg.sprite.Group()
    def __init__(self, parent=None, family=None, pos=None):
        """
            parent:surface asset is being rendered to
            family:type of asset(type is not used due to it being a buil-in)
            pos: position of asset
        """
        super().__init__()

        self.parent = parent
        self.family = family

        self.pos = VEC(pos)
        self.size = [20, 20]
        #background will be based on family
        self.background = {"block":"#000000","player":"#ff0000"}[self.family]

        self.image, self.rect = self.set_image()

        #see class documention for explanation
        if self.family == "block":
            Asset.GROUP.add(self)
        #if family is player add essential fields for physics
        else:
            #velocity
            self.vel = VEC(3, 3)
            #acceleration(x:friction, y:gravity)
            self.ac = VEC(.3, .3)
            #jump height
            self.height = 5


    def update(self):
        if self.family == "player":
            #fall code
            self.vel.y += self.ac.y
            self.pos.y += self.vel.y

            #prevents player from falling of the edge and adds teleportation
            if self.pos.x + self.size[0] <= 0:
                self.pos.x = 399
            elif self.pos.x >= 400:
                self.pos.x = 1 - self.size[0]

        #updates asset rect postion
        self.rect.topleft = self.pos

    def render(self):
        """ renders image to parent surface """
        self.parent.blit(self.image, self.rect)


    def set_image(self):
        """creates initial image and rect for sprite"""
        self.image = pg.Surface(self.size)
        paint(self)

        self.rect = self.image.get_rect()
        self.rect.topleft = self.pos

        return self.image, self.rect

    def move(self, key):
        """handles player movmet"""
        for i in range(2):
            #checks for right or left movment
            if key[[pg.K_LEFT, pg.K_RIGHT][i]]:
                self.pos.x += self.vel.x*[-1, 1][i]

    def jump(self):
        """ handles jumping """
        self.vel.y = -self.height


def block_collision(player, blocks):
    """collision detection between blocks and player"""
    hit = pg.sprite.spritecollide(player, blocks, False)

    if hit:
        if player.rect.bottom >= hit[0].rect.top:
            player.pos.y = hit[0].rect.top - hit[0].rect.height
            player.vel.y = 0


def main():
    POS = [0, 0]
    SIZE = [400, 400]
    TITLE = "Test"
    BACKGROUND = "#ffffff"

    CLOCK = pg.time.Clock()
    FPS = 60
    RUN = True
    os.environ["SDL_VIDEO_CENTERED"] = "1"

    win = pg.display.set_mode(SIZE)
    pg.display.set_caption(TITLE)

    # create blocks group
    #NOTE:blocks do not need a variable instance because they are 
    #automatically added to the class group on construction
    for x in range(20):
        Asset(family="block", pos=[x*20, 380])

    #set player filed
    player = Asset(win, family="player", pos=[20, 20])


    while RUN:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                RUN = False
            elif event.type == pg.KEYDOWN:
                if event.key == pg.K_UP:
                    #player jump
                    player.jump()

        #player movement
        player.move(pg.key.get_pressed())

        #fill window background
        paint(win, BACKGROUND)

        #check for collision
        block_collision(player, Asset.GROUP)

        #update player
        player.update()
        #update block group
        Asset.GROUP.update()

        #render player
        player.render()
        #render block group
        Asset.GROUP.draw(win)

        pg.display.update()
        CLOCK.tick(FPS)


if __name__ == '__main__':
    main()
将pygame导入为pg
导入操作系统
#载体
向量=pg.math.Vector2
def油漆(父级,颜色=无):
“”“填充曲面的背景。
颜色:对于无精灵曲面,如果给定可选参数
"""
#根据曲面类型设置背景色
如果颜色:
#基于类的曲面或精灵
背景=pg.Color(颜色)
其他:
#无精灵表面通常不属于类
background=pg.Color(parent.background)
#检查图像属性
#如果找到,则填充图像的背面
如果hasattr(父级,“图像”):
parent.image.fill(背景)
#如果未找到“图像”,则填充曲面本身
其他:
父项填充(背景)
类别资产(pg.sprite.sprite):
“”“在这种情况下,资产类用作各种资产的基类
玩家资产或区块资产组“”
#如果给定的族名称(请参见构造函数中的以下部分)为block,
#块的所有实例都将添加到此组,否则其将被忽略
GROUP=pg.sprite.GROUP()
定义初始化(self,parent=None,family=None,pos=None):
"""
父对象:曲面资源正在渲染到
族:资产类型(由于类型是内置的,所以未使用该类型)
pos:资产头寸
"""
super()。\uuuu init\uuuuu()
self.parent=parent
self.family=家庭
self.pos=VEC(pos)
self.size=[20,20]
#背景将以家庭为基础
self.background={“block”:“#000000”,“player”:“#ff0000”}[self.family]
self.image,self.rect=self.set_image()
#有关说明,请参见课堂文档
如果self.family==“块”:
Asset.GROUP.add(self)
#如果玩家是家庭成员,请为物理添加基本字段
其他:
#速度
self.vel=VEC(3,3)
#加速度(x:摩擦力,y:重力)
self.ac=VEC(.3,.3)
#跳跃高度
自我高度=5
def更新(自我):
如果self.family==“玩家”:
#坠落代码
self.vel.y+=self.ac.y
自我位置y+=自我水平y
#防止玩家从边缘掉落并增加远程传送
如果self.pos.x+self.size[0]=400:
self.pos.x=1-self.size[0]
#更新资产rect position
self.rect.topleft=self.pos
def渲染(自):
“”“将图像渲染到父曲面”“”
self.parent.blit(self.image、self.rect)
def set_图像(自身):
“”“为精灵创建初始图像和矩形”“”
self.image=pg.Surface(self.size)
绘画(自我)
self.rect=self.image.get_rect()
self.rect.topleft=self.pos
返回self.image,self.rect
def移动(自身,钥匙):
“”“处理播放器移动”“”
对于范围(2)中的i:
#检查左右移动
如果键[[pg.K_左,pg.K_右][i]]:
self.pos.x+=self.vel.x*[-1,1][i]
def跳转(自):
“处理跳跃”
self.vel.y=-self.height
def block_碰撞(播放器,块):
“”“区块和播放器之间的碰撞检测”“”
命中=pg.sprite.SpriteClide(玩家、方块、假)
如果击中:
如果player.rect.bottom>=点击[0]。rect.top:
player.pos.y=hit[0].rect.top-hit[0].rect.height
player.vel.y=0
def main():
POS=[0,0]
大小=[400400]
TITLE=“测试”
背景=“#ffffff”
时钟=pg.time.CLOCK()
FPS=60
运行=真
操作系统环境[“以SDL\U视频为中心”]=“1”
win=pg.display.set_模式(大小)
pg.display.set_标题(标题)
#创建块组
#注意:块不需要变量实例,因为它们是
#在构造时自动添加到类组
对于范围(20)内的x:
资产(family=“block”,pos=[x*20380])
#定位球运动员
玩家=资产(赢,family=“玩家”,位置=[20,20])
运行时:
对于pg.event.get()中的事件:
如果event.type==pg.QUIT:
运行=错误
elif event.type==pg.KEYDOWN:
如果event.key==pg.K_UP:
#运动员跳跃
player.jump()
#球员运动
玩家移动(pg.key.get_pressed())
#填充窗口背景
绘画(赢,背景)
#检查有无碰撞
阻止碰撞(玩家、资源组)
#更新播放器
player.update()
#更新块组
Asset.GROUP.update()
#渲染播放器
player.render()
#渲染块组
资产组抽签(赢)
pg.display.update()
时钟滴答声(FPS)
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
main()

有两个错误一起导致了问题

第一:

player.pos.y = hit[0].rect.top - hit[0].rect.height
这毫无意义<代码>顶部-高度与
底部
相同,因此

player.pos.y = hit[0].rect.bottom
但是您需要使用
player.rect.bottom

player.rect.bottom = hit[0].rect.top
player.pos.y = player.rect.y 
(在这里我不知道为什么要使用
player.pos
,如果它总是与
player.rect
具有相同的值,
rect
有许多有用的字段,比如
rect.bottom
rect.center
,当您更改
rect.bottom
时,它会自动重新计算
rect.x
rect.y
)>矩形中心
等)

所以第一个修正是

if hit:
    if player.rect.bottom >= hit[0].rect.top:
        #player.pos.y = hit[0].rect.top - hit[0].rect.height

        player.rect.bottom = hit[0].rect.top
        player.pos.y = player.rect.y

        player.vel.y = 0
第二:

你应该在检查之前做所有的动作
    # player movement
    player.move(pg.key.get_pressed())

   # update player
    player.update()

    #update block group
    Asset.GROUP.update()

    # check for collision - after all moves
    block_collision(player, Asset.GROUP)
import pygame as pg
import os

#vector
VEC = pg.math.Vector2

def paint(parent, color=None):
    """ fills the background for surfaces.
        color: for none sprite surfaces optional parameter if given
    """

    #set background color based on surface type
    if color:
        #class based surfaces or sprites
        background = pg.Color(color)
    else:
        #none sprite surfaces usuallly not class
        background = pg.Color(parent.background)

    #check for image attribure
    #if found fill the image's backround
    if hasattr(parent, "image"):
        parent.image.fill(background)
    #if "image" not found fill the surface itself
    else:
        parent.fill(background)



class Asset(pg.sprite.Sprite):
    """ asset class functions as base class for various assets in this case
        either a player asset or a block asset group """

    #if the given family name(see bellow in constructor) is block,
    #all instances of block will be added to this group else its ignored
    GROUP = pg.sprite.Group()
    def __init__(self, parent=None, family=None, pos=None):
        """
            parent:surface asset is being rendered to
            family:type of asset(type is not used due to it being a buil-in)
            pos: position of asset
        """
        super().__init__()

        self.parent = parent
        self.family = family

        self.pos = VEC(pos)
        self.size = [20, 20]
        #background will be based on family
        self.background = {"block":"#000000","player":"#ff0000"}[self.family]

        self.image, self.rect = self.set_image()

        #see class documention for explanation
        if self.family == "block":
            Asset.GROUP.add(self)
        #if family is player add essential fields for physics
        else:
            #velocity
            self.vel = VEC(3, 3)
            #acceleration(x:friction, y:gravity)
            self.ac = VEC(.3, .3)
            #jump height
            self.height = 5


    def update(self):
        if self.family == "player":
            #fall code
            self.vel.y += self.ac.y
            self.pos.y += self.vel.y

            #prevents player from falling of the edge and adds teleportation
            if self.pos.x + self.size[0] <= 0:
                self.pos.x = 399
            elif self.pos.x >= 400:
                self.pos.x = 1 - self.size[0]

        #updates asset rect postion
        self.rect.topleft = self.pos

    def render(self):
        """ renders image to parent surface """
        self.parent.blit(self.image, self.rect)


    def set_image(self):
        """creates initial image and rect for sprite"""
        self.image = pg.Surface(self.size)
        paint(self)

        self.rect = self.image.get_rect()
        self.rect.topleft = self.pos

        return self.image, self.rect

    def move(self, key):
        """handles player movmet"""
        for i in range(2):
            #checks for right or left movment
            if key[[pg.K_LEFT, pg.K_RIGHT][i]]:
                self.pos.x += self.vel.x*[-1, 1][i]

    def jump(self):
        """ handles jumping """
        self.vel.y = -self.height


def block_collision(player, blocks):
    """collision detection between blocks and player"""
    hit = pg.sprite.spritecollide(player, blocks, False)

    if hit:
        if player.rect.bottom >= hit[0].rect.top:
            #player.pos.y = hit[0].rect.top - hit[0].rect.height
            player.rect.bottom = hit[0].rect.top
            player.pos.y = player.rect.y
            player.vel.y = 0


def main():
    POS = [0, 0]
    SIZE = [400, 400]
    TITLE = "Test"
    BACKGROUND = "#ffffff"

    CLOCK = pg.time.Clock()
    FPS = 60
    RUN = True
    os.environ["SDL_VIDEO_CENTERED"] = "1"

    win = pg.display.set_mode(SIZE)
    pg.display.set_caption(TITLE)

    # create blocks group
    #NOTE:blocks do not need a variable instance because they are 
    #automatically added to the class group on construction
    for x in range(20):
        Asset(family="block", pos=[x*20, 380])

    #set player filed
    player = Asset(win, family="player", pos=[20, 20])


    while RUN:

        # --- events ---

        for event in pg.event.get():
            if event.type == pg.QUIT:
                RUN = False
            elif event.type == pg.KEYDOWN:
                if event.key == pg.K_UP:
                    #player jump
                    player.jump()

        #player movement
        player.move(pg.key.get_pressed())

        # --- updates --

        #update player
        player.update()

        #update block group
        Asset.GROUP.update()

        #check for collision
        block_collision(player, Asset.GROUP)

        # --- draws ---

        #fill window background
        paint(win, BACKGROUND)

        #render player
        player.render()

        #render block group
        Asset.GROUP.draw(win)

        pg.display.update()
        CLOCK.tick(FPS)

    # ---- end ---
    pg.quit()

if __name__ == '__main__':
    main()