当前位置: 首页 > news >正文

UE4_AI_行为树_行为树快速入门指南

声明:学习笔记。

在 行为树快速入门指南 中,你将学会如何创建一个敌方AI,该AI看到玩家后会做出反应并展开追逐。当玩家离开视线后,AI将在几秒钟后(这可根据你的需求进行调整)放弃追逐,并在场景中随机移动,再次看到玩家时便会继续追逐。

阅读完本指南后,你将了解以下系统: 

  1. 蓝图可视化脚本(Blueprint Visual Scripting)

  2. AI控制器(AI Controllers)

  3. 黑板(Blackboard)

  4. 行为树(Behavior Trees)

  5. 行为树服务节点(Behavior Tree Services)

  6. 行为树装饰器节点(Behavior Tree Decorators)

  7. 行为树任务节点(Behavior Tree Tasks)

  • 操作步骤

  • 一、我们将用需要的资产来设置项目,使AI角色在场景中活动。 

1、新建第三人称模板游戏项目 

2、打开面板,右键单击 ThirdPersonBP 文件夹,创建一个名为 AI 的 新文件夹

3、在 ThirdPersonBP > Blueprints 文件夹中,将 ThirdPersonCharacter 拖到 AI 文件夹上,然后选择 复制到此处(Copy Here)

4、在 AI 文件夹中,基于 AIController 类创建一个新的 蓝图类。将 AIController 蓝图命名为 Enemy_Controller,将 ThirdPersonCharacter 蓝图命名为 Enemy_Character

5、打开 Enemy_Character,从图表中删除所有脚本。选择 角色移动(Character Movement) 组件,然后在 细节(Details) 面板中将 最大行走速度(Max Walk Speed) 设置为 120.0

当AI角色(AI Character)进行巡逻且并未追逐玩家时,这会降低它们在场景中的移动速度。

6、从工具栏中选择 默认类(Class Defaults),然后在 细节(Details) 面板中,将 Enemy_Controller 分配为 AI控制器类(AI Controller Class)

我们将会把AI放入场景,如果要在该场景加载后生成AI,则需要把 自动控制AI(Auto Possess AI) 设置改为 已生成(Spawned)

7、将 Enemy_Character 从 内容浏览器拖入关卡。 在 放置Actor(Place Actors) 面板中,把 导航网格体边界体积(Nav Mesh Bounds Volume) 拖入关卡。

8、选中 寻路网格体边界体积(Nav Mesh Bounds Volume) 后,按下 R 缩放体积,封装整个关卡。

这将生成寻路网格体,使我们的AI角色能够在场景中移动。你可以按下 P 键打开或关闭视口中寻路网格体的显示(绿色区域表示可导航的位置)。

二、黑板设置

我们将创建 黑板 资产,它本质上是AI的大脑。我们希望AI知道的任何信息都会有一个能够引用的 黑板键。我们将创建用于玩家的键,用于控制AI是否能看到玩家,以及AI不追逐玩家时可以移动到的位置

1、在 AI(Artificial Intelligence) 下,选择 黑板(Blackboard) 并将其命名为 BB_Enemy

2、双击打开黑板,单击 创建新键(New Key) 按钮并选择 Object黑板 资产由两个面板组成,黑板(Blackboard) 使你能够添加并对 黑板键(要监控的变量)进行跟踪,通过 黑板细节(Blackboard Details) 能够命名和指定键的类型。

3、针对 Object 键,输入 EnemyActor 作为 条目名称,输入 Actor 作为 基类

4、添加另一个 , 将 键类型 设置为 布尔,并命名为 HasLineOfSight。这将用于记录AI是否能看到玩家。

5、添加另一个 键(Key), 将 键类型 设置为 向量,并命名为 巡逻位置(PatrolLocation)

这将被用来记录AI不追逐玩家时可以移动到的关卡位置。

在 黑板 上设置好需要跟踪的内容后,下一步我们将对我们的 行为树 进行布局。

三、行为树布局

通过在 行为树 中布局出AI可能进入的状态,有助于你了解进入这些状态需要创建何种类型的逻辑和规则。

1、在 内容浏览器 中,单击 新增(Add New),在 AI(Artificial Intelligence) 下,选择 行为树(Behavior Tree) 并将其命名为 BT_Enemy

2、打开 BT_Enemy ,并将 BB_Enemy 指定为 黑板资产

