多人连线的枪战游戏

2023-10-26

Simple Blueprint Multiplayer 是一个完全由 蓝图 和 UMG 界面 编写的游戏,可以作为如何使用蓝图的 Session Nodes 打造游戏中的多人部分的使用示例。 这里有一个主菜单,一个服务器列表,以及一个简单的地图,地图中有一个带有记分牌的 HUD 显示。在主菜单中点击 PLAY 便能作为主机创建一个 Session,并加载进入游戏地图。 其他玩家在自己的菜单界面中,点击 Find games 来查看所有存在的游戏主机列表,点击列表中的一个已查到的游戏时则会尝试加入它。如果出现任何错误,则会回到主菜单并显示一个错误框。

这个游戏同时也是一个说明蓝图中如何使用 GameInstance 来管理游戏状态的范例。GameInstance 是一个管理游戏状态的方便的手段,它能够在地图加载卸载的过程中一直存在,同时它还是用于接收错误事件的对象。 大部分 Session 相关的调用以及菜单的切换都由 GameInstance 来处理。

目前,这篇文档涵盖了 在线会话节点 和它对于用于多人游戏中的创建主机,发现、加入、离开游戏的实现。这篇文档将会在之后更新,来进一步说明射击游戏的其他方面,比如攻击命中其他玩家,死亡和重生,以及得分的计算。

开始/进行游戏

这个章节汉高了如何开始并进行游戏,提供了游戏的各个组件的单独分析。

如果您对虚幻引擎 4 中如何测试多人游戏还不熟悉的话,应该先看一下 测试多人游戏 文档。

加载游戏:

  1. 在 Game/Maps 目录打开 MainMenu 地图。

  2. MainMenu 地图打开后,点击 Play 按钮右边的向下箭头,将 Number of players 设置为 2。

    Run Dedicated Server 选项在这个范例中可能会造成服务器列表显示不正确,这部分还需要被修正。

  3. 点击 Play 按钮启动游戏。

  4. 当游戏启动后,会显示一下窗口。

    上图是我们在 New Editor Windows 的设置下,并把每个窗口设置为 640x480 的分辨率,这些可以在 Advanced Settings... 选项内设置。

    如果是在网络上测试这个范例,不像上述过程直接使用本地的多人游戏的话,应采用 Standalone Play Mode 的方式来加入其他人的游戏,或者自己作为主机创建并让其他人来加入。通过 PIE(Play In Editor,在编辑器中启动游戏)来进行一个实际网络的游戏目前并不稳定,这个问题我们还在处理。

对于主菜单的拆分说明如下。

单项

描述

1

以当前游戏窗口作为主机开始游戏。

2

显示当前有效的 服务器列表,可用于选择并加入。

3

退出当前游戏。

4

在 LAN 和 Internet 两个连接模式间切换选择。

一旦选择作为主机开始游戏,或者加入其他主机的游戏,则会看到下图所示的游戏窗口。

在屏幕的左上角(黄色高亮框),可以看到一些文字,这是当前角色的名字。在名字右边的框框中,是当前该角色的得分。当有玩家加入时,角色名字和得分的部分都会得到更新,来显示当前游戏中的所有玩家和对应的的得分。

上图用到的玩家名字,是来自于 LAN 的连线。但当使用诸如 Steam 的服务时,应该显示玩家在 Steam 的用户名。

一旦有主机已经创建后,在第二个玩家的窗口中点击 Find Games 按钮来显示 Server List

经过一小段时间搜索后,当前可用的游戏就会显示在列表中。在这个窗口中,从左到右显示的是,服务器的名字,玩家的数量,以及该游戏主机的 ping 值。 在屏幕的左下角可以点击 Refresh 按钮来刷新列表,或者点击 Back 按钮返回主菜单。直接点击列表中的条目就会尝试连接服务器,并且在游戏中生成角色。

连入游戏后,必须要点击 鼠标左键 作为准备好开始游戏的信号。

当点击完成准备后(主机端或客户端),左上角会显示一个文字来显示玩家已经准备好了。

完成准备后,可以用以下这几个控制方式来玩游戏。

