Seth Kenlon 发布的文章

了解更多关于长期受喜爱的文本编辑器的信息,它为每个用户提供了一些东西。

KDE Plasma 桌面提供了很多东西:一个很棒的桌面、一个灵活的文件管理器,以及紧密集成的应用。然而,人们很容易忽视它的默认文本编辑器,其中之一就是 Kate。2020 年 12 月 14 日,Kate 将迎来 20 岁生日,在其 20 年的发展历程中,它在一个简单明了的编辑器和一个适度的集成开发环境 (IDE) 之间取得了完美的平衡。

安装 Kate

如果你正在运行 KDE Plasma 桌面,你可能已经安装了 Kate。如果还没有安装,不管你运行的是哪个桌面,你都可以在你的软件仓库中找到它。

另外,你可以在 Linux 或 Chromebook 上从 Flathub.org 使用 Flatpak 安装 Kate。

在 Windows 上,你可以从 Windows 商店获得 Kate。

要在 macOS 上安装它,从 KDE build 网站下载 macOS 版本。

所有的功能

第一眼,Kate 似乎并不显眼。它看起来就像其他的文本编辑器一样:一个巨大的空白窗口,可以接受大量的输入,顶部有一个菜单栏,边缘有一些元数据来指示字符编码和行数。但当你开始使用它的那一刻,你会发现它拥有你所需要的所有功能,就在你想要的地方。

例如,沿着 Kate 窗口的左侧是行数,默认情况下是启用的。更好的是,当你输入常用的编码语言或标记语法时,代码折叠就会被激活。窗口右侧的文件导航面板可以快速、直观地访问文件的不同部分。而窗口底部则包含了常用模式设置,包括插入或覆盖、字符编码(如 UTF-8)和语法高亮。

 title=

不过,这还不是全部。听起来可能有很多,但布局很方便,很直观。在菜单和设置中还有更多的功能。

Kate 的功能有集成 Git、文件浏览器、shell、打开文档或项目的面板、单词补全、XML 补全、标签式界面、分屏和插件结构,因此你可以进一步扩展其功能。

给所有用户使用的编辑器

Kate 的极强灵活性使它既是一个简单的文本编辑器,又是一个强大的 IDE。通过默认使用的熟悉的界面,它吸引了广大的用户;而通过提供与调试器、编译器和代码检查器集成的能力,它吸引了开发人员。

因为它允许用户控制它的 UI 布局,Kate 确保每个用户都能优化使用它的体验。传统上,这一直是一个难以平衡的问题:一个拥有太多功能的编辑器给人的感觉太像一个臃肿的 IDE,而一个界面简单、功能模糊的编辑器给人的感觉是基础的或不方便的。Kate 将真正有用的功能放在了 UI 的最前面,从而让每个人都能享受到,同时又让高级功能可以被发现,但又不碍事。

坦率地说,Kate 让人难以割舍。它使用起来很愉快,配置起来很简单,探索起来也很有趣。今天就安装 Kate 吧,试一试它。它在所有主要的平台上都可以使用,你没有什么理由不选择 Kate 作为你的新宠编辑器。


via: https://opensource.com/article/20/12/kate-text-editor

作者:Seth Kenlon 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Emacs 并不是一个单纯的文本编辑器,它将掌控置于你手中,让你几乎可以解决你遇到的任何问题。

 title=

我是一个典型的 Emacs 用户。不是我选择的 Emacs,而是它选择了我。早在我刚开始学习 Unix 的时候,我偶然发现了一个奇怪的名为 Emacs 的应用程序,它隐藏在我的电脑上,其中有一个鲜为人知的功能。传说中(而且被证明是真的),如果你在终端上输入 emacs,按 Alt+X,然后输入 tetris,你就可以玩一个掉方块的游戏。

 title=

那就是我对 GNU Emacs 的印象。虽然这很肤浅,但它也准确地表明了 Emacs 的意义:用户可以重新编程他们的(虚拟)世界,并且可以用一个应用程序做任何他们想做的事情。在你的文本编辑器中玩俄罗斯方块可能不是你日常的主要目标,但这说明 Emacs 是一个值得骄傲的编程平台。事实上,你可以把它看作是 Jupyter 的一种先驱,它把一种强大的编程语言(准确的说叫 elisp)和自己的实时环境结合起来。因此,Emacs 作为一个文本编辑器是灵活的、可定制的、强大的。

如果你习惯于 Bash、Python 或类似的语言,elisp(以及扩展的 Common Lisp)不一定是最容易入门的语言。但是这种 LISP 方言是很强大的,而且因为 Emacs 是一个 LISP 解释器,所以你可以用它构建应用程序,不管它们是 Emacs 插件还是你想开发成一个独立项目的原型。极其流行的 org 模式项目就是一个例子:它是一个 Emacs 插件,同时也是一个标记语法,有移动应用可以解释和扩展其功能。类似的有用的 Emacs 内应用的例子还有很多,包括电子邮件客户端、PDF 浏览器、Web 浏览器、shell 和文件管理器。

两个界面

GNU Emacs 至少有两个用户界面:图形用户界面(GUI)和终端用户界面(TUI)。这有时会让人感到惊讶,因为 Emacs 经常与运行在终端中的 Vi 相提并论(尽管 gVim 为现代 Vi 的实现提供了一个 GUI)。如果你想把 GNU Emacs 以终端程序来运行,你可以用 -nw 选项来启动它。

$ emacs -nw

有了 GUI 程序,你可以直接从应用程序菜单或终端启动 Emacs。

你可能会认为 GUI 会降低 Emacs 的效率,好像“真正的文本编辑器是在终端中运行的”,但 GUI 可以使 Emacs 更容易学习,因为它的 GUI 遵循了一些典型的惯例(菜单栏、可调节的组件、鼠标交互等)。

