游戏制作指南(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.