操作

描述

鼠标右键

拔枪(持续按住保持举枪动作)。

鼠标移动

进入 瞄准模式,移动鼠标能够瞄准武器。

鼠标左键

开火。

鼠标中键(上下滚动)

在非瞄准状态下进行换弹。

Q 键

打开游戏暂停菜单(能够继续游戏或者离开游戏)。

这个游戏的目标是射击其他玩家,命中就能得到 1 分,被命中的人则要重生。每个玩家有六次射击机会,然后就要用鼠标中间(上下滚轮)来换弹(一次一颗)。

项目设置/配置

这个章节涵盖了 内容浏览器 中创建(或修改)的每个素材,并有各自的描述。有几个蓝图和 UMG UI 的素材互相调用(或有依赖关系),因此如果您想要重新做这个项目中类似的事情,最好是从头开始创建并再将它们关联起来。

在 内容浏览器中,每种资源素材都能在它们对应的目录中找到。

游戏/蓝图/控件

名字

描述

ErrorDialog

这是一个当有错误时用来显示错误的对话框。

HUD

这个是游戏内的记分牌,显示 ScoreboardRow 控件,囊括了玩家的名字和得分。

InGameMenu

这个是游戏内的菜单,提供离开游戏返回主菜单的功能。

LoadingScreen

这是个简单的 loading 界面,它会在加入游戏的过程中显示。

MainMenu

游戏启动时的主菜单,玩家能够创建主机,或者加入其他主机的游戏,或者退出游戏。

ScoreboardRow

这是个添加到 HUD 的控件,包括了玩家的名字和得分。

ServerList

这个菜单包括了一组 ServerRow 的控件,用于显示所有的可用游戏主机清单。

ServerRow

这个控件具有一个可用主机的信息,包括服务器名字,玩家数量,Ping 值。它被传递到 ServerList 控件上。

游戏/蓝图

名字

描述

MyGameInstance

(Instance 蓝图) - 处理所有从主菜单到其他界面过程中发生的状态变化,以及错误消息。

MyGameMode

(Game Mode 蓝图) - 记录了所有的默认类(Pawn,HUD,PlayerController 等)。并处理玩家重生的逻辑,以及当玩家加入游戏时的逻辑。

MyPlayerController

(PlayerController 蓝图)设置游戏内界面,并允许玩家打开游戏中的暂停界面。

MyPlayerState

(PlayerState 蓝图)处理每个玩家的分数。

ProjectileBP

(Actor 蓝图)是每个玩家开火后的子弹,并能对对手造成伤害。

State

(枚举)这是游戏可能处于的状态的全集列表。

游戏/Gunslinger

Name

Description

Gunslinger_BP

这个是游戏中可以使用的角色。

GunslingerTTP

给 Gunslinger_BP 用的 SkeletalMesh。

Gunslinger_AnimBP

用来驱动 Gunslinger_BP 的动画。

游戏/Character

名字

描述

Character_Parent

(Parent) Gunslinger_BP 用的材质

Character_Instance

(Instanced) Gunslinger_BP 用的材质实例。

游戏/Fonts

名字

描述

ScoreFont

Scoreboard 控件用的字体

游戏/Maps

名字

描述

MainMenu

项目和编辑器的默认加载地图,包含了启动的显示。

Level_01

这是游戏地图,给玩家创建主机或者加入游戏。

配置设置

为了成功的创建主机或者连接到多人游戏中,在 DefaultEngine.ini 中有些设置需要做,该文件在 UnrealProjectDirectory/ProjectName/Config

打开后,需要先找到 OnlineSubsystem 以及要用的 DefaultPlatformService

比如,要在 LAN 上连线,要添加 DefaultPlatformService=Null

[OnlineSubsystem]
DefaultPlatformService=Null

或者如果要在 Steam 上玩,需要使用这样的 OnlineSubsystemSteam

[/Script/Engine.GameEngine]
+NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="OnlineSubsystemSteam.SteamNetDriver",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver")

[OnlineSubsystem]
DefaultPlatformService=Steam

[OnlineSubsystemSteam]
bEnabled=true
SteamDevAppId=480
bVACEnabled=0