事实上,如果你把 Emacs 作为一个 GUI 应用程序来运行,你可能在一天的时间里会完全没有意识到你在 Emacs 中。只要你使用过 GUI,大多数常用的惯例都适用。例如,你可以用鼠标选择文本,导航到编辑菜单,选择复制,然后将光标放在其他地方,选择粘贴。要保存文档,你可以进入文件,然后选择保存或另存为。你可以按 Ctrl 键并向上滚动,使屏幕字体变大,你可以使用滚动条来浏览你的文档,等等。

了解 Emacs 的 GUI 形式是拉平学习曲线的好方法。

Emacs 键盘快捷键

GNU Emacs 以复杂的键盘组合而恶名远扬。它们不仅陌生(Alt+W 来复制?Ctrl+Y 来粘贴?),而且还用晦涩难懂的术语来标注(Alt 被称为 Meta),有时它们成双成对(Ctrl+X 后是 Ctrl+S 来保存),有时则单独出现(Ctrl+S 来搜索)。为什么有人会故意选择使用这些呢?

嗯,有些人不会。但那些喜欢这些的人是因为这些组合很容易融入到日常打字的节奏中(而且经常让 Caps Lock 键充当 Ctrl 键)。然而,那些喜欢不同的东西的人有几个选择:

  • “邪恶”模式让你在 Emacs 中使用 Vim 键绑定。就是这么简单。你可以保留你的肌肉记忆中的按键组合,并继承最强大的文本编辑器。
  • 通用用户访问(CUA)键保留了所有 Emacs 常用的组合键,但最令人头疼的键(复制、剪切、粘贴和撤消)都被映射到现代的键盘绑定中(分别为 Ctrl+C、Ctrl+X、Ctrl+V 和 Ctrl+Z)。
  • global-set-key 函数,是 Emacs 编程的一部分,允许你定义自己的键盘快捷键。传统上,用户定义的快捷键以 Ctrl+C 开头,但没有什么能阻止你发明自己的方案。Emacs 并不敝帚自珍,欢迎你按照自己的意愿来扭转它。

学习 Emacs

要想很好地使用 Emacs 是需要时间的。对我来说,这意味着打印出一张速记表,每天都把它放在键盘旁边。当我忘了一个键组合时,我就在我的速记表上查找它。如果它不在我的速记表上,我就学习这个键盘组合,要么通过执行该函数,并注意 Emacs 告诉我如何更快地访问它,要么通过使用 describe-function:

M-x describe-function: save-buffer

save-buffer is an interactive compiled Lisp function in ‘files.el’.

It is bound to C-x C-s, <menu-bar> <file> <save-buffer>.
[...]

当你使用它的时候,你就会学习它。你对它了解得越多,你就越有能力去改进它,使它变成你自己的。

尝试 Emacs

人们常开玩笑说 Emacs 是一个包含文本编辑器的操作系统。也许这是在暗示 Emacs 臃肿和过于复杂,当然也有一种说法是文本编辑器根据其默认配置不应该需要 libpoppler(你可以不需要它来编译 Emacs)。

但这个笑话背后潜藏着一个更大的真相,它揭示了 Emacs 如此有趣的原因。将 Emacs 与其他文本编辑器,如 Vim、Nano,甚至 VSCodium 进行比较是没有意义的,因为 Emacs 真正重要的部分并不是你可以在窗口中输入东西并保存的这种思路。那是连 Bash 都能提供的基本功能。Emacs 的真正意义在于它如何将控制置身于你的手中,以及如何通过 Emacs Lisp(Elisp)解决几乎任何问题。


via: https://opensource.com/article/20/12/emacs

作者:Seth Kenlon 选题:lujun9972 译者:wxy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Jed 方便的下拉菜单,让新用户可以轻松地使用终端文本编辑器。

你可能听说过 Emacs、Vim 和 Nano 这些典型的 Linux 文本编辑器,但 Linux 有大量的开源文本编辑器,我的目标是在 12 月份对其中的 31 个文本编辑器进行一次公平的测试。

在这篇文章中,我将介绍 Jed,它是一个基于终端的编辑器,它的特点是有一个方便的下拉菜单,这让那些刚刚接触终端编辑器的用户,以及那些不喜欢记住每个功能的组合键的用户而言变得特别容易。

安装 Jed

在 Linux 上,你的发行版软件仓库可能会让 Jed 通过你的软件包管理器安装:

$ sudo dnf install jed

并不是所有发行版都是如此,但它是一个很容易从源码编译的应用。首先,下载 S 语言(Jed 的编写语言)并安装(其中 x.y.z 请替换为对应的版本号):

$ wget https://www.jedsoft.org/releases/slang/slang-x.y.z.tar.bz2
$ tar xvf slang*bz2
$ cd slang-x.y.z
$ ./configure ; make
$ sudo make install

安装好后,对 Jed 源码也同样操作(其中 x.y.z 请替换为对应的版本号):

$ wget https://www.jedsoft.org/releases/jed/jed-x.y.z.tar.bz2
$ tar xvf jed*bz2
$ cd jed-x.y.z
$ ./configure ; make
$ sudo make install

启动 Jed

Jed 在终端中运行,所以要启动它,只需打开终端,输入 jed:

F10 key ==> File   Edit   Search   Buffers   Windows   System   Help


     This is a scratch buffer.  It is NOT saved when you exit.

     To access the menus, press F10 or ESC-m and the use the arrow
     keys to navigate.

     Latest version information is available on the web from
     <http://www.jedsoft.org/jed/>.  Other sources of JED
     information include the usenet newsgroups comp.editors and
     alt.lang.s-lang.  To subscribe to the jed-users mailing list, see
     <http://www.jedsoft.org/jed/mailinglists.html>.

     Copyright (C) 1994, 2000-2009  John E. Davis
     Email comments or suggestions to <[email protected]>.

