想学习Python不知从哪开始,来看我和同事通过游戏开发学习Python

学习python?说得通。它拥有大量稳定的机器学习和数据操作库。在本文中,将您的Python研究带到下一个层次。

我的同事和我决定一起做一个小游戏来帮助我们学习语言。这篇文章介绍了我们最初的经验,把一切都放在一起。

这个计划

就像我学过的其他语言一样,我通常喜欢开发一个应用程序,它涉及到一些功能,比如读取文件、网络、用户输入和可视化。这迫使我熟悉了库和语言的功能,这让我加快了重新实现算法和完成教程项目的速度。它还迫使我了解Python的环境,以安装依赖关系和创建发行版。

我们查阅了一些与游戏创建和网络相关的库,并决定使用pygame,因为它似乎提供了一种功能,可以从开发中删除许多单调乏味的内容。它看起来也像Python里有一系列的网络库,所以我们决定在使用它的时候把它弄清楚。

安装Python

Python本身相对容易安装。我们刚刚从网站上下载了自动安装程序,并在一分钟内将运行时准备好。

安装Pygame

事实证明,安装Pygame有点令人沮丧。在我们设法下载脚本并以正确的方式安装它之前,我们尝试了好几次。我们必须找到这个库的正确版本(它与我们安装的Python版本相匹配)在一个不容易找到的依赖项列表上,然后用Python包安装实用程序pip3.exe来提取它。这看起来比实际要困难得多,特别是由于库的不同版本的数量,以及如果我们安装了不同版本的Python,我们需要做些什么。

在这里还是要推荐下我自己建的Python开发学习群:483546416,群里都是学Python开发的,如果你正在学习Python ,小编欢迎你加入,大家都是软件开发党,不定期分享干货(只有Python软件开发相关的),包括我自己整理的一份2018最新的Python进阶资料和高级开发教程,欢迎进阶中和进想深入Python的小伙伴

最终,我们建立了一些东西,并寻找了一个关于如何获得游戏基础的教程。

Drawing a Sprite

在开始使用任何图形的时候,首先要做的事情就是将某些东西(或任何东西)呈现在屏幕上。我们发现了一大堆关于这方面的复杂的教程,并基于他们的例子提出了一个基本的渲染循环:

import pygame, sys

from pygame.locals import *

WIDTH = 400

HEIGHT = 400

screen = pygame.display.set_mode((WIDTH, HEIGHT))

pygame.display.set_caption('Hello World!')

clock = pygame.time.Clock()

thing = pygame.image.load('images/TrashPanda/TrashPanda_front.png')

x = 0

y = 0

whileTrue:

for event in pygame.event.get():

if event.type == QUIT:

pygame.quit()

sys.exit()

clock.tick(30)

screen.fill((0,0,0))

screen.blit(thing, (x, y))

pygame.display.flip()

这段代码产生:

在那之后,我们将重点放在捕获用户输入以移动字符。我们还为玩家角色创建了一个类来将其逻辑内部化:

classMinion:

def__init__(self, x, y):

self.x = x

self.y = y

self.vx = 0

self.vy = 0

defupdate(self):

self.x += self.vx

self.y += self.vy

#this keeps the player character within the bounds of the screen

ifself.x > WIDTH - 50:

self.x = WIDTH - 50

ifself.x < 0:

self.x = 0

ifself.y > HEIGHT - 50:

self.y = HEIGHT - 50

ifself.y < 0:

self.y = 0

defrender(self):

screen.blit(thing, (self.x, self.y))

用户输入在游戏循环中被捕获:

for event in pygame.event.get():

if event.type == QUIT:

pygame.quit()

sys.exit()

if event.type == KEYDOWN:

if event.key == K_LEFT: cc.vx = -10

if event.key == K_RIGHT: cc.vx = 10

if event.key == K_UP: cc.vy = -10

if event.key == K_DOWN: cc.vy = 10

if event.type == KEYUP:

if event.key == K_LEFT and cc.vx == -10: cc.vx = 0

if event.key == K_RIGHT and cc.vx == 10: cc.vx = 0

if event.key == K_UP and cc.vy == -10: cc.vy = 0

if event.key == K_DOWN and cc.vy == 10: cc.vy = 0

角色的位置被更新和渲染(同样在游戏中):

cc.update()

cc.render()

现在我们有了基本的字符移动工作,我们想开始构建一些简单的多人游戏功能。

我们决定采用一个非常简单的数据传输模型:

· 客户端连接到服务器,然后不断广播自己的字符的位置

服务器将所有字符的位置广播给所有的客户

我们决定使用TCP套接字,因为它们处理诸如连接和断开连接比UDP更容易。另外,这并不是一个性能关键的应用程序。

