sv验证中记分板的作用
这是有关使用Pygame模块在Python 3中创建视频游戏的系列文章的第10部分。 以前的文章是:
- 通过构建一个简单的骰子游戏,学习如何用Python编程
- 使用Pygame模块使用Python构建游戏框架
- 如何将玩家添加到您的Python游戏中
- 使用Pygame移动游戏角色
- 没有小人的英雄是什么? 如何在您的Python游戏中添加一个
- 在Python游戏中模拟重力
- 将跳跃添加到您的Python平台游戏
- 使您的Python游戏玩家可以向前和向后运行
- 使用Python在Pygame中设置战利品
如果您按照本系列教程进行了学习,则已经学习了使用Python创建视频游戏所需的所有基本语法和模式。 但是,它仍然缺少一个重要组成部分。 这个组件并不仅仅对用Python编程游戏很重要。 无论您探索计算的哪个分支,都必须掌握它:通过阅读语言或图书馆的文档,以程序员的身份学习新技巧。
幸运的是,您正在阅读本文的事实表明您熟悉文档。 为了使平台游戏更加美观,在本文中,您将在游戏屏幕上添加得分和健康状况显示。 但是,本课的课程并不那么秘密,它是要教您如何找出图书馆提供的内容以及如何使用新功能。
在Pygame中显示分数
既然您拥有可以被玩家收集的战利品,那么就有充分的理由保持得分,以便您的玩家看到他们收集了多少战利品。 您还可以跟踪玩家的健康状况,以便当他们击中一个敌人时会产生后果。
您已经具有跟踪得分和健康状况的变量,但是所有这些都在后台发生。 本文教您在玩游戏时在游戏屏幕上以您选择的字体显示这些统计信息。
阅读文档
大多数Python模块都有文档,甚至那些没有的模块也可以通过Python的Help函数进行最少的文档记录。 Pygame的主页链接到其文档。 但是,Pygame是一个包含大量文档的重要模块,并且其文档的书写方式与Opensource.com上的文章使用的平易近人(友好,阐释和有益的)叙述风格并不完全相同。 它们是技术文档,并且列出了模块中可用的每个类和功能,各自期望什么样的输入,等等。 如果您不习惯参考代码组件的描述,这可能会令人不知所措。
在困扰图书馆的文档之前,要做的第一件事就是考虑您要实现的目标。 在这种情况下,您想在屏幕上显示玩家的得分和健康状况。
确定所需的结果后,请考虑所需的组件。 您可以从变量和函数的角度来考虑这一点,或者,如果这还不自然,您可以进行一般性的思考。 您可能认识到,显示乐谱需要一些文本,您希望Pygame在屏幕上绘制这些文本。 如果仔细考虑,您可能会意识到它与在屏幕上呈现播放器或战利品或平台并没有太大区别。
从技术上讲,您可以使用数字图形并让Pygame显示这些图形。 这不是实现目标的最简单方法,但是,如果这是您知道的唯一方法,那么它就是一种有效方法。 但是,如果您参考Pygame的文档,则会看到列出的模块之一是font ,这是Pygame的使在屏幕上打印文本像键入一样容易的方法。
解密技术文档
字体文档页面以pygame.font.init()开头,该页面列为用于初始化字体模块的函数。 pygame.init()自动调用了它,您已经在代码中调用了它。 再一次,您已经达到了技术上足够好的地步。 虽然您还不知道如何操作,但是您知道可以使用pygame.font函数在屏幕上打印文本。
但是,如果您进一步阅读,会发现还有一种更好的打印字体的方法。 pygame.freetype模块在文档中的描述方式如下:
pygame.freetype模块替代了pygame.fontpygame模块,用于加载和呈现字体。 它具有原始功能,以及许多新功能。
在pygame.freetype文档页面的下方 ,有一些示例代码:
import pygame
import pygame.
freetype
您的代码已经导入了Pygame,但是修改了导入语句以包括Freetype模块:
import pygame
import
sys
import
os
import pygame.
freetype
在Pygame中使用字体
从字体模块的描述中可以清楚地看出,Pygame使用一种字体(无论是您提供的字体还是Pygame内置的默认字体)在屏幕上呈现文本。 滚动浏览pygame.freetype文档以找到pygame.freetype.Font函数:
pygame.
freetype .
Font
Create a
new Font instance
from a supported font
file .
Font
(
file
, size
=
0
, font_index
=
0
, resolution
=
0
, ucs4
=
False
) -
> Font
pygame.
freetype .
Font .
name
Proper font name.
pygame .
freetype .
Font .
path
Font
file path
pygame.
freetype .
Font .
size
The default point size used
in rendering
这描述了如何在Pygame中构造字体“对象”。 将屏幕上的一个简单对象视为几个代码属性的组合对于您来说可能并不自然,但这与您构建英雄和敌人精灵的方式非常相似。 您需要一个字体文件来代替图像文件。 拥有字体文件后,可以使用pygame.freetype.Font函数在代码中创建一个字体对象,然后使用该对象在屏幕上呈现文本。
由于并非世界上每个人的计算机上都具有完全相同的字体,因此将所选字体与游戏捆绑在一起非常重要。 要捆绑字体,请首先在您的游戏文件夹中创建一个新目录,并为您的图像创建目录。 称它为fonts 。
即使您的计算机随附了几种字体,也不能放弃这些字体。 看起来很奇怪,但这是法律的运作方式。 如果要随游戏一起提供字体,则必须找到一种开放源代码或Creative Commons字体,以允许您随游戏一起提供该字体。
专门提供免费和合法字体的网站包括:
当找到所需字体时,请下载该字体。 解压缩ZIP或TAR文件,然后将.ttf或.otf文件移动到游戏项目目录中的fonts文件夹中。
fonts文件夹,以便Pygame可以使用它。 您可以根据需要在计算机上安装字体,但这不是必需的。 重要的是将其保存在游戏目录中,以便Pygame可以将其“跟踪”到屏幕上。如果字体文件的名称复杂且带有空格或特殊字符,则只需重命名即可。 文件名是完全任意的,它越简单,就越容易在代码中键入。
现在,告诉Pygame您的字体。 从文档中您知道,至少提供pygame.freetype.Font字体文件的路径时,您将获得一个字体对象作为回报(文档明确声明所有剩余属性都是可选的):
Font ( file , size = 0 , font_index = 0 , resolution = 0 , ucs4 = False ) - > Font
创建一个名为myfont的新变量作为游戏中的字体 ,并将Font函数的结果放入该变量中。 本示例使用amazdoom.ttf字体,但是您可以使用所需的任何字体。 将此代码放在“设置”部分中:
font_path
=
os .
path .
join
(
os .
path .
dirname
(
os .
path .
realpath
( __file__
)
)
,
"fonts"
,
"amazdoom.ttf"
)
font_size
= tx
myfont
= pygame.
freetype .
Font
( font_path
, font_size
)
在Pygame中显示文字
现在,您已经创建了字体对象,您需要一个函数将所需的文本绘制到屏幕上。 这是您用来绘制游戏背景和平台的相同原理。
首先,创建一个函数,并使用myfont对象创建一些文本,将颜色设置为某些RGB值。 这必须是全局函数; 它不属于任何特定类别:
def stats
( score
, health
) :
myfont.
render_to
( world
,
(
4
,
4
)
,
"Score:" +
str
( score
)
, WHITE
,
None
, size
=
64
)
myfont.
render_to
( world
,
(
4
,
72
)
,
"Health:" +
str
( health
)
, WHITE
,
None
, size
=
64
)
当然,您现在知道,如果不在Main循环中,则游戏中什么也不会发生,因此请在文件底部附近的stats函数中添加一个调用:
for e
in enemy_list:
e.
move
(
)
stats
( player.
score
, player.
health
)
# draw text
pygame.
display .
flip
(
)
试试看你的游戏。
当玩家收集战利品时,分数会上升。 当玩家被敌人击中时,生命值降低。 成功!
不过,有一个问题。 当玩家被被敌人击中,健康云一路下跌,这是不公平的。 您刚刚发现了一个非致命错误。 非致命错误是应用程序中的那些小问题,这些问题不会阻止应用程序启动甚至无法正常工作(但大多数情况下),但是它们要么没有意义,要么使用户烦恼。 这是解决此问题的方法。
修复健康计数器
当前的健康点系统存在的问题是,敌人正在触摸玩家的Pygame时钟的每一个滴答都会减去健康。 这意味着,行动缓慢的敌人一次就可以使玩家的生命值降低至–200,这是不公平的。 当然,您可以给玩家起跑得分10,000,而不必担心。 那会起作用,可能没人会介意。 但是有更好的方法。
当前,您的代码可以检测玩家和敌人何时发生碰撞。 健康点问题的解决方法是检测两个单独的事件:玩家和敌人发生碰撞时,一旦它们发生碰撞,何时停止碰撞。
首先,在Player类中,创建一个变量来表示玩家和敌人何时碰撞:
self .
frame
=
0
self .
health
=
10
self .
damage
=
0
在Player类的更新函数中, 删除以下代码块:
for enemy
in enemy_hit_list:
self .
health -
=
1
#print(self.health)
并在其位置上检查碰撞,只要玩家当前未被击中即可:
if
self .
damage
==
0 :
for enemy
in enemy_hit_list:
if
not
self .
rect .
contains
( enemy
) :
self .
damage
=
self .
rect .
colliderect
( enemy
)
您可能会看到删除的块和刚添加的块之间的相似之处。 他们俩都在做同样的工作,但是新代码更复杂。 最重要的是,仅当当前未击中播放器时,新代码才运行。 这意味着该代码在玩家和敌人碰撞时仅运行一次,并且在碰撞发生之前不会像以前那样持续不断运行。
新代码使用了两个新的Pygame函数。 self.rect.contains函数会检查玩家的边界框内当前是否有敌人,并且self.rect.colliderect会将新的self.damage变量设置为true,无论它是多少次都为true。
现在,即使被敌人击中三秒钟,对Pygame来说也仍然像被击中。
我通过阅读Pygame的文档发现了这些功能。 您不必一次阅读所有文档,也不必阅读每个函数的每个单词。 但是,重要的是要花时间处理正在使用的新库或模块的文档。 否则,您极有可能重新发明轮子。 不要花一个下午的时间来尝试为您正在使用的框架已解决的问题找到解决方案。 阅读文档,找到功能,并从他人的工作中受益!
最后,添加另一个代码块以检测玩家和敌人何时不再接触。 然后直到那时,才从玩家身上减去一个生命值。
if
self .
damage
==
1 :
idx
=
self .
rect .
collidelist
( enemy_hit_list
)
if idx
== -
1 :
self .
damage
=
0
# set damage back to 0
self .
health -
=
1
# subtract 1 hp
请注意, 只有在玩家被击中时,才会触发此新代码。 这意味着当玩家在游戏世界中探索或收集战利品时,该代码不会运行。 它仅在self.damage变量被激活时运行。
代码运行时,它将使用self.rect.collidelist来查看玩家是否仍在触摸敌人列表中的敌人( colliderlist在未检测到碰撞时返回负数)。 一旦不与敌人接触,就该偿还自残债务:将self.damage变量设置回零并减去一点生命值,以停用self.damage变量。
立即尝试游戏。
分数React
现在,您已经可以让玩家知道他们的得分和健康状况,可以在玩家达到某些里程碑时使某些事件发生。 例如,也许有一个特殊的战利品可以恢复一些生命值。 也许达到零健康点的玩家必须从关卡的起点重新开始。
您可以在代码中检查这些事件并相应地操纵游戏世界。 您已经知道如何使用,因此请浏览文档以获取新的技巧,然后自行尝试。
到目前为止,所有代码如下:
#!/usr/bin/env python3
# draw a world
# add a player and player control
# add player movement
# add enemy and basic collision
# add platform
# add gravity
# add jumping
# add scrolling
# add loot
# add score
# GNU All-Permissive License
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# notice and this notice are preserved. This file is offered as-is,
# without any warranty.
import pygame
import
sys
import
os
import pygame.
freetype
'''
Objects
'''
class Platform
( pygame.
sprite .
Sprite
) :
# x location, y location, img width, img height, img file
def
__init__
(
self
, xloc
, yloc
, imgw
, imgh
, img
) :
pygame.
sprite .
Sprite .
__init__
(
self
)
self .
image
= pygame.
image .
load
(
os .
path .
join
(
'images'
, img
)
) .
convert
(
)
self .
image .
convert_alpha
(
)
self .
rect
=
self .
image .
get_rect
(
)
self .
rect .
y
= yloc
self .
rect .
x
= xloc
class Player
( pygame.
sprite .
Sprite
) :
'''
Spawn a player
'''
def
__init__
(
self
) :
pygame.
sprite .
Sprite .
__init__
(
self
)
self .
movex
=
0
self .
movey
=
0
self .
frame
=
0
self .
health
=
10
self .
damage
=
0
self .
collide_delta
=
0
self .
jump_delta
=
6
self .
score
=
1
self .
images
=
[
]
for i
in
range
(
1
,
9
) :
img
= pygame.
image .
load
(
os .
path .
join
(
'images'
,
'hero' +
str
( i
) +
'.png'
)
) .
convert
(
)
img.
convert_alpha
(
)
img.
set_colorkey
( ALPHA
)
self .
images .
append
( img
)
self .
image
=
self .
images
[
0
]
self .
rect
=
self .
image .
get_rect
(
)
def jump
(
self
, platform_list
) :
self .
jump_delta
=
0
def gravity
(
self
) :
self .
movey +
=
3.2
# how fast player falls
if
self .
rect .
y
> worldy
and
self .
movey
>=
0 :
self .
movey
=
0
self .
rect .
y
= worldy-ty
def control
(
self
, x
, y
) :
'''
control player movement
'''
self .
movex +
= x
self .
movey +
= y
def update
(
self
) :
'''
Update sprite position
'''
self .
rect .
x
=
self .
rect .
x +
self .
movex
self .
rect .
y
=
self .
rect .
y +
self .
movey
# moving left
if
self .
movex
<
0 :
self .
frame +
=
1
if
self .
frame
> ani*
3 :
self .
frame
=
0
self .
image
=
self .
images
[
self .
frame //ani
]
# moving right
if
self .
movex
>
0 :
self .
frame +
=
1
if
self .
frame
> ani*
3 :
self .
frame
=
0
self .
image
=
self .
images
[
(
self .
frame //ani
) +
4
]
# collisions
enemy_hit_list
= pygame.
sprite .
spritecollide
(
self
, enemy_list
,
False
)
if
self .
damage
==
0 :
for enemy
in enemy_hit_list:
if
not
self .
rect .
contains
( enemy
) :
self .
damage
=
self .
rect .
colliderect
( enemy
)
if
self .
damage
==
1 :
idx
=
self .
rect .
collidelist
( enemy_hit_list
)
if idx
== -
1 :
self .
damage
=
0
# set damage back to 0
self .
health -
=
1
# subtract 1 hp
loot_hit_list
= pygame.
sprite .
spritecollide
(
self
, loot_list
,
False
)
for loot
in loot_hit_list:
loot_list.
remove
( loot
)
self .
score +
=
1
print
(
self .
score
)
plat_hit_list
= pygame.
sprite .
spritecollide
(
self
, plat_list
,
False
)
for p
in plat_hit_list:
self .
collide_delta
=
0
# stop jumping
self .
movey
=
0
if
self .
rect .
y
> p.
rect .
y :
self .
rect .
y
= p.
rect .
y +ty
else :
self .
rect .
y
= p.
rect .
y -ty
ground_hit_list
= pygame.
sprite .
spritecollide
(
self
, ground_list
,
False
)
for g
in ground_hit_list:
self .
movey
=
0
self .
rect .
y
= worldy-ty-ty
self .
collide_delta
=
0
# stop jumping
if
self .
rect .
y
> g.
rect .
y :
self .
health -
=
1
print
(
self .
health
)
if
self .
collide_delta
<
6
and
self .
jump_delta
<
6 :
self .
jump_delta
=
6 *
2
self .
movey -
=
33
# how high to jump
self .
collide_delta +
=
6
self .
jump_delta +
=
6
class Enemy
( pygame.
sprite .
Sprite
) :
'''
Spawn an enemy
'''
def
__init__
(
self
, x
, y
, img
) :
pygame.
sprite .
Sprite .
__init__
(
self
)
self .
image
= pygame.
image .
load
(
os .
path .
join
(
'images'
, img
)
)
self .
movey
=
0
#self.image.convert_alpha()
#self.image.set_colorkey(ALPHA)
self .
rect
=
self .
image .
get_rect
(
)
self .
rect .
x
= x
self .
rect .
y
= y
self .
counter
=
0
def move
(
self
) :
'''
enemy movement
'''
distance
=
80
speed
=
8
self .
movey +
=
3.2
if
self .
counter
>=
0
and
self .
counter
<= distance:
self .
rect .
x +
= speed
elif
self .
counter
>= distance
and
self .
counter
<= distance*
2 :
self .
rect .
x -
= speed
else :
self .
counter
=
0
self .
counter +
=
1
if
not
self .
rect .
y
>= worldy-ty-ty:
self .
rect .
y +
=
self .
movey
plat_hit_list
= pygame.
sprite .
spritecollide
(
self
, plat_list
,
False
)
for p
in plat_hit_list:
self .
movey
=
0
if
self .
rect .
y
> p.
rect .
y :
self .
rect .
y
= p.
rect .
y +ty
else :
self .
rect .
y
= p.
rect .
y -ty
ground_hit_list
= pygame.
sprite .
spritecollide
(
self
, ground_list
,
False
)
for g
in ground_hit_list:
self .
rect .
y
= worldy-ty-ty
class Level
(
) :
def bad
( lvl
, eloc
) :
if lvl
==
1 :
enemy
= Enemy
( eloc
[
0
]
, eloc
[
1
]
,
'yeti.png'
)
# spawn enemy
enemy_list
= pygame.
sprite .
Group
(
)
# create enemy group
enemy_list.
add
( enemy
)
# add enemy to group
if lvl
==
2 :
print
(
"Level " +
str
( lvl
)
)
return enemy_list
def loot
( lvl
, tx
, ty
) :
if lvl
==
1 :
loot_list
= pygame.
sprite .
Group
(
)
loot
= Platform
(
200
, ty*
7
, tx
, ty
,
'loot_1.png'
)
loot_list.
add
( loot
)
if lvl
==
2 :
print
( lvl
)
return loot_list
def ground
( lvl
, gloc
, tx
, ty
) :
ground_list
= pygame.
sprite .
Group
(
)
i
=
0
if lvl
==
1 :
while i
<
len
( gloc
) :
ground
= Platform
( gloc
[ i
]
, worldy-ty
, tx
, ty
,
'ground.png'
)
ground_list.
add
( ground
)
i
= i+
1
if lvl
==
2 :
print
(
"Level " +
str
( lvl
)
)
return ground_list
def
platform
( lvl
, tx
, ty
) :
plat_list
= pygame.
sprite .
Group
(
)
ploc
=
[
]
i
=
0
if lvl
==
1 :
ploc.
append
(
(
20
, worldy-ty-
128
,
3
)
)
ploc.
append
(
(
300
, worldy-ty-
256
,
3
)
)
ploc.
append
(
(
500
, worldy-ty-
128
,
4
)
)
while i
<
len
( ploc
) :
j
=
0
while j
<= ploc
[ i
]
[
2
] :
plat
= Platform
(
( ploc
[ i
]
[
0
] +
( j*tx
)
)
, ploc
[ i
]
[
1
]
, tx
, ty
,
'ground.png'
)
plat_list.
add
( plat
)
j
= j+
1
print
(
'run' +
str
( i
) +
str
( ploc
[ i
]
)
)
i
= i+
1
if lvl
==
2 :
print
(
"Level " +
str
( lvl
)
)
return plat_list
def stats
( score
, health
) :
myfont.
render_to
( world
,
(
4
,
4
)
,
"Score:" +
str
( score
)
, SNOWGRAY
,
None
, size
=
64
)
myfont.
render_to
( world
,
(
4
,
72
)
,
"Health:" +
str
( health
)
, SNOWGRAY
,
None
, size
=
64
)
'''
Setup
'''
worldx
=
960
worldy
=
720
fps
=
40
# frame rate
ani
=
4
# animation cycles
clock
= pygame.
time .
Clock
(
)
pygame.
init
(
)
main
=
True
BLUE
=
(
25
,
25
,
200
)
BLACK
=
(
23
,
23
,
23
)
WHITE
=
(
254
,
254
,
254
)
SNOWGRAY
=
(
137
,
164
,
166
)
ALPHA
=
(
0
,
255
,
0
)
world
= pygame.
display .
set_mode
(
[ worldx
, worldy
]
)
backdrop
= pygame.
image .
load
(
os .
path .
join
(
'images'
,
'stage.png'
)
) .
convert
(
)
backdropbox
= world.
get_rect
(
)
player
= Player
(
)
# spawn player
player.
rect .
x
=
0
player.
rect .
y
=
0
player_list
= pygame.
sprite .
Group
(
)
player_list.
add
( player
)
steps
=
10
forwardx
=
600
backwardx
=
230
eloc
=
[
]
eloc
=
[
200
,
20
]
gloc
=
[
]
tx
=
64
#tile size
ty
=
64
#tile size
font_path
=
os .
path .
join
(
os .
path .
dirname
(
os .
path .
realpath
( __file__
)
)
,
"fonts"
,
"amazdoom.ttf"
)
font_size
= tx
myfont
= pygame.
freetype .
Font
( font_path
, font_size
)
i
=
0
while i
<=
( worldx/tx
) +tx:
gloc.
append
( i*tx
)
i
= i+
1
enemy_list
= Level.
bad
(
1
, eloc
)
ground_list
= Level.
ground
(
1
, gloc
, tx
, ty
)
plat_list
= Level.
platform
(
1
, tx
, ty
)
loot_list
= Level.
loot
(
1
, tx
, ty
)
'''
Main loop
'''
while main
==
True :
for event
in pygame.
event .
get
(
) :
if event.
type
== pygame.
QUIT :
pygame.
quit
(
)
;
sys .
exit
(
)
main
=
False
if event.
type
== pygame.
KEYDOWN :
if event.
key
== pygame.
K_LEFT
or event.
key
==
ord
(
'a'
) :
print
(
"LEFT"
)
player.
control
( -steps
,
0
)
if event.
key
== pygame.
K_RIGHT
or event.
key
==
ord
(
'd'
) :
print
(
"RIGHT"
)
player.
control
( steps
,
0
)
if event.
key
== pygame.
K_UP
or event.
key
==
ord
(
'w'
) :
print
(
'jump'
)
if event.
type
== pygame.
KEYUP :
if event.
key
== pygame.
K_LEFT
or event.
key
==
ord
(
'a'
) :
player.
control
( steps
,
0
)
if event.
key
== pygame.
K_RIGHT
or event.
key
==
ord
(
'd'
) :
player.
control
( -steps
,
0
)
if event.
key
== pygame.
K_UP
or event.
key
==
ord
(
'w'
) :
player.
jump
( plat_list
)
if event.
key
==
ord
(
'q'
) :
pygame.
quit
(
)
sys .
exit
(
)
main
=
False
# scroll the world forward
if player.
rect .
x
>= forwardx:
scroll
= player.
rect .
x - forwardx
player.
rect .
x
= forwardx
for p
in plat_list:
p.
rect .
x -
= scroll
for e
in enemy_list:
e.
rect .
x -
= scroll
for l
in loot_list:
l.
rect .
x -
= scroll
# scroll the world backward
if player.
rect .
x
<= backwardx:
scroll
= backwardx - player.
rect .
x
player.
rect .
x
= backwardx
for p
in plat_list:
p.
rect .
x +
= scroll
for e
in enemy_list:
e.
rect .
x +
= scroll
for l
in loot_list:
l.
rect .
x +
= scroll
world.
blit
( backdrop
, backdropbox
)
player.
gravity
(
)
# check gravity
player.
update
(
)
player_list.
draw
( world
)
#refresh player position
enemy_list.
draw
( world
)
# refresh enemies
ground_list.
draw
( world
)
# refresh enemies
plat_list.
draw
( world
)
# refresh platforms
loot_list.
draw
( world
)
# refresh loot
for e
in enemy_list:
e.
move
(
)
stats
( player.
score
, player.
health
)
# draw text
pygame.
display .
flip
(
)
clock.
tick
( fps
)
翻译自: https://opensource.com/article/20/1/add-scorekeeping-your-python-game
sv验证中记分板的作用