pygame 精灵运动机制
pygame sprite movement mechanics
我尝试在 pygame 中创建的平台游戏的机制有问题。我是一名尝试学习 python 的初学者,所以这基本上是我从在线教程和此处找到的信息片段中使用的代码。
有人可以帮忙吗?
问题是当玩家精灵向左移动时(通过按向左箭头键)它不会像向右移动时那样稳定地停止。
我找不到导致错误的原因。
代码:
import pygame as pg
GAME_TITLE = "Plat Game"
SCREEN_WIDTH = 400
SCREEN_HEIGHT = 400
FPS = 60
GAME_WINDOW = pg.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
GAME_TIMER = 0
RUNNING_GAME = True
PLAYER_SIZE_X = 20
PLAYER_SIZE_Y = 20
PLAYER_FRICTION = -0.1
PLAYER_GRAVITY = 0.8
PLAYER_SPEED = 0.5
PLAYER_VELOCITY_X = 0.0
PLAYER_VELOCITY_Y = PLAYER_GRAVITY
PLAT_SIZE_X = 4
PLAT_SIZE_Y = 4
player_move_left_key = pg.K_LEFT
player_move_right_key = pg.K_RIGHT
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
YELLOW = (255, 255, 0)
BACK_GROUND_COLOUR = BLACK
PLAYER_COLOUR = RED
class Player(pg.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pg.Surface([PLAYER_SIZE_X, PLAYER_SIZE_Y])
self.image.fill(RED)
self.rect = self.image.get_rect()
self.PLAYER_VELOCITY_X = 0
self.PLAYER_VELOCITY_Y = 0
self.PLAYER_ACCELERATION_X = 0
self.PLAYER_ACCELERATION_Y = 0
def calc_x_movement(self):
self.PLAYER_ACCELERATION_X += self.PLAYER_VELOCITY_X * PLAYER_FRICTION
self.PLAYER_VELOCITY_X += self.PLAYER_ACCELERATION_X
self.rect.x += self.PLAYER_VELOCITY_X + 0.5 * self.PLAYER_ACCELERATION_X
def calc_gravity(self):
self.PLAYER_ACCELERATION_Y += PLAYER_GRAVITY
self.PLAYER_VELOCITY_Y += self.PLAYER_ACCELERATION_Y
self.rect.y += self.PLAYER_VELOCITY_Y + 0.5 * self.PLAYER_ACCELERATION_Y
def check_for_V_collisions(self):
block_hit_list = pg.sprite.spritecollide(self, self.level.platform_list, False)
for block in block_hit_list:
if self.PLAYER_VELOCITY_Y > 0:
self.rect.bottom = block.rect.top
self.PLAYER_Y = self.rect.bottom
self.PLAYER_VELOCITY_Y = 0
def controls(self):
keys = pg.key.get_pressed()
if keys[player_move_left_key]:
self.PLAYER_ACCELERATION_X = -PLAYER_SPEED
if keys[player_move_right_key]:
self.PLAYER_ACCELERATION_X = PLAYER_SPEED
for event in pg.event.get():
if event.type == pg.QUIT:
quitGame()
if event.type == pg.KEYDOWN:
if event.key == pg.K_ESCAPE:
pg.quit()
sys.exit()
if event.type == pg.QUIT:
quitGame()
def movement(self):
self.PLAYER_ACCELERATION_X = 0
self.PLAYER_ACCELERATION_Y = 0
self.PLAYER_GRAVITY = PLAYER_GRAVITY
self.controls()
self.calc_x_movement()
self.calc_gravity()
self.check_for_V_collisions()
class Platform(pg.sprite.Sprite):
def __init__(self, width, height):
super().__init__()
self.image = pg.Surface([width, height])
self.image.fill(YELLOW)
self.rect = self.image.get_rect()
class Level():
def __init__(self, player):
self.platform_list = pg.sprite.Group()
self.enemy_list = pg.sprite.Group()
self.player = player
self.level = []
def update(self):
self.platform_list.update()
def draw(self, screen):
screen.fill(BACK_GROUND_COLOUR)
self.platform_list.draw(screen)
class Level_01(Level):
def __init__(self, player):
Level.__init__(self, player)
level = [[1000, 10, 0, 300]]
for platform in level:
block = Platform(platform[0], platform[1])
block.rect.x = platform[2]
block.rect.y = platform[3]
block.player = self.player
self.platform_list.add(block)
showStats = True
class Game:
def __init__(self):
pg.init()
pg.font.init()
pg.mixer.init()
pg.display.set_caption(GAME_TITLE)
self.clock = pg.time.Clock()
self.screen = pg.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
self.fontTypeA = pg.font.SysFont("Comic Sans MS", 10)
self.GAME_TIMER = 0
self.running = True
def new(self):
self.player = Player()
level_list = []
level_list.append(Level_01(self.player))
self.current_level_no = 0
self.current_level = level_list[self.current_level_no]
self.active_sprite_list = pg.sprite.Group()
self.player.level = self.current_level
self.player.rect.x = 200
self.player.rect.y = 0
self.active_sprite_list.add(self.player)
self.run()
def run(self):
self.playing = True
while self.playing:
self.update()
self.draw()
def update(self):
self.active_sprite_list.update()
self.current_level.update()
self.player.movement()
self.GAME_TIMER += (1 / FPS)
self.clock.tick(FPS)
def draw(self):
self.current_level.draw(GAME_WINDOW)
self.active_sprite_list.draw(GAME_WINDOW)
if showStats is True:
self.display_player_stats()
pg.display.flip()
def draw_text(self, font_name, text, size, colour, x, y):
font = pg.font.SysFont(font_name, size)
text_surface = font.render(text, True, colour)
text_rect = text_surface.get_rect()
text_rect.midtop = (x, y)
GAME_WINDOW.blit(text_surface, text_rect)
def display_player_stats(self):
text1 = "Vel.Y = " + ("%.2f" % self.player.PLAYER_VELOCITY_Y)
text2 = "Acc.Y = " + ("%.2f" % self.player.PLAYER_ACCELERATION_Y)
text3 = "Pos.Y = " + ("%.2f" % self.player.rect.y)
text4 = "Vel.X = " + ("%.2f" % self.player.PLAYER_VELOCITY_X)
text5 = "Acc.X = " + ("%.2f" % self.player.PLAYER_ACCELERATION_X)
text6 = "Pos.X = " + ("%.2f" % self.player.rect.x)
text7 = "Timer = " + ("%.2f" % self.GAME_TIMER)
self.draw_text("Comic Sans MS", text1, 10, GREEN, 40, 5)
self.draw_text("Comic Sans MS", text2, 10, GREEN, 40, 15)
self.draw_text("Comic Sans MS", text3, 10, GREEN, 40, 25)
self.draw_text("Comic Sans MS", text4, 10, YELLOW, 40, 40)
self.draw_text("Comic Sans MS", text5, 10, YELLOW, 40, 50)
self.draw_text("Comic Sans MS", text6, 10, YELLOW, 40, 60)
self.draw_text("Comic Sans MS", text7, 10, WHITE, SCREEN_WIDTH - 50, 5)
g = Game() while g.running:
g.new()
pg.quit()
问题是 float
值。
您对 float
值进行计算,但 rect.x
仅保留 integer
值,因此它将所有 float
计算四舍五入为 integer
- 但它确实如此以错误的方式给出了错误的结果。
您可以将所有计算保留为 float
- 即。在变量 self.x
中
self.x += self.PLAYER_VELOCITY_X + 0.5 * self.PLAYER_ACCELERATION_X
所有计算后设置self.rect.x
self.rect.x = self.x
编辑:
可能 rect.x
将添加的值四舍五入为 floor
因此它将 -0.1
转换为 -1
而不是 0
然后 rect.x += -0.1
给出 rect.x += -1
而不是 rect.x += 0
您也可以在添加之前使用round()
手动转换float
self.rect.x += round(self.PLAYER_VELOCITY_X + 0.5 * self.PLAYER_ACCELERATION_X)
但是使用 self.x
来保持 float
值可以得到更好的结果。
(float
无法保留所有真实值,但这应该不是问题。或者您可以使用 decimal 模块)
我尝试在 pygame 中创建的平台游戏的机制有问题。我是一名尝试学习 python 的初学者,所以这基本上是我从在线教程和此处找到的信息片段中使用的代码。 有人可以帮忙吗?
问题是当玩家精灵向左移动时(通过按向左箭头键)它不会像向右移动时那样稳定地停止。 我找不到导致错误的原因。
代码:
import pygame as pg
GAME_TITLE = "Plat Game"
SCREEN_WIDTH = 400
SCREEN_HEIGHT = 400
FPS = 60
GAME_WINDOW = pg.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
GAME_TIMER = 0
RUNNING_GAME = True
PLAYER_SIZE_X = 20
PLAYER_SIZE_Y = 20
PLAYER_FRICTION = -0.1
PLAYER_GRAVITY = 0.8
PLAYER_SPEED = 0.5
PLAYER_VELOCITY_X = 0.0
PLAYER_VELOCITY_Y = PLAYER_GRAVITY
PLAT_SIZE_X = 4
PLAT_SIZE_Y = 4
player_move_left_key = pg.K_LEFT
player_move_right_key = pg.K_RIGHT
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
YELLOW = (255, 255, 0)
BACK_GROUND_COLOUR = BLACK
PLAYER_COLOUR = RED
class Player(pg.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pg.Surface([PLAYER_SIZE_X, PLAYER_SIZE_Y])
self.image.fill(RED)
self.rect = self.image.get_rect()
self.PLAYER_VELOCITY_X = 0
self.PLAYER_VELOCITY_Y = 0
self.PLAYER_ACCELERATION_X = 0
self.PLAYER_ACCELERATION_Y = 0
def calc_x_movement(self):
self.PLAYER_ACCELERATION_X += self.PLAYER_VELOCITY_X * PLAYER_FRICTION
self.PLAYER_VELOCITY_X += self.PLAYER_ACCELERATION_X
self.rect.x += self.PLAYER_VELOCITY_X + 0.5 * self.PLAYER_ACCELERATION_X
def calc_gravity(self):
self.PLAYER_ACCELERATION_Y += PLAYER_GRAVITY
self.PLAYER_VELOCITY_Y += self.PLAYER_ACCELERATION_Y
self.rect.y += self.PLAYER_VELOCITY_Y + 0.5 * self.PLAYER_ACCELERATION_Y
def check_for_V_collisions(self):
block_hit_list = pg.sprite.spritecollide(self, self.level.platform_list, False)
for block in block_hit_list:
if self.PLAYER_VELOCITY_Y > 0:
self.rect.bottom = block.rect.top
self.PLAYER_Y = self.rect.bottom
self.PLAYER_VELOCITY_Y = 0
def controls(self):
keys = pg.key.get_pressed()
if keys[player_move_left_key]:
self.PLAYER_ACCELERATION_X = -PLAYER_SPEED
if keys[player_move_right_key]:
self.PLAYER_ACCELERATION_X = PLAYER_SPEED
for event in pg.event.get():
if event.type == pg.QUIT:
quitGame()
if event.type == pg.KEYDOWN:
if event.key == pg.K_ESCAPE:
pg.quit()
sys.exit()
if event.type == pg.QUIT:
quitGame()
def movement(self):
self.PLAYER_ACCELERATION_X = 0
self.PLAYER_ACCELERATION_Y = 0
self.PLAYER_GRAVITY = PLAYER_GRAVITY
self.controls()
self.calc_x_movement()
self.calc_gravity()
self.check_for_V_collisions()
class Platform(pg.sprite.Sprite):
def __init__(self, width, height):
super().__init__()
self.image = pg.Surface([width, height])
self.image.fill(YELLOW)
self.rect = self.image.get_rect()
class Level():
def __init__(self, player):
self.platform_list = pg.sprite.Group()
self.enemy_list = pg.sprite.Group()
self.player = player
self.level = []
def update(self):
self.platform_list.update()
def draw(self, screen):
screen.fill(BACK_GROUND_COLOUR)
self.platform_list.draw(screen)
class Level_01(Level):
def __init__(self, player):
Level.__init__(self, player)
level = [[1000, 10, 0, 300]]
for platform in level:
block = Platform(platform[0], platform[1])
block.rect.x = platform[2]
block.rect.y = platform[3]
block.player = self.player
self.platform_list.add(block)
showStats = True
class Game:
def __init__(self):
pg.init()
pg.font.init()
pg.mixer.init()
pg.display.set_caption(GAME_TITLE)
self.clock = pg.time.Clock()
self.screen = pg.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
self.fontTypeA = pg.font.SysFont("Comic Sans MS", 10)
self.GAME_TIMER = 0
self.running = True
def new(self):
self.player = Player()
level_list = []
level_list.append(Level_01(self.player))
self.current_level_no = 0
self.current_level = level_list[self.current_level_no]
self.active_sprite_list = pg.sprite.Group()
self.player.level = self.current_level
self.player.rect.x = 200
self.player.rect.y = 0
self.active_sprite_list.add(self.player)
self.run()
def run(self):
self.playing = True
while self.playing:
self.update()
self.draw()
def update(self):
self.active_sprite_list.update()
self.current_level.update()
self.player.movement()
self.GAME_TIMER += (1 / FPS)
self.clock.tick(FPS)
def draw(self):
self.current_level.draw(GAME_WINDOW)
self.active_sprite_list.draw(GAME_WINDOW)
if showStats is True:
self.display_player_stats()
pg.display.flip()
def draw_text(self, font_name, text, size, colour, x, y):
font = pg.font.SysFont(font_name, size)
text_surface = font.render(text, True, colour)
text_rect = text_surface.get_rect()
text_rect.midtop = (x, y)
GAME_WINDOW.blit(text_surface, text_rect)
def display_player_stats(self):
text1 = "Vel.Y = " + ("%.2f" % self.player.PLAYER_VELOCITY_Y)
text2 = "Acc.Y = " + ("%.2f" % self.player.PLAYER_ACCELERATION_Y)
text3 = "Pos.Y = " + ("%.2f" % self.player.rect.y)
text4 = "Vel.X = " + ("%.2f" % self.player.PLAYER_VELOCITY_X)
text5 = "Acc.X = " + ("%.2f" % self.player.PLAYER_ACCELERATION_X)
text6 = "Pos.X = " + ("%.2f" % self.player.rect.x)
text7 = "Timer = " + ("%.2f" % self.GAME_TIMER)
self.draw_text("Comic Sans MS", text1, 10, GREEN, 40, 5)
self.draw_text("Comic Sans MS", text2, 10, GREEN, 40, 15)
self.draw_text("Comic Sans MS", text3, 10, GREEN, 40, 25)
self.draw_text("Comic Sans MS", text4, 10, YELLOW, 40, 40)
self.draw_text("Comic Sans MS", text5, 10, YELLOW, 40, 50)
self.draw_text("Comic Sans MS", text6, 10, YELLOW, 40, 60)
self.draw_text("Comic Sans MS", text7, 10, WHITE, SCREEN_WIDTH - 50, 5)
g = Game() while g.running:
g.new()
pg.quit()
问题是 float
值。
您对 float
值进行计算,但 rect.x
仅保留 integer
值,因此它将所有 float
计算四舍五入为 integer
- 但它确实如此以错误的方式给出了错误的结果。
您可以将所有计算保留为 float
- 即。在变量 self.x
self.x += self.PLAYER_VELOCITY_X + 0.5 * self.PLAYER_ACCELERATION_X
所有计算后设置self.rect.x
self.rect.x = self.x
编辑:
可能 rect.x
将添加的值四舍五入为 floor
因此它将 -0.1
转换为 -1
而不是 0
然后 rect.x += -0.1
给出 rect.x += -1
而不是 rect.x += 0
您也可以在添加之前使用round()
手动转换float
self.rect.x += round(self.PLAYER_VELOCITY_X + 0.5 * self.PLAYER_ACCELERATION_X)
但是使用 self.x
来保持 float
值可以得到更好的结果。
(float
无法保留所有真实值,但这应该不是问题。或者您可以使用 decimal 模块)