为什么当我按一次向左箭头键时,字符会滑动?

问题描述

我正在创建一个平台游戏,当我按向左箭头键移动玩家一次时,它继续滑动,而当我按一次向右箭头键时,它移动一次并且不滑动。我已经查看了我的代码,但我不知道出了什么问题,这就是我来到这里的原因。我将在下面展示主要的游戏循环类,以及我的玩家类,因为我认为这是相关的。

玩家等级:

vec = p.math.Vector2 #Allows a 2 dimensional vector

class Player(p.sprite.Sprite): #Creates a class for the player
    def __init__(self,game):
        p.sprite.Sprite.__init__(self) #Initalises the sprite
        self.game = game #Creates a variable to use the game class
        self.image = p.Surface((p_width,p_height)) #Creates an image with a designated width and height
        self.image.fill(p_colour) #Sets the colour of the image to the designated colour
        self.rect = self.image.get_rect() #Establishes the rectangular border of the image
        self.rect.center = (50,height - 30) #Positions the image at the center of the screen
        self.pos = vec(50,height - 30) #Where the player is positioned at initally
        self.vel = vec(0,0) #VeLocity in (x direction,y direction)
        self.acc = vec(0,0) #acceleration in (x direction,y direction)

    def jump(self): #Used for when the player wants to jump
        #Jump if only standing on a platform
        self.rect.y += 1 #Checking 1 pixel below the player
        hits = p.sprite.spritecollide(self,self.game.platforms,False) #Checks for collisions between the player and the platforms group
        self.rect.y -= 1 #Dont want player to be in platform,so need to move it up 1 pixel again
        if hits: #If the player is standing on a platform
            self.vel.y = p_jump #Projects the player up the designated amount of pixels up

    def update(self): #Runs every frame of the animation
        self.acc = vec(0,p_grav) #Prevents image from moving infinitely after 1 press of key
        #acceleration due to gravity is always active which is why it assigned to the self.acc variable
        keys = p.key.get_pressed() #Allows the program to kNow which keys have been pressed
        #Setting acceleration
        if keys[p.K_LEFT]: #If left arrow is pressed
            self.acc.x = -p_acc #acceleration is the set value in the left direction
        if keys[p.K_RIGHT]: #If right arrow is pressed
            self.acc.x = p_acc #acceleration is the set value in the right direction
        #Before moving the image,the acceleration needs to be adjusted by the friction
        self.acc.x += self.vel.x * p_friction #Since friction is negative,the acceleration is constantly getting smaller until is it reaches 0
        #Moving image
        self.vel += self.acc #New veLocity is the old veLocity + the new acceleration
        self.pos += self.vel + 0.5 * self.acc #Uses SUVAT to work out position - v^2 = u^2 + 2as --> rearrange for s
        #Image doesnt move off of screen - sides
        if self.rect.right > width - 1: #Position is bigger than the width when it goes past the right side of the screen
            self.rect.right = width - 1 #Makes it stay at the right side of the screen/right border
            self.pos = self.rect.midbottom #redefines where the rectangle is after it has hit the right border
        if self.rect.left < 1: #Position is smaller than 0 when it goes past the left side of the screen
            self.rect.left = 1 #Makes it stay at the left side of the screen/left border
            self.pos = self.rect.midbottom #redefines where the rectangle is after it has hit the left border
        self.rect.midbottom = self.pos #Puts the center of the image to the new position
        #Image doesnt move off of screen - bottom
        if self.rect.top > height: #If the top of the player rectangle is bigger than the height of the screen/off the screen
            self.rect.midbottom = (50,height - 30) #redefines where the bottom of the rectangle is
            self.pos = self.rect.midbottom #Pus the image in the new position
            self.game.deaths += 1
       #Enemy collisions
        e_hits = p.sprite.spritecollide(self,self.game.enemies,False) #Checks for collisions between player and enemies. False means not to delete
        if e_hits: #If an enemy is hit
            self.rect.midbottom = (50,height - 30) #redefines where the bottom of the rectangle is
            self.pos = self.rect.midbottom #Pus the image in the new position
            self.game.deaths += 1
        #Goal Collisions
        g_hits = p.sprite.spritecollide(self,self.game.goals,False) #Checks for collisions between player and goals. False means not to delete
        if g_hits: #If a goal is hit
            print("Well Done,you completed the level") #Prints a message - Temp
            self.game.running = False #Sets the game running state to false
            p.quit() #Quits the window

主游戏循环类:

import pygame as p #Imports the PyGame library and is referenced with 'p'
from sprites import * #Allows the contents of sprites.py to be used
from settings import * #Allows the contents of settings.py to be used