[ (Jed 0.99.19U) Emacs: *scratch*    ()  1/16   8:49am ]

如何使用 Jed

Jed 自动加载的说明很清晰且很有帮助。你可以按 F10 键或 Esc 键,然后按字母 M 进入顶部菜单。这将使你的光标进入 Jed 顶部的菜单栏,但它不会打开菜单。要打开菜单,请按键盘上的回车键。使用方向键来浏览每个菜单。

屏幕上的菜单不仅对初次使用的用户很有帮助,对有经验的用户来说,它还提供了很好的键盘快捷键提醒。例如,你大概能猜到如何保存正在处理的文件。进入 File 菜单,选择 Save。如果你想加快这个过程,你可以记住 Ctrl+X,然后 Ctrl+S 的组合键(是的,这是连续的两个组合键)。

探索 Jed 的功能

对于一个简单的编辑器来说,Jed 拥有一系列令人惊讶的实用功能。它有一个内置的多路复用器,允许你同时打开多个文件,但它会“叠”在另一个文件之上,所以你可以在它们之间切换。你可以分割你的 Jed 窗口,让多个文件同时出现在屏幕上,改变你的颜色主题,或者打开一个 shell。

对于任何有 Emacs 使用经验的人来说,Jed 的许多“没有宣传”的功能,例如用于导航和控制的组合键,都是一目了然的。然而,当一个组合键与你所期望的大相径庭时,就会有一个轻微的学习(或者说没有学习)曲线。例如,GNU Emacs 中的 Alt+B 可以将光标向后移动一个字,但在 Jed 中,默认情况下,它是 Buffers 菜单的快捷键。这让我措手不及,大约本文每句话都遇到一次。

 title=

Jed 也有模式,允许你加载模块或插件来帮助你编写特定种类的文本。例如,我使用默认的 text 模式写了这篇文章,但当我在编写 Lua 时,我能够切换到 lua 模式。这些模式提供语法高亮,并帮助匹配括号和其他分隔符。你可以在 /usr/share/jed/lib 中查看 Jed 捆绑了哪些模式,而且因为它们是用 S 语言编写的,你可以浏览代码,并可能学习一种新的语言。

尝试 Jed

Jed 是一个令人愉快且清新的 Linux 终端文本编辑器。它轻量级,易于使用,设计相对简单。作为 Vi 的替代方案,你可以在你的 ~/.bashrc 文件中(如果你是 root 用户,在 root 用户的 ~/.bashrc 文件中)将 Jed 设置为 EDITOR 和 VISUAL 变量。今天就试试 Jed 吧。


via: https://opensource.com/article/20/12/jed

作者:Seth Kenlon 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

通过添加声音到你的游戏中,听听当你的英雄战斗、跳跃、收集战利品时会发生什么。学习如何在这个 Pygame 系列的第十三篇文章中,创建一个声音平台类精灵。

在 Python 3 中使用 Pygame 模块来创建电脑游戏的系列文章仍在进行中,这是第十三部分。先前的文章是:

  1. 通过构建一个简单的掷骰子游戏去学习怎么用 Python 编程
  2. 使用 Python 和 Pygame 模块构建一个游戏框架
  3. 如何在你的 Python 游戏中添加一个玩家
  4. 用 Pygame 使你的游戏角色移动起来
  5. 如何向你的 Python 游戏中添加一个敌人
  6. 在 Pygame 游戏中放置平台
  7. 在你的 Python 游戏中模拟引力
  8. 为你的 Python 平台类游戏添加跳跃功能
  9. 使你的 Python 游戏玩家能够向前和向后跑
  10. 在你的 Python 平台类游戏中放一些奖励
  11. 添加计分到你的 Python 游戏
  12. 在你的 Python 游戏中加入投掷机制

Pygame 提供了一种简单的方法来集成声音到你的 Python 电脑游戏中。Pygame 的 mixer 模块 可以依据命令播放一个或多个声音,并且你也可以将这些声音混合在一起,例如,你能够在听到你的英雄收集奖励或跳过敌人声音的同时播放背景音乐。

集成 mixer 模块到一个已存在的游戏中是很容易的,因此,与其给你代码示例来向你展示放置它们的位置,不如在这篇文章解释在你的应用程序中获得声音所需的四个步骤。

启动 mixer

首先,在你代码的设置部分,启动 mixer 进程。你的代码已经启动 Pygame 和 Pygame 字体,因此将它们归类到一起是一个很好的主意:

pygame.init()
pygame.font.init()
pygame.mixer.init() # add this line

定义声音

接下来,你必需定义你想要使用的声音。这样就要求你的计算机上有声音文件,就像使用字体就要求你有字体文件,使用图像就要求你有图像文件一样。

你还必需把这些声音与你的游戏捆绑在一起,以便任何玩你游戏的人都有这些声音文件。

为将一个声音与你的游戏捆绑在一起,首先在你的游戏目录中创建一个新的目录,就像你为你图像和字体创建的目录一样。称它为 sound:

s = 'sound'

尽管在互联网上有很多声音文件,下载这些声音文件并将其与你的游戏一起分发并不一定是合法的。这看起来是很奇怪的,因为这么多来自著名电脑游戏的声音是流行文化的一部分,但法律就是这样运作的。如果你想与你的游戏一起分发一个声音文件,你必需找到一个开源或共创许可的声音文件,它们准许与游戏一起提供声音。

这里有一些专门提供自由和合法的声音文件的网站,包括:

一些声音文件只要你给予作曲家或声音设计师一个致谢就可以自由使用。在与你的游戏捆绑前,仔细阅读使用条件!音乐家和声音设计师在声音上的工作就像你在代码上的工作一样努力,因此即使他们不要求,给予他们致谢也是极好的。