[/Script/OnlineSubsystemSteam.SteamNetDriver]
NetConnectionClassName="OnlineSubsystemSteam.SteamNetConnection"

如果要用 Steam 的话,需要 额外的 SDK 和 INI 配置,跳转到该链接查看更多信息。

蓝图说明

在接下去的章节中,我们来看一下游戏的各个状态,以及驱动这些状态工作的蓝图。

我们先看看 Startup 的序列。

开始

在 MainMenu 的地图中,打开 Level Blueprint

关卡蓝图打开后,可以看到一部分脚本标记了"Game Logic Starts Here"。

这个备注不错,游戏正是从这部分脚本开始。游戏开始时,获取了 Game Instance,并 转换 为 MyGameInstance。这么做便能够访问 MyGameInstance 的变量、函数和脚本了,这里接下去调用了一个叫做 Begin Play Show Main Menu 的函数(见下图)。

当 Begin Play Show Main Menu 被调用时,首先运行了一个预先创建的宏,叫做 IsCurrentState

IsCurrentState 宏

IsCurrentState 会检查 In State(在宏的节点上定义) 是否和一个叫做 Current State 的变量一致。Current State 变量是一个由 State 创建的枚举类型,记录游戏可能处于的每一个状态。

既然 Current State 和 In Satet 一致,便能调用 ShowMainMenuEvent 这个自定义事件了。

ShowMainMenuEvent 做的第一件事情是运行 IsCurrentState Macro 检查当前状态是否是 Playing,如果 True 的话,则加载 MainMenu 的关卡,如果游戏当前在 Startup 的状态,则返回 False 。这里调用了另一个叫做 TransitionToState 预创建的宏。

TransitionToState 宏

下图中,在 TransitionToState 中,首先运行了 IsCurrentState 宏 (1)对照 Current State 和 Desired State,如果两者一致则在屏幕上显示错误并提示原因(2)如果它们不一致,则执行 Switch on State 节点(3)这里会获取 Current State 并调用一个叫做 Hide Widget 的函数(这里将会隐藏当前显示的 UI 控件),或者运行一个叫做 Destroy Session Caller 的自定义事件(将会销毁玩家要调用的游戏 Session)。

当 Switch on State 完成后,Current State 会根据 Desired State 被更新。

完成 TransitionToState 宏后,我们可以继续 ShowMainMenuEvent 自定义事件。

上图中,IsValid 节点(1)执行,它会检查 Main Menu 变量是否有效,第一次运行该变量会无效(如果有效的话则跳过图中的(2)和(3))。步骤(2)调用了 Create Widget 节点来创建一个叫做 Main Menu 的 UMG 控件蓝图,并将结果作为一个变量(3)保存,这样便能在以后的脚本中直接访问而无需重新创建。在(4)中 Set Input Mode UIOnly 节点用来限制输入仅被 UI 接收,在(5)中则将 Main Menu 添加到窗口并显示出来。

然后我们来看一下玩家点击 Play 来创建主机会发生什么。

创建一个游戏

在 Main Menu 加载后,可以在 MainMenu 控件中点击 Play 按钮,然后会运行下图中的脚本。

在 MainMenu 的控件蓝图中的 Designer 分页上,可以为 "Play" 按钮创建一个 按钮 事件关联到 OnClicked 事件上。 当这个事件触发时,获取 game instance 并 转换 为 MyGameInstance 蓝图,这样就能调用它内部的自定义事件 Host Game Event 了。

HostGameEvent 首先运行了一个 MyGameInstance 蓝图内的自定义事件:ShowLoadingScreen

当 ShowLoadingScreen 被调用时,先运行了 TransitionToState 宏(并将 Desired State 设置为 Loading Screen)。

然后继续 ShowLoadingScreen 事件,调用了 IsValid 的检查,如下图(1)部分。

IsValid 检查变量 Loading Widget 是否有效,第一次运行是无效的(如果有效则跳过步骤(2)和(3))。步骤(2)创建了 Loading Screen 的 UMG 控件蓝图,并将结果保存到变量中(3),一边将来直接访问而无需重新创建。在步骤(4)中,Loading Widget 被添加到窗口中,并调用 Set Input Mode UIOnly 在加载过程中将输入限制到 UI 上(这是会在游戏中显示)。