行为树 由三个面板组成:可以在 行为树(Behavior Tree) 图表面板直观地布局定义行为的分支和节点;可以在 细节(Details) 面板定义节点的属性;可以在 黑板(Blackboard) 面板看到游戏运行时的 黑板键 及其当前值,这对调试非常有用。

3、在图表中,单击鼠标左键并拖出 根(Root) 节点,然后添加一个 选择器(Selector) 节点。

合成(Composites) 节点是流控制的一种形式,决定了与其相连的子分支的执行方式。

4、针对 选择器 节点,在细节(Details)面板中将 节点名称(Node Name) 更改为 AI Root。 

重命名图表中的节点是一个很好的方法,可以很容易地从较高层面识别节点所完成的操作。在本例中,我们将其命名为 AI Root,因为这是行为树真正的"根",它将在子分支之间切换。创建行为树时自动添加的默认根节点,可用于配置行为树的属性并指定它所使用的黑板资产。 

5、单击左键并拖出 AI Root,添加一个名为 Chase Player 的 序列 节点。

此处使用了序列节点,因为我们打算告诉AI完成一系列动作:旋转朝向玩家,改变移动速度,然后移动并追逐玩家。

6、单击左键并拖出 AI Root,添加一个名为 Patrol 的 序列 节点

对于AI,我们将使用 序列 节点在地图中找到一个随机位置,移动到该位置,然后在该位置等待一段时间,最后再重复该过程来寻找新位置。

你可能还会注意到节点右上角的数字,它表示操作的顺序。行为树 会从左到右和自上而下执行,因此节点的排列很重要。对AI最重要的动作通常应该放在左边,而次要的动作(或退却行为)应该放在右边。子分支会以相同的方式执行,如果任何子分支失败,整个分支将会停止执行,导致失败并返回上级树。举例而言,如果 追逐玩家(Chase Player) 节点失败,它将返回至上级 AI根(AI Root),然后转变为 巡逻 节点。

7、从 AI Root 连出引线,然后在 巡逻(Patrol) 右侧添加 等待(Wait) 任务节点,并将 等待时间(Wait Time) 设置为 1.0

你可以看到这个节点是紫色的,表示它是一个 任务 节点。任务节点是你希望 行为树 执行的具体操作。如果某种原因使 行为树 让Chase Player或Patrol节点皆运行失败,那么 等待(Wait)任务节点将接管事件。

8、从Chase Player 追逐玩家 节点连出引线并添加 旋转至面向黑板条目(Rotate to Face BBEntry) 节点。 

这个特殊的 任务 节点使你能够指定一个想要旋转并面向的 黑板条目,在本例中此条目是 Enemy Actor(玩家)。添加该节点后,你会在 细节(Details) 面板中看到 黑板键 将自动设为 EnemyActor,因为它过滤Actor的黑板变量,而且是列表中的第一个变量。如果需要调整成功条件范围并更改 节点名称(Node Name),可对 精度(Precision) 选项进行调整。

这个未设置,会导致

选项中缺少EnemyActor选项。

9、在 工具栏 中,单击 新建任务(New Task) 按钮。

除使用内置任务节点外,你还可以创建和指定自己的自定义任务节点,可以为这些节点自定义并定义附加逻辑。该任务节点将用于改变AI的移动速度,以便它追逐玩家。新建任务节点时,将自动创建并打开新的 蓝图

10、在 内容浏览器 中,将该新资产重命名为 BTT _ChasePlayer

创建 任务(Tasks)装饰器(Decorators) 或 服务(Service) 节点时,建议立即对这些新建节点进行重命名。正确的命名规则是用创建的资产类型为资产命名添加前缀:BTT 代表行为树任务节点,BTD 代表行为树装饰器节点,BTS 代表行为树服务节点。 

11、在 BT_Enemy 中,添加 BTT_ChasePlayer 任务,随后添加 移动至(Move To)。 

我们的新任务目前还没有逻辑,但随后将返回来添加改变AI角色移动速度的逻辑,之后AI将移动至(Move To)EnemyActor(玩家)。

12、创建一个新 任务节点,并将其重命名为 BTT_FindRandomPatrol,然后将其连接到 巡逻(Patrol) 节点。 

13、添加 移动至(Move To) 任务节点,并将 黑板键 设为 巡逻位置(PatrolLocation)

 

这将指示AI移动至(Move To)巡逻位置(PatrolLocation),该位置将在BTT_FindRandomPatrol任务中进行设置。 

14、在 移动至(Move To) 后添加 等待(Wait) 任务节点,设置 等待时间(Wait Time) 为 4.0随机偏差(Random Deviation) 为 1.0。 