给予你的声音源文件一些致谢,在一个名为 CREDIT 的文本文件中列出你使用的声音,并在你的游戏文件夹中放置该文本文件。

你也可以尝试制作你自己的音乐。优秀的 LMMS 音频工作站易于使用,并带有很多有趣的声音。它在所有主要的平台上都可以使用,也可以导出为 Ogg Vorbis(OGG)音频格式。

添加声音到 Pygame

当你找到你喜欢的一个声音文件时,下载它。如果它是一个 ZIP 或 TAR 文件,提取它并将其移动到你游戏目录中的 sound 文件夹中。

如果声音文件的名字带有空格或特殊字符,重新命名它。文件名称是完全随意的,它的名称越简单,你就越容易输入到你的代码中。

大多数的电脑游戏使用 OGG 格式声音文件,因为这种格式可以占有较小空间而提供高质量的声音。当你下载一个声音文件时,它可能是一个 MP3、WAVE、FLAC 或者其它的音频格式。为保持你的文件的较高兼容性和降低下载文件大小,使用一个像 fre:ac 或 Miro 这样的工具来转换这些的文件格式为 Ogg 格式。

例如,假设你已经下载一个称为 ouch.ogg 的声音文件。

在你代码的设置部分中,创建一个变量,代表你想使用的声音文件:

ouch = pygame.mixer.Sound(os.path.join(s, 'ouch.ogg'))

触发一个声音

为使用一个声音,你所要做的就是在你想触发它的时候调用这个变量。例如,当你的玩家击中一名敌人时,触发 OUCH 声音效果:

for enemy in enemy_hit_list:
    pygame.mixer.Sound.play(ouch)
    score -= 1

你可以为各种动作创建声音,例如,跳跃、收集奖励、投掷、碰撞,以及其他任何你能想象到的动作。

添加背景音乐

如果你有想在你的游戏的背景中播放的音乐或令人激动的音效,你可以使用 Pygame 中的 mixer 模块中的 music 函数。在你的设置部分中,加载音乐文件:

music = pygame.mixer.music.load(os.path.join(s, 'music.ogg'))

然后,开始音乐:

pygame.mixer.music.play(-1)

-1 值告诉 Pygame 无限循环音乐文件。你可以将其设置为从 0 到更高的值之间的任意数值,以定义音乐在停止前循环多少次。

享受音效

音乐和声音可以为你的游戏添加很多韵味。尝试添加一些声音到你的 Pygame 工程!


via: https://opensource.com/article/20/9/add-sound-python-game

作者:Seth Kenlon 选题:lujun9972 译者:robsean 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

使用色键(或绿屏)技巧来设置你电脑游戏中图片的透明度。

不管你是否正在使用 Python 或 Lua 编程一个游戏或一个 APP,你都有可能在你的游戏资源中使用 PNG 图像。PNG 格式图像的一个优点是能够存储一个 alpha 通道,这在一个 JPEG 格式的图像中是不可能获得的。alpha 在本质上是不可见的或透明的“颜色”。alpha 是你图像 不可见 的一部分。例如,你要绘制一个甜甜圈,甜甜圈的空洞将使用 alpha 填充,你就可以看到它后面的任何东西。

一个常见的问题是如何找到一幅图像的 alpha 部分。有时你的编程框架,不管它是 Python Arcade、Pygame、LÖVE,或者其它的任何东西都会检测出 alpha 通道,(在适当地调用函数后)将其作为透明处理。这意味着它将不会在 alpha 部分来渲染新的像素,而留下甜甜圈的空洞。100% 是透明的,0% 是不透明的,在功能上起到“不可见”的作用。

有些时候,你的框架与你的图像资源在 alpha 通道的位置上是不一致的(或者,alpha 通道根本就不存在),你在你想要透明度的地方却得到像素。

这篇文章描述了我所知道的最可靠的方法来解决透明度的问题。

色键

在计算机图形学中,有一些有助于确定每一个像素是如何渲染的值。 色度 Chrominance (或者 chroma),描述一个像素的饱和度或强度。 色键 chroma key 技术(也称为 绿屏 green screening )最初是作为一种化学工艺而发展起来的,在复印一张底片时,使用一种特定的 无光泽 的颜色(最初是蓝色,后来是绿色)来故意遮掩,以允许使用另一幅图像来取代曾经有蓝色或绿色屏幕的地方。这是一种简化的解释,但是它说明了计算机图形学中被称为 alpha 通道的起源。

alpha 通道是保存在图像中的信息,用以标识要透明的像素。例如,RGB 图像有红、绿、蓝通道。RGBA 图像包含红、绿、蓝通道,以及 alpha 通道。alpha 值的范围可以从 0 到 1 ,使用小数是也有效的。

因为一个 alpha 通道可以用几种不同的方法表达,因此依赖于嵌入的 alpha 通道可能是有问题的。作为替代方案,你可以在你的游戏框架中选择一种颜色并将其转化为一个 0 的 alpha 值。要做到这一点,你必须知道在你图像中的颜色值。

准备你的图片

要准备一个专门为色度键保留明确颜色的图形,在你最喜欢的图片编辑器中打开图片。我建议使用 GIMP 或 Glimpse,但是 mtPaint 或 Pinta,甚至 Inkscape 也能很好地工作,这取决于你的图像的性质,以及你将这些操作指南转换到一种不同图片编辑器工具的能力。

首先打开这幅 Tux 企鹅的图像:

 title=

选择图片

在图片打开后,转到 窗口 菜单,选择 可停靠对话框 ,接下来选择 图层。在 图层 面板中 Tux 图层上右击。从弹出菜单中,选择 Alpha 到选区 。如果你的图像没有内置的 alpha 通道,那么你必须手动创建你自己的选区。

 title=

为手动创建一个选区,单击来自工具箱的 路径 工具。

 title=