当 Loading Screen 显示时,脚本就回到了 HostGameEvent 上继续执行 Create Session 的节点。

在 Create Session 节点上,Public Connections 的数量(允许玩家加入该游戏 Session)设置为 4。还有一个 布尔 变量叫做 Enable LAN 直接连到了 Use LAN 的输入上。 Enable LAN 变量是在主菜单上的 Play Mode 来切换的,我们在这个文档稍后再来讨论。如果 Session 被成功创建的话,则调用 Open Level 节点来加载地图并准备用来开始这个 Session 的游戏。 如果失败的话,则执行 OnFailure 的部分,这里会运行一个预先创建的宏,叫做 DisplayErrorDialog

DisplayErrorDialog 宏

调用 DisplayErrorDialog 宏时,先会执行 (1)TransitionToState 宏 切换到 Error Dialog 状态中。 当切换到新状态后,执行自定义事件 Destroy Session Caller (2) 来销毁当前玩家的 Session(可以在 Event Graph 中找到)。 在 Session 销毁后,调用引擎的宏 IsValid 来检查 Error Dialog 这个变量(这是一个 Error Dialog 的控件蓝图)是否有效。

在上面的图第一次运行时,Error Dialog 是无效的,如果有效的话,下图的(1)和(2)会跳过直接进入(3)。

第一次运行是,(1)将使用 Create Widget 创建一个 ErrorDialog 控件蓝图。(2)中将该控件蓝图记录为 Error Dialog 的变量(以便该宏下次被调用时直接使用)。 在(3)中,执行函数 Set Message 将输入的错误信息(下图黄色高亮框)设置到 ErrorDialog 上,并调用 Add to Viewport 将 Error Dialog 控件蓝图显示在屏幕上(5), 以及使用 Set Input Mode UIOnly 来限定输入仅被 UI 获取。

如果没有错误的话,在 Open Level 节点中定义的地图则会加载,玩家进入 游戏

加入一个游戏

在主惨淡中,点击 Find Games 按钮会执行 MainMenu 控件蓝图中的相应脚本,见下图:

在 MainMenu 的控件蓝图中的 Designer 分页上,可以为 Find games" 按钮创建一个 按钮 事件关联到 OnClicked 事件上。 当这个事件触发时,获取 game instance 并 转换 为 MyGameInstance 蓝图,这样就能调用它内部的自定义事件 Show Server List Event 了。

ShowServerListEvent 首先运行 TransitionToState 宏(1)将游戏状态设置为 Server List。 然后用 IsValid(2)检查 Server List 控件蓝图变量,如果有效的话就用 Add to Viewport(5)节点添加到屏幕上显示。 第一次运行时,Server List 需要先被创建一次,通过 Create Widget(3)节点,并将结果保存到变量(4)中,然后再显示到屏幕上。 当显示到屏幕后,再用 Set Input Mode UIOnly 节点限制输入设置给 UI。

在 Server List 菜单显示的时候,在上述的步骤(5)时,步骤(6)前,ServerList 控件蓝图内还有一段脚本会被执行,这里会更新当前可用的游戏到 Server List 上。可以在 Server List 控件蓝图中看到,如下图。 Event Construct 节点会在该控件蓝图被构造(创建)时调用,并立刻执行了一段预先设置的 RefreshListMacro 宏。

RefreshListMacro 宏

RefreshListMacro 宏干了不少事情。下面先解释第一部分。

如上,当宏被调用时,首先它先设置了一个名为 Refresh Button Enabled 的布尔变量为 False(1),这会禁用 Refresh 按钮,防止玩家点击。 然后一个叫做 Status Text 的变量设置为 Searching...(2)并将它的 Visibility 设置为 Visible(3),就能更新控件并显示这些文字。