这会指示AI在巡逻位置(PatrolLocation)等待3-5秒(随机偏差将在等待时间上+或-1秒)。

我们已经完成了 行为树 的框架。在下一步中,我们将添加AI在巡逻时找到一个随机的导航位置后改变其移动速度的逻辑,以及确定AI应该在什么时候追逐玩家或进行巡逻的逻辑。

四、任务设置 - 追逐玩家

在这一步中,我们设置了 追逐玩家任务(Chase Player Task),改变AI追逐玩家时的移动速度。

1、在 BTT_ChasePlayer 中点击右键并添加 Event Receive Execute AI 节点。

当在 行为树 中激活此任务时将触发Event Receive Execute AI节点。

如果代理是AI控制器,则应该固定选择AI版本的 Event Receive ExecuteEvent Receive Abort 和 Event Receive Tick。如果泛型和AI事件版本都已实现,则只会调用更合适的版本,这意味着会针对AI调用AI版本,其他情况下会调用泛型版本。

2、Controlled Pawn 引脚后连接 Cast to Enemy_Character 节点。

在此,我们将使用 Cast 节点来访问名为 Enemy_Character 的AI 角色蓝图

3、在 内容浏览器中,打开 Enemy_Character 蓝图,并添加一个名为 更新行走速度(Update Walk Speed) 的 函数

该函数将从我们的行为树中调用,并将用于修改AI的移动速度。

从技术角度而言,我们可以在追逐玩家任务(Chase Player Task)中访问Cast节点之后的 角色移动(Character Movement) 组件,并在任务节点中调整移动速度,但让行为树直接改变子对象的属性并不是推荐的最佳做法。相反,我们可以让行为树在角色内部调用一个函数,然后进行所需的修改。 

4、在 更新行走速度(Update Walk Speed) 函数的 细节(Details) 面板中,添加名为 新行走速度(NewWalkSpeed) 的 浮点 输入。 

5、将 角色移动(CharacterMovement) 组件拖离"组件(Component)"选项卡,然后使用 设置最大行走速度(Set Max Walk Speed) 进行如下连接。

从行为树调用这个函数时,我们可以传递一个数值作为新的速度。

6、回到 BTT_ChasePlayer 任务节点内,从 作为敌人角色(As Enemy Character) 节点调用 更新行走速度(Update Walk Speed) 函数并设置为 500.0,然后按下图进行连接。

如果看不到已创建的 更新行走速度(Update Walk Speed) 函数?你可能需要先 编译 Enemy_Character 蓝图,然后才能将其添加到 追逐玩家任务(Chase Player Task) 中。

7、在 更新行走速度(Update Walk Speed) 之后,添加两个 结束执行(Finish Execute) 节点,并按下图进行连接。

我们成功将其投射到Enemy_Character后,我们在此将该任务标记为成功结束。如果被控制的Pawn不是Enemy_Character,我们需要处理这种情况,所以我们将任务标记为不成功,将任务中止。

8、右键单击 新行走速度(New Walk Speed) 引脚,然后将其提升为变量,并命名为 追逐速度(ChaseSpeed)

9、必须启用 追逐速度(ChaseSpeed) 的 实例可编辑(Instance Editable)

将 最大行走速度(Max Walk Speed) 的数值提升为 实例可编辑(Instance Editable) 变量后,我们便可以从蓝图外部对其进行设置,并将该值作为 行为树 的内部属性进行调用。

我们现在可以很轻松地改变被发送至 Enemy_Character 蓝图的 追逐速度(Chase Speed) 数值,以便调整AI追逐玩家的速度。

我们完成了 追逐玩家任务(Chase Player Task),下一步我们将设置 寻找随机巡逻任务(Find Random Patrol Task) 逻辑,让AI获得可前往的随机位置。 

五、任务设置 - 寻找随机巡逻

在这一步中,我们将设置 寻找随机巡逻任务(Find Random Patrol Task),AI没有追逐玩家时就会移动到一个随机位置。

应用蓝图行为树任务节点是一种快速迭代的明智方法。但是如果需要考虑性能问题,那么你可以决定转移至本地行为树任务节点。

1、在 BTT_FindRandomPatrol 中,使用 Event Receive Execute AI 和 Cast to Enemy_Character