使用 路径 工具,在图像周围移动鼠标,在其轮廓的每个主要交叉点处都单击和释放(不要拖动)。 不要担心沿着曲线走;只需要找到主要的交叉点和拐角。这将在每个点处创建一个节点,并在节点中间绘制一条条线段。你不需要闭合你的路径,因此当你最后到达你开始时的交叉点时,你就完成了。

 title=

在你创建你的轮廓路径后,转到 窗口 菜单,选择 可停靠对话框 ,接下来选择 工具选项 。在 工具选项 面板中,选择 编辑 (Ctrl) 。随着这项操作的激活,你可以编辑你刚刚通过单击线或单击节点绘制的路径,并通过调整它们来更好地适应你的图像。你甚至能够将直线弯曲。

 title=

现在从 窗口 > 可停靠对话框 菜单中选择 路径 面板。在 路径 面板中,单击 路径到选区 按钮。你的绘图现在已经被选中了。

扩大选区

如果你觉得你的选区太紧贴了,你可以通过扩大来给予你自己的选区一些富余。当我想在一张图像周围采用或加厚一个边框时,我有时会这么扩大选区。

为扩大一个选区,单击 选择 菜单,选择 扩大 。输入一个像素值并单击 确定 。

反转选区

你已经选择了你的图形,但是你真正想选择的东西却 不包括 你所选择的图像。这是因为你要创建一个 alpha 蒙版来定义图像中的一些内容的来被其它一些内容所替换。换句话说,你需要标记那些将被转变为不可见的像素。

为了反转选择区,单击 选择 菜单,选择 反转 。现在除你的图像以外的一切东西都是被选择的。

使用 alpha 填充

随着选择了除了你的图像以外的一切东西,再选择你想使用的颜色来指定你的 alpha 蒙版。最常用的颜色是绿色(正如你可能从术语“绿屏”中所猜到的一样)。绿色不是什么神奇的颜色,甚至也不是特定的绿色色调。之所以使用它是因为人们经常处理不包含绿色色素的图像,这样人们能够很容易分离出绿色,而不会意外地分离出图像中重要的部分。当然,如果你的图像是一位绿色的外星人或一枚绿宝石或一些 确实 包含绿色的东西,那么你应该使用一种不同的颜色。只要你所选择的颜色是单一的单色,那么你就可以使用你所希望的任意颜色。如果你正在处理很多图像,你的选择应该在所有图像中保持一致。

 title=

使用你选择的颜色值来设置你的前景色。为确保你的选择是精确的,使用 HTML 或 HSV 表示的颜色。例如,如果你正在使用纯绿色,它可以在 GIMP(以及大多数的开放源码图像应用程序)中表示为 00ff00(00 是红色,FF 是绿色,00 是蓝色,F 是最大值)。

 title=

不管你选择什么颜色,务必记录下 HTML 或 HSV 的值,以便你可以为每一张图像使用完全相同的颜色。

为填充你的 alpha 蒙版,单击 编辑 菜单,选择 使用前景色填充 。

平整和导出

如果你在你的图像周围留下边框,设置背景颜色来着色你想使用的边界笔刷。这通常是黑色或白色,但是它也可以是任何适宜你游戏审美观的颜色。

在你设置背景颜色后,单击 图像 菜单,选择 平整图像。不管你是否添加了边框,这样做都是安全的。这个过程将从图像中移除 alpha 通道,并使用背景色填充任何“透明的”像素。

 title=

你现在已经为你的游戏引擎准备好了一张图像。导出图像为你的游戏引擎喜欢的任何格式,接下来使用游戏引擎所需要的每一个函数来将图像导入的你的游戏中。在的代码中,设置 alpha 值为 00ff00(或你使用的任何颜色),接下来使用游戏引擎的图像转换器来将该颜色作为 alpha 通道处理。

其它的方法

这不是唯一能在你游戏图像中获取透明度的方法。查看你游戏引擎的文档来找出它是如何默认尝试处理 alpha 通道的,在你不确定的时候,尝试让你的游戏引擎来自动侦测图像中透明度,然后再去编辑它。有时,你游戏引擎的预期值和你图像的预设值恰巧匹配,那么你就可以直接获取透明度,而不需要做任何额外的工作。

不过,当这些尝试都失败时,尝试一下色键。它为电影业工作了将近 100 年,它也可以为你工作。


via: https://opensource.com/article/20/9/chroma-key-gimp

作者:Seth Kenlon 选题:lujun9972 译者:robsean 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

四处奔跑躲避敌人是一回事,反击敌人是另一回事。学习如何在这系列的第十二篇文章中在 Pygame 中创建平台游戏。

这是仍在进行中的关于使用 Pygame 模块在 Python 3 中创建电脑游戏的第十二部分。先前的文章是:

  1. 通过构建一个简单的掷骰子游戏去学习怎么用 Python 编程
  2. 使用 Python 和 Pygame 模块构建一个游戏框架
  3. 如何在你的 Python 游戏中添加一个玩家
  4. 用 Pygame 使你的游戏角色移动起来
  5. 如何向你的 Python 游戏中添加一个敌人
  6. 在 Pygame 游戏中放置平台
  7. 在你的 Python 游戏中模拟引力
  8. 为你的 Python 平台类游戏添加跳跃功能
  9. 使你的 Python 游戏玩家能够向前和向后跑
  10. 在你的 Python 平台类游戏中放一些奖励
  11. 添加计分到你的 Python 游戏

我的上一篇文章本来是这一系列文章的最后一篇,它鼓励你为这个游戏编写自己的附加程序。你们很多人都这么做了!我收到了一些电子邮件,要求帮助我还没有涵盖的常用机制:战斗。毕竟,跳起来躲避坏人是一回事,但是有时候让他们走开是一件非常令人满意的事。在电脑游戏中向你的敌人投掷一些物品是很常见的,不管是一个火球、一支箭、一道闪电,还是其它适合游戏的东西。

