草庐IT

《Python多人游戏项目实战》第三节 在窗口上显示玩家ID以及对话内容

la_vie_est_belle 2023-10-31 原文

目录

3.1 显示不同的人物图片

3.2 显示玩家ID

3.3 显示玩家对话内容

3.4 完整代码下载地址


本节只是在上一节内容的基础上加一些小功能:显示不同的人物图片、在人物头顶上显示玩家ID以及人物头顶上显示一个聊天对话框。大家可以把这一节内容当做一个过渡,用来巩固下多人游戏程序中pickle的用法。程序完成后的运行结果如下:

本项目结构显示如下:

├── SimHei.ttf        # 字体文件
├── client.py         # 客户端代码
├── pics              # 图片文件夹
│   ├── 1.png
│   ├── 2.png
│   ├── 3.png
│   ├── 4.png
│   ├── 5.png
│   └── 6.png
├── player.py         # 包含Player类
└── server.py         # 服务端代码

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

import sys
import pygame
import pickle
import socket
from player import Player
from random import randint

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

import pygame
import random

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

import socket
import pickle
from player import Player
from threading import Thread

3.1 显示不同的人物图片

pics文件夹下有6个人物方位图,玩家打开游戏窗口时,程序都会从这6个方位图中随机选择一张进行显示。因为是随机的,所以其他玩家的人物图片可能跟当前玩家的一样,也可能不一样。

首先修改Player类:

# player.py
class Player:
    def __init__(self, p_id, x, y,  pic_num, frame_width, frame_height):
        self.id = p_id
        self.dis = 3
        self.x = x
        self.y = y

        self.pic_num = pic_num      # 1
        self.frame_width = frame_width
        self.frame_height = frame_height
        self.frame_num = 0
        self.frame_rect = (self.frame_num * self.frame_width, 0 * self.frame_height,
                           self.frame_width, self.frame_height)

        self.current_dir = "下"
        self.last_dir = self.current_dir
    
    ...

代码解释如下:

1. 在初始化函数中加一个pic_num变量用来保存玩家显示的人物图片编号,这样就能从服务端传送过来的数据中知道某个玩家用的什么人物图了。

接着修改GameWindow类:

# client.py
class GameWindow:
    def __init__(self):
        ...

        self.pic_dict = {                                   # 1
            1: pygame.image.load("./pics/1.png"),
            2: pygame.image.load("./pics/2.png"),
            3: pygame.image.load("./pics/3.png"),
            4: pygame.image.load("./pics/4.png"),
            5: pygame.image.load("./pics/5.png"),
            6: pygame.image.load("./pics/6.png")
        }
        frame_width = self.pic_dict[1].get_width() // 4     # 2
        frame_height = self.pic_dict[1].get_height() // 4

        self.player = Player(p_id=None,
                             x=randint(0, self.width-frame_width),
                             y=randint(0, self.height-frame_height),
                             pic_num=randint(1, 6),         # 3
                             frame_width=frame_width,
                             frame_height=frame_height)

        ...

    ...

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

        self.player.move()
        self.player.draw(self.window, self.pic_dict[self.player.pic_num])   # 4

        other_players_data = pickle.loads(self.send_player_data())
        self.update_other_players_data(other_players_data)

        pygame.display.update()

    def update_other_players_data(self, data):
        for player in data.values():
            player.draw(self.window, self.pic_dict[player.pic_num])         # 5

    ...

代码解释如下:

1. 创建一个pic_dict字典变量,用来保存各个人物方位图对象,字典的键就是图片编号。

2. 因为所有人物方位图的大小都是一样的,所以这里就直接通过第一张方位图获取帧的宽高值。

3. 随机获取1-6之间的整数,这样就可以随机选择一张人物方位图了。

4. & 5. 根据pic_num值从pic_dict字典中选择对应的人物方位图,然后绘制到窗口上。

运行结果如下:

3.2 显示玩家ID

很多游戏会把玩家的id或名称显示在人物图片上方,并跟随人物移动,如下图所示。

这点其实很好实现,因为我们已经将玩家id值保存在Player对象的id属性中,所以只需要将这个id值绘制到窗口上就可以了。

将Player类的draw()函数修改如下:

# player.py
def draw(self, win, pic):
    win.blit(pic, (self.x, self.y), self.frame_rect)

    font = pygame.font.SysFont("Arial", 10)          # 1
    id_text = font.render(self.id, True, (150, 150, 150))
    win.blit(id_text, (self.x + round(self.frame_width/2) - round(id_text.get_width()/2), self.y - id_text.get_height()))

代码解释如下:

1. 设置文字字体并将文本绘制到人物图片的正上方。

运行结果如下:

3.3 显示玩家对话内容

如果要聊天的话,玩家就会在文本输入框中输入消息,然后按下回车键发送,其他玩家随后也会收到该玩家发送的消息。Pygame没有提供一个文本输入框控件,如果要自己实现的话会增加很多跟本教程主题无关的代码。所以为了保持代码简洁,笔者就预先设置好一些聊天内容,然后随机发送一条。

修改Player类:

# player.py
class Player:
    def __init__(self, p_id, x, y,  pic_num, frame_width, frame_height):
        ...

        self.message = ""           # 1

    ...

    def speak(self):                # 2
        messages_list = ["你好", "最近咋样", "我在打怪呢", "你说的有道理", "你哪里人", "不玩了,去吃饭了", "待会再玩", "今天很开心"]
        self.message = random.choice(messages_list)