在(4)的位置,有一个叫做 Found Session Widgets 的数组变量,它是一组 ServerRow 的空间用来刷新 Server List。对每个当前存在的元素调用 Remove Child(5)节点。 这将会先删除数组中所有的内容。在(6)的位置,Found Session Widgets 变量再次被清理以消除先前保存过的 Session,这样在继续进一步查找前先把所有的东西清理干净。

然后,该宏获取 game instance 并调用 Cast To MyGameIntance 节点(下图 1 处)来访问 Enable LAN 的值(在查询 session 时会用到)

Find Session 节点(2)用来查询当前存在的 session(会作为返回值输出)。在这个节点中,还可以设置 Max Results(返回查询结果的数量)。 Find Sessions 的返回结果保存在 Found Sessions 数组变量中(3)。这里会有一个 Branch(4)来检查结果,如果是 0 的话, 继续 True 的输出(如果不是的话则作为 False 输出)。如果 Find Sessions 的节点执行时出了什么错误,则会触发 OnFailure 输出,并修改 Status Text 显示为 Search failed(5) 通知用户查找 session 并未完成。

在 Branch 后(上图中的 4),如果是 True(也就是并未找到任何 session),Status Text 会被设置为 No sessions found(下图中的 7), 并且 Refresh Button Enabled 变量被设置为 True(8),这样玩家便能点击 Refresh 按钮以再次查找游戏。

如果有 Session 找到,Status Text Visibility 被设置为 Collapsed(1)便能将该控件隐藏掉。然后用了 ForEachLoop 节点(2)来处理找到的每个 result, 并使用 Create ServerRow 节点(3)为每个结果创建一个 Server Row 控件。这就是 Server List 里每个独立的行项。

在(4)的位置,Results 也被添加到 Found Session Widgets 数组里,然后再调用 ServerRow 控件蓝图(用于生成服务器名称,玩家数量和 Ping 值)中的 Set Serach Result 函数。 在设置完 ServerRow 控件蓝图的内容后, Add Child 节点(6) 用来给每个 ServerRow 控件设置内容,并设置 Scrolling Servers 显示在 Server List 菜单中。

将每个找到的 Session 都有了 ServerRow 控件创建后,把它们添加到 Scrolling Servers 控件上,ForEachLoop 完成,并将 Refresh Button Enabled 变量设置为 True(8)。

在运行了 RefreshListMacro 搜索 Session 后,任何找到的可用 Session 将会显示在 Server List 的菜单中。

点击 Refresh 按钮将会再次运行 RefreshListMacro 来搜寻 Session。点击 Back 按钮将会执行自定义事件 ShowMainMenuEvent(这在 开始 章节有说明)。

在服务器列表中点击其中一个服务器将会触发在 ServerRow 控件蓝图中的 OnClicked 事件(见下图)。

ServerRow 控件的 OnClicked 事件,先获取了 game instance,并使用 Cast To MyGameInstance 节点来进一步调用它的 Join from Server List Event 事件。 ServerResult 变量(由 RefreshListMacro 的过程中生成)被传入 MyGameInstance 的蓝图,并让玩家点击时加入服务器。

在 MyGameInstance 的蓝图中的 JoinFromServerListEvent(下图)被调用时,先运行自定义事件 ShowLoadingScreen,在 创建一个游戏 章节作了描述。

当 LoadingScreen 显示的时候,Join Session 节点会尝试加入玩家当前点击的由 ServerRow 控件提供的 SearchResult。 如果成功的话,玩家就能加入该服务器,并在游戏中生成角色。如果有错误的话,就会执行 DisplayErrorDialog 并显示 Failed To Join Session 为 错误信息

游戏性

在玩家控制角色前,角色和游戏状态都需要被设置好(或者,比如要加入一个游戏时,游戏自身需要得到更新,并被告知有新玩家要加入进来)。 当点击主菜单的 Play 按钮,或者从 Server List 中选择一个服务器加入时,首先要调用 GameMode 蓝图中的 Post Login 事件,这里将会启动下图所示的设置过程。