与迄今为止你在这个系列中为你的平台游戏编程的任何东西不同,可投掷物品有一个生存时间。在你投掷一个物品后,它会如期在移动一段距离后消失。如果它是一支箭或其它类似的东西,它可能会在通过屏幕的边缘时而消失。如果它是一个火球或一道闪电,它可能会在一段时间后熄灭。

这意味着每次生成一个可投掷的物品时,也必须生成一个独特的衡量其生存时间的标准。为了介绍这个概念,这篇文章演示如何一次只投掷一个物品。(换句话说,每次仅存在一个投掷物品)。 一方面,这是一个游戏的限制条件,但另一方面,它却是游戏本身的运行机制。你的玩家不能每次同时投掷 50 个火球,因为每次仅允许一个投掷物品,所以当你的玩家释放一个火球来尝试击中一名敌人就成为了一项挑战。而在幕后,这也使你的代码保持简单。

如果你想启用每次投掷多个项目,在完成这篇教程后,通过学习这篇教程所获取的知识来挑战你自己。

创建 Throwable 类

如果你跟随学习这系列的其它文章,那么你应该熟悉在屏幕上生成一个新的对象基础的 __init__ 函数。这和你用来生成你的 玩家 和 敌人 的函数是一样的。这里是生成一个 throwable 对象的 __init__ 函数来:

class Throwable(pygame.sprite.Sprite):
    """
    生成一个 throwable 对象
    """
    def __init__(self, x, y, img, throw):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(os.path.join('images',img))
        self.image.convert_alpha()
        self.image.set_colorkey(ALPHA)
        self.rect   = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.firing = throw

同你的 Player 类或 Enemy 类的 __init__ 函数相比,这个函数的主要区别是,它有一个 self.firing 变量。这个变量保持跟踪一个投掷的物品是否在当前屏幕上活动,因此当一个 throwable 对象创建时,将变量设置为 1 的合乎情理的。

判断存活时间

接下来,就像使用 Player 和 Enemy 一样,你需要一个 update 函数,以便投掷的物品在瞄准敌人抛向空中时,它会自己移动。

测定一个投掷的物品存活时间的最简单方法是侦测它何时离开屏幕。你需要监视的屏幕边缘取决于你投掷的物品的物理特性。

  • 如果你的玩家正在投掷的物品是沿着水平轴快速移动的,像一只弩箭或箭或一股非常快的魔法力量,而你想监视你游戏屏幕的水平轴极限。这可以通过 worldx 定义。
  • 如果你的玩家正在投掷的物品是沿着垂直方向或同时沿着水平方向和垂直方向移动的,那么你必须监视你游戏屏幕的垂直轴极限。这可以通过 worldy 定义。

这个示例假设你投掷的物品向前移动一点并最终落到地面上。不过,投掷的物品不会从地面上反弹起来,而是继续掉落出屏幕。你可以尝试不同的设置来看看什么最适合你的游戏:

    def update(self,worldy):
        '''
        投掷物理学
        '''
        if self.rect.y < worldy:   #垂直轴 
            self.rect.x  += 15     #它向前移动的速度有多快
            self.rect.y  += 5      #它掉落的速度有多快
        else:
            self.kill()            #移除投掷对象
            self.firing = 0        #解除火力发射

为使你的投掷物品移动地更快,增加 self.rect 的动量值。

如果投掷物品不在屏幕上,那么该物品将被销毁,以及释放其所占用的寄存器。另外,self.firing 将被设置回 0 以允许你的玩家来进行另一次射击。

设置你的投掷对象

就像使用你的玩家和敌人一样,你必须在你的设置部分中创建一个精灵组来保持投掷对象。

此外,你必须创建一个非活动的投掷对象来供开始的游戏使用。如果在游戏开始时却没有一个投掷对象,那么玩家在第一次尝试投掷一柄武器时,投掷将失败。

这个示例假设你的玩家使用一个火球作为开始的武器,因此,每一个投掷实例都是由 fire 变量指派的。在后面的关卡中,当玩家获取新的技能时,你可以使用相同的 Throwable 类来引入一个新的变量以使用一张不同的图像。

在这代码块中,前两行已经在你的代码中,因此不要重新键入它们:

player_list = pygame.sprite.Group() #上下文
player_list.add(player)             #上下文
fire = Throwable(player.rect.x,player.rect.y,'fire.png',0)
firepower = pygame.sprite.Group()

注意,每一个投掷对象的起始位置都是和玩家所在的位置相同。这使得它看起来像是投掷对象来自玩家。在第一个火球生成时,使用 0 来显示 self.firing 是可用的。

在主循环中获取投掷行为

没有在主循环中出现的代码不会在游戏中使用,因此你需要在你的主循环中添加一些东西,以便能在你的游戏世界中获取投掷对象。

首先,添加玩家控制。当前,你没有火力触发器。在键盘上的按键是有两种状态的:释放的按键,按下的按键。为了移动,你要使用这两种状态:按下按键来启动玩家移动,释放按键来停止玩家移动。开火仅需要一个信号。你使用哪个按键事件(按键按下或按键释放)来触发你的投掷对象取决于你的品味。

在这个代码语句块中,前两行是用于上下文的:

            if event.key == pygame.K_UP or event.key == ord('w'):
                player.jump(platform_list)
            if event.key == pygame.K_SPACE:
                if not fire.firing:
                    fire = Throwable(player.rect.x,player.rect.y,'fire.png',1)
                    firepower.add(fire)

与你在设置部分创建的火球不同,你使用一个 1 来设置 self.firing 为不可用。

