找回密码
 立即注册
首页 业界区 安全 [自制翻译]Pyglets 官方文档: In-depth game example翻 ...

[自制翻译]Pyglets 官方文档: In-depth game example翻译 (第一部分)

硫辨姥 2025-7-8 21:40:33
提示:

  • 本文为民间自制翻译,内容可能会有误差
  • 本文为第一部分,剩下的我会利用空余时间尽快赶出来的。
  • 因为学习需要翻译了这篇文章,有一部分是对照着机器翻译来的,建议把原文与译文对照着看
  • 如果有翻译的不到位的地方或是我哪里脑子一抽写出来纯整活的文字劳烦一一指出,谢谢。
In-depth game example / 深度游戏指引

这个教程会带你完成编写一个Asteroids clone的小程序!(假定你已熟悉如何编写和运行Python程序)
This tutorial will walk you through the steps of writing a simple Asteroids clone. It is assumed that the reader is familiar with writing and running Python programs.
请注意,这不是编程教程,但希望你能够理解!如果你在某一个环节卡住了,可以阅览一下编程指南的相关章节。
This is not a programming tutorial, but it should hopefully be clear enough to follow even if you’re a beginner. If you get stuck, first have a look at the relevant sections of the programming guide.
完整的源代码在examples/game/(Pyglet 源目录的文件夹),你可以使用这个文件夹作为参考!如果有任何错误或疑问,请与我们联系!
The full source code can also be found in the examples/game/ folder of the pyglet source directory, which you can follow along with. If anything is still not clear, let us know!
Basic graphics / 基本图形

来吧!我们的第一个版本将会基础地显示一个为0的计分板,一个显示我们游戏的名字的Label文本控件,三个随机放置的小行星和玩家的飞船。目前还没有一样东西会动。
Lets begin! The first version of our game will simply show a score of zero, a label showing the name of the program, three randomly placed asteroids, and the player’s ship. Nothing will move.
Setting up / 环境配置

第一件事,确保你已经安装了Pyglet。接下来我们会创建一个游戏文件夹。
*First things first, make sure you have pyglet installed. Then, we will set up the folder structure for our project. *
在不同的开发阶段里,我们会有几个不同的游戏版本。
Since this example game is written in stages, we will have several version folders at various stages of development.
我们还有一个公用的素材文件夹(./resources)用于储存素材在示例文件夹外。
We will also have a shared resource folder with the images, called ‘resources,’ outside of the example folders.
每个版本的单独的文件夹内有一个Python文件名为astroid.py用于运行示例,其内部的子文件夹(./game)我们会用来存储额外的文件;这就是我们大致的构想。
Each version folder contains a Python file called asteroid.py which runs the game, as well as a sub-folder named game where we will place additional modules; this is where most of the logic will be.
你的文件夹应该看起来像这样!
Your folder structure should look like this:
  1. game/
  2.     resources/
  3.         (images go here)
  4.     version1
  5.         astroid.py
  6.         game/
  7.             \_\_init__.py
复制代码
Getting a window / 创建窗口

创建一个窗口,只需要导入pyglet,创建一个新的pyglet.window.Window,然后调用pyglet.app.run():
To set up a window, simply import pyglet, create a new instance of pyglet.window.Window, and call pyglet.app.run():
  1. import pyglet
  2. game_window = pyglet.window.Window(800, 600)
  3. # 译者注: 800, 600 分别为宽度,高度。当然,也可以这样:
  4. # game_window = pyglet.window.Window(weight = 800, heigh = 600)
  5. if __name__ == '__main__':
  6.     pyglet.app.run()
复制代码
如果你这样做了,你会得到一个充满垃圾内存的窗口。当你按下Esc键时,窗口会关闭。(你看到的只是一些没有经过初始化的图形内存)
If you run the code above, you should see a window full of junk that goes away when you press Esc. (What you are seeing is raw uninitialized graphics memory).
Loading and displaying an image / 加载图像

由于我们的图像文件夹位于主程序文件夹的上一级中,因此我们需要告诉Pylget我们的图像在哪。
Since our images will reside in a directory other than the example’s root directory, we need to tell pyglet where to find them:
  1. import pyglet
  2. pyglet.resource.path = ['../resources']
  3. # ../ 返回上一级
  4. pyglet.resource.reindex()
