《Python多人游戏项目实战》第一节 简单的方块移动

这篇具有很好参考价值的文章主要介绍了《Python多人游戏项目实战》第一节 简单的方块移动。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1.1 设置游戏窗口

1.2 绘制一个方块

1.3 编写服务端代码

1.4 完善客户端代码

1.5 完整代码下载地址


在本节,我们将通过一个简单的方块移动程序进入多人联机游戏的大门。每个玩家打开游戏窗口后都可以控制一个方块,当某个玩家移动方块后,其余玩家的窗口上会自动更新该玩家的方块位置。运行示例如下:

《Python多人游戏项目实战》第一节 简单的方块移动

本项目结构显示如下:

├── client.py         # 客户端代码

└── server.py         # 服务端代码

在client.py中我们一共导入了以下几个模块或库:

import sys
import json
import pygame
import socket
from random import randint

在server.py中我们一共导入了以下几个模块或库:

import json
import socket
from threading import Thread

1.1 设置游戏窗口

我们首先要设置好游戏窗口的相关属性,比如窗口标题、宽高以及背景等等。

# client.py
class GameWindow:
    def __init__(self):
        self.width = 500
        self.height = 500
        self.window = self.init_window()

    def init_window(self):                      # 1
        pygame.init()
        pygame.display.set_caption('移动方块')
        return pygame.display.set_mode((self.width, self.height))

    def update_window(self):                    # 2
        self.window.fill((255, 255, 255))
        pygame.display.update()

    def start(self):                            # 3
        clock = pygame.time.Clock()

        while True:
            clock.tick(60)

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()

            self.update_window()


if __name__ == "__main__":
    game = GameWindow()
    game.start()

代码解释如下:

1. init_window()函数用来初始化pygame窗口的相关属性,我们在该函数中设置了窗口的标题和大小。

2. update_window()函数用来更新窗口,将窗口背景设置为白色,后续我们也会在该函数中不断更新玩家的状态。

3. start()函数是游戏入口,重点是要调用update_window()函数不断更新窗口。

运行结果如下:

《Python多人游戏项目实战》第一节 简单的方块移动

1.2 绘制一个方块

游戏窗口设置好了之后,我们就可以往窗口上添加方块了。一个方块代表一个玩家,我们就用Player类来实现方块的相关功能。

class Player:
    def __init__(self, win, p_id, x, y, color):
        self.win = win              # 1
        self.id = p_id              # 2
        self.dis = 3                # 3
        self.x = x
        self.y = y
        self.width = 100
        self.height = 100
        self.color = color

    def move(self):                 # 4
        keys = pygame.key.get_pressed()

        if keys[pygame.K_LEFT]:
            self.x -= self.dis
        elif keys[pygame.K_RIGHT]:
            self.x += self.dis
        elif keys[pygame.K_UP]:
            self.y -= self.dis
        elif keys[pygame.K_DOWN]:
            self.y += self.dis

    def draw(self):                 # 5
        pygame.draw.rect(self.win, self.color, (self.x, self.y, self.width, self.height))

代码解释如下:

1. Player类接收一个游戏窗口实例,后面我们会在GameWindow类中实例化一个Player对象并传入游戏窗口实例的。

2. 每个玩家都会拥有一个id,该id会在服务端生成并从服务端获取过来。

3. dis变量为方块每次移动的距离。方块的宽高都为100,坐标和颜色是随机的。

4. 根据按键改变方块位置。

5. 将方块绘制到窗口上。

玩家类已经编写好了,接下来就是要在游戏窗口上添加玩家了。

# client.py
class GameWindow:
    def __init__(self):
        ...
        self.player = Player(win=self.window,           # 1
                             p_id=None,
                             x=randint(0, self.width - 100),
                             y=randint(0, self.height - 100),
                             color=(randint(0, 200), randint(0, 200), randint(0, 200)))

    ...

    def update_window(self):
        self.window.fill((255, 255, 255))
        self.player.move()                              # 2
        self.player.draw()
        pygame.display.update()
    
    ...

代码解释如下:

1. 实例化一个Player对象并传入相关参数。因为还没有连接到服务端,所以p_id先设置为None。x和y坐标是随机的,减去100(也就是方块的宽高)是为了让方块显示在窗口内。之所以将颜色值设定在0-200之间,是为了防止方块和窗口背景颜色太接近。假如方块颜色特别接近白色,那和窗口背景就混在一起,很难辨别了。

