问题描述
我正在创建一个平台游戏,当我按向左箭头键移动玩家一次时,它继续滑动,而当我按一次向右箭头键时,它移动一次并且不滑动。我已经查看了我的代码,但我不知道出了什么问题,这就是我来到这里的原因。我将在下面展示主要的游戏循环类,以及我的玩家类,因为我认为这是相关的。
玩家等级:
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
请原谅任何格式错误。
解决方法
很难遵循您的代码。但是,有两个明显的错误:
-
在对
self.rect
执行越界检查之前,通过slef.pos
更新slef.rect
的位置。 -
pygame.Rect
对象只能存储积分坐标。使用round
将self.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])
# [...]