<p>下面是您的代码和建议的修复程序,以及其他一些改进建议。在</p>
<p>第一件事:错的是你从来没有检查过水平移动是否会使块碰撞。你只做了一次检查,实际上它只在碰撞发生后才做任何事情。在这种情况下,它会阻止“重力”做任何事情。因为你正在逐像素更新你的播放器,碰撞已经发生的事实基本上没有被注意到。解决方法是创建一个检查来验证移动块是否可以移动到某个位置,然后才允许它移动。做所有的运动,而不仅仅是重力运动。在</p>
<p>因此,在这里我将解释一些改进,这些改进可以使这个示例代码演变成一个完整的游戏。在</p>
<ol>
<li><p>消除模块级的“浮动代码”,将所有内容都放在函数中。这对于未来任何一款游戏来说都是必不可少的,它甚至会有一个简单的开始屏幕(可能还有一个“再次播放”屏幕)。我建立了两个函数
你的游戏环境和创建你的对象,以及游戏的主要功能。你以后应该把“设置”阶段进一步分开,
为了能够创建更多的游戏对象,或者不同的游戏级别。</p></li>
<li><p>重用代码。您的<code>__init__</code>方法中的行大部分在两个类之间重复,但是对于初始位置和图像名常量。所以实际上只需要一个类,并为这些属性传递不同的值。因为你暗示你将有两种不同的游戏对象类型,通过使用群组等,我保留了这两个类,但考虑了共同的代码。</p></li>
<li><p>帧延迟:你的代码只会“尽可能快地”移动——这将在不同的计算机上创建不同的游戏速度,并使用100%的CPU——导致可能的机器减速和过度的功耗。我加入了一个30毫秒的硬编码暂停-这将允许你的游戏以接近30帧/秒的速度移动,在不同的设备之间保持稳定的速度。另一个需要改变的是你硬编码的重力和速度的“1”值,这将使玩家每次移动1px,必须更改为更大的值。</p></li>
<li><p>最后,但并非最不重要的是,“我能移动到那里吗”逻辑:它实际上看起来很广泛,但是可以感觉到,对“这个物体能移到那个位置吗”的简单的英语描述是冗长的。该方法的实质是:“存储当前位置”在给定的x和y位移上伪造一个位置检查我是否与什么东西相撞如果没有,恢复先前的位置,并说允许移动否则,恢复y位置,检查X方向是否有碰撞,如有,限制水平移动”恢复x位置,将y位置移动所需量,检查是否发生碰撞。如果是这样,请限制y方向的移动如果移动不受x或y方向的限制,则只是组合移动导致碰撞:限制两个方向的移动是公平的”恢复原始位置,返回x和y位置的允许更改,以及我们将要碰撞的对象”。所有这些逻辑都放在“living”objects类本身中,因此它可以在任何时候使用,在主游戏逻辑中只有一行代码。</p></li>
</ol>
<p>代码:</p>
<pre><code>import pygame
class GameObject(pygame.sprite.Sprite):
def __init__(self, x=0, y=0, s=100, image=""):
super(GameObject, self).__init__()
if not image:
image = self.image
self.image = pygame.transform.scale(pygame.image.load(image), (s, s))
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
class Player(GameObject):
image = "player.png"
def can_move(self, xCh, yCh, group):
old_pos = self.rect.x, self.rect.y
self.rect.x += xCh
self.rect.y += yCh
collided_with = pygame.sprite.spritecollideany(self, group)
if not collided_with:
# No Collisions at all - allow movement
self.rect.x = old_pos[0]
self.rect.y = old_pos[1]
return True, xCh, yCh, None
# Check if the colision was due to horizontal movement:
self.rect.y = old_pos[1]
if pygame.sprite.spritecollideany(self, group):
# Yes, then indicate horizontal movement should be 0
xCh = 0
# check if collision was due to vertical movement:
self.rect.y += yCh
self.rect.x = old_pos[0]
if pygame.sprite.spritecollideany(self, group):
# Yes - indicate vertical movemnt should be 0
yCh = 0
# if only diagonal movement causes collision, then
# cancel movrment in both axes:
# (i.e. just the diagonal movement would hit the
# obstacle )
if not xCh == 0 and not yCh == 0:
xCh = 0
yCh = 0
self.rect.x = old_pos[0]
self.rect.y = old_pos[1]
return False, xCh, yCh, collided_with
class Block(GameObject):
image = "wall.png"
def setup():
global display, player, living, noliving, gravity, speed, sprites
pygame.init()
display = pygame.display.set_mode((800,600))
player = Player()
block = Block(y=500)
sprites = pygame.sprite.Group()
living = pygame.sprite.Group()
noliving = pygame.sprite.Group()
sprites.add(player)
sprites.add(block)
living.add(player)
noliving.add(block)
gravity = 10
speed = 10
def main():
xCh = 0
yCh = 0
while True:
display.fill((159, 159, 159))
for event in pygame.event.get():
if(event.type == pygame.KEYDOWN):
if(event.key == pygame.K_ESCAPE):
quit()
elif(event.key == pygame.K_a or event.key == pygame.K_LEFT):
xCh = -speed
elif(event.key == pygame.K_d or event.key == pygame.K_RIGHT):
xCh = speed
elif(event.type == pygame.KEYUP):
xCh = 0
yCh = 0
elif(event.type == pygame.QUIT):
quit()
yCh = gravity
for liv in living:
if liv == player:
check_x_ch = xCh
else:
check_x_ch = 0
can_move, xCh, yCh, obstacle = liv.can_move(xCh, yCh, noliving)
liv.rect.x += xCh
liv.rect.y += yCh
# Do other actions if "can_move" indicates a block was hit...
sprites.draw(display)
pygame.display.update()
pygame.time.delay(30)
setup()
main()
</code></pre>