2. 在update_window()函数中调用Player实例对象的move()和draw()方法,不断在游戏窗口中更新自身的状态。

运行结果如下:

《Python多人游戏项目实战》第一节 简单的方块移动

1.3 编写服务端代码

服务端的代码逻辑很简单,就是创建套接字等待客户端连接,然后将各个客户端发送过来的玩家数据保存起来,整理之后再发送出去。代码编写如下:

# server.py
import json
import socket
from threading import Thread


class Server:
    def __init__(self):
        self.port = 5000                # 1
        self.host = "127.0.0.1"
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.players_data = {}          # 2

    def start(self):                    # 3
        self.get_socket_ready()
        self.handle_connection()

    def get_socket_ready(self):         # 4
        self.sock.bind((self.host, self.port))
        self.sock.listen()
        print("服务器已准备接收客户端连接")

    def handle_connection(self):        # 5
        while True:
            conn, addr = self.sock.accept()
            print(f"接收到来自{addr}的连接")
            conn.send(str(id(conn)).encode("utf-8"))
            Thread(target=self.handle_message, args=(conn, )).start()

    def handle_message(self, conn):     # 6
        while True:
            try:
                data = conn.recv(2048)
                if not data:
                    print("未接收到数据,关闭连接")
                    self.players_data.pop(str(id(conn)))
                    conn.close()
                    break
                else:
                    data = json.loads(data.decode("utf-8"))
                    self.update_one_player_data(data)
                    conn.sendall(json.dumps(self.get_other_players_data(data["id"])).encode("utf-8"))
            except Exception as e:
                print(repr(e))
                break

    def update_one_player_data(self, data):
        key = data["id"]
        pos = data["pos"]
        color = data["color"]
        self.players_data[key] = {"pos": pos, "color": color}

    def get_other_players_data(self, current_player_id):
        data = {}
        for key, value in self.players_data.items():
            if key != current_player_id:
                data[key] = value
        return data


if __name__ == '__main__':
    server = Server()
    server.start()

代码解释如下:

1. 服务端监听的地址为127.0.0.1:5000,所以后续再客户端中编写代码时要连接到这个地址。

2. players_data是一个字典变量,用来存储各个玩家的数据。该字典的各个键是玩家id,值包含玩家的位置和颜色。示例如下:

{
    "玩家id": {
        "pos" [x坐标值, y坐标值],
        "color": (r, g, b)
    }
}

3. start()函数是程序入口。

4.  在get_socket_ready()函数中,我们让套接字绑定监听了127.0.0.1:5000,准备就绪。

5. 如果服务端收到了来自客户端的连接,那就会将str(id(conn))作为玩家id值发送到客户端,而客户端也会将该值保存到Player对象的id属性中。为了不让连接堵塞,我们使用多线程技术来处理后续的消息通信。

6. 在handle_message()函数中,我们不断接收来自客户端的消息。如果客户端没有发送消息过来(data为空),说明客户端已经被关闭,玩家离开游戏了,那我们就要从players_data变量中删除对应玩家的数据并关闭套接字。如果有消息发送过来(data不为空),那么我们就将该玩家的数据保存或更新到player_data变量中,通过update_one_player_data()函数实现。最后将其他玩家的数据全部发送给该玩家,好让客户端窗口更新其他玩家的位置信息,其他玩家的数据通过get_other_players_data()函数获取。

简而言之:玩家A发送自身数据到服务端,服务端保存并返回除A之外的其他所有玩家的数据。玩家B发送自身数据到服务端,服务端保存并返回除B之外的其他所有玩家的数据。

注:或者也可以这样通信,玩家A发送自身数据到服务端,服务端将该数据发送给其他所有玩家。玩家B发送自身数据到服务端,服务端将该数据发送给其他所有玩家。这种通信方式有个缺点,就是服务端的发送的消息次数会很多。假如现在有10个玩家,当每个玩家发送1次数据到服务端,服务端还需要发送9次将数据同步给其他玩家。

运行结果如下:

《Python多人游戏项目实战》第一节 简单的方块移动

1.4 完善客户端代码