2、在 作为敌人角色(As Enemy Character 之后调用 更新行走速度(Update Walk Speed),并将 新行走速度(New Walk Speed) 提升为一个变量,命名为 巡逻速度(Patrol Speed),设置步骤如下:

勾选可编辑实例,默认值125.我们在此处降低了敌人巡逻时的移动速度。

3、在 Controlled Pawn 之后连出引线,连接Get Actor Location,然后设置 GetRandomReachablePointInRadius,其 返回数值 需连接至一个 分支蓝图节点。

4、使用以下设置将 GetRandomReachablePointInRadius 上的 半径 提升为变量PatrolRadius,勾选可编辑实例,编译后,默认值设为1000.

此处我们在敌人角色当前位置的1000个单位内寻找一个随机位置。我们还要使用分支节点,处理无法找到要移动到的随机点这种极端情况。

5、在 随机位置(Random Location) 引脚后连出引线,使用 设置黑板数值作为向量(Set Blackboard Value as Vector),并将 键(Key) 提升为名为 巡逻位置(PatrolLocation) 的变量。

6、使用另一个 设置黑板数值作为向量(Set Blackboard Value as Vector) 节点,设置该 数值 的来源为 Get Actor Location

7、继续上一步,如下所示连接两个节点,使得 结束执行(Finish Execute) 被标记为 成功(Success)

如果敌人找到了一个要前往的随机位置,它将作为要前往的位置被存储在黑板中。如果找不到位置,它将使用当前位置,并在尝试新位置之前保持不动。我们还需要对受控Pawn不是Enemy_Character的极端情况进行处理。

8、从 Cast 节点的 Cast Failed 引脚连出引线,使用 结束执行(Finish Execute)节点,将 成功(Success) 禁用。

如果受控Pawn不是Enemy_Character,此任务将被标记为不成功并被中止。

我们已完成 寻找随机巡逻任务(Find Random Patrol Task)。在下一步中,我们将学习更多关于 装饰器节点 的知识,以及它们如何被用作条件语句、如何设置AI控制器。

六、AI控制器设置

在这一步中,我们将在AI控制器内部进行设置,为最后一步做准备,设置一个 装饰器节点 来决定进入 行为树 的哪个分支。 

1、在 内容浏览器 中打开 Enemy_Controller 蓝图,添加一个 Event On Possess 节点

2、从 Event On Possess 连出引线,添加一个 运行行为树(Run Behavior Tree) 节点,将其 BTAsset 设为 BT_Enemy

运行行为树 节点是以AI控制器类蓝图为目标的情境函数调用,使你能够执行指定的 行为树 资产。

3、在 组件(Components) 窗口中单击 +添加组件(Add Component),搜索并添加 AI感知组件(AIPerception Component)

4、AI感知组件(AIPerception Component) 用于在 AI感知系统(AI Perception System) 中创建一个刺激监听器,收集可以响应的已注册刺激(本例中我们可以使用视觉)。这将使我们能够确定AI何时能实际看到玩家,并做出相应的反应。

使用 按归属检测检测(Detection by Affiliation) 属性可以建立基于团队的AI,与相同归属的队友并肩作战,攻击敌方成员。默认情况下 Actor 不会被指定归属,会被视为中立。

目前,你还不能通过蓝图分配归属,因此为了检测玩家,我们要启用 检测中立方(Detect Neutral) 标签。另外一种方法是使用 Actor标签(Actor Tagging) 来确定哪个角色是玩家,并强制AI角色只追逐被标记为玩家的Actor。

5、在 AI感知(AIPerception) 的 事件(Events) 部分,点击 On Target Perception Updated 旁边的 + 符号。

6、在图表中 On Target Perception Updated 后连出引线,添加一个 Actor Has Tag 节点,将 Tag 设为 玩家。 

7、从 刺激(Stimulus) 引脚,添加 Break AIStimulus 节点。

8、添加 分支 节点,将其 条件(Condition) 按照下图进行设置。

此处我们检查该Actor是否被成功感知,以及该Actor是否拥有玩家标签。 你可以选择 Break AIStimulus 节点,在 细节(Details) 面板中,使用 隐藏未连接的引脚(Hide Unconnected Pins),隐藏所有未连接的引脚,使你的图表看起来与上图相似。

9、从该 分支 的 False 连出引线,使用 按事件设置定时器(Set Timer by Event),将其 时间(Time)设为 4.0

10、右键单击将 时间(Time) 提升为变量,并将其命名为 Line Of Sight Timer

该变量和指定的数值将决定AI在多长时间后将放弃追逐玩家,并在此时执行附加的事件。

11、右键单击 按事件设置定时器(Set Timer by Event) 的 返回数值(Return Value),将其提升为名为 敌人计时器(EnemyTimer) 的变量。

这通过 句柄(Handle) 的方式保存对定时器(Timer)的引用。该句柄可以通过脚本调用,从而使自身无效,并清除任何相关事件(防止相关事件被执行)。我们可以在稍后当AI在Line of Sight Timer耗尽之前再次看到玩家时使用该节点,这可以防止AI让玩家离开视野并放弃追逐。 

12、创建一个 自定义事件,并将其命名为 StartEnemyTimer,然后将其连接至 按事件设置定时器(Set Timer by Event) 的 事件(Event)引脚。

13、单击右键,然后在 变量 > AI 下,添加一个 获取黑板(Get Blackboard) 节点。

14、在 黑板(Blackboard) 后连出引线,使用 Set Value as Bool 和 Set Value as Object,并按如下所示进行连接。 

这使我们能够更新用新数值定义的 黑板键

15、右键单击并将两个 键名(Key Name) 分别提升为名为 HasLineOfSight 和 EnemyActor 的 变量。 

16、编译 蓝图,并将两个 键名(Key Name) 的 默认值 分别设置为 HasLineOfSight 和 EnemyActor。 

17、断开 分支 的 True,使用 Get EnemyTimer,然后使用 Clear and Invalidate Timer by Handle

当AI看到玩家时,它将清除Line Of Sight Timer,直到它再次让玩家离开视线(新的Line Of Sight Timer将从这里开始)。

18、复制并粘贴 黑板 节点,如图对 将数值设置为(Set Value as) 节点和 键名(Key Name) 节点进行设置。 

19、在 Set Value as Bool 节点上启用 Bool Value,并将 Actor**引脚拖至 Object Value**,如图所示。

这会把 Has Line Of Sight 的 黑板键数值(Blackboard Key Values) 设为 True,并把 EnemyActor 设置为被我们感知的 Actor(我们已将其设置为只有玩家才能使其触发)。

千万注意默认值为键值。

20、单击 编译(Compile) 进行编译,然后关闭蓝图。

七、装饰器及最终设置

在最后部分,我们会调整玩家角色和敌人角色蓝图上的一些设置。我们还会在 行为树 中设置 装饰器节点,它将根据特定条件决定我们可以进入哪个分支。

1、在 内容浏览器 中的 内容 > 第三人称蓝图 > 蓝图(Content > ThirdPersonBP > Blueprints)下,打开 第三人称角色(ThirdPersonCharacter) 蓝图。在 细节(Details) 面板中,查找并添加 标签,并设置为 玩家。 

2、打开 AI 文件夹中的 Enemy_Character 蓝图。 在 细节(Details) 面板中查找 旋转(Rotation) 并启用 使用控制器旋转Yaw(Use Controller Rotation Yaw)

这会使AI在从我们的 行为树 调用 旋转至面向黑板条目(Rotate to face BB entry) 时进行正确旋转。

3、打开 BT_Enemy,右键单击 追逐玩家(Chase Player),然后在 添加装饰器..(Add Decorator..) 下选择 黑板(Blackboard)。 

4、参数设置如下:

  • 将观察者终止(Observer aborts) 设为 两者(Both)**

  • 将 黑板键(Blackboard Key) 设为 HasLineOfSIght

  • 将 节点名称(Node Name) 设为 Has Line of Sight?

我们在此说明,当 HasLineOfSight 的数值为 已设置(Is Set) (或为true)时,会执行这个 追逐玩家(Chase Player) 分支。观察者终止(Observer aborts) 中设置为 两者(Both) 意味着当我们分配的 黑板键 改变时,中止本身(追逐玩家)和任何低优先级的任务。这意味着,当 HasLineOfSight 的数值改变且未设置时,其将中止自身(追逐玩家),此时将执行下一个分支(巡逻)。当 HasLineOfSight 数值再次变成 已设置(Is Set) 时,观察者将中止优先级较低的任务,并使 追逐玩家(Chase Player) 分支能够再次被执行。 

5、编译 并关闭 行为树,然后在编辑器中 运行

八、最终结果

你现在可以对AI进行测试,它看到你的时候便会尾随着你。

当你走出AI的视野后,它仍然会尝试追踪并重新让你进入视线。输入 Line Of Sight Timer 数值后,它将放弃追逐,返回巡逻状态。

你可以在运行时观察 行为树 的运行情况,看它如何在不同的分支之间切换。当你在进行游戏的时候,也可以在 行为树 中观察这些变量,以了解AI当前的数值。

九、自行设置!

现在你的AI角色将追逐玩家,如果在特定的时间之后没有看到玩家,它会返回巡逻状态,你可以尝试在 行为树 中添加以下元素!

  • 在 行为树 中,向两个 移动至(Move To) 任务节点添加新的 服务 节点,该服务集成了优先于它的 任务 节点的功能。

    • 我们最初的 行为树 使用了一个单独的 任务 节点调整移动速度(在追逐玩家的情况下)和寻找一个随机的巡逻位置并降低移动速度(在巡逻的情况下)。将这些转换为附加到 移动至任务(Move To Task) 的 服务 节点。

    • 提示:服务 节点中的脚本类似于每个 任务 节点中的脚本,但是你最好使用 Event Receive Activation AI,而不使用 Event Receive Execute AI。因为它们是 服务 节点,所以你也不需要 结束执行(Finish Execute)** 节点。

  • 不要使用随机巡逻点来移动。创建一个 巡逻路径蓝图,其中包含一个 数组变量的向量数值,AI可以在这些向量之间移动。

    • 提示:循环并获取数组中的下一个条目,从而在各个位置之间移动AI,然后根据数组中的当前条目更新 黑板键 的数值 巡逻位置(PatrolLocation)

  • 当玩家走出视线后,将AI移动到玩家最后已知的位置,而不是获取玩家的当前位置。

    • 提示:在 转向和追逐玩家(Turning and Chasing the Player) 的 移动至(Move To) 命令中,你需要指定一个位置,而不是使用 EnemyActor,因为它当前被设为获取 EnemyActor 的位置(它会获取玩家的当前位置)。

  • 当AI追上玩家后,为其增加攻击功能。

    • 提示:添加一个新的 合成 节点作为 转向和追逐玩家(Turning and Chasing the Player) 的 后台任务(Background Task)。当AI追上玩家后,为其增加攻击动画的 任务 节点。

相关文章:

UE4_AI_行为树_行为树快速入门指南

声明:学习笔记。 在 行为树快速入门指南 中,你将学会如何创建一个敌方AI,该AI看到玩家后会做出反应并展开追逐。当玩家离开视线后,AI将在几秒钟后(这可根据你的需求进行调整)放弃追逐,并在场景中…...

c++ 面试100个题目中的编程题目

88、下列程序的运行结果是? #include <stdlib.h> #include <stdio.h> #include <string.h> #include <iostream> const char* str = "vermeer"; using namespace std; int main(){ const char* pstr = str;cout << "The add…...

C++初阶:类与对象(尾篇)

目录 1. 构造函数与初始化列表1.1 对象的创建与构造函数的初始化1.2 初始化列表及构造函数存在的意义1.3 explicit关键字与构造函数的类型转换 2. static成员变量与static成员函数2.1 static成员变量2.2 static成员函数 3. 日期类流插入操作符的重载与友元3.1 友元3.2 友元函数…...

Spring状态机简单实现

一、什么是状态机 状态机&#xff0c;又称有限状态自动机&#xff0c;是表示有限个状态以及在这些状态之间的转移和动作等行为的计算模型。状态机的概念其实可以应用的各种领域&#xff0c;包括电子工程、语言学、哲学、生物学、数学和逻辑学等&#xff0c;例如日常生活中的电…...

WebServer -- 面试题(下)

&#x1f442; 夏风 - Gifty - 单曲 - 网易云音乐 目录 &#x1f33c;前言 &#x1f382;面试题(下) 4&#xff09;HTTP报文解析 为什么要用状态机 状态转移图画一下 https 协议为什么安全 https 的 ssl 连接过程 GET 和 POST 的区别 5&#xff09;数据库注册登录 登…...

企业微信如何接入第三方应用?

1.登录企业微信管理后台&#xff1a;https://work.weixin.qq.com/wework_admin​​​​​ 2.点击创建应用&#xff1b; ​​​​​​​ 3. 此时可以看到已经创建好的应用&#xff0c;并且生成应用的唯一id&#xff08;agentId&#xff09; 4. 第三方应用申请域名 (举例&…...

JAVA后端编码的主键字段存储为什么倾向于使用雪花算法

1.背景 最近有人问&#xff0c;什么是雪花算法&#xff0c;为什么使用雪花算法不使用数据库UUID&#xff0c;基于此&#xff0c;写一个说明。 2.简介 &#xff08;1&#xff09;雪花算法&#xff0c;英文名为snowflake&#xff0c;翻译过来就是是雪花&#xff0c;所以叫雪花…...

Rust 深度学习库 Burn

一、概述 Burn 它是一个新的综合动态深度学习框架&#xff0c;使用 Rust 构建的&#xff0c;以极高的灵活性、计算效率和可移植性作为其主要目标。 Rust Burn 是一个以灵活性、高性能和易用性为核心设计原则工具&#xff0c;主打就是灵活性 、高性能 及易用性。 二、Rust B…...

C语言-存储期2.0

静态存储期 在数据段中分配的变量&#xff0c;统统拥有静态存储期&#xff0c;因此也都被称为静态变量。这里静态的含义&#xff0c;指的是这些变量的不会因为程序的运行而发生临时性的分配和释放&#xff0c;它们的生命周期是恒定的&#xff0c;跟整个程序一致。 静态变量包含…...

计算机网络面经八股-HTTP请求报文和响应报文的格式?

请求报文格式&#xff1a; 请求行&#xff08;请求方法URI协议版本&#xff09;请求头部空行请求主体 请求行&#xff1a;GET /sample.jsp HTTP/1.1 表示使用 GET 方法请求 /sample.jsp 资源&#xff0c;并使用 HTTP/1.1 协议。请求头部&#xff1a;包含多个字段&#xff0c;…...

Ubuntu 18.04安装最新版Visual Studio Code(VS Code)报依赖库版本过低错误

Ubuntu 18.04安装最新版Visual Studio Code&#xff08;VS Code&#xff09;报依赖库版本过低错误 1. 问题描述2. 解决方案2.1 修复之前安装的错误2.2 安装VS Code 1.85.2 3. 原因分析 1. 问题描述 在Ubuntu 18.04系统上安装VS Code ≥ v1.86.2&#xff08;测试到v1.87.1&…...

Android NDK入门:在应用中加入C和C++的力量

目录 ​编辑 引 NDK的设计目的 与Java/Kotlin的结合 使用场景 开发流程 设置项目以支持NDK 编写本地代码 使用JNI连接本地代码和Java/Kotlin代码 编译和运行你的应用 附 引 自诩方向是android方向的移动端开发工程师&#xff0c;却从来没有真正仔细了解过NDK&#…...

2024年华为OD机试真题-田忌赛马-Java-OD统一考试(C卷)

题目描述: 给定两个只包含数字的数组a,b,调整数组 a 里面数字的顺序,使得尽可能多的 a[i] >b[i]。数组 a和 b 中的数字各不相同。 输出所有可以达到最优结果的 a 数组的数量 输入描述: 输入的第一行是数组 a 中的数字,其中只包含数字,每两个数字之间相隔一个空格,a…...

C++ 网络编程学习五

C网络编程学习五 网络结构的更新单例模式懒汉单例模式饿汉单例模式懒汉式指针智能指针设计单例类 服务器优雅退出asio的多线程模型IOServiceasio多线程IOThreadPoolepoll 和 iocp的一些知识点 网络结构的更新 asio网络层&#xff0c;会使用io_context进行数据封装&#xff0c;…...

案例分析篇05:数据库设计相关28个考点(9~16)(2024年软考高级系统架构设计师冲刺知识点总结系列文章)

专栏系列文章推荐: 2024高级系统架构设计师备考资料(高频考点&真题&经验)https://blog.csdn.net/seeker1994/category_12593400.html 【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】(2024年软考高级系统架构设计师冲刺知识点总结-案例分析篇-…...

pip 和conda 更换镜像源介绍

1、前言 很多深度学习的项目免不了安装库文件、配置环境等等&#xff0c;如果利用官方提供的连接&#xff0c;网速很慢&#xff0c;而且很容易download掉。 所以配置好了虚拟环境&#xff0c;将pip换源属实重要 常见的国内镜像源有清华、中科大、阿里等等... 这里建议用中科…...

Git概述及安装步骤

一、Git简介 Git是一个免费的、开源的分布式版本控制系统&#xff0c;可以快速高效地处理从小型到大型的各种项目。Git 易于学习&#xff0c;占地面积小&#xff0c;性能极快。它具有廉价的本地库&#xff0c;方便的暂存区域和多个工作流分支等特性。其性能优于Subversion、CV…...

北京保险服务中心携手镜舟科技,助推新能源车险市场规范化

2022 年&#xff0c;一辆新能源汽车在泥泞的小路上不慎拖底&#xff0c;动力电池底壳受损&#xff0c;电池电量低。车主向保险公司报案&#xff0c;希望能够得到赔偿。然而&#xff0c;在定损过程中&#xff0c;保司发现这辆车的电池故障并非由拖底事件引起&#xff0c;而是由于…...

给女朋友的浪漫微信消息推送超详细版

1. 下载代码 地址&#xff1a; 链接: https://pan.baidu.com/s/1lESgRoWn8bXyE0jsSVCHqQ?pwdimr6 提取码: imr6 根据 resources/db 下sql文件创建表 修改yml文件中数据库连接 2. 进入微信测试平台 地址&#xff1a;微信公众平台 扫码登录获取测试号信息 修改代…...

Android开发 Activity启动模式、ViewModel与LiveData,及Kotlin Coroutines

目录 Activity启动模式 onNewIntent解释 Activity启动模式的考虑时机 Service启动模式 ContentProvider的作用 Broadcast的注册方式 AsyncTask的作用 ViewModel LiveData Kotlin Coroutines 结合使用 Activity启动模式 Android中Activity的启动模式有四种&#xff0…...

MQL语言实现抽象工厂模式

文章目录 一、定义抽象产品接口二、定义抽象工厂接口三、定义具体产品四、定义具体工厂五、定义工厂客户端六、客户端调用工厂客户端七、抽象工厂模式的结构 一、定义抽象产品接口 //------------------------------------------------------------------ //| participants …...

UE4开个头-简易小汽车

跟着谌嘉诚学的小Demo&#xff0c;记录一下 主要涉及到小白人上下车和镜头切换操作 1、动态演示效果 2、静态展示图片 3、蓝图-上下车...

Java基础入门day04

day04 包 包可以用来区分相同的类名 将相同的类放在不同包下&#xff0c;可以进行存储 一个目录下没有办法存在两个同名的文件 包最终在文件系统中与文件目录结构是一一对应的 在不同包下可以存放相同类名的文件 包后期还可以实现项目中模块的精确划分&#xff0c;controller,…...

中值定理j

f ( n ) ( ξ ) 0 f^{(n)}(\xi)0 f(n)(ξ)0...

第2篇【Docker项目实战】使用Docker部署Raneto知识库平台(转载)

【Docker项目实战】使用Docker部署Raneto知识库平台 一、Raneto介绍 1.1 Raneto简介 Raneto是一个免费、开放、简单的 Markdown 支持的 Node.js 知识库。 1.2 知识库介绍 知识库 知识库是指存储和组织知识的系统或库&#xff0c;它包括了各种类型的信息和知识&#xff0c;如…...

【Javascript】 Promise 对象(二)

【Javascript】 Promise 对象&#xff08;一&#xff09;-CSDN博客 Promise.all() Promise.all()方法用于将多个 Promise 实例&#xff0c;包装成一个新的 Promise 实例。 const p Promise.all([p1, p2, p3]);上面代码中&#xff0c;Promise.all()方法接受一个数组作为参数&…...

细说C++反向迭代器:原理与用法

文章目录 一、引言二、反向迭代器的原理与实现细节三、模拟实现C反向迭代器反向迭代器模板类的设计反向迭代器的使用示例与测试 一、引言 迭代器与反向迭代器的概念引入 迭代器&#xff08;Iterator&#xff09;是C标准模板库&#xff08;STL&#xff09;中的一个核心概念&am…...

SpringBoot(依赖管理和自动配置)

文章目录 1.基本介绍1.springboot是什么&#xff1f;2.快速入门1.需求分析2.环境配置1.确认开发环境2.创建一个maven项目3.依赖配置 pom.xml4.文件目录5.MainApp.java &#xff08;启动类&#xff0c;常规配置&#xff09;6.HelloController.java &#xff08;测试Controller&a…...

cad怎么转换成黑白的pdf图纸?分享3个常用的软件!

在工程设计、建筑、机械制造等领域&#xff0c;CAD图纸的应用非常广泛。然而&#xff0c;有时出于某些需要&#xff0c;我们可能需要将CAD图纸转换为黑白的PDF格式。那么&#xff0c;如何实现这一转换呢&#xff1f;本文将为您详细介绍几种常用的转换软件及其操作步骤。 迅捷CA…...

maven本地仓库依赖上传到远程仓库

本地仓库上传到远程仓库 批量上传&#xff1a; 批量本地仓库依赖&#xff08;jar包&#xff09;上传脚本&#xff1a; #!/bin/bash # copy and run this script to the root of the repository directory containing files # this script attempts to exclude uploading itse…...