2017-12-03 88 views
1

我试图在pygame中创建一个下降的效果,但我被困在一个特定的问题。那就是当玩家跌倒并与平台相撞时,我的玩家阶层开始上下摆动。我确信这与我的更新循环有关,但我不确定它到底是什么。我已经尝试了几种方法,例如重新排列层次结构无济于事。我结束了在线搜索,但没有得到答案。所以,如果任何人都可以帮助我,我真的很感激它。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() 
+0

首先使用'print()'来查看变量中的值,if/else语句的结果等 - 可能有助于找出为什么它不能正常工作。 – furas

回答

2

有两个失误一起给问题

第一:

player.pos.y = hit[0].rect.top - hit[0].rect.height 

这是没有意义的。 top - height给同bottom所以你必须

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

但你需要hit[0].rect.topplayer.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.center等时,它会自动重新计算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 

二:

你应该尽一切举动检查collisionts

# 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()