最后一步就是要在客户端程序中添加和服务端通信的代码。客户端会把玩家数据发送到服务端,然后把服务端接收过来的其他玩家的数据更新到窗口上。代码编写如下:

class GameWindow:
    def __init__(self):
        ...
        self.port = 5000                # 1
        self.host = "127.0.0.1"
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        self.connect()                  # 2
        self.other_players_dict = {}    # 3

    ...

    def connect(self):
        self.sock.connect((self.host, self.port))
        self.player.id = self.sock.recv(2048).decode("utf-8")

    def send_player_data(self):         # 4
        data = {
            "id": self.player.id,
            "pos": [self.player.x, self.player.y],
            "color": self.player.color
        }
        self.sock.send(json.dumps(data).encode("utf-8"))
        return self.sock.recv(2048).decode("utf-8")

    def update_window(self):            # 5
        self.window.fill((255, 255, 255))

        self.player.move()
        self.player.draw()

        other_players_data = json.loads(self.send_player_data())
        self.update_other_players_data(other_players_data)
        self.delete_offline_players(other_players_data)

        pygame.display.update()

    def update_other_players_data(self, data):  # 6
        for key, value in data.items():
            if not self.other_players_dict.get(key):
                self.add_one_player(key, value)
            else:
                pos = value["pos"]
                self.other_players_dict[key].x = pos[0]
                self.other_players_dict[key].y = pos[1]
                self.other_players_dict[key].draw()

    def add_one_player(self, player_id, value): # 7
        pos = value["pos"]
        color = value["color"]
        self.other_players_dict[player_id] = Player(self.window, player_id, pos[0], pos[1], color)

    def delete_offline_players(self, data):     # 8
        new_dict = {}
        for key in self.other_players_dict.keys():
            if data.get(key):
                new_dict[key] = self.other_players_dict[key]
        self.other_players_dict = new_dict

    ...

代码解释如下:

1. 客户端要连接到127.0.0.1:5000,即服务端监听的地址。

2. 在connect()函数中,我们让客户端和服务端进行了连接。连接后,服务端会发送str(id(conn))作为玩家id,我们将该值保存到Player对象的id属性中。

3. other_players_dict字典变量用来保存其他所有玩家的数据,字典的键是玩家id,值是代表该玩家的Player对象,格式如下所示。

{
    "玩家id": Player实例对象
}

4. send_player_data()函数用来将当前玩家的数据发送到服务端,并返回从服务端接收到的其他玩家的数据。

5. 在update_window()函数中,我们不仅要更新当前玩家的状态,还要更新其他所有玩家的状态。

6.&7. 在update_other_players_data()函数中,我们分析从服务端接收回来的数据,如果出现一个新的玩家id,那么就实例化一个Player对象并保存到other_players_dict字典变量中。如果该玩家id已存在,则更新该玩家的位置即可。

8. 如果某个玩家退出了游戏,那从服务端接收回来的数据中,肯定会少一个玩家id。但是该玩家id之前又已经保存到other_players_dict字典变量中,所以我们每次还要更新下other_players_dict,把不需要的玩家id给去掉。

现在先运行服务端程序,然后再运行任意数量的客户端程序,笔者这里就打开三个客户端。我们发现,每个游戏窗口上都会出现三个方块,在任何一个窗口上移动方块,其他两个窗口也会立即更新方块位置。运行结果如下:

《Python多人游戏项目实战》第一节 简单的方块移动

1.5 完整代码下载地址

链接:https://pan.baidu.com/s/15uFqYp98R0LoH92LOsRzug  

密码:18ik文章来源地址https://www.toymoban.com/news/detail-418665.html