代码解释如下: 

1. message变量用来存储玩家要发送消息。

2. speak()函数会随机生成一条聊天消息,并保存在message变量中。

修改GameWindow类:

# client.py
class GameWindow:
    def __init__(self):
        ...

        self.all_players_messages = []      # 1
        self.max_messages_shown = 6         # 2

        self.port = 5000
        self.host = "127.0.0.1"
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        self.connect()

    ...

    def send_player_data(self):
        data = {
            "id": self.player.id,
            "player": self.player
        }
        self.sock.send(pickle.dumps(data))
        self.player.message = ""            # 3
        return self.sock.recv(2048)

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

        self.player.move()
        self.player.draw(self.window, self.pic_dict[self.player.pic_num])
        if randint(0, 1000) < 50:           # 4
            self.player.speak()
        self.update_messages(self.player)   # 5

        other_players_data = pickle.loads(self.send_player_data())
        self.update_other_players_data(other_players_data)

        pygame.display.update()

    def update_other_players_data(self, data):
        for player in data.values():
            player.draw(self.window, self.pic_dict[player.pic_num])
            self.update_messages(player)    # 6

    def update_messages(self, player):
        if player.message:
            self.all_players_messages.insert(0, f"{player.id}: {player.message}")
            if len(self.all_players_messages) > self.max_messages_shown:
                self.all_players_messages.pop()

        font_size = 12
        font = pygame.font.Font("SimHei.ttf", font_size)
        for i, msg in enumerate(self.all_players_messages):
            msg_text = font.render(msg, True, (150, 150, 150))
            self.window.blit(msg_text, (0, self.height-(font_size*(i+1))))

代码解释如下:

1. 所有玩家的消息都会存储在all_players_messages列表变量中。

2. max_messages_shown变量用来控制在窗口上显示的聊天消息数。

3. 当前玩家的数据被发送到服务器后,就要清空Player对象的message属性,否则其他玩家会重复收到一条不变的消息。

4. 使用随机数来模拟玩家聊天。

5. update_messages()函数是重点,在该函数中,我们首先判断玩家数据中的message变量是否有值,有的话说明有发送消息,那就将这个值插到all_players_messages列表开头。如果消息总数大于max_messages_shown变量的值,那就把列表中最后一条聊天消息删除掉,不再显示。最后将所有聊天内容都显示到窗口左下角。为了解决Pygame中文显示的问题,笔者这里使用了另外的SimHei.ttf字体,已放入到项目文件夹中。

6. 接收到其他玩家的数据后,要记得调用update_messages()函数更新聊天内容。

运行结果如下:

3.4 完整代码下载地址

链接:https://pan.baidu.com/s/1chQ5c94X07Oi3Iv_eX3xDg  

密码:9qbz

有关《Python多人游戏项目实战》第三节 在窗口上显示玩家ID以及对话内容的更多相关文章

  1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  2. ruby-on-rails - Rails 编辑表单不显示嵌套项 - 2

    我得到了一个包含嵌套链接的表单。编辑时链接字段为空的问题。这是我的表格:Editingkategori{:action=>'update',:id=>@konkurrancer.id})do|f|%>'Trackingurl',:style=>'width:500;'%>'Editkonkurrence'%>|我的konkurrencer模型:has_one:link我的链接模型:classLink我的konkurrancer编辑操作:defedit@konkurrancer=Konkurrancer.find(params[:id])@konkurrancer.link_attrib

  3. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  4. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  5. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  6. ruby - 将数组的内容转换为 int - 2

    我需要读入一个包含数字列表的文件。此代码读取文件并将其放入二维数组中。现在我需要获取数组中所有数字的平均值,但我需要将数组的内容更改为int。有什么想法可以将to_i方法放在哪里吗?ClassTerraindefinitializefile_name@input=IO.readlines(file_name)#readinfile@size=@input[0].to_i@land=[@size]x=1whilex 最佳答案 只需将数组映射为整数:@land边注如果你想得到一条线的平均值,你可以这样做:values=@input[x]

  7. ruby-on-rails - 项目升级后 Pow 不会更改 ruby​​ 版本 - 2

    我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby​​版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby​​版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘

  8. ruby-on-rails - 使用 Sublime Text 3 突出显示 HTML 背景语法中的 ERB? - 2

    所以我在关注Railscast,我注意到在html.erb文件中,ruby代码有一个微弱的背景高亮效果,以区别于其他代码HTML文档。我知道Ryan使用TextMate。我正在使用SublimeText3。我怎样才能达到同样的效果?谢谢! 最佳答案 为SublimeText安装ERB包。假设您安装了SublimeText包管理器*,只需点击cmd+shift+P即可获得命令菜单,然后键入installpackage并选择PackageControl:InstallPackage获取包管理器菜单。在该菜单中,键入ERB并在看到包时选择

  9. ruby-on-rails - 新 Rails 项目 : 'bundle install' can't install rails in gemfile - 2

    我已经像这样安装了一个新的Rails项目:$railsnewsite它执行并到达:bundleinstall但是当它似乎尝试安装依赖项时我得到了这个错误Gem::Ext::BuildError:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcheckingforlibkern/OSAtomic.h...yescreatingMakefilemake"DESTDIR="cleanmake"DESTDIR="

  10. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

    我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

随机推荐