class Game:
    def __init__(self): #Initiallises game window
        #Initialises PyGame and creates a window
        p.init()  #Initialises pygame and gets it ready to be used
        p.mixer.init() #Initialises sound effects and music that will be used
        self.window = p.display.set_mode((width,height)) #Creates the window with the designated width and height
        p.display.set_caption(title) #Titles the window
        self.clock = p.time.Clock() #Handles the speed and keeps track of speed
        self.running = True #Allows the loop to run indefinitely
        self.font_name = p.font.match_font(font_name) #Matches the font as closes as possible

    def new(self): #Starts a new game
        self.deaths = 0 #Set to 0 when a new game is made
        self.all_sprites = p.sprite.Group() #Creates a group for the sprites that will be used later
        self.goals = p.sprite.Group()
        self.platforms = p.sprite.Group() #Creates a group for just platforms
        self.enemies = p.sprite.Group() #Creates a group for just enemies
        self.player = Player(self) #Creates an instance of the Player class,sends a copy of the game class as well
        self.all_sprites.add(self.player) #Adds the player class to the all sprites group
        #Platforms
        for plat in l1_platforms: #For each item in the list
            pl = Platform(plat[0],plat[1],plat[2],plat[3]) #Create a platform with its components from the list
            self.all_sprites.add(pl) #Adds the platform to the all sprites list/group
            self.platforms.add(pl) #Adds the platform to the all platforms list/group
        #Enemies
        for enemy in l1_enemies: #For each item in the list
            e = Enemy(enemy[0],enemy[1],enemy[2],enemy[3]) #Create a enemy with its components from the list
            self.all_sprites.add(e) #Adds the platform to the all sprites list/group
            self.enemies.add(e) #Adds the platform to the all enemies list/group
        #Goals
        for goal in l1_goal:
            g = Goal(goal[0],goal[1],goal[2],goal[3]) #Create a enemy with its components from the list
            self.all_sprites.add(g) #Adds the platform to the all sprites list/group
            self.goals.add(g) #Adds the platform to the all enemies list/group
        self.run()

    def run(self): #Game Loop
        self.playing = True
        while self.playing: #Indefinetly runs while the player is playing the game
            self.clock.tick(fps) #Tells PyGame to run the whole loop at the set frames per second
            self.events()
            self.update()
            self.draw()

    def update(self): #Game Loop Updates
        self.all_sprites.update() #Updates the group so that sprites are removed/added correctly - so that the list is up to date
        #Collision checks
        p_hits = p.sprite.spritecollide(self.player,self.platforms,False) #Checks for collisions between player and platforms. False means not to delete
        #Platform collisions
        if self.player.vel.y > 0: #If the player is falling
            if p_hits: #If a platform is hit
                self.player.rect.bottom = p_hits[0].rect.top + 1 #Takes the bottom position of the player rectangle,and sets it to the top of the first object it hit
                self.player.pos = self.player.rect.midbottom #Sets the image position
                self.player.vel.y = 0 #Prevents player from sinking through platform with left over momemtum
        if self.player.vel.y < 0: #If the player is jumping
            if p_hits: #If a platforrm is hit
                self.player.rect.top = p_hits[0].rect.bottom #Takes the top position of the player rectangle,and sets it to the bottom of the first object it hit
                self.player.pos = self.player.rect.midbottom #Sets the image position
                self.player.vel.y = 0 #Prevents player from sinking through platform with left over momemtum
    def events(self): #Game Loop Events
        for event in p.event.get(): #Checks for any events while the loop is running
            if event.type == p.QUIT: #Checks if the 'X' has been pressed at top right window
                if self.playing:
                    self.playing = False #If they quit,then they are no longer playing the game
                self.running = False #Ends the game loop by stopping the loop
            if event.type == p.KEYDOWN: #If a key is pressed down
                if event.key == p.K_SPACE: #If space is pressed down
                    self.player.jump() #Jumps using the code in player class

    def draw(self): #Game Loop Draw section
        self.window.fill(bg_colour) #Sets the colour of the window to the variable bg_colour
        self.all_sprites.draw(self.window) #Draws what is in the all_sprites group onto the window
        self.draw_text('Deaths: ' +str(self.deaths),32,white,width / 2,20)
        p.display.flip() #Flips the display after everything is drawn. Outputs to the screen

    def draw_text(self,text,size,colour,x,y): #Used to draw text onto the screen - other attributes are needed
        font = p.font.Font(self.font_name,size) #Creates a font object
        text_surface = font.render(text,True,colour) #Generates a surface to render the font onto (text,AA,colour)
        text_rect = text_surface.get_rect() #Generates a rectangle to help locate it
        text_rect.midtop = (x,y) #Positions the rectangle.
        self.window.blit(text_surface,text_rect) #Outputs it onto the screen

        g = Game() #Creates an instance of the Game class
        while g.running:
            g.new() #Starts a new game
        p.quit() #Exits the program

请原谅任何格式错误

解决方法

很难遵循您的代码。但是,有两个明显的错误:

  1. 在对 self.rect 执行越界检查之前,通过 slef.pos 更新 slef.rect 的位置。

  2. pygame.Rect 对象只能存储积分坐标。使用 roundself.rect 的位置更新为 slef.pos

class Player(p.sprite.Sprite):
    # [...]

    def update(self):
        # [...]

        self.pos += self.vel + 0.5 * self.acc

        # update "self.rect" before out of bounds check
        self.rect.midbottom = round(self.pos[0]),round(self.pos[1]) 

        if self.rect.right > width - 1: 
            self.rect.right = width - 1
            self.pos = self.rect.midbottom
        if self.rect.left < 1:
            self.rect.left = 1 
            self.pos = self.rect.midbottom 

        self.rect.midbottom = round(self.pos[0]),round(self.pos[1])

        # [...]