pygame 如果 window 较小,精灵移动得更快
pygame sprite move faster if window is smaller
如果我的游戏处于 window 模式,我的角色精灵移动得更快。为了设置我使用的速度 ROOTwidth
,理论上应该按比例缩放速度...
这是我的代码(简化版)
#MAIN CODE
#ROOT dimension don't change (window can't be resized while playing,
#only in main menu function where ROOTwidth, ROOTheight are obtained)
ROOTwidth, ROOTheight = pygame.display.get_surface().get_size()
velocity = ROOTheight/450
playertopx = ROOTwidth/2.2
playertopy = ROOTwidth/2
playermovement = PlayerMovement(playertopx, playertopy)
while True:
key = pygame.key.get_pressed()
if key[pygame.K_w]:
playermovement.human_moveup(velocity)
#PLAYER MOVEMENT CLASS
import pygame
class PlayerMovement:
#init
def __init__(self, playertopx, playertopy):
self.x = playertopx
self.y = playertopy
#movement
def human_moveup(self, velocity):
self.y -= velocity
#MAIN CODE
ROOT.blit(playermovement.spritesheet_human, (playermovement.x, playermovement.y), (0, 50, 25, 18))
我不知道该怎么做...对于我游戏中的每个元素,使用 ROOT
维度都可以正常工作,只有 velocity
我有问题
我想这可能是因为您的主事件循环的循环速度取决于在 window 上绘制所需的时间,而没有考虑到这一点。
执行速度
假设您将此代码作为主事件循环:
while NotExited:
doGameLogic() # Your velocity computations and other stuff
drawOnWindow() # Filling entire window with background, drawing sprites over, refreshing it, etc
现在,假设 doGameLogic()
总是需要 1 毫秒(0.001 秒)的时间,而 drawOnWindow()
总是需要 50 毫秒。虽然此循环是 运行ning,因此,该循环总共将占用 51 毫秒,因此 doGameLogic()
将每 51 毫秒调用一次。
然后你在那里进行速度计算。为简单起见,假设您每次都在其中 playermovement.x += 5
。
因此,您的玩家的 X 坐标每 51 毫秒增加 5 个单位。这相当于在一秒钟内增加了大约 98 个单位。
执行速度的差异
现在假设 drawOnWindow()
开始花费 20 毫秒的时间。然后循环总共花费 21 毫秒的时间到 运行,这也会导致每 21 毫秒从 doGameLogic()
到 运行。在这种情况下,X 坐标每 21 毫秒增加 5 个单位,相当于每秒增加 238 个单位。
这比之前的每秒 98 个单位快多了。因为现在绘图花费的时间更少,所以您的角色最终移动得更快。
我认为这就是您的情况。当您使 window 更小时,绘图调用(例如用颜色绘制 background/filling 它)花费的时间更少,因为要绘制的像素更少,因此改变了 drawOnWindow()
花费的时间,因此 doGameLogic()
为 运行 的频率发生变化。
修复
有许多不同的方法可以解决这个问题。这里有一些:
强制循环速度
其中之一是确保您的循环始终花费完全相同的时间到 运行,而不管调用花费多少时间:
import time
while NotExited:
startTime = time.time() # Record when the loop was started
doGameLogic()
drawOnWindow()
# Calculate how long did it take the loop to run.
HowLong = time.time() - startTime
# Sleep until this loop takes exactly 0.05 seconds.
# The "max" call is to ensure we don't try to sleep
# for a negative value if the loop took longer than that.
time.sleep(max(0, 0.05-HowLong))
或者,您用于渲染的库可能允许您设置 FPS(每秒帧数)的上限,这也可以确保绘制所需的时间恒定。
这种方法有一个缺点,如果循环时间超过指定的时间,它就会失效,并且在相反的情况下限制你的游戏速度 运行s,但它很容易实现。
随速度扩展
与其确保 playermovement.x += 5
和其余逻辑恰好每 50 毫秒一次 运行,您可以确保它 运行 的值按比例缩放通常是 运行,产生相同的结果。
换句话说,运行ning playermovement.x += 5
每 50 毫秒一次完全等同于 运行ning playermovement.x += 1
每 10 毫秒一次:作为任一结果,每 50 毫秒该值增加了 5 个单位。
我们可以计算渲染最后一帧所花费的时间,然后按比例调整计算中的值:
import time
# This will store when was the last frame started.
# Initialize with a reasonable value for now.
previousTime = time.time()
while NotExited:
# Get how long it took to run the loop the last time.
difference = time.time() - previousTime
# Get a scale value to adjust for the delay.
# The faster the game runs, the smaller this value is.
# If difference is 50ms, this returns 1.
# If difference is 100ms, this returns 2.
timeScale = difference / 0.05
doGameLogic(timeScale)
drawOnWindow()
previousTime = time.time()
# ... in the game logic:
def doGameLogic(timeScale):
# ...
# Perform game logic proportionally to the loop speed.
playermovement.x += 5 * timeScale
此方法根据速度更具适应性,但无论何时执行此类依赖于时间的操作都需要考虑。
它也可能是独特问题的根源:例如,如果您的游戏 运行 即使是一帧也非常非常慢,时间比例值可能会变得不成比例地大,从而导致 playermovement.x
增加 5*100000
,将您的玩家角色传送到很远的地方。如果循环速度不稳定,它也会产生不稳定的结果,并且由于它是用浮点数学执行的,所以会产生更多问题。
解耦逻辑和渲染
另一种比其他方法更可靠但更难实现的方法是将 doGameLogic()
与 drawOnWindow()
分离,允许一个 运行 独立于另一个。这通常通过使用多线程来实现。
您可以同时进行两个循环 运行ning:一个 运行s doGameLogic()
在固定的时间间隔内,例如 10 毫秒,使用上述“强制循环速度”方法,以及另一个 运行s drawOnWindow()
尽可能快地以任意速度在 window 上呈现。
这个方法还涉及到插值的问题(如果drawOnWindow()
运行比doGameLogic()
快一倍,你可能不希望每次都画出一模一样的图像,但中间的一个看起来更平滑)和线程管理(确保你不在 window 上绘制而 doGameLogic()
仍然是 运行ning,因为你可能绘制不完整的游戏状态正在处理中)。
不幸的是,我的知识不足以提供代码示例,我什至不确定这在 Python 或 PyGame.
中是否可行
如果我的游戏处于 window 模式,我的角色精灵移动得更快。为了设置我使用的速度 ROOTwidth
,理论上应该按比例缩放速度...
这是我的代码(简化版)
#MAIN CODE
#ROOT dimension don't change (window can't be resized while playing,
#only in main menu function where ROOTwidth, ROOTheight are obtained)
ROOTwidth, ROOTheight = pygame.display.get_surface().get_size()
velocity = ROOTheight/450
playertopx = ROOTwidth/2.2
playertopy = ROOTwidth/2
playermovement = PlayerMovement(playertopx, playertopy)
while True:
key = pygame.key.get_pressed()
if key[pygame.K_w]:
playermovement.human_moveup(velocity)
#PLAYER MOVEMENT CLASS
import pygame
class PlayerMovement:
#init
def __init__(self, playertopx, playertopy):
self.x = playertopx
self.y = playertopy
#movement
def human_moveup(self, velocity):
self.y -= velocity
#MAIN CODE
ROOT.blit(playermovement.spritesheet_human, (playermovement.x, playermovement.y), (0, 50, 25, 18))
我不知道该怎么做...对于我游戏中的每个元素,使用 ROOT
维度都可以正常工作,只有 velocity
我有问题
我想这可能是因为您的主事件循环的循环速度取决于在 window 上绘制所需的时间,而没有考虑到这一点。
执行速度
假设您将此代码作为主事件循环:
while NotExited:
doGameLogic() # Your velocity computations and other stuff
drawOnWindow() # Filling entire window with background, drawing sprites over, refreshing it, etc
现在,假设 doGameLogic()
总是需要 1 毫秒(0.001 秒)的时间,而 drawOnWindow()
总是需要 50 毫秒。虽然此循环是 运行ning,因此,该循环总共将占用 51 毫秒,因此 doGameLogic()
将每 51 毫秒调用一次。
然后你在那里进行速度计算。为简单起见,假设您每次都在其中 playermovement.x += 5
。
因此,您的玩家的 X 坐标每 51 毫秒增加 5 个单位。这相当于在一秒钟内增加了大约 98 个单位。
执行速度的差异
现在假设 drawOnWindow()
开始花费 20 毫秒的时间。然后循环总共花费 21 毫秒的时间到 运行,这也会导致每 21 毫秒从 doGameLogic()
到 运行。在这种情况下,X 坐标每 21 毫秒增加 5 个单位,相当于每秒增加 238 个单位。
这比之前的每秒 98 个单位快多了。因为现在绘图花费的时间更少,所以您的角色最终移动得更快。
我认为这就是您的情况。当您使 window 更小时,绘图调用(例如用颜色绘制 background/filling 它)花费的时间更少,因为要绘制的像素更少,因此改变了 drawOnWindow()
花费的时间,因此 doGameLogic()
为 运行 的频率发生变化。
修复
有许多不同的方法可以解决这个问题。这里有一些:
强制循环速度
其中之一是确保您的循环始终花费完全相同的时间到 运行,而不管调用花费多少时间:
import time
while NotExited:
startTime = time.time() # Record when the loop was started
doGameLogic()
drawOnWindow()
# Calculate how long did it take the loop to run.
HowLong = time.time() - startTime
# Sleep until this loop takes exactly 0.05 seconds.
# The "max" call is to ensure we don't try to sleep
# for a negative value if the loop took longer than that.
time.sleep(max(0, 0.05-HowLong))
或者,您用于渲染的库可能允许您设置 FPS(每秒帧数)的上限,这也可以确保绘制所需的时间恒定。
这种方法有一个缺点,如果循环时间超过指定的时间,它就会失效,并且在相反的情况下限制你的游戏速度 运行s,但它很容易实现。
随速度扩展
与其确保 playermovement.x += 5
和其余逻辑恰好每 50 毫秒一次 运行,您可以确保它 运行 的值按比例缩放通常是 运行,产生相同的结果。
换句话说,运行ning playermovement.x += 5
每 50 毫秒一次完全等同于 运行ning playermovement.x += 1
每 10 毫秒一次:作为任一结果,每 50 毫秒该值增加了 5 个单位。
我们可以计算渲染最后一帧所花费的时间,然后按比例调整计算中的值:
import time
# This will store when was the last frame started.
# Initialize with a reasonable value for now.
previousTime = time.time()
while NotExited:
# Get how long it took to run the loop the last time.
difference = time.time() - previousTime
# Get a scale value to adjust for the delay.
# The faster the game runs, the smaller this value is.
# If difference is 50ms, this returns 1.
# If difference is 100ms, this returns 2.
timeScale = difference / 0.05
doGameLogic(timeScale)
drawOnWindow()
previousTime = time.time()
# ... in the game logic:
def doGameLogic(timeScale):
# ...
# Perform game logic proportionally to the loop speed.
playermovement.x += 5 * timeScale
此方法根据速度更具适应性,但无论何时执行此类依赖于时间的操作都需要考虑。
它也可能是独特问题的根源:例如,如果您的游戏 运行 即使是一帧也非常非常慢,时间比例值可能会变得不成比例地大,从而导致 playermovement.x
增加 5*100000
,将您的玩家角色传送到很远的地方。如果循环速度不稳定,它也会产生不稳定的结果,并且由于它是用浮点数学执行的,所以会产生更多问题。
解耦逻辑和渲染
另一种比其他方法更可靠但更难实现的方法是将 doGameLogic()
与 drawOnWindow()
分离,允许一个 运行 独立于另一个。这通常通过使用多线程来实现。
您可以同时进行两个循环 运行ning:一个 运行s doGameLogic()
在固定的时间间隔内,例如 10 毫秒,使用上述“强制循环速度”方法,以及另一个 运行s drawOnWindow()
尽可能快地以任意速度在 window 上呈现。
这个方法还涉及到插值的问题(如果drawOnWindow()
运行比doGameLogic()
快一倍,你可能不希望每次都画出一模一样的图像,但中间的一个看起来更平滑)和线程管理(确保你不在 window 上绘制而 doGameLogic()
仍然是 运行ning,因为你可能绘制不完整的游戏状态正在处理中)。
不幸的是,我的知识不足以提供代码示例,我什至不确定这在 Python 或 PyGame.
中是否可行