python 构建
Python已作为一种出色的初学者编程语言而享有盛誉。 但是,从哪里开始呢?
我最喜欢的使人们对编程感兴趣的方法之一就是编写游戏。
PursuedPyBear (ppb)是为教学而优化的游戏编程库,我最近使用它来向孩子们传授更多有关我最喜欢的编程语言的知识 。
Jupyter项目是一个基于浏览器的Python控制台,最初是为数据科学家设计的,用于处理数据。
我有一个Jupyter笔记本,旨在教您如何制作简单的互动游戏,您可以从此处下载。 为了打开文件,您将需要安装最新的Jupyter项目JupyterLab。
先决条件:
运行最新版本的Python( Linux , Mac和Windows的说明 ) 运行最新版本的Git( 此处的说明)我们将简要配置一个虚拟环境,以为所需的库创建单独的空间。 (您可以在此处了解有关虚拟环境如何工作的更多信息。)
$ cd penguin-bit-by-bit $ python -m venv venv $ source ./venv/bin/activate $ pip install -r requirements. txt $ jupyter lab .$ git clone https://github. com /moshez/penguin-bit-by-bit. git
最后一条命令应在默认浏览器中打开http:// localhost:8888 / lab的 JupyterLab。 在左侧栏中选择dynamic_penguin.ipynb文件,我们就可以开始!
将运行游戏的事件循环
asyncio和PursuedPyBear运行自己的事件循环。我们可以使用另一个库Twisted集成这两个库,例如粘胶。 这听起来很复杂,但值得庆幸的是,复杂性隐藏在库中,这将为我们完成所有艰苦的工作。
Jupyter中的以下单元格负责上半部分-将Twisted与asyncio事件循环集成。
__file__ = None
不需要将PursuedPyBear与Jupyter集成。
asyncioreactor. install ( ) __file__ = Nonefrom twisted. internet import asyncioreactor
接下来,我们需要一个“设置”功能。 设置功能是关键游戏元素配置的常用术语。 但是,我们的功能只会将游戏“场景”放在全局变量中。 就像我们定义要玩游戏的桌子一样。
Jupyter Notebook中的以下单元将完成此操作。
global SCENE SCENE = scenedef setup ( scene ) :
现在,我们需要将PursuedPyBear的事件循环与Twisted集成。 我们txppb
使用txppb
模块:
d = txppb. run ( setup ) d. addBoth ( print )import txppb
如果游戏由于错误而崩溃,最后的print
将为我们提供帮助-它将打印出对Jupyter输出的追溯。
这将显示一个空窗口,准备好游戏元素。
这就是我们开始利用Jupyter的地方-传统上,在开始玩游戏之前,需要先编写整个游戏。 但是,我们违反约定,立即开始玩游戏!
通过互动使游戏变得有趣
但是,这不是一个非常有趣的游戏。 它什么都没有,只坐在那里。 如果我们想要一些东西,我们最好添加它。
在视频游戏编程中,屏幕上移动的东西称为“精灵”。 在PursuedPyBear中,子画面由类表示。 精灵会自动使用与该类相同名称的图像。 我从Kenney那里得到了一张小企鹅图片,这是免费和开源视频游戏资产的集合。
class Penguin ( ppb. Sprite ) : passimport ppb
现在让我们把企鹅的权利放到中间。
SCENE. add ( Penguin ( pos = ( 0 , 0 ) ) )
它小心地坐在中间。 这比什么都不是要有趣得多。 很好-这正是我们想要的。 在渐进式游戏开发中,每一步都只会稍微有趣一点。
使用ppb为我们的企鹅游戏增添动感
但是企鹅并不能坐以待!! 企鹅应该四处走动。 我们将让玩家使用箭头键控制企鹅。 首先,让我们将键映射到向量:
DIRECTIONS = {keycodes. Left : ppb. Vector ( - 1 , 0 ) , keycodes. Right : ppb. Vector ( 1 , 0 ) , keycodes. Up : ppb. Vector ( 0 , 1 ) , keycodes. Down : ppb. Vector ( 0 , - 1 ) }from ppb import keycodes
现在,我们将使用一个实用程序库。set_in_class
函数设置类中的方法。 Python能够向类追溯添加功能的功能真的派上用场了!
Penguin. direction = ppb. Vector ( 0 , 0 ) @ set_in_class ( Penguin ) def on_update ( self , update_event , signal ) : self . position + = update_event. time_delta * self . directionfrom mzutil import set_in_class
set_in_class
的代码set_in_class
不长,但是确实使用了一些不平凡的Python技巧。 我们将完整的实用程序库放在文章的末尾以进行回顾,并且为了顺畅起见,我们暂时将其跳过。
回到企鹅!
哦,嗯。
企鹅正在努力地以……零速运动,正好无处可走。 让我们手动设置方向以查看会发生什么。
Penguin. direction = DIRECTIONS [ keycodes. Up ] / 4
方向是向上,但是有点慢。 这给了足够的时间来手动设置企鹅的方向为零。 现在就开始吧!
Penguin. direction = ppb. Vector ( 0 , 0 )
为我们的企鹅游戏增添互动性
哎呀,那很令人兴奋-但不是我们想要的。 我们希望企鹅对按键进行响应。 通过代码控制它是游戏玩家所称的“作弊”。
我们将其设置为将方向设置为按键,并在释放键时将其设置回零。
def on_key_pressed ( self , key_event , signal ) : self . direction = DIRECTIONS. get ( key_event. key , ppb. Vector ( 0 , 0 ) ) @ set_in_class ( Penguin ) def on_key_released ( self , key_event , signal ) : if key_event. key in DIRECTIONS: self . direction = ppb. Vector ( 0 , 0 )@ set_in_class ( Penguin )
企鹅有点无聊,不是吗? 也许我们应该给它一个橙色的球来玩。
passclass OrangeBall ( ppb. Sprite ) :
同样,我确保有一个名为orangeball.png
的图像。 现在,让我们将球放在屏幕的左侧。
SCENE. add ( OrangeBall ( pos = ( - 4 , 0 ) ) )
尽可能尝试,企鹅不能踢球。 让我们在球接近时将球从企鹅上移开。
首先,让我们定义“踢”球的含义。 踢球意味着决定一秒钟内它将在哪里,然后将其状态设置为“移动”。
首先,我们将通过第一次更新将其移动到目标位置来移动它。
@ set_in_class ( OrangeBall ) def kick ( self , direction ) : self . target_position = self . position + direction self . original_position = self . position self . time_passed = 0 self . is_moving = True @ set_in_class ( OrangeBall ) def on_update ( self , update_event , signal ) : if self . is_moving : self . position = self . target_position self . is_moving = FalseOrangeBall. is_moving = False
现在,让我们踢吧!
ball. kick ( ppb. Vector ( 1 , 1 ) )ball , = SCENE. get ( kind = OrangeBall )
但这只是传送球。 它立即改变位置。 在现实生活中,球在中间点之间移动。 移动时,它将在其位置和需要移动的位置之间进行插值。
天真的,我们将使用线性插值 。 但是,很酷的视频游戏技巧是使用“缓动”功能。 在这里,我们使用常见的“平滑步骤”。
@ set_in_class ( OrangeBall ) def maybe_move ( self , update_event , signal ) : if not self . is_moving : return False self . time_passed + = update_event. time_delta if self . time_passed >= 1 : self . position = self . target_position self . is_moving = False return False t = smooth_step ( self . time_passed ) self . position = ( 1 -t ) * self . original_position + t * self . target_position return True OrangeBall. on_update = OrangeBall. maybe_movefrom mzutil import smooth_step
现在,让我们尝试再次踢它。
ball. kick ( ppb. Vector ( 1 , - 1 ) )ball , = SCENE. get ( kind = OrangeBall )
但实际上,企鹅应该踢球了。 当球看到它与企鹅碰撞时,它将向相反的方向踢。 如果企鹅正好位于其顶部,则球将选择一个随机方向。
现在,更新函数将调用maybe_move
并且仅在我们当前不移动时才检查碰撞。
import random OrangeBall. x_offset = OrangeBall. y_offset = 0.25 @ set_in_class ( OrangeBall ) def on_update ( self , update_event , signal ) : if self . maybe_move ( update_event , signal ) : return penguin , = update_event. scene . get ( kind = Penguin ) if not collide ( penguin , self ) : return try : direction = ( self . position - penguin. position ) . normalize ( ) except ZeroDivisionError : direction = ppb. Vector ( random . uniform ( - 1 , 1 ) , random . uniform ( - 1 , 1 ) ) . normalize ( ) self . kick ( direction )from mzutil import collide
但是,只是踢球并没有那么有趣。 让我们添加一个目标。
passclass Target ( ppb. Sprite ) :
让我们将目标放在屏幕的右侧。
SCENE. add ( Target ( pos = ( 4 , 0 ) ) )
奖励我们的企鹅
现在,当企鹅将球踢入目标时,我们将希望获得奖励。 一条鱼怎么样?
passclass Fish ( ppb. Sprite ) :
当目标拿到球时,它应该将其删除并在屏幕的另一端创建一个新球。 然后,它将导致一条鱼出现。
def on_update ( self , update_event , signal ) : for ball in update_event. scene . get ( kind = OrangeBall ) : if not collide ( ball , self ) : continue update_event. scene . remove ( ball ) update_event. scene . add ( OrangeBall ( pos = ( - 4 , random . uniform ( - 3 , 3 ) ) ) ) update_event. scene . add ( Fish ( pos = ( random . uniform ( - 4 , - 3 ) , random . uniform ( - 3 , 3 ) ) ) )@ set_in_class ( Target )
我们想让企鹅吃鱼。 当鱼看见企鹅时,它应该消失。
Fish. y_offset = 0.2 @ set_in_class ( Fish ) def on_update ( self , update_event , signal ) : penguin , = update_event. scene . get ( kind = Penguin ) if collide ( penguin , self ) : update_event. scene . remove ( self )Fish. x_offset = 0.05
有用!
迭代游戏设计对企鹅和其他人来说都很有趣!
这具有游戏的所有功能:由玩家控制的企鹅将球踢入目标,获得鱼,吃鱼并踢出新球。 这可以作为游戏的“磨削级别”部分,或者我们可以添加障碍来使企鹅的生活更加艰难。
无论您是经验丰富的程序员,还是刚开始使用,对视频游戏进行编程都是很有趣的。 具有Jupyter的PursuedPyBear通过经典环境(如Logo和Smalltalk)的交互编程功能,带来了经典2D游戏的所有乐趣。 是时候享受复古80年代了!
附录
这是实用程序库的完整源代码。 它提供了一些有趣的概念来使游戏板正常工作。 有关如何执行此操作的更多信息,请阅读冲突检测 setattr 。 和__name__属性 。
def retval ( func ) : setattr ( klass , func.__name__ , func ) return func return retval def smooth_step ( t ) : return t * t * ( 3 - 2 * t ) _WHICH_OFFSET = dict ( top = 'y_offset' , bottom = 'y_offset' , left = 'x_offset' , right = 'x_offset' ) _WHICH_SIGN = dict ( top = 1 , bottom = - 1 , left = - 1 , right = 1 ) def _effective_side ( sprite , direction ) : return ( getattr ( sprite , direction ) - _WHICH_SIGN [ direction ] * getattr ( sprite , _WHICH_OFFSET [ direction ] , 0 ) ) def _extreme_side ( sprite1 , sprite2 , direction ) : sign = -_WHICH_SIGN [ direction ] return sign * max ( sign * _effective_side ( sprite1 , direction ) , sign * _effective_side ( sprite2 , direction ) ) def collide ( sprite1 , sprite2 ) : return ( _extreme_side ( sprite1 , sprite2 , 'bottom' ) < _extreme_side ( sprite1 , sprite2 , 'top' ) and _extreme_side ( sprite1 , sprite2 , 'left' ) < _extreme_side ( sprite1 , sprite2 , 'right' ) )def set_in_class ( klass ) :
翻译自: /article/20/5/python-games
python 构建