当 Event Post Login 触发后,先运行了一个引擎的宏,叫做 Switch Has Authority(1),这里会检查脚本是否在一个具有网络权威性(Network Authority)(在服务器上)的地方运行还是在一台远程机器(一个客户端)上运行。 这个过程会在服务端为主机玩家和加入游戏的客户端运行,Remote 的输出并不作任何事情,而 Authority 的输出会继续执行 Post Login 的脚本。

还有一个关于 Switch Has Authority 的示例,请查看 在蓝图中检测网络主机和远程客户端 页面。

在(2)处,新玩家的 PlayerState 被 转换为 Cast MyPlayerState 蓝图,这里会设置 Player Number(3)用来保证玩家记分牌中的顺序和玩家顺序一致(每个新玩家的加入,都会添加到记分牌中)。 在设置 Player Number 后,New Player 被 转换 Cast 成 MyPlayerController 蓝图,用于调用 ClientPostLogin 这个自定义事件。

如图,ClientPostLogin 事件的明细面板中还有一些属性设置。

在 Graph 区块下,Replicates 选项设置为 Run in owning Client 并且勾选了 Reliable 框。通过这两个选项,我们是在服务端运行的这段脚本,但该节点则仅在当前所属权的客户端上执行。 Reliable 的设置保证了这个脚本一定会被客户端执行到,而不会因为严重的网络负载而丢弃(大部分处理一些视觉效果同步复制的远程调用都是 Unreliable 来避免过多的消耗网络负载,在这个例子下,我们需要该事件必须执行,因此使用 Reliable)。

确认了脚本应该在何处触发后,Setup Ingame UI 函数就能进一步调用了。

这个函数以 Branch(1)开始,检查当前的目标是否是本地玩家的 PlayerController,如果是的话,则使用 Create Widget(2)节点创建 HUD 控件蓝图。 然后将 HUD 控件保存到一个叫做 HUD Widget 的变量(3)中,并 Add to Viewport(4)来把它添加到玩家窗口中。在(5)处,再用一个 Create Widget 节点创建 InGame Menu 控件蓝图, 并保存到 InGameMenuWidget 变量(6)中,这会在玩家点击按钮调用游戏内菜单时显示。

到这里,"login" 的过程就完成了。角色的设置在 Character Animation Blueprint 中,定义了角色的每个动作,这部分内容这里并不涵盖,如果想了解的话需要去浏览 Animation Blueprints, 文档在 动画蓝图 这里。

在 Character Animation Blueprint 设置后,关卡 Level_01 就打开了,在这个地图的 关卡蓝图 中,

上图中的 Event Begin Play 是这个地图的事件,调用了 Cast To MyGameInstance 节点,并调用 Start Playing State 函数。这是一个 MyGameInstance 蓝图内的函数,用于将 game state 设置为 Playing,如下图所示。

在 state 被设置为 Player 后,通过调用 Set Focus To Game Viewport 的节点(上图中的(3)),鼠标会被锁定在游戏窗口内,并调用 Set Input Mode Game Only 节点(上图的(4))使 UI 不再截获用户输入。

从游戏中离开

从一个游戏中断开的过程非常简单直接,只需要关闭一些 UMG 控件的显示,并执行第一次加载游戏进入主菜单同样的过程即可。

在游戏中按 Q 键后,则能看到游戏内的暂停菜单,点击 Leave Game 选项,则执行在 InGameMenu 控件蓝图中的以下步骤:

在上图中,点击了 Leave 按钮后触发了这个事件(1)接着 通过获取到当前玩家并 转换 Cast 成 MyPlayerController 的蓝图(2)。在 MyPlayerController 蓝图内的函数 Hide in Game Menu(3)如图这样被调用。

这部分脚本拿到 In Game Menu Widget 变量(先前创建并保存的变量),并使用 Remove From Parent 节点来将它从屏幕上移除。 然后,调用 Set Input Mode Game Only 来防止玩家操作其他 UI,一直到我们再打开 UI 的输入为止。

然后脚本返回到 InGameMenu 的控件蓝图上,并使用 Cast To MyGameInstance 节点(4)来得到当前 game instance 并调用它内部的函数 Show Main Menu Event, 接下去先执行 IsCurrentState 宏,它之后的蓝图内容就是 开始 章节中加载游戏的部分。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

