Pygame 官方文档 - Tutorials - 游戏制作指南(Making Games Tutorial)- 把所有东西放在一起(Putting it all together)

游戏制作指南(Making Games Tutorial)

点我回总目录

6.把所有东西放在一起(Putting it all together)

        到目前为止,您已经学会了构建简单游戏所需的所有基础知识。 你应该了解如何创建Pygame对象,Pygame如何显示对象,它如何处理事件,以及如何使用物理来为游戏引入一些动作。 现在我将展示如何将所有这些代码块放在一起工作游戏中。 我们首先需要的是让球击中屏幕的两侧,以及球棒能够击球,否则不会有太多的游戏内容。我们使用Pygame的碰撞(collision)方法来做到这一点。

6.1.让球触碰边缘(Let the ball hit sides)

        使其反弹的背后的基本原理很容易掌握。 您抓住球的四个角的坐标,并检查它们是否与屏幕边缘的x或y坐标相对应。 因此,如果右上角和左上角的y坐标均为零,则表示球当前位于屏幕的上边缘。 在我们计算出球的新位置之后,我们在更新功能中执行了所有这些操作。

if not self.area.contains(newpos):
      tl = not self.area.collidepoint(newpos.topleft)
      tr = not self.area.collidepoint(newpos.topright)
      bl = not self.area.collidepoint(newpos.bottomleft)
      br = not self.area.collidepoint(newpos.bottomright)
      if tr and tl or (br and bl):
              angle = -angle
      if tl and bl:
              self.offcourt(player=2)
      if tr and br:
              self.offcourt(player=1)