我们成功地找到了一篇关于在Python中使用Python编写异步服务器的好文章。

基本的服务器代码是这样开始的:

import socket

import asyncore

import random

import pickle

import time

BUFFERSIZE = 512

outgoing = []

#additional logic here...

classMainServer(asyncore.dispatcher):

def__init__(self, port):

asyncore.dispatcher.__init__(self)

self.create_socket(socket.AF_INET, socket.SOCK_STREAM)

self.bind(('', port))

self.listen(10)

defhandle_accept(self):

conn, addr = self.accept()

print ('Connection address:' + addr[0] + " " + str(addr[1]))

outgoing.append(conn)

playerid = random.randint(1000, 1000000)

playerminion = Minion(playerid)

minionmap[playerid] = playerminion

conn.send(pickle.dumps(['id update', playerid]))

SecondaryServer(conn)

classSecondaryServer(asyncore.dispatcher_with_send):

defhandle_read(self):

recievedData = self.recv(BUFFERSIZE)

if recievedData:

updateWorld(recievedData)

else: self.close()

MainServer(4321)

asyncore.loop()

这定义了一个负责接受新的TCP连接的主服务器,然后它创建一个二级服务器。辅助服务器处理来自每个客户机的所有传入数据。接收到传入的数据包时,将数据传递给updateWorld。这是定义如下:

classMinion:

def__init__(self, ownerid):

self.x = 50

self.y = 50

self.ownerid = ownerid

minionmap = {}

defupdateWorld(message):

arr = pickle.loads(message)

playerid = arr[1]

x = arr[2]

y = arr[3]

if playerid == 0: return

minionmap[playerid].x = x

minionmap[playerid].y = y

remove = []

for i in outgoing:

update = ['player locations']

for key, value in minionmap.items():

update.append([value.ownerid, value.x, value.y])

try:

i.send(pickle.dumps(update))

except Exception:

remove.append(i)

continue

for r in remove:

outgoing.remove(r)

updateWorld只是负责更新包含每个玩家角色位置的字典。然后,它通过将它们的位置序列化为数组的数组,向每个播放器广播这些位置。

现在客户端已经构建好了,我们可以实现客户端发送和接收更新的逻辑。当游戏开始时,我们添加了一些逻辑来启动一个简单的套接字并连接到一个服务器地址。这可选地获取命令行指定的IP地址,但其他方式连接到本地主机:

serverAddr = '127.0.0.1'

iflen(sys.argv) == 2:

serverAddr = sys.argv[1]

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.connect((serverAddr, 4321))

然后,我们在游戏循环的开始部分添加了一些逻辑,以便从套接字读取。我们使用“select”包只在有数据的情况下从套接字读取传入的包。如果我们使用了插座。如果套接字没有读取的包,则游戏程序将停止。使用“select”允许gameloop继续执行,即使没有什么可读的:

ins, outs, ex = select.select([s], [], [], 0)

for inm in ins:

gameEvent = pickle.loads(inm.recv(BUFFERSIZE))

if gameEvent[0] == 'id update':

playerid = gameEvent[1]

print(playerid)

if gameEvent[0] == 'player locations':

gameEvent.pop(0)

minions = []

for minion in gameEvent:

if minion[0] != playerid:

minions.append(Minion(minion[1], minion[2], minion[0]))

上面的代码处理了服务器可能生成的两个序列化的有效负载。

1. 包含玩家服务器分配标识符的初始包。

客户端使用此方法在所有位置更新中标识自己到服务器。它还用于忽略服务器广播的自己的播放器数据,因此没有一个带有阴影版本的玩家角色。

2. 球员的位置有效载荷

这包含一组包含玩家标识符和字符位置的数组。当检索到现有的Minion对象时,将为每个传输的对象创建新的Minion对象。

其他的小黄人则在游戏循环中呈现:

for m in minions:

m.render()

我们要做的最后一件事是向客户端添加一些代码,告诉服务器玩家的位置。这是通过在gameloop的结尾添加一个广播来序列化当前播放器的位置,然后使用“pickle”,然后将这个bytestream发送到服务器:

ge = ['position update', playerid, cc.x, cc.y]

s.send(pickle.dumps(ge))

一旦这是完整的玩家连接到同一个服务器可以看到其他玩家移动。

一些额外的更新,例如显示基于playerid的不同的化身被实现。

当完成时,当前的迭代有两个参与者是这样的:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,784评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,745评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,702评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,229评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,245评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,376评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,798评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,471评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,655评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,485评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,535评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,235评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,793评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,863评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,096评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,654评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,233评论 2 341

推荐阅读更多精彩内容