多人连线的枪战游戏 的相关文章

  • Electron桌面程序开发入门

    1 Electron结合vue项目配置 Electron是利用web前端技术进行桌面应用开发的一套框架 是由 github 开发的开源框架 允许开发者使用 Web 技术构建跨平台的桌面应用 它的基本结构 Electron Chromium
  • Vuluhub靶场-breach1

    网络设置和准备 该靶场的ip 192 168 110 140 我们要设置为仅主机模式 在虚拟机中将仅主机模式的ip地址范围包含靶机的ip 除了网络设置 还要准备两台kali 一台连接外网 一台和靶机一样要仅主机模式 信息收集 Nmap扫描
  • lvgl 自定义控制表格行高、颜色和外框样式

    lvgl 自定义控制表格行高 颜色和外框样式 lvgl版本 8 3 7 lvgl自带表格控件能够指定列宽 但是表格行高是根据内容动态渲染的 表格自带样式如图 带有蓝色的外框和白底 如果想要手动控制表格行高 颜色和外框等属性 需要监听表格绘制
  • 国产加密实际运用:使用SM3加盐存储密码,并且使用SM2进行登录认证

    目录 1 简要 2 开发环境及工具 3 后台密码加密部分 3 1加密代码 3 2 SM3加密类 Sm3crypto 3 3国密SM3工具类 Sm3Utils 3 4国密相关依赖包 4 登录认证部分 4 1前端部分关键代码 4 2后端logi
  • 查看tensorflow是否支持GPU,以及测试程序

    测试程序 Python import tensorflow as tf hello tf constant Hello TensorFlow sess tf Session print sess run hello 是否支持GPU impo
  • 【新手入门篇】React+ant design

    本篇着重讲解如何使用官方的demo 至于React及antd的安装及配置在本文末尾会给出相应的参考链接 创建一个React项目之后 create react app 你的项目名 在新建的项目目录下引入antd组件库 yarn add ant