self.vector = (angle,z)

        在这里我们检查该区域是否包含球的新位置(但在其他情况下你可能想要考虑它。然后,我们检查四个角的坐标是否与区域的边缘碰撞,并为每个结果创建对象。如果是,则对象的值为1或True。如果不是,则值为None或False。然后我们看到 如果它已经击中了顶部或底部,并且如果它已经改变了球的方向。轻松地,使用弧度我们可以通过简单地反转它的正/负值来做到这一点。我们还检查球是否已经偏离了两侧, 如果它有我们调用offcourt函数。在我的游戏中,这会重置球,在调用该函数时指定的玩家得分加1点,并显示新得分。
        最后,我们基于新角度重新编译矢量。 就是这样。 球现在将快乐地从墙上反弹,并以优雅的姿态离开球场。

6.2.让球触碰板子(Let the ball hit bats)

        让球击中板子非常类似于击中屏幕的两侧。 我们仍然使用碰撞方法,但这次我们检查球和任何一个球棒的矩形是否发生碰撞。 在这段代码中,我还添加了一些额外的代码以避免各种故障。 你会发现你必须加入各种额外的代码来避免故障和错误,所以习惯看见它会很好。

else:
    # 缩小矩形,这样就不会在球棒后面接球了
    player1.rect.inflate(-3, -3)
    player2.rect.inflate(-3, -3)

	#球与球棒相撞吗?
	#注意我输入了一个奇怪的规则,当它们碰撞时将self.hit设置为1,并在下一个中取消设置
	#迭代。 这是为了阻止奇怪的球行为,它发现在球棒内部发生碰撞*,
	#球反转时,并且仍在球棒内部,因此在内部反弹。
	#这样,球总能逃脱并干净地弹开
    if self.rect.colliderect(player1.rect) == 1 and not self.hit:
        angle = math.pi - angle
        self.hit = not self.hit
    elif self.rect.colliderect(player2.rect) == 1 and not self.hit:
        angle = math.pi - angle
        self.hit = not self.hit
    elif self.hit:
        self.hit = not self.hit
self.vector = (angle,z)

        我们用else语句开始这一部分,因为这会继续前一段代码来检查球是否击中了两侧。有意义的是,如果它没有击中侧面,它可能击中板子,所以我们继续进行条件语句。要解决的第一个小故障是将玩家的矩形缩小3个像素,以阻止球棒捕捉到球后面的球(如果你想象你只是移动球棒以便当球在球后面移动时,矩形重叠,所以通常球会被“击中” - 这可以防止这种情况发生。
        接下来,我们检查矩形是否发生碰撞,还有一个小故障修复。请注意,我已经对这些奇怪的代码进行了注释 - 对于那些查看代码的人来说,解释一些异常的代码总是很好的,所以当你回到它时你就会理解它。如果没有固定,球可能会击中球棒的一个角落,改变方向,一帧之后仍然会发现球棒内部。然后它会再次认为它已被击中,并改变其方向。这可能会发生几次,使得球的运动完全不切实际。所以我们有一个变量self.hit,当它被命中时我们设置为True,之后设置为False。当我们检查矩形是否发生碰撞时,我们还检查self.hit是否为True / False,以停止内部弹跳。
        这里重要的代码很容易理解。所有矩形都有一个colliderect函数,你可以在其中输入另一个对象的矩形,如果矩形重叠则返回True,否则返回False。如果他们这样做,我们可以通过从pi减去当前角度来改变方向(再次,你可以用弧度做一个方便的技巧,它会将角度调整90度并将其向正确的方向发送;您可能会发现,在这一点上,一个彻底的理解弧度是正确的!)。为了完成故障检查,如果它们是被击中后的帧,我们将self.hit切换回False。
        然后我们也重新编译矢量。您当然希望删除前一块代码中的相同行,这样您只能在if-else条件语句之后执行此操作。就是这样!组合代码现在允许球击中侧面和击球。

6.3.最终成品(The Finished product)

        最终成品,将所有代码放在一起,以及将它们粘合在一起的其他一些代码,将如下所示:【注意!下面的代码基于Python 2.X,看来这个教程有点历史?】

#
# Tom's Pong
# 一个简单的乒乓球游戏,具有逼真的物理和AI
# http://www.tomchance.uklinux.net/projects/pong.shtml
#
# 根据GNU通用公共许可证发布

VERSION = "0.4"

try:
    import sys
    import random
    import math
    import os
    import getopt
    import pygame
    from socket import *
    from pygame.locals import *
except ImportError, err:
    print "couldn't load module. %s" % (err)
    sys.exit(2)

def load_png(name):
    """ 加载图像并返回图像对象"""
    fullname = os.path.join('data', name)
    try:
        image = pygame.image.load(fullname)
        if image.get_alpha is None:
            image = image.convert()
        else:
            image = image.convert_alpha()
    except pygame.error, message:
        print 'Cannot load image:', fullname
        raise SystemExit, message
    return image, image.get_rect()

class Ball(pygame.sprite.Sprite):
    """一个将在屏幕上移动的球
    Returns: ball object
    Functions: update, calcnewpos
    Attributes: area, vector"""

    def __init__(self, (xy), vector):
        pygame.sprite.Sprite.__init__(self)
        self.image, self.rect = load_png('ball.png')
        screen = pygame.display.get_surface()
        self.area = screen.get_rect()
        self.vector = vector
        self.hit = 0

    def update(self):
        newpos = self.calcnewpos(self.rect,self.vector)
        self.rect = newpos
        (angle,z) = self.vector

        if not self.area.contains(newpos):
            tl = not self.area.collidepoint(newpos.topleft)
            tr = not self.area.collidepoint(newpos.topright)
            bl = not self.area.collidepoint(newpos.bottomleft)
            br = not self.area.collidepoint(newpos.bottomright)
            if tr and tl or (br and bl):
                angle = -angle
            if tl and bl:
                #self.offcourt()
                angle = math.pi - angle
            if tr and br:
                angle = math.pi - angle
                #self.offcourt()
        else:
            # 缩小矩形,这样就不会在球棒后面接球了
            player1.rect.inflate(-3, -3)
            player2.rect.inflate(-3, -3)

            # Do ball and bat collide?
            # Note I put in an odd rule that sets self.hit to 1 when they collide, and unsets it in the next
            # iteration. this is to stop odd ball behaviour where it finds a collision *inside* the
            # bat, the ball reverses, and is still inside the bat, so bounces around inside.
            # This way, the ball can always escape and bounce away cleanly
			#球与球棒相撞吗?
			#注意我输入了一个奇怪的规则,当它们碰撞时将self.hit设置为1,并在下一个中取消设置
			#迭代。 这是为了阻止奇怪的球行为,它发现在球棒内部发生碰撞*,
			#球反转时,并且仍在球棒内部,因此在内部反弹。
			#这样,球总能逃脱并干净地弹开
            if self.rect.colliderect(player1.rect) == 1 and not self.hit:
                angle = math.pi - angle
                self.hit = not self.hit
            elif self.rect.colliderect(player2.rect) == 1 and not self.hit:
                angle = math.pi - angle
                self.hit = not self.hit
            elif self.hit:
                self.hit = not self.hit
        self.vector = (angle,z)

    def calcnewpos(self,rect,vector):
        (angle,z) = vector
        (dx,dy) = (z*math.cos(angle),z*math.sin(angle))
        return rect.move(dx,dy)

class Bat(pygame.sprite.Sprite):
    """可动的网球'棒子',用于击球
    Returns: bat object
    Functions: reinit, update, moveup, movedown
    Attributes: which, speed"""

    def __init__(self, side):
        pygame.sprite.Sprite.__init__(self)
        self.image, self.rect = load_png('bat.png')
        screen = pygame.display.get_surface()
        self.area = screen.get_rect()
        self.side = side
        self.speed = 10
        self.state = "still"
        self.reinit()

    def reinit(self):
        self.state = "still"
        self.movepos = [0,0]
        if self.side == "left":
            self.rect.midleft = self.area.midleft
        elif self.side == "right":
            self.rect.midright = self.area.midright

    def update(self):
        newpos = self.rect.move(self.movepos)
        if self.area.contains(newpos):
            self.rect = newpos
        pygame.event.pump()

    def moveup(self):
        self.movepos[1] = self.movepos[1] - (self.speed)
        self.state = "moveup"

    def movedown(self):
        self.movepos[1] = self.movepos[1] + (self.speed)
        self.state = "movedown"


def main():
    # 初始化screen
    pygame.init()
    screen = pygame.display.set_mode((640, 480))
    pygame.display.set_caption('Basic Pong')

    # 填充 background
    background = pygame.Surface(screen.get_size())
    background = background.convert()
    background.fill((0, 0, 0))

    # 初始化 players
    global player1
    global player2
    player1 = Bat("left")
    player2 = Bat("right")

    # 初始化 ball
    speed = 13
    rand = ((0.1 * (random.randint(5,8))))
    ball = Ball((0,0),(0.47,speed))

    # 初始化 sprites
    playersprites = pygame.sprite.RenderPlain((player1, player2))
    ballsprite = pygame.sprite.RenderPlain(ball)

    # 把所有东西blit到screen上
    screen.blit(background, (0, 0))
    pygame.display.flip()

    # 初始化 clock
    clock = pygame.time.Clock()

    # 事件循环
    while 1:
        # 确保游戏的运行速度不超过每秒60帧
        clock.tick(60)

        for event in pygame.event.get():
            if event.type == QUIT:
                return
            elif event.type == KEYDOWN:
                if event.key == K_a:
                    player1.moveup()
                if event.key == K_z:
                    player1.movedown()
                if event.key == K_UP:
                    player2.moveup()
                if event.key == K_DOWN:
                    player2.movedown()
            elif event.type == KEYUP:
                if event.key == K_a or event.key == K_z:
                    player1.movepos = [0,0]
                    player1.state = "still"
                if event.key == K_UP or event.key == K_DOWN:
                    player2.movepos = [0,0]
                    player2.state = "still"

        screen.blit(background, ball.rect, ball.rect)
        screen.blit(background, player1.rect, player1.rect)
        screen.blit(background, player2.rect, player2.rect)
        ballsprite.update()
        playersprites.update()
        ballsprite.draw(screen)
        playersprites.draw(screen)
        pygame.display.flip()


if __name__ == '__main__': 
	main()

        除了向您展示最终成品外,我还会向您回顾TomPong,所有这一切都建立在这个基础上的。 下载它,看看源代码,你会看到使用你在本教程中看到的所有代码的pong的完整实现,以及我在各种版本中添加的许多其他代码,例如 作为spin(旋转?)的一些额外物理,以及各种其他错误和故障修复。
        哦,可以在 http://www.tomchance.uklinux.net/projects/pong.shtml 找到TomPong。【PS.网站上不去】
【原文完】

译者的话

上述代码,稍微修改一下,改为了Python3.X的代码(自己这能运行),稍微测试了一下,发现有点bug。。。,有空在仔细看看吧,游戏素材可以自己找:

#
# Tom's Pong
# A simple pong game with realistic physics and AI
# http://www.tomchance.uklinux.net/projects/pong.shtml
#
# Released under the GNU General Public License

VERSION = "0.4"

try:
    import sys
    import random
    import math
    import os
    import getopt
    import pygame
    from socket import *
    from pygame.locals import *
except ImportError as err:
    print("couldn't load module. %s" % (err))
    sys.exit(2)

def load_png(name):
    """ Load image and return image object"""
    fullname = os.path.join('data', name)
    try:
        image = pygame.image.load(fullname)
        if image.get_alpha is None:
            image = image.convert()
        else:
            image = image.convert_alpha()
    except pygame.error as message:
        print('Cannot load image:', fullname)
        raise SystemExit(message)
    return image, image.get_rect()

class Ball(pygame.sprite.Sprite):
    """A ball that will move across the screen
    Returns: ball object
    Functions: update, calcnewpos
    Attributes: area, vector"""

    def __init__(self, xy, vector):
        pygame.sprite.Sprite.__init__(self)
        self.image, self.rect = load_png('ball.png')
        screen = pygame.display.get_surface()
        self.area = screen.get_rect()
        self.vector = vector
        self.hit = 0

    def update(self):
        newpos = self.calcnewpos(self.rect,self.vector)
        self.rect = newpos
        (angle,z) = self.vector

        if not self.area.contains(newpos):
            tl = not self.area.collidepoint(newpos.topleft)
            tr = not self.area.collidepoint(newpos.topright)
            bl = not self.area.collidepoint(newpos.bottomleft)
            br = not self.area.collidepoint(newpos.bottomright)
            if tr and tl or (br and bl):
                angle = -angle
            if tl and bl:
                #self.offcourt()
                angle = math.pi - angle
            if tr and br:
                angle = math.pi - angle
                #self.offcourt()
        else:
            # Deflate the rectangles so you can't catch a ball behind the bat
            player1.rect.inflate(-3, -3)
            player2.rect.inflate(-3, -3)

            # Do ball and bat collide?
            # Note I put in an odd rule that sets self.hit to 1 when they collide, and unsets it in the next
            # iteration. this is to stop odd ball behaviour where it finds a collision *inside* the
            # bat, the ball reverses, and is still inside the bat, so bounces around inside.
            # This way, the ball can always escape and bounce away cleanly
            if self.rect.colliderect(player1.rect) == 1 and not self.hit:
                angle = math.pi - angle
                self.hit = not self.hit
            elif self.rect.colliderect(player2.rect) == 1 and not self.hit:
                angle = math.pi - angle
                self.hit = not self.hit
            elif self.hit:
                self.hit = not self.hit
        self.vector = (angle,z)

    def calcnewpos(self,rect,vector):
        (angle,z) = vector
        (dx,dy) = (z*math.cos(angle),z*math.sin(angle))
        return rect.move(dx,dy)

class Bat(pygame.sprite.Sprite):
    """Movable tennis 'bat' with which one hits the ball
    Returns: bat object
    Functions: reinit, update, moveup, movedown
    Attributes: which, speed"""

    def __init__(self, side):
        pygame.sprite.Sprite.__init__(self)
        self.image, self.rect = load_png('bat.png')
        screen = pygame.display.get_surface()
        self.area = screen.get_rect()
        self.side = side
        self.speed = 10
        self.state = "still"
        self.reinit()

    def reinit(self):
        self.state = "still"
        self.movepos = [0,0]
        if self.side == "left":
            self.rect.midleft = self.area.midleft
        elif self.side == "right":
            self.rect.midright = self.area.midright

    def update(self):
        newpos = self.rect.move(self.movepos)
        if self.area.contains(newpos):
            self.rect = newpos
        pygame.event.pump()

    def moveup(self):
        self.movepos[1] = self.movepos[1] - (self.speed)
        self.state = "moveup"

    def movedown(self):
        self.movepos[1] = self.movepos[1] + (self.speed)
        self.state = "movedown"


def main():
    # Initialise screen
    pygame.init()
    screen = pygame.display.set_mode((640, 480))
    pygame.display.set_caption('Basic Pong')

    # Fill background
    background = pygame.Surface(screen.get_size())
    background = background.convert()
    background.fill((0, 0, 0))

    # Initialise players
    global player1
    global player2
    player1 = Bat("left")
    player2 = Bat("right")

    # Initialise ball
    speed = 13
    rand = ((0.1 * (random.randint(5,8))))
    ball = Ball((0,0),(0.47,speed))

    # Initialise sprites
    playersprites = pygame.sprite.RenderPlain((player1, player2))
    ballsprite = pygame.sprite.RenderPlain(ball)

    # Blit everything to the screen
    screen.blit(background, (0, 0))
    pygame.display.flip()

    # Initialise clock
    clock = pygame.time.Clock()

    # Event loop
    while 1:
        # Make sure game doesn't run at more than 60 frames per second
        clock.tick(60)

        for event in pygame.event.get():
            if event.type == QUIT:
                return
            elif event.type == KEYDOWN:
                if event.key == K_w:
                    player1.moveup()
                if event.key == K_s:
                    player1.movedown()
                if event.key == K_UP:
                    player2.moveup()
                if event.key == K_DOWN:
                    player2.movedown()
            elif event.type == KEYUP:
                if event.key == K_w or event.key == K_s:
                    player1.movepos = [0,0]
                    player1.state = "still"
                if event.key == K_UP or event.key == K_DOWN:
                    player2.movepos = [0,0]
                    player2.state = "still"

        screen.blit(background, ball.rect, ball.rect)
        screen.blit(background, player1.rect, player1.rect)
        screen.blit(background, player2.rect, player2.rect)
        ballsprite.update()
        playersprites.update()
        ballsprite.draw(screen)
        playersprites.draw(screen)
        pygame.display.flip()


if __name__ == '__main__': 
	main()

素材的话,随便找的(O(∩_∩)O~):
球:
在这里插入图片描述
板:
在这里插入图片描述
捣鼓成这样就能运行了:
在这里插入图片描述
随便提供一个百度云链接:懒人通道
提取码:j2gy
 
上一篇:Tutorials - 游戏制作指南(Making Games Tutorial)- 用户可控制的对象(User-controllable objects)
以上内容,自己翻译,可能有误,可参考:Tutorials - 游戏制作指南(Making Games Tutorial)- 把所有东西放在一起(Putting it all together)

点我回顶部

 
 
 
 
 
 
 
Fin.

猜你喜欢

转载自blog.csdn.net/Enderman_xiaohei/article/details/88951539