复制代码
Pyglet的pyglet.resource模块将会处理所有的工作像是查找和导入游戏资源像是图像、声音、以及其他的文件。
pyglet’s pyglet.resource module takes all of the hard work out of finding and loading game resources such as images, sounds, etc..
你要做的就只是告诉它文件夹在哪里,然后重索引它。资源文件夹以../开头是因为资源文件夹在它的上一级。如果我们将其移除,Pyglet就会在version1里寻找文件夹(报错)。
All that you need to do is tell it where to look, and reindex it. In this example game, the resource path starts with ../ because the resources folder is on the same level as the version1 folder. If we left it off, pyglet would look inside version1/ for the resources/ folder.
现在游戏的资源文件已经准备好了,我们可以很轻易地用resource中的image()加载图像了~
Now that pyglet’s resource module is initialized, we can easily load the images with the image() function of the resource module:
  1. player_image = pyglet.resource.image("player.png")
  2. bullet_image = pyglet.resource.image("bullet.png")
  3. asteroid_image = pyglet.resource.image("asteroid.png")
复制代码
Centering the images / 让图像居中

默认情况下,Pyglet将从图像的左下角绘制图像(坐标同理)。我们当然不希望出现这种屌问题,因为图像需要绕中心旋转。我们需要做的就是设定它们的锚点。当然,让我们创建一个函数用来简化这一过程:
Pyglet will draw and position all images from their lower left corner by default. We don’t want this behavior for our images, which need to rotate around their centers. All we have to do to achieve this is to set their anchor points. Lets create a function to simplify this:
  1. def center_image(image):
  2.     """设置图像的锚点位于中央。"""
  3.     image.anchor_x = image.width // 2
  4.     image.anchor_y = image.height // 2
复制代码
现在我们就只需要调用center_image()就能使图像剧中了~
Now we can just call center_image() on all of our loaded images:
  1. center_image(player_image)
  2. center_image(bullet_image)
  3. center_image(asteroid_image)
复制代码
记住,center_image()必须在被调用前创建!当然,在Pyglet中,0度都是指向右侧的,因此所有的图像都将指向右侧。
Remember that the center_image() function must be defined before it can be called at the module level. Also, note that zero degrees points directly to the right in pyglet, so the images are all drawn with their front pointing to the right.
要访问astroid.py中的文件,我们应当用些类似于from game import resources的方法,这种我们会在下一部分介绍。
To access the images from asteroid.py, we need to use something like from game import resources, which we’ll get into in the next section.
Initializing objects / 初始化!

我们的任务是要在我们的游戏中加入一些关于玩家分数标签和当前难度信息的文本组件。在最后的效果呈现中,我们的游戏中会有分数,关卡难易度和剩余生命数量的图标。
We want to put some labels at the top of the window to give the player some information about the score and the current difficulty level. Eventually, we will have a score display, the name of the level, and a row of icons representing the number of remaining lives.
Making the labels / 文本控件!