最后,你必须更新和绘制你的投掷物品。这个顺序很重要,因此把这段代码放置到你现有的 enemy.move 和 player_list.draw 的代码行之间:

    enemy.move()  # 上下文

    if fire.firing:
        fire.update(worldy)
        firepower.draw(world)
    player_list.draw(screen)  # 上下文
    enemy_list.draw(screen)   # 上下文

注意,这些更新仅在 self.firing 变量被设置为 1 时执行。如果它被设置为 0 ,那么 fire.firing 就不为 true,接下来就跳过更新。如果你尝试做上述这些更新,不管怎样,你的游戏都会崩溃,因为在游戏中将不会更新或绘制一个 fire 对象。

启动你的游戏,尝试挑战你的武器。

检测碰撞

如果你玩使用了新投掷技巧的游戏,你可能会注意到,你可以投掷对象,但是它却不会对你的敌人有任何影响。

原因是你的敌人没有被查到碰撞事故。一名敌人可能会被你的投掷物品所击中,但是敌人却从来不知道被击中了。

你已经在你的 Player 类中完成了碰撞检测,这非常类似。在你的 Enemy 类中,添加一个新的 update 函数:

    def update(self,firepower, enemy_list):
        """
        检测火力碰撞
        """
        fire_hit_list = pygame.sprite.spritecollide(self,firepower,False)
        for fire in fire_hit_list:
            enemy_list.remove(self)

代码很简单。每个敌人对象都检查并看看它自己是否被 firepower 精灵组的成员所击中。如果它被击中,那么敌人就会从敌人组中移除和消失。

为集成这些功能到你的游戏之中,在主循环中调用位于新触发语句块中的函数:

    if fire.firing:                             # 上下文
        fire.update(worldy)                     # 上下文
        firepower.draw(screen)                  # 上下文
        enemy_list.update(firepower,enemy_list) # 更新敌人

你现在可以尝试一下你的游戏了,大多数的事情都如预期般的那样工作。不过,这里仍然有一个问题,那就是投掷的方向。

更改投掷机制的方向

当前,你英雄的火球只会向右移动。这是因为 Throwable 类的 update 函数将像素添加到火球的位置,在 Pygame 中,在 X 轴上一个较大的数字意味着向屏幕的右侧移动。当你的英雄转向另一个方向时,你可能希望它投掷的火球也抛向左侧。

到目前为止,你已经知道如何实现这一点,至少在技术上是这样的。然而,最简单的解决方案却是使用一个变量,在一定程度上对你来说可能是一种新的方法。一般来说,你可以“设置一个标记”(有时也被称为“翻转一个位”)来标明你的英雄所面向的方向。在你做完后,你就可以检查这个变量来得知火球是向左移动还是向右移动。

首先,在你的 Player 类中创建一个新的变量来代表你的游戏所面向的方向。因为我的游戏天然地面向右侧,由此我把面向右侧作为默认值:

        self.score = 0
        self.facing_right = True  # 添加这行
        self.is_jumping = True

当这个变量是 True 时,你的英雄精灵是面向右侧的。当玩家每次更改英雄的方向时,变量也必须重新设置,因此,在你的主循环中相关的 keyup 事件中这样做:

        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT or event.key == ord('a'):
                player.control(steps, 0)
                player.facing_right = False  # 添加这行
            if event.key == pygame.K_RIGHT or event.key == ord('d'):
                player.control(-steps, 0)
                player.facing_right = True   # 添加这行

最后,更改你的 Throwable 类的 update 函数,以检测英雄是否面向右侧,并恰当地添加或减去来自火球位置的像素:

        if self.rect.y < worldy:
            if player.facing_right:
                self.rect.x += 15
            else:
                self.rect.x -= 15
            self.rect.y += 5

再次尝试你的游戏,清除掉你游戏世界中的一些坏人。

 title=

作为一项额外的挑战,当彻底打败敌人时,尝试增加你玩家的得分。

完整的代码

#!/usr/bin/env python3
# 作者: Seth Kenlon

# GPLv3
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <[http://www.gnu.org/licenses/>][17].

import pygame
import pygame.freetype
import sys
import os

'''
变量
'''

worldx = 960
worldy = 720
fps = 40
ani = 4
world = pygame.display.set_mode([worldx, worldy])
forwardx  = 600
backwardx = 120

BLUE = (80, 80, 155)
BLACK = (23, 23, 23)
WHITE = (254, 254, 254)
ALPHA = (0, 255, 0)

tx = 64
ty = 64

font_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "fonts", "amazdoom.ttf")
font_size = tx
pygame.freetype.init()
myfont = pygame.freetype.Font(font_path, font_size)

'''
对象
'''

def stats(score, health):
    myfont.render_to(world, (4, 4), "Score:"+str(score), BLUE, None, size=64)
    myfont.render_to(world, (4, 72), "Health:"+str(health), BLUE, None, size=64)

class Throwable(pygame.sprite.Sprite):
    """
    生成一个投掷的对象
    """
    def __init__(self, x, y, img, throw):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(os.path.join('images', img))
        self.image.convert_alpha()
        self.image.set_colorkey(ALPHA)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.firing = throw

    def update(self, worldy):
        '''
        投掷物理学
        '''
        if self.rect.y < worldy:
            if player.facing_right:
                self.rect.x += 15
            else:
                self.rect.x -= 15
            self.rect.y += 5
        else:
            self.kill()
            self.firing = 0

# x 位置, y 位置, img 宽度, img 高度, img 文件
class Platform(pygame.sprite.Sprite):
    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.image.set_colorkey(ALPHA)
        self.rect = self.image.get_rect()
        self.rect.y = yloc
        self.rect.x = xloc

