python 构建_通过构建互动游戏来教孩子Python

python 构建

Python已作为一种出色的初学者编程语言而享有盛誉。 但是,从哪里开始呢?

我最喜欢的使人们对编程感兴趣的方法之一就是编写游戏。

PursuedPyBear (ppb)是为教学而优化的游戏编程库,我最近使用它来向孩子们传授更多有关我最喜欢的编程语言的知识

Jupyter项目是一个基于浏览器的Python控制台,最初是为数据科学家设计的,用于处理数据。

我有一个Jupyter笔记本,旨在教您如何制作简单的互动游戏,您可以从此处下载。 为了打开文件,您将需要安装最新的Jupyter项目JupyterLab。

先决条件:

我们将简要配置一个虚拟环境,以为所需的库创建单独的空间。 (您可以在此处了解有关虚拟环境如何工作的更多信息。)


   
   
$ git clone https://github. com /moshez/penguin-bit-by-bit. git
$ cd penguin-bit-by-bit
$ python -m venv venv
$ source ./venv/bin/activate
$ pip install -r requirements. txt
$ jupyter lab .

最后一条命令应在默认浏览器中打开http:// localhost:8888 / lab的 JupyterLab。 在左侧栏中选择dynamic_penguin.ipynb文件,我们就可以开始!

Jupyter screenshot

将运行游戏的事件循环

asyncio和PursuedPyBear运行自己的事件循环。

我们可以使用另一个库Twisted集成这两个库,例如粘胶。 这听起来很复杂,但值得庆幸的是,复杂性隐藏在库中,这将为我们完成所有艰苦的工作。

Jupyter中的以下单元格负责上半部分-将Twisted与asyncio事件循环集成。

__file__ = None不需要将PursuedPyBear与Jupyter集成。


   
   
from twisted. internet import asyncioreactor
asyncioreactor. install ( )
__file__ = None

接下来,我们需要一个“设置”功能。 设置功能是关键游戏元素配置的常用术语。 但是,我们的功能只会将游戏“场景”放在全局变量中。 就像我们定义要玩游戏的桌子一样。

Jupyter Notebook中的以下单元将完成此操作。


   
   
def setup ( scene ) :
    global SCENE
    SCENE = scene

现在,我们需要将PursuedPyBear的事件循环与Twisted集成。 我们txppb使用txppb模块:


   
   
import txppb
d = txppb. run ( setup )
d. addBoth ( print )

如果游戏由于错误而崩溃,最后的print将为我们提供帮助-它将打印出对Jupyter输出的追溯。

这将显示一个空窗口,准备好游戏元素。

这就是我们开始利用Jupyter的地方-传统上,在开始玩游戏之前,需要先编写整个游戏。 但是,我们违反约定,立即开始玩游戏!

通过互动使游戏变得有趣

但是,这不是一个非常有趣的游戏。 它什么都没有,只坐在那里。 如果我们想要一些东西,我们最好添加它。

在视频游戏编程中,屏幕上移动的东西称为“精灵”。 在PursuedPyBear中,子画面由类表示。 精灵会自动使用与该类相同名称的图像。 我从Kenney那里得到了一张小企鹅图片,这是免费和开源视频游戏资产的集合。


   
   
import ppb

class Penguin ( ppb. Sprite ) :
    pass

现在让我们把企鹅的权利放到中间。


   
   
SCENE. add ( Penguin ( pos = ( 0 , 0 ) ) )

它小心地坐在中间。 这比什么都不是要有趣得多。 很好-这正是我们想要的。 在渐进式游戏开发中,每一步都只会稍微有趣一点。

使用ppb为我们的企鹅游戏增添动感

但是企鹅并不能坐以待!! 企鹅应该四处走动。 我们将让玩家使用箭头键控制企鹅。 首先,让我们将键映射到向量:


   
   
from ppb import keycodes

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 ) }

现在,我们将使用一个实用程序库。 set_in_class函数设置类中的方法。 Python能够向类追溯添加功能的功能真的派上用场了!


   
   
from mzutil import set_in_class

Penguin. direction = ppb. Vector ( 0 , 0 )

@ set_in_class ( Penguin )
def on_update ( self , update_event , signal ) :
    self . position + = update_event. time_delta * self . direction

set_in_class的代码set_in_class不长,但是确实使用了一些不平凡的Python技巧。 我们将完整的实用程序库放在文章的末尾以进行回顾,并且为了顺畅起见,我们暂时将其跳过。