只需要一句简单的pyglet.text.Labal就能创建一个文本组件。如下:
To make a text label in pyglet, just initialize a pyglet.text.Label object:
  1. score_label = pyglet.text.Label(text="Score: 0", x=10, y=575)
  2. level_label = pyglet.text.Label(text="我的逆天游戏的神人标题",
  3.                             x=game_window.width//2, y=575, anchor_x='center')
复制代码
在创建第二条文本时,我们使用了anchor_x来让他关于窗口的x轴对称。
Notice that the second label is centered using the anchor_x attribute.
Drawing the labels / 显示!

现在的你只是创建了一个标签而非正式的绘制了它。要绘制它也是很简单啊,只需要给 Pyglet 发送一个事件就行啦。我们可以使用如下最简单的方法来绘制它:
We want pyglet to run some specific code whenever the window is drawn. An on_draw() event is dispatched to the window to give it a chance to redraw its contents. pyglet provides several ways to attach event handlers to objects; a simple way is to use a decorator:
  1. @game_window.event
  2. def on_draw():
  3.     # 在这里绘制
复制代码
我们的装饰器@game_window.event让 Pyglet 知道了你有这样的一个绘制事件叫做on_draw()。他会在每次窗口绘制时被调用。类似的事件还有很多,如on_mouse_press()和on_key_press()等。真是简简又单单啊。
The @game_window.event decorator lets the Window instance know that our on_draw() function is an event handler. The on_draw() event is fired whenever - you guessed it - the window needs to be redrawn. Other events include on_mouse_press() and on_key_press().
现在我们就能把绘制文本控件的函数塞入那个函数了。在开始绘制之前,请记住,一定要清空窗口上绘制的内容! Pyglet 可不会自动调用clear()。
Now we can fill the method with the functions necessary to draw our labels. Before we draw anything, we should clear the screen. After that, we can simply call each object’s draw() function:
  1. @game_window.event
  2. def on_draw():
  3.     game_window.clear()
  4.     level_label.draw()
  5.     score_label.draw()
复制代码
我们还没有看到我们的游戏是什么样的呢。别管别的,尽管打开运行你Ctrl CV写出来的东西吧!这应该会有一个写着我的逆天游戏的神人标题的神人程序跑着。
Now when you run asteroid.py, you should get a window with a score of zero in the upper left corner and a centered label reading “My Amazing Game” at the top of the screen.
Making the player and asteroid sprites / 精灵上场喽!

玩家肯定不能只是一张图片。我们得让他动起来!作为精灵的子类,我们可以这样写:
The player should be an instance or subclass of pyglet.sprite.Sprite, like so:
  1. from game import resources
  2. ...
  3. player_ship = pyglet.sprite.Sprite(img=resources.player_image, x=400, y=300)
复制代码
现在,把他像绘制文本控件一样把他画上去:
To get the player to draw on the screen, add a line to on_draw():
  1. @game_window.event
  2. def on_draw():
  3.     ...
  4.     player_ship.draw()
复制代码
加载小行星可能会有点复杂,因为我们要避免多个小行星不至于贴脸放在玩家头上。让我们写个load.py来应对生成小行星的问题。
Loading the asteroids is a little more complicated, since we’ll need to place more than one at random locations that don’t immediately collide with the player. Let’s put the loading code in a new game submodule called load.py:
  1. import pyglet
  2. import random
  3. from . import resources
  4. def asteroids(num_asteroids):
  5.     asteroids = []
  6.     for i in range(num_asteroids):
  7.         asteroid_x = random.randint(0, 800)
  8.         asteroid_y = random.randint(0, 600)
  9.         new_asteroid = pyglet.sprite.Sprite(img=resources.asteroid_image,
  10.                                             x=asteroid_x, y=asteroid_y)
  11.         new_asteroid.rotation = random.randint(0, 360)
  12.         asteroids.append(new_asteroid)
  13.     return asteroids
复制代码
问题才解决一半呢。如果你真觉得解决了,我想你可以试试因为自己的游戏逻辑问题导致立即死亡是什么感受。让我们来写一个简单的计算距离的函数,以确保玩家不至于开门撞大运:
All we are doing here is making a few new sprites with random positions. There’s still a problem, though - an asteroid might randomly be placed exactly where the player is, causing immediate death. To fix this issue, we’ll need to be able to tell how far away new asteroids are from the player. Here is a simple function to calculate that distance:
  1. import math
  2. ...
  3. def distance(point_1=(0, 0), point_2=(0, 0)):
  4.     """Returns the distance between two points"""
  5.     return math.sqrt((point_1[0] - point_2[0]) ** 2 + (point_1[1] - point_2[1]) ** 2)
  6. # 使用勾股定理求直角坐标系中任意两点之间的距离。距离d = √(|x0 - x1| ^ 2 + |y0 - y1| ^ 2)
复制代码
介绍如何实现,这里上面的注释已经解释了,不再翻译。使用Sprite.position获取(x, y, z)。接下来怎么办,想必你已经很清楚了。下面是完整的生成代码:
To check new asteroids against the player’s position, we need to pass the player’s position into the asteroids() function and keep regenerating new coordinates until the asteroid is far enough away. pyglet sprites keep track of their position both as a tuple (Sprite.position) and as x, y, and z attributes (Sprite.x, Sprite.y, Sprite.z). To keep our code short, we’ll just pass the position tuple into the function. We’re not using the z value, so we just use a throwaway variable for that:
  1. def asteroids(num_asteroids, player_position):
  2.     asteroids = []
  3.     for i in range(num_asteroids):
  4.         asteroid_x, asteroid_y, _ = player_position
  5.         while distance((asteroid_x, asteroid_y), player_position) < 100:
  6.             asteroid_x = random.randint(0, 800)
  7.             asteroid_y = random.randint(0, 600)
  8.         new_asteroid = pyglet.sprite.Sprite(
  9.             img=resources.asteroid_image, x=asteroid_x, y=asteroid_y)
  10.         new_asteroid.rotation = random.randint(0, 360)
  11.         asteroids.append(new_asteroid)
  12.     return asteroids
复制代码
对于每个小行星来说,他最终会找到一个远离玩家的位置。现在创建这个角色,然后给予其一个随机的角度。所有被函数吐出来的小行星最后都会放入列表中。
For each asteroid, it chooses random positions until it finds one away from the player, creates the sprite, and gives it a random rotation. Each asteroid is appended to a list, which is returned.
现在你就能加载三个陨石啦。
Now you can load three asteroids like this:
  1. from game import resources, load
  2. ...
  3. asteroids = load.asteroids(3, player_ship.position)
复制代码
Copyright 2006-2008, Alex Holkner. 2008-2025 pyglet contributors.
Trans. made by Tsing.xl.

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册