class Player(pygame.sprite.Sprite):
    """
    生成一名玩家
    """

    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.movex = 0
        self.movey = 0
        self.frame = 0
        self.health = 10
        self.damage = 0
        self.score = 0
        self.facing_right = True
        self.is_jumping = True
        self.is_falling = True
        self.images = []
        for i in range(1, 5):
            img = pygame.image.load(os.path.join('images', 'walk' + 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 gravity(self):
        if self.is_jumping:
            self.movey += 3.2

    def control(self, x, y):
        """
        控制玩家移动
        """
        self.movex += x

    def jump(self):
        if self.is_jumping is False:
            self.is_falling = False
            self.is_jumping = True

    def update(self):
        """
        更新精灵位置
        """

        # 向左移动
        if self.movex < 0:
            self.is_jumping = True
            self.frame += 1
            if self.frame > 3 * ani:
                self.frame = 0
            self.image = pygame.transform.flip(self.images[self.frame // ani], True, False)

        # 向右移动
        if self.movex > 0:
            self.is_jumping = True
            self.frame += 1
            if self.frame > 3 * ani:
                self.frame = 0
            self.image = self.images[self.frame // ani]

        # 碰撞
        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   # 设置伤害回 0
                self.health -= 1  # 减去 1 单位健康度

        ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
        for g in ground_hit_list:
            self.movey = 0
            self.rect.bottom = g.rect.top
            self.is_jumping = False  # 停止跳跃

        # 掉落世界
        if self.rect.y > worldy:
            self.health -=1
            print(self.health)
            self.rect.x = tx
            self.rect.y = ty

        plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)
        for p in plat_hit_list:
            self.is_jumping = False  # 停止跳跃
            self.movey = 0
            if self.rect.bottom <= p.rect.bottom:
               self.rect.bottom = p.rect.top
            else:
               self.movey += 3.2

        if self.is_jumping and self.is_falling is False:
            self.is_falling = True
            self.movey -= 33  # 跳跃多高

        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)

        self.rect.x += self.movex
        self.rect.y += self.movey

class Enemy(pygame.sprite.Sprite):
    """
    生成一名敌人
    """

    def __init__(self, x, y, img):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(os.path.join('images', img))
        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):
        """
        敌人移动
        """
        distance = 80
        speed = 8

        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

    def update(self, firepower, enemy_list):
        """
        检测火力碰撞
        """
        fire_hit_list = pygame.sprite.spritecollide(self, firepower, False)
        for fire in fire_hit_list:
            enemy_list.remove(self)

class Level:
    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, 'tile-ground.png')
                ground_list.add(ground)
                i = i + 1

        if lvl == 2:
            print("Level " + str(lvl))

        return ground_list

    def bad(lvl, eloc):
        if lvl == 1:
            enemy = Enemy(eloc[0], eloc[1], 'enemy.png')
            enemy_list = pygame.sprite.Group()
            enemy_list.add(enemy)
        if lvl == 2:
            print("Level " + str(lvl))

        return enemy_list

    # x 位置, y 位置, img 宽度, img 高度, img 文件
    def platform(lvl, tx, ty):
        plat_list = pygame.sprite.Group()
        ploc = []
        i = 0
        if lvl == 1:
            ploc.append((200, worldy - ty - 128, 3))
            ploc.append((300, worldy - ty - 256, 3))
            ploc.append((550, worldy - ty - 128, 4))
            while i &lt; len(ploc):
                j = 0
                while j <= ploc[i][2]:
                    plat = Platform((ploc[i][0] + (j * tx)), ploc[i][1], tx, ty, 'tile.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 loot(lvl):
        if lvl == 1:
            loot_list = pygame.sprite.Group()
            loot = Platform(tx*5, ty*5, tx, ty, 'loot_1.png')
            loot_list.add(loot)

        if lvl == 2:
            print(lvl)

        return loot_list

'''
Setup 部分
'''

backdrop = pygame.image.load(os.path.join('images', 'stage.png'))
clock = pygame.time.Clock()
pygame.init()
backdropbox = world.get_rect()
main = True

player = Player()         # 生成玩家
player.rect.x = 0         # 转到 x
player.rect.y = 30        # 转到 y
player_list = pygame.sprite.Group()
player_list.add(player)
steps = 10
fire = Throwable(player.rect.x, player.rect.y, 'fire.png', 0)
firepower = pygame.sprite.Group()

eloc = []
eloc = [300, worldy-ty-80]
enemy_list = Level.bad(1, eloc)
gloc = []

i = 0
while i &lt;= (worldx / tx) + tx:
    gloc.append(i * tx)
    i = i + 1

ground_list = Level.ground(1, gloc, tx, ty)
plat_list = Level.platform(1, tx, ty)
enemy_list = Level.bad( 1, eloc )
loot_list = Level.loot(1)

'''
主循环
'''

while main:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            try:
                sys.exit()
            finally:
                main = False

        if event.type == pygame.KEYDOWN:
            if event.key == ord('q'):
                pygame.quit()
                try:
                    sys.exit()
                finally:
                    main = False
            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()

        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT or event.key == ord('a'):
                player.control(steps, 0)
                player.facing_right = False
            if event.key == pygame.K_RIGHT or event.key == ord('d'):
                player.control(-steps, 0)
                player.facing_right = True
            if event.key == pygame.K_SPACE:
                if not fire.firing:
                    fire = Throwable(player.rect.x, player.rect.y, 'fire.png', 1)
                    firepower.add(fire)

    # 向向滚动世界
    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

    # 向后滚动世界
    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.update()
    player.gravity()
    player_list.draw(world)
    if fire.firing:
        fire.update(worldy)
        firepower.draw(world)
    enemy_list.draw(world)
    enemy_list.update(firepower, enemy_list)
    loot_list.draw(world)
    ground_list.draw(world)
    plat_list.draw(world)
    for e in enemy_list:
        e.move()
    stats(player.score, player.health)
    pygame.display.flip()
    clock.tick(fps)

via: https://opensource.com/article/20/9/add-throwing-python-game

作者:Seth Kenlon 选题:lujun9972 译者:robsean 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出