回到企鹅!

哦,嗯。

企鹅正在努力地以……零速运动,正好无处可走。 让我们手动设置方向以查看会发生什么。


   
   
Penguin. direction = DIRECTIONS [ keycodes. Up ] / 4

方向是向上,但是有点慢。 这给了足够的时间来手动设置企鹅的方向为零。 现在就开始吧!


   
   
Penguin. direction = ppb. Vector ( 0 , 0 )

为我们的企鹅游戏增添互动性

哎呀,那很令人兴奋-但不是我们想要的。 我们希望企鹅对按键进行响应。 通过代码控制它是游戏玩家所称的“作弊”。

我们将其设置为将方向设置为按键,并在释放键时将其设置回零。


   
   
@ set_in_class ( Penguin )
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 )

企鹅有点无聊,不是吗? 也许我们应该给它一个橙色的球来玩。


   
   
class OrangeBall ( ppb. Sprite ) :
    pass

同样,我确保有一个名为orangeball.png的图像。 现在,让我们将球放在屏幕的左侧。


   
   
SCENE. add ( OrangeBall ( pos = ( - 4 , 0 ) ) )

尽可能尝试,企鹅不能踢球。 让我们在球接近时将球从企鹅上移开。

首先,让我们定义“踢”球的含义。 踢球意味着决定一秒钟内它将在哪里,然后将其状态设置为“移动”。

首先,我们将通过第一次更新将其移动到目标位置来移动它。


   
   
OrangeBall. is_moving = False

@ 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 = False

现在,让我们踢吧!


   
   
ball , = SCENE. get ( kind = OrangeBall )
ball. kick ( ppb. Vector ( 1 , 1 ) )

但这只是传送球。 它立即改变位置。 在现实生活中,球在中间点之间移动。 移动时,它将在其位置和需要移动的位置之间进行插值。

天真的,我们将使用线性插值 。 但是,很酷的视频游戏技巧是使用“缓动”功能。 在这里,我们使用常见的“平滑步骤”。


   
   
from mzutil import smooth_step

@ 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_move

现在,让我们尝试再次踢它。


   
   
ball , = SCENE. get ( kind = OrangeBall )
ball. kick ( ppb. Vector ( 1 , - 1 ) )

但实际上,企鹅应该踢球了。 当球看到它与企鹅碰撞时,它将向相反的方向踢。 如果企鹅正好位于其顶部,则球将选择一个随机方向。

现在,更新函数将调用maybe_move并且仅在我们当前不移动时才检查碰撞。


   
   
from mzutil import collide
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 )

但是,只是踢球并没有那么有趣。 让我们添加一个目标。


   
   
class Target ( ppb. Sprite ) :
    pass

让我们将目标放在屏幕的右侧。


   
   
SCENE. add ( Target ( pos = ( 4 , 0 ) ) )

奖励我们的企鹅

现在,当企鹅将球踢入目标时,我们将希望获得奖励。 一条鱼怎么样?


   
   
class Fish ( ppb. Sprite ) :
    pass

当目标拿到球时,它应该将其删除并在屏幕的另一端创建一个新球。 然后,它将导致一条鱼出现。


   
   
@ set_in_class ( Target )
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 ) ) ) )

我们想让企鹅吃鱼。 当鱼看见企鹅时,它应该消失。


   
   
Fish. x_offset = 0.05
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 )

有用!

迭代游戏设计对企鹅和其他人来说都很有趣!

这具有游戏的所有功能:由玩家控制的企鹅将球踢入目标,获得鱼,吃鱼并踢出新球。 这可以作为游戏的“磨削级别”部分,或者我们可以添加障碍来使企鹅的生活更加艰难。

无论您是经验丰富的程序员,还是刚开始使用,对视频游戏进行编程都是很有趣的。 具有Jupyter的PursuedPyBear通过经典环境(如Logo和Smalltalk)的交互编程功能,带来了经典2D游戏的所有乐趣。 是时候享受复古80年代了!

附录

这是实用程序库的完整源代码。 它提供了一些有趣的概念来使游戏板正常工作。 有关如何执行此操作的更多信息,请阅读冲突检测 setattr 。 和__name__属性


   
   
def set_in_class ( klass ) :
    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' ) )

翻译自: https://opensource.com/article/20/5/python-games

python 构建

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值