随机推荐

  • Ubuntu 23.10 支持基于 TPM 的全磁盘加密

    将于下个月发布的 Ubuntu 23 10 增加了一项实验性功能 初步支持基于 TPM 的全磁盘加密 该功能利用系统的可信平台模块 TPM 缺点是这种额外的安全性依赖于 Snaps 包括内核和 GRUB 引导加载器 Ubuntu 开发商 C
  • 输出该单链表的中间结点的值,如果链表长度为偶数,则输出中间靠右的结点

    输出该单链表的中间结点的值 如果链表长度为偶数 则输出中间靠右的结点 题目要求 输入数据创建一个单链表 实现一种算法 输出该单链表的中间结点的值 如果链表长度为偶数 则输出 中间靠右 的结点 如果链表只有一个元素 则输出唯一的元素 算法思路
  • 【华为机试真题 JAVA】水果搬运问题-200

    题目描述 一组工人搬运一批水果 用一维数组存储工人编号和水果名称以及搬运重量 要求先按水果分组 然后按搬运重量排序输出 输入描述 第一行包括一个整数 N 1 N 100 代表工人的个数 接下来的 N 行每行包括两个整数 p 和 q 分别代表
  • 关于STM32的SPI使用DAM首发的回调问题

    本人第一次使用HAL库 然后用SPI操作FLAH 担心数据量大 于是打算使用DMA 之前是用的LL库 然后发现了一个问题 SPI怎么都接收不到数据 想了一下应该是片选引脚的问题 我应该在DMA传输结束时关闭引脚 但是之前都是用LL库 判断标
  • spring无侵入自动生成接口文档

    背景 spring cloud多个微服务开发了很多接口 紧急对接前端 需要快速提供一批接口的文档 且不同微服务的接口由多位同事开发且注释非常的少各有不同 现在需要不修改代码不添加注释的情况下能自动的扫描接口并生成文档 本文将详细介绍实现此需
  • X264的参考帧设置

    1 以r1884为例 r ref lt 整数 gt Reference Frame 即参考帧 决定最多可能的参考帧数 有效范围值1 16 该值越大 压缩率越高 但大于6后对压缩率的贡献很低 可以看压制完后x264 info ref 项 例如
  • sqlserver 登录名和用户名

    解释 登录名 通俗的讲 平时连接数据库是用的就是登录名 而不是用户名 是数据库服务级别 登录数据库之后 这个登录名有什么权限 比如可以访问那个数据库 或者表 存储过程 视图等 甚至字段权限 是有与之对应的用户 用户名 决定 注 也可以从服务
  • 手风琴(折叠面板)

    目录 一 Layui手风琴 1 1 引用layui的css和js 1 2 开启手风琴的代码示例 1 3 静态数据 1 4 最终效果图 二 Bootstrap手风琴 2 1 引用Bootstrap的css和js 2 2 开启手风琴的代码示例
  • Python 第一章 基础知识(6) 函数

    函数就像可以用来实现特定功能的小程序一样 Python的很多函数都能做很奇妙的事情 先来介绍一个内建函数 即是Python自带的已经定义好的函数 可以直接用 gt gt gt pow 2 3 8 这个函数实现了2 2 2的算法 这种使用函数
  • Angular 中 web worker的使用

    web worker就是在web应用程序中使用的worker 这个worker是独立于web主线程的 在后台运行的线程 web worker的优点就是可以将工作交给独立的其他线程去做 这样就不会阻塞主线程 第一步 ng g webWorke
  • 快速生成26个英文字母

    在学习中经常会拿26个英文字母序列做为字符串的例子来说明 但是自己又不想每次都自己手动输入 所以就想写个方法能快速的生成这个字符串 generate 26 english Characters return void public stat
  • C# 9.0:Records

    转自 翁智华 cnblogs com wzh2010 p 13950647 html 概述 在C 9 0下record是一个关键字 微软官方目前暂时将它翻译为记录类型 传统面向对象的编程的核心思想是一个对象有着唯一标识 封装着随时可变的状态
  • JenKins结合cppcheck及cpplint进行代码风格及静态代码检测

    JenKins结合cppcheck及cpplint 最近公司需要在Jenkins上安装cppcheck及cpplint进行代码风格及静态代码检测 这里记录下过程 前提条件 安装了Jenkins 步骤如下 第一步 安装cppcheck并配置环
  • [Linux] yum和apt-get用法及区别

    一般来说著名的linux系统基本上分两大类 1 RedHat系列 Redhat Centos Fedora等 2 Debian系列 Debian Ubuntu等 RedHat 系列 1 常见的安装包格式 rpm包 安装rpm包的命令是 rp
  • vs2019调试时中文乱码解决办法

    vs2013 vs2019系列文章目录 文章目录 vs2013 vs2019系列文章目录 问题描述 一 解决 解决方法1 在我机器上仍然未解决 解决方法2 在我机器上可行 调试时中文显示效果 问题描述 vs2019调试时中文乱码 但是在vs
  • except Exception as e作用

    小记 今天在查看poc时 发现这段代码不理解 所以B站搜索了一下 把别人讲的内容爬了一下 coding utf 8 a int input 请输入数字0 try if a 0 print a except Exception as e 作用
  • Redis高可用集群(哨兵、集群)

    文章目录 前言 一 主从复制 1 1 主从复制的作用 1 2 主从复制流程 1 3 主从复制搭建 二 哨兵模式 2 1 哨兵模式的作用 2 2 哨兵结构的组成 2 3 故障转移机制 2 4 哨兵模式搭建 三 集群模式 3 1 集群的作用 3
  • shell 脚本调试工具

    bashdb 是一个类似GDB的脚本调试软件 具有断点 单步执行 观察变量等功能 安装方法 sudo apt get install bashdb bashdb 使用方法 bashdb options script name script
  • vue element-ui el-table表格二次封装 自定义el-table表格组件 vue封装表格组件

    CommTable vue table组件
  • 多人连线的枪战游戏

    Simple Blueprint Multiplayer 是一个完全由 蓝图 和 UMG 界面 编写的游戏 可以作为如何使用蓝图的 Session Nodes 打造游戏中的多人部分的使用示例 这里有一个主菜单 一个服务器列表 以及一个简单的