分类 技术 下的文章

通过别名和其他捷径来提高你经常忘记的那些事情的效率。

要是你整天使用计算机,如果能找到需要重复执行的命令并记下它们以便以后轻松使用那就太棒了。它们全都呆在那里,藏在 ~/.bashrc 中(或 zsh 用户~/.zshrc 中),等待着改善你的生活!

在本文中,我分享了我最喜欢的这些助手命令,对于我经常遗忘的事情,它们很有用,也希望这可以帮助到你,以及为你解决一些经常头疼的问题。

完事吱一声

当我执行一个需要长时间运行的命令时,我经常采用多任务的方式,然后就必须回头去检查该操作是否已完成。然而通过有用的 say 命令,现在就不用再这样了(这是在 MacOS 上;请根据你的本地环境更改为等效的方式):

function looooooooong {
    START=$(date +%s.%N)
    $*
    EXIT_CODE=$?
    END=$(date +%s.%N)
    DIFF=$(echo "$END - $START" | bc)
    RES=$(python -c "diff = $DIFF; min = int(diff / 60); print('%s min' % min)")
    result="$1 completed in $RES, exit code $EXIT_CODE."
    echo -e "\n⏰  $result"
    ( say -r 250 $result 2>&1 > /dev/null & )
}

这个命令会记录命令的开始和结束时间,计算所需的分钟数,并“说”出调用的命令、花费的时间和退出码。当简单的控制台铃声无法使用时,我发现这个超级有用。

安装小助手

我在小时候就开始使用 Ubuntu,而我需要学习的第一件事就是如何安装软件包。我曾经首先添加的别名之一是它的助手(根据当天的流行梗命名的):

alias canhas="sudo apt-get install -y"

GPG 签名

有时候,我必须在没有 GPG 扩展程序或应用程序的情况下给电子邮件签署 GPG 签名,我会跳到命令行并使用以下令人讨厌的别名:

alias gibson="gpg --encrypt --sign --armor"
alias ungibson="gpg --decrypt"

Docker

Docker 的子命令很多,但是 Docker compose 的更多。我曾经使用这些别名来将 --rm 标志丢到脑后,但是现在不再使用这些有用的别名了:

alias dc="docker-compose"
alias dcr="docker-compose run --rm"
alias dcb="docker-compose run --rm --build"

Google Cloud 的 gcurl 助手

对于我来说,Google Cloud 是一个相对较新的东西,而它有极多的文档gcurl 是一个别名,可确保在用带有身份验证标头的本地 curl 命令连接 Google Cloud API 时,可以获得所有正确的标头。

Git 和 ~/.gitignore

我工作中用 Git 很多,因此我有一个专门的部分来介绍 Git 助手。

我最有用的助手之一是我用来克隆 GitHub 存储库的。你不必运行:

git clone [email protected]:org/repo /Users/glasnt/git/org/repo

我设置了一个克隆函数:

clone(){
    echo Cloning $1 to ~/git/$1
    cd ~/git
    git clone [email protected]:$1 $1
    cd $1
}

即使每次进入 ~/.bashrc 文件看到这个时,我总是会忘记和傻笑,我也有一个“刷新上游”命令:

alias yoink="git checkout master && git fetch upstream master && git merge upstream/master"

给 Git 一族的另一个助手是全局忽略文件。在你的 git config --global --list 中,你应该看到一个 core.excludesfile。如果没有,请创建一个,然后将你总是放到各个 .gitignore 文件中的内容填满它。作为 MacOS 上的 Python 开发人员,对我来说,这些内容是:

.DS_Store     # macOS clutter
venv/         # I never want to commit my virtualenv
*.egg-info/*  # ... nor any locally compiled packages
__pycache__   # ... or source
*.swp         # ... nor any files open in vim

你可以在 Gitignore.io 或 GitHub 上的 Gitignore 存储库上找到其他建议。

轮到你了

你最喜欢的助手命令是什么?请在评论中分享。


via: https://opensource.com/article/20/1/bash-scripts-aliases

作者:Katie McLaughlin 选题:lujun9972 译者:wxy 校对:wxy

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

在本系列的第十一篇有关使用 Python Pygame 模块进行编程的文章中,显示玩家获得战利品或受到伤害时的得分。

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

如果你已经跟随这一系列很久,那么已经学习了使用 Python 创建一个视频游戏所需的所有基本语法和模式。然而,它仍然缺少一个至关重要的组成部分。这一组成部分不仅仅对用 Python 编程游戏重要;不管你探究哪个计算机分支,你都必需精通:作为一个程序员,通过阅读一种语言的或库的文档来学习新的技巧。

幸运的是,你正在阅读本文的事实表明你熟悉文档。为了使你的平台类游戏更加美观,在这篇文章中,你将在游戏屏幕上添加得分和生命值显示。不过,教你如何找到一个库的功能以及如何使用这些新的功能的这节课程并没有多神秘。

在 Pygame 中显示得分

现在,既然你有了可以被玩家收集的奖励,那就有充分的理由来记录分数,以便你的玩家看到他们收集了多少奖励。你也可以跟踪玩家的生命值,以便当他们被敌人击中时会有相应结果。

你已经有了跟踪分数和生命值的变量,但是这一切都发生在后台。这篇文章教你在游戏期间在游戏屏幕上以你选择的一种字体来显示这些统计数字。

阅读文档

大多数 Python 模块都有文档,即使那些没有文档的模块,也能通过 Python 的帮助功能来进行最小的文档化。Pygame 的主页面 链接了它的文档。不过,Pygame 是一个带有很多文档的大模块,并且它的文档不像在 Opensource.com 上的文章一样,以同样易理解的(和友好的、易解释的、有用的)叙述风格来撰写的。它们是技术文档,并且列出在模块中可用的每个类和函数,各自要求的输入类型等等。如果你不适应参考代码组件描述,这可能会令人不知所措。

在烦恼于库的文档前,第一件要做的事,就是来想想你正在尝试达到的目标。在这种情况下,你想在屏幕上显示玩家的得分和生命值。

在你确定你需要的结果后,想想它需要什么的组件。你可以从变量和函数的方面考虑这一点,或者,如果你还没有自然地想到这一点,你可以进行一般性思考。你可能意识到需要一些文本来显示一个分数,你希望 Pygame 在屏幕上绘制这些文本。如果你仔细思考,你可能会意识到它与在屏幕上渲染一个玩家、奖励或一个平台并多么大的不同。

从技术上讲,你可以使用数字图形,并让 Pygame 显示这些数字图形。它不是达到你目标的最容易的方法,但是如果它是你唯一知道的方法,那么它是一个有效的方法。不过,如果你参考 Pygame 的文档,你看到列出的模块之一是 font,这是 Pygame 使得在屏幕上来使打印文本像输入文字一样容易的方法。

解密技术文档

font 文档页面以 pygame.font.init() 开始,它列出了用于初始化字体模块的函数。它由 pygame.init() 自动地调用,你已经在代码中调用了它。再强调一次,从技术上讲,你已经到达一个足够好的点。虽然你尚不知道如何做,你知道你能够使用 pygame.font 函数来在屏幕上打印文本。

然而,如果你阅读更多一些,你会找到这里还有一种更好的方法来打印字体。pygame.freetype 模块在文档中的描述方式如下:

pygame.freetype 模块是 pygame.fontpygame 模块的一个替代品,用于加载和渲染字体。它有原函数的所有功能,外加很多新的功能。

pygame.freetype 文档页面的下方,有一些示例代码:

import pygame
import pygame.freetype

你的代码应该已经导入了 Pygame,不过,请修改你的 import 语句以包含 Freetype 模块:

import pygame
import sys
import os
import pygame.freetype

在 Pygame 中使用字体

font 模块的描述中可以看出,显然 Pygame 使用一种字体(不管它的你提供的或内置到 Pygame 的默认字体)在屏幕上渲染字体。滚动浏览 pygame.freetype 文档来找到 pygame.freetype.Font 函数:

pygame.freetype.Font
从支持的字体文件中创建一个新的字体实例。

Font(file, size=0, font_index=0, resolution=0, ucs4=False) -> Font

pygame.freetype.Font.name
  符合规则的字体名称。

pygame.freetype.Font.path
  字体文件路径。

pygame.freetype.Font.size
  在渲染中使用的默认点大小

这描述了如何在 Pygame 中构建一个字体“对象”。把屏幕上的一个简单对象视为一些代码属性的组合对你来说可能不太自然,但是这与你构建英雄和敌人精灵的方式非常类似。你需要一个字体文件,而不是一个图像文件。在你有一个字体文件后,你可以在你的代码中使用 pygame.freetype.Font 函数来创建一个字体对象,然后使用该对象来在屏幕上渲染文本。

因为并不是世界上的每个人的电脑上都有完全一样的字体,因此将你选择的字体与你的游戏捆绑在一起是很重要的。要捆绑字体,首先在你的游戏文件夹中创建一个新的目录,放在你为图像而创建的文件目录旁边。称其为 fonts

即使你的计算机操作系统随附了几种字体,但是将这些字体给予其他人是非法的。这看起来很奇怪,但法律就是这样运作的。如果想与你的游戏一起随附一种字体,你必需找到一种开源或知识共享的字体,以允许你随游戏一起提供该字体。

专门提供自由和合法字体的网站包括:

当你找到你喜欢的字体后,下载下来。解压缩 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)

当然,你此刻已经知道,如果它不在主循环中,你的游戏将不会发生任何事,所以在文件的底部添加一个对你的 stats 函数的调用:

    for e in enemy_list:
        e.move()
    stats(player.score,player.health) # draw text
    pygame.display.flip()

尝试你的游戏。

当玩家收集奖励品时,得分会上升。当玩家被敌人击中时,生命值下降。成功!

 title=

不过,这里有一个问题。当一个玩家被敌人击中时,健康度会一路下降,这是不公平的。你刚刚发现一个非致命的错误。非致命的错误是这些在应用程序中小问题,(通常)不会阻止应用程序启动或甚至导致停止工作,但是它们要么没有意义,要么会惹恼用户。这里是如何解决这个问题的方法。

修复生命值计数

当前生命值系统的问题是,敌人接触玩家时,Pygame 时钟的每一次滴答,健康度都会减少。这意味着一个缓慢移动的敌人可能在一次遭遇中将一个玩家降低健康度至 -200 ,这不公平。当然,你可以给你的玩家一个 10000 的起始健康度得分,而不用担心它;这可以工作,并且可能没有人会注意。但是这里有一个更好的方法。

当前,你的代码侦查出一个玩家和一个敌人发生碰撞的时候。生命值问题的修复是检测两个独立的事件:什么时候玩家和敌人碰撞,并且,在它们碰撞后,什么时候它们停止碰撞。

首先,在你的玩家类中,创建一个变量来代表玩家和敌人碰撞在一起:

        self.frame = 0
        self.health = 10
        self.damage = 0

在你的 Player 类的 update 函数中,移除这块代码块:

        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 函数检查一个敌人当前是否在玩家的边界框内,并且当它是 true 时, self.rect.colliderect 设置你的新的 self.damage 变量为 1,而不管它多少次是 true

现在,即使被一个敌人击中 3 秒,对 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 来查看玩家是否仍然接触在你敌人列表中的敌人(当其未侦查到碰撞时,collidelist 返回 -1)。在它没有接触敌人时,是该处理 self.damage 的时机:通过设置 self.damage 变量回到 0 来使其无效,并减少一点生命值。

现在尝试你的游戏。

得分反应

现在,你有一个来让你的玩家知道它们分数和生命值的方法,当你的玩家达到某些里程碑时,你可以确保某些事件发生。例如,也许这里有一个特殊的恢复一些生命值的奖励项目。也许一个到达 0 生命值的玩家不得不从一个关卡的起始位置重新开始。

你可以在你的代码中检查这些事件,并且相应地操纵你的游戏世界。你已经知道该怎么做,所以请浏览文档来寻找新的技巧,并且独立地尝试这些技巧。

这里是到目前为止所有的代码:

#!/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)

via: https://opensource.com/article/20/1/add-scorekeeping-your-python-game

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

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

你安装 Ubuntu 时,它会要求你设置时区。如果你选择一个错误的时区,或者你移动到世界的一些其它地方,你可以很容易地在以后更改它。

如何在 Ubuntu 和其它 Linux 发行版中更改时区

这里有两种方法来更改 Ubuntu 中的时区。你可以使用图形化设置或在终端中使用 timedatectl 命令。你也可以直接更改 /etc/timezone 文件,但是我不建议这样做。

在这篇初学者教程中,我将向你展示图形化和终端两种方法:

方法 1: 通过终端更改 Ubuntu 时区

Ubuntu 或一些使用 systemd 的其它发行版可以在 Linux 终端中使用 timedatectl 命令来设置时区。

你可以使用没有任何参数的 timedatectl 命令来检查当前是日期和时区设置:

[email protected]:~$ timedatectl
                      Local time: Sat 2020-01-18 17:39:52 IST
                  Universal time: Sat 2020-01-18 12:09:52 UTC
                        RTC time: Sat 2020-01-18 12:09:52
                       Time zone: Asia/Kolkata (IST, +0530)
       System clock synchronized: yes
systemd-timesyncd.service active: yes
                 RTC in local TZ: no

正如你在上面的输出中所看,我的系统使用 Asia/Kolkata 。它也告诉我现在比世界时早 5 小时 30 分钟。

为在 Linux 中设置时区,你需要知道准确的时区。你必需使用时区的正确的格式 (时区格式是洲/城市)。

为获取时区列表,使用 timedatectl 命令的 list-timezones 参数:

timedatectl list-timezones

它将向你显示大量可用的时区列表。

Timezones List

你可以使用向上箭头和向下箭头或 PgUpPgDown 键来在页面之间移动。

你也可以 grep 输出,并搜索你的时区。例如,假如你正在寻找欧洲的时区,你可以使用:

timedatectl list-timezones | grep -i europe

比方说,你想设置时区为巴黎。在这里,使用的时区值的 Europe/Paris :

timedatectl set-timezone Europe/Paris

它虽然不显示任何成功信息,但是时区会立即更改。你不需要重新启动或注销。

记住,虽然你不需要成为 root 用户并对命令使用 sudo,但是你的账户仍然需要拥有管理器权限来更改时区。

你可以使用 date 命令 来验证更改的时间好时区:

[email protected]:~$ date
Sat Jan 18 13:56:26 CET 2020

方法 2: 通过 GUI 更改 Ubuntu 时区

按下 super 键 (Windows 键) ,并搜索设置:

Applications Menu Settings

在左侧边栏中,向下滚动一点,查看详细信息:

Go to Settings -&gt; Details

在详细信息中,你将在左侧边栏中找到“日期和时间”。在这里,你应该关闭自动时区选项(如果它已经被启用),然后在时区上单击:

In Details -&gt; Date & Time, turn off the Automatic Time Zone

当你单击时区时,它将打开一个交互式地图,你可以在你选择的地理位置上单击,关闭窗口。

Select a timezone

在选择新的时区后,除了关闭这个地图后,你不必做任何事情。不需要注销或 关闭 Ubuntu

我希望这篇快速教程能帮助你在 Ubuntu 和其它 Linux 发行版中更改时区。如果你有问题或建议,请告诉我。


via: https://itsfoss.com/change-timezone-ubuntu/

作者:Abhishek Prakash 选题:lujun9972 译者:robsean 校对:wxy

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

当你刚开始使用 Linux 时,你将发现与 Windows 的很多不同。其中一个“不同的东西”是 root 用户的概念。

在这个初学者系列中,我将解释几个关于 Ubuntu 的 root 用户的重要的东西。

请记住,尽管我正在从 Ubuntu 用户的角度编写这篇文章,它应该对大多数的 Linux 发行版也是有效的。

你将在这篇文章中学到下面的内容:

  • 为什么在 Ubuntu 中禁用 root 用户
  • 像 root 用户一样使用命
  • 切换为 root 用户
  • 解锁 root 用户

什么是 root 用户?为什么它在 Ubuntu 中被锁定?

在 Linux 中,有一个称为 root 的超级用户。这是超级管理员账号,它可以做任何事以及使用系统的一切东西。它可以在你的 Linux 系统上访问任何文件和运行任何命令。

能力越大,责任越大。root 用户给予你完全控制系统的能力,因此,它应该被谨慎地使用。root 用户可以访问系统文件,运行更改系统配置的命令。因此,一个错误的命令可能会破坏系统。

这就是为什么 Ubuntu 和其它基于 Ubuntu 的发行版默认锁定 root 用户,以从意外的灾难中挽救你的原因。

对于你的日常任务,像移动你家目录中的文件,从互联网下载文件,创建文档等等,你不需要拥有 root 权限。

打个比方来更好地理解它。假设你想要切一个水果,你可以使用一把厨房用刀。假设你想要砍一颗树,你就得使用一把锯子。现在,你可以使用锯子来切水果,但是那不明智,不是吗?\_

这意味着,你不能是 Ubuntu 中 root 用户或者不能使用 root 权限来使用系统吗?不,你仍然可以在 sudo 的帮助下来拥有 root 权限来访问(在下一节中解释)。

要点: 使用于常规任务,root 用户权限太过强大。这就是为什么不建议一直使用 root 用户。你仍然可以使用 root 用户来运行特殊的命令。

如何在 Ubuntu 中像 root 用户一样运行命令?

Image Credit: xkcd

对于一些系统的特殊任务来说,你将需要 root 权限。例如。如果你想通过命令行更新 Ubuntu,你不能作为一个常规用户运行该命令。它将给出权限被拒绝的错误。

apt update
Reading package lists... Done
E: Could not open lock file /var/lib/apt/lists/lock - open (13: Permission denied)
E: Unable to lock directory /var/lib/apt/lists/
W: Problem unlinking the file /var/cache/apt/pkgcache.bin - RemoveCaches (13: Permission denied)
W: Problem unlinking the file /var/cache/apt/srcpkgcache.bin - RemoveCaches (13: Permission denied)

那么,你如何像 root 用户一样运行命令?简单的答案是,在命令前添加 sudo,来像 root 用户一样运行。

sudo apt update

Ubuntu 和很多其它的 Linux 发行版使用一个被称为 sudo 的特殊程序机制。sudo 是一个以 root 用户(或其它用户)来控制运行命令访问的程序。

实际上,sudo 是一个非常多用途的工具。它可以配置为允许一个用户像 root 用户一样来运行所有的命令,或者仅仅一些命令。你也可以配置为无需密码即可使用 sudo 运行命令。这个主题内容比较丰富,也许我将在另一篇文章中详细讨论它。

就目前而言,你应该知道当你安装 Ubuntu 时,你必须创建一个用户账号。这个用户账号在你系统上以管理员身份来工作,并且按照 Ubuntu 中的默认 sudo 策略,它可以在你的系统上使用 root 用户权限来运行任何命令。

sudo 的问题是,运行 sudo 不需要 root 用户密码,而是需要用户自己的密码

并且这就是为什么当你使用 sudo 运行一个命令,会要求输入正在运行 sudo 命令的用户的密码的原因:

[email protected]:~$ sudo apt update
[sudo] password for abhishek:

正如你在上面示例中所见 abhishek 在尝试使用 sudo 来运行 apt update 命令,系统要求输入 abhishek 的密码。

如果你对 Linux 完全不熟悉,当你在终端中开始输入密码时,你可能会惊讶,在屏幕上什么都没有发生。这是十分正常的,因为作为默认的安全功能,在屏幕上什么都不会显示。甚至星号(*)都没有。输入你的密码并按回车键。

要点:为在 Ubuntu 中像 root 用户一样运行命令,在命令前添加 sudo。 当被要求输入密码时,输入你的账户的密码。当你在屏幕上输入密码时,什么都看不到。请继续输入密码,并按回车键。

如何在 Ubuntu 中成为 root 用户?

你可以使用 sudo 来像 root 用户一样运行命令。但是,在某些情况下,你必须以 root 用户身份来运行一些命令,而你总是忘了在命令前添加 sudo,那么你可以临时切换为 root 用户。

sudo 命令允许你来模拟一个 root 用户登录的 shell ,使用这个命令:

sudo -i
[email protected]:~$ sudo -i
[sudo] password for abhishek:
[email protected]:~# whoami
root

你将注意到,当你切换为 root 用户时,shell 命令提示符从 $(美元符号)更改为 #(英镑符号)。我开个(拙劣的)玩笑,英镑比美元强大。

虽然我已经向你显示如何成为 root 用户,但是我必须警告你,你应该避免作为 root 用户使用系统。毕竟它有阻拦你使用 root 用户的原因。

另外一种临时切换为 root 用户的方法是使用 su 命令:

sudo su

如果你尝试使用不带有的 sudosu 命令,你将遇到 “su authentication failure” 错误。

你可以使用 exit 命令来恢复为正常用户。

exit

如何在 Ubuntu 中启用 root 用户?

现在你知道,root 用户在基于 Ubuntu 发行版中是默认锁定的。

Linux 给予你在系统上想做什么就做什么的自由。解锁 root 用户就是这些自由之一。

如果出于某些原因,你决定启用 root 用户,你可以通过为其设置一个密码来做到:

sudo passwd root

再强调一次,不建议使用 root 用户,并且我也不鼓励你在桌面上这样做。如果你忘记了密码,你将不能再次在 Ubuntu 中更改 root 用户密码。(LCTT 译注:可以通过单用户模式修改。)

你可以通过移除密码来再次锁定 root 用户:

sudo passwd -dl root

最后…

我希望你现在对 root 概念理解得更好一点。如果你仍然有些关于它的困惑和问题,请在评论中让我知道。我将尝试回答你的问题,并且也可能更新这篇文章。


via: https://itsfoss.com/root-user-ubuntu/

作者:Abhishek Prakash 选题:lujun9972 译者:robsean 校对:wxy

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

这本免费的电子书使经验丰富的程序员更深入了解 Linux 中进程间通信(IPC)的核心概念和机制。

让一个软件过程与另一个软件过程进行对话是一个微妙的平衡行为。但是,它对于应用程序而言可能是至关重要的功能,因此这是任何从事复杂项目的程序员都必须解决的问题。你的应用程序是否需要启动由其它软件处理的工作;监视外设或网络上正在执行的操作;或者检测来自其它来源的信号,当你的软件需要依赖其自身代码之外的东西来知道下一步做什么或什么时候做时,你就需要考虑 进程间通信 inter-process communication (IPC)。

这在 Unix 操作系统上已经由来已久了,这可能是因为人们早期预期软件会来自各种来源。按照相同的传统,Linux 提供了一些同样的 IPC 接口和一些新接口。Linux 内核具有多种 IPC 方法,util-linux 包包含了 ipcmkipcrmipcslsipc 命令,用于监视和管理 IPC 消息。

显示进程间通信信息

在尝试 IPC 之前,你应该知道系统上已经有哪些 IPC 设施。lsipc 命令提供了该信息。

RESOURCE DESCRIPTION               LIMIT  USED  USE%
MSGMNI   Number of message queues  32000     0 0.00%
MSGMAX   Max size of message (byt.. 8192     -     -
MSGMNB   Default max size of queue 16384     -     -
SHMMNI   Shared memory segments     4096    79 1.93%
SHMALL   Shared memory pages       184[...] 25452 0.00%
SHMMAX   Max size of shared memory 18446744073692774399
SHMMIN   Min size of shared memory     1     -     -
SEMMNI   Number of semaphore ident 32000     0 0.00%
SEMMNS   Total number of semaphore 1024000.. 0 0.00%
SEMMSL   Max semaphores per semap  32000     -     -
SEMOPM   Max number of operations p  500     -     -
SEMVMX   Semaphore max value       32767     -     -

你可能注意到,这个示例清单包含三种不同类型的 IPC 机制,每种机制在 Linux 内核中都是可用的:消息(MSG)、共享内存(SHM)和信号量(SEM)。你可以用 ipcs 命令查看每个子系统的当前活动:

$ ipcs

------ Message Queues Creators/Owners ---
msqid     perms     cuid      cgid  [...]

------ Shared Memory Segment Creators/Owners
shmid     perms    cuid    cgid  [...]
557056    700      seth    users [...]
3571713   700      seth    users [...]
2654210   600      seth    users [...]
2457603   700      seth    users [...]

------ Semaphore Arrays Creators/Owners ---
semid     perms     cuid      cgid  [...]

这表明当前没有消息或信号量阵列,但是使用了一些共享内存段。

你可以在系统上执行一个简单的示例,这样就可以看到正在工作的系统之一。它涉及到一些 C 代码,所以你必须在系统上有构建工具。必须安装这些软件包才能从源代码构建软件,这些软件包的名称取决于发行版,因此请参考文档以获取详细信息。例如,在基于 Debian 的发行版上,你可以在 wiki 的构建教程部分了解构建需求,而在基于 Fedora 的发行版上,你可以参考该文档的从源代码安装软件部分。

创建一个消息队列

你的系统已经有一个默认的消息队列,但是你可以使用 ipcmk 命令创建你自己的消息队列:

$ ipcmk --queue
Message queue id: 32764

编写一个简单的 IPC 消息发送器,为了简单,在队列 ID 中硬编码:

#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>

struct msgbuffer {
  char text[24];
} message;

int main() {
    int msqid = 32764;
    strcpy(message.text,"opensource.com");
    msgsnd(msqid, &message, sizeof(message), 0);
    printf("Message: %s\n",message.text);
    printf("Queue: %d\n",msqid);
    return 0;
        }

编译该应用程序并运行:

$ gcc msgsend.c -o msg.bin
$ ./msg.bin
Message: opensource.com
Queue: 32769

你刚刚向你的消息队列发送了一条消息。你可以使用 ipcs 命令验证这一点,可以使用 ——queue 选项将输出限制到该消息队列:

$ ipcs -q

------ Message Queues --------
key        msqid   owner  perms  used-bytes  messages
0x7b341ab9 0       seth   666    0          0
0x72bd8410 32764   seth   644    24         1

你也可以检索这些消息:

#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>

struct msgbuffer {
    char text[24];
} message;

int main() {
    int msqid = 32764;
    msgrcv(msqid, &message, sizeof(message),0,0);
    printf("\nQueue: %d\n",msqid);
    printf("Got this message: %s\n", message.text);
    msgctl(msqid,IPC_RMID,NULL);
    return 0;

编译并运行:

$ gcc get.c -o get.bin
$ ./get.bin

Queue: 32764
Got this message: opensource.com

下载这本电子书

这只是 Marty Kalin 的《Linux 进程间通信指南》中课程的一个例子,可从 Opensource.com 下载的这本最新免费(且 CC 授权)的电子书。在短短的几节课中,你将从消息队列、共享内存和信号量、套接字、信号等中了解 IPC 的 POSIX 方法。认真阅读 Marty 的书,你将成为一个博识的程序员。而这不仅适用于经验丰富的编码人员,如果你编写的只是 shell 脚本,那么你将拥有有关管道(命名和未命名)和共享文件的大量实践知识,以及使用共享文件或外部消息队列时需要了解的重要概念。

如果你对制作具有动态和具有系统感知的优秀软件感兴趣,那么你需要了解 IPC。让这本书做你的向导。


via: https://opensource.com/article/20/1/inter-process-communication-linux

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

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

使用一组公钥/私钥对让你不需要密码登录到远程 Linux 系统或使用 ssh 运行命令,这会非常方便,但是设置过程有点复杂。下面是帮助你的方法和脚本。

Linux 系统上设置一个允许你无需密码即可远程登录或运行命令的帐户并不难,但是要使它正常工作,你还需要掌握一些繁琐的细节。在本文,我们将完成整个过程,然后给出一个可以帮助处理琐碎细节的脚本。

设置好之后,如果希望在脚本中运行 ssh 命令,尤其是希望配置自动运行的命令,那么免密访问特别有用。

需要注意的是,你不需要在两个系统上使用相同的用户帐户。实际上,你可以把公用密钥用于系统上的多个帐户或多个系统上的不同帐户。

设置方法如下。

在哪个系统上启动?

首先,你需要从要发出命令的系统上着手。那就是你用来创建 ssh 密钥的系统。你还需要可以访问远程系统上的帐户并在其上运行这些命令。

为了使角色清晰明了,我们将场景中的第一个系统称为 “boss”,因为它将发出要在另一个系统上运行的命令。

因此,命令提示符如下:

boss$

如果你还没有在 boss 系统上为你的帐户设置公钥/私钥对,请使用如下所示的命令创建一个密钥对。注意,你可以在各种加密算法之间进行选择。(一般使用 RSA 或 DSA。)注意,要在不输入密码的情况下访问系统,你需要在下面的对话框中的两个提示符出不输入密码。

如果你已经有一个与此帐户关联的公钥/私钥对,请跳过此步骤。

boss$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/myself/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):            <== 按下回车键即可
Enter same passphrase again:                           <== 按下回车键即可
Your identification has been saved in /home/myself/.ssh/id_rsa.
Your public key has been saved in /home/myself/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:1zz6pZcMjA1av8iyojqo6NVYgTl1+cc+N43kIwGKOUI myself@boss
The key's randomart image is:
+---[RSA 3072]----+
|     . ..        |
|   E+ .. .       |
|  .+ .o + o      |
|   ..+.. .o* .   |
|    ... So+*B o  |
|    +   ...==B . |
| . o .   ....++. |
|o o   . . o..o+  |
|=..o.. ..o  o.   |
+----[SHA256]-----+

上面显示的命令将创建公钥和私钥。其中公钥用于加密,私钥用于解密。因此,这些密钥之间的关系是关键的,私有密钥绝不应该被共享。相反,它应该保存在 boss 系统的 .ssh 文件夹中。

注意,在创建时,你的公钥和私钥将会保存在 .ssh 文件夹中。

下一步是将公钥复制到你希望从 boss 系统免密访问的系统。你可以使用 scp 命令来完成此操作,但此时你仍然需要输入密码。在本例中,该系统称为 “target”。

boss$ scp .ssh/id_rsa.pub myacct@target:/home/myaccount
myacct@target's password:

你需要安装公钥在 target 系统(将运行命令的系统)上。如果你没有 .ssh 目录(例如,你从未在该系统上使用过 ssh),运行这样的命令将为你设置一个目录:

target$ ssh localhost date
target$ ls -la .ssh
total 12
drwx------ 2 myacct myacct 4096 Jan 19 11:48 .
drwxr-xr-x 6 myacct myacct 4096 Jan 19 11:49 ..
-rw-r--r-- 1 myacct myacct  222 Jan 19 11:48 known_hosts

仍然在目标系统上,你需要将从“boss”系统传输的公钥添加到 .ssh/authorized_keys 文件中。如果该文件已经存在,使用下面的命令将把它添加到文件的末尾;如果文件不存在,则创建该文件并添加密钥。

target$ cat id_rsa.pub >> .ssh/authorized_keys

下一步,你需要确保你的 authorized_keys 文件权限为 600。如果还不是,执行命令 chmod 600 .ssh/authorized_keys

target$ ls -l authorized_keys
-rw------- 1 myself myself 569 Jan 19 12:10 authorized_keys

还要检查目标系统上 .ssh 目录的权限是否设置为 700。如果需要,执行 chmod 700 .ssh 命令修改权限。

target$ ls -ld .ssh
drwx------ 2 myacct myacct 4096 Jan 14 15:54 .ssh

此时,你应该能够从 boss 系统远程免密运行命令到目标系统。除非目标系统上的目标用户帐户拥有与你试图连接的用户和主机相同的旧公钥,否则这应该可以工作。如果是这样,你应该删除早期的(并冲突的)条目。

使用脚本

使用脚本可以使某些工作变得更加容易。但是,在下面的示例脚本中,你会遇到的一个烦人的问题是,在配置免密访问权限之前,你必须多次输入目标用户的密码。一种选择是将脚本分为两部分——需要在 boss 系统上运行的命令和需要在 target 系统上运行的命令。

这是“一步到位”版本的脚本:

#!/bin/bash
# NOTE: This script requires that you have the password for the remote acct
#       in order to set up password-free access using your public key

LOC=`hostname`  # the local system from which you want to run commands from
                # wo a password

# get target system and account
echo -n "target system> "
read REM
echo -n "target user> "
read user

# create a key pair if no public key exists
if [ ! -f ~/.ssh/id_rsa.pub ]; then
  ssh-keygen -t rsa
fi

# ensure a .ssh directory exists in the remote account
echo checking for .ssh directory on remote system
ssh $user@$REM "if [ ! -d /home/$user/.ssh ]; then mkdir /home/$user/.ssh; fi"

# share the public key (using local hostname)
echo copying the public key
scp ~/.ssh/id_rsa.pub $user@$REM:/home/$user/$user-$LOC.pub

# put the public key into the proper location
echo adding key to authorized_keys
ssh $user@$REM "cat /home/$user/$user-$LOC.pub >> /home/$user/.ssh/authorized_ke
ys"

# set permissions on authorized_keys and .ssh (might be OK already)
echo setting permissions
ssh $user@$REM "chmod 600 ~/.ssh/authorized_keys"
ssh $user@$REM "chmod 700 ~/.ssh"

# try it out -- should NOT ask for a password
echo testing -- if no password is requested, you are all set
ssh $user@$REM /bin/hostname

脚本已经配置为在你每次必须输入密码时告诉你它正在做什么。交互看起来是这样的:

$ ./rem_login_setup
target system> fruitfly
target user> lola
checking for .ssh directory on remote system
lola@fruitfly's password:
copying the public key
lola@fruitfly's password:
id_rsa.pub                                    100%  567   219.1KB/s   00:00
adding key to authorized_keys
lola@fruitfly's password:
setting permissions
lola@fruitfly's password:
testing -- if no password is requested, you are all set
fruitfly

在上面的场景之后,你就可以像这样登录到 lola 的帐户:

$ ssh lola@fruitfly
[lola@fruitfly ~]$

一旦设置了免密登录,你就可以不需要键入密码从 boss 系统登录到 target 系统,并且运行任意的 ssh 命令。以这种免密的方式运行并不意味着你的帐户不安全。然而,根据 target 系统的性质,保护你在 boss 系统上的密码可能变得更加重要。


via: https://www.networkworld.com/article/3514607/setting-up-passwordless-linux-logins-using-publicprivate-keys.html

作者:Sandra Henry-Stocker 选题:lujun9972 译者:laingke 校对:wxy

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