到了这里,关于《Python多人游戏项目实战》第一节 简单的方块移动的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Vue2项目练手——通用后台管理项目第一节

    npm的缺点: npm install时候巨慢 同一个项目,安装的时候无法保持一致性。 由于package.json文件中版本号的特点。 “5.0.3” 安装指定的5.0.3版本 “~5.0.3” 表示安装5.0.X中最新的版本 “^5.0.3” 表示安装5.X.X中最新的版本 有时候会出现版本不一致不能运行的情况。 yarn的优点 速度

    2024年02月11日
    浏览(53)
  • HTML5 游戏开发实战 | 俄罗斯方块

      俄罗斯方块是一款风靡全球的电视游戏机和掌上游戏机游戏,它曾经造成的轰动与造成的经济价值可以说是游戏史上的一件大事。这款游戏看似简单但却变化无穷,游戏过程仅需要玩家将不断下落的各种形状的方块移动、翻转,如果某一行被方块充满了,那就将这一行消掉

    2024年02月11日
    浏览(44)
  • 软件设计实战:基于Java的俄罗斯方块游戏【完整版】

     个人简介 👨🏻‍💻个人主页:陈橘又青 🏃🏻‍♂️博客记录心情,代码编写人生。 🌟如果文章对你有用,麻烦关注点赞收藏走一波,感谢支持! 🌱强力推荐我平时学习编程和准备面试的刷题网站:点这里! 前言 大家好,今天用Java编程实现一个GUI界面的经典俄罗斯方

    2024年02月02日
    浏览(40)
  • 【Python零基础学习入门篇①】——第一节:基本语法与变量

    ⬇️⬇️⬇️⬇️⬇️⬇️ ⭐⭐⭐Hello,大家好呀我是陈童学哦,一个普通大一在校生,请大家多多关照呀嘿嘿😁😊😘 🌟🌟🌟 技术这条路固然很艰辛,但既已选择,该当坚毅地走下去,加油! 🌤️PUA: ” 你所看到的惊艳都曾平庸历练 **“**🚀🚀🚀 🍉🍉🍉 最后让我

    2024年02月03日
    浏览(61)
  • C语言项目小游戏之俄罗斯方块

    今天给大家带来一个用C语言实现的俄罗斯方块小游戏 游戏截图:       首先我们先创建一个名为mywindows.h的头文件。用来设置我们操作台的各种功能实现 每一个函数的功能都有注释向大家解释,现在给大家放出函数功能的具体实现,博主创建了了个名为mywindows.c的源文件 这

    2024年02月15日
    浏览(46)
  • 【Unity工具,简单学习】PUN 2,多人在线游戏开发,初步使用

    链接 PUN 可以让你 简单地开发多人游戏 ,在 全球范围 推出 让开发者 不用管托管、链接和延迟 支持移动端、台式、一些主机 全球范围内低延迟 始终连接,无需穿透 免费20 CCU(Concurrent User, 并发用户) 支持 Unity 2019 - 2022 安装包,输入邮箱来注册,或输入Appid来登录。 输入邮箱

    2024年02月07日
    浏览(54)
  • 【用unity实现100个游戏之10】复刻经典俄罗斯方块游戏(附项目源码)

    【视频】:https://www.bilibili.com/video/BV1Fr4y1x7mx 注意 :本文为学习笔记记录,推荐支持原作者,去看原视频自己手敲代码理解更加深入

    2024年02月04日
    浏览(76)
  • 【俄罗斯方块】单机游戏-微信小程序项目开发入门

    这是一个仿俄罗斯方块小游戏的微信小程序,只需要写一小段代码就实现出来了,有兴趣的同学完全可以自己动手开发,来看看实现过程是怎样的呢,边写边做,一起来回忆小时候玩过的经典俄罗斯方块游戏吧。 💡 给读者一个小小提示 需要有HTML和CSS基础 还有JavaScript基础

    2024年02月10日
    浏览(84)
  • 第一篇【传奇开心果系列】Python的游戏库pygame技术点案例示例:深度解读实战开发飞机大战经典游戏案例

    开发飞机大战游戏不仅能满足广大玩家的娱乐需求,提供寓教于乐的学习机会,也是技术实践、创新实验和商业探索的重要载体,具有多维度的社会与经济价值。 开发飞机大战游戏具有多方面的意义,涵盖娱乐、教育、技术实践与创新等多个层面: 娱乐价值 : 休闲娱乐 :

    2024年04月23日
    浏览(86)
  • Steam多人联机游戏unturned(未转变者)超级简单教程,作者亲自联机游玩

            unturned在往年可是一群人挤在一起玩,当然我也不是说这个游戏已经过时了,今天咱们开始unturned的联机教程,我也是熬了一夜才整明白。 此次联机教程适用于个人服游玩(几个小伙伴一起游玩一下,当然没问题),分为两种 一,局域网联机:开服成功后,基本上

    2024年02月05